diff --git a/resources/ui_layout/default/extruder.ui b/resources/ui_layout/default/extruder.ui index e7db8c7e341..0babd70f4be 100644 --- a/resources/ui_layout/default/extruder.ui +++ b/resources/ui_layout/default/extruder.ui @@ -11,7 +11,7 @@ group:Offsets (for multi-extruder printers) setting:idx:extruder_offset setting:idx:extruder_temperature_offset setting:idx:extruder_fan_offset - extruder_extrusion_multiplier_speed + setting:idx:extruder_extrusion_multiplier_speed group:Retraction setting:idx:retract_length setting:idx:retract_lift diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index ea3a565f88d..96f4fa6d95c 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -238,6 +238,10 @@ group:Advanced Infill options setting:label_width$8:width$5:fill_smooth_distribution setting:label_width$26:label$Spacing between ironing lines:width$5:sidetext_width$7:fill_smooth_width end_line + line:Small Area Infill Flow Compensation (beta) + setting:label$_:sidetext_width$0:small_area_infill_flow_compensation + setting:label$_:small_area_infill_flow_compensation_model + end_line group:title_width$19:Ironing post-process (This will go on top of infills and perimeters) line:Enable ironing post-process setting:label$_:sidetext_width$0:ironing @@ -499,7 +503,10 @@ group:Wipe tower setting:wipe_tower_rotation_angle setting:wipe_tower_bridging setting:wipe_tower_no_sparse_layers - setting:single_extruder_multi_material_priming + line:Priming + setting:single_extruder_multi_material_priming + setting:priming_position + end_line group:Advanced setting:interface_shells setting:mmu_segmented_region_max_width diff --git a/resources/ui_layout/example/print.ui b/resources/ui_layout/example/print.ui index 5d90c54bdaf..228c58d59e8 100644 --- a/resources/ui_layout/example/print.ui +++ b/resources/ui_layout/example/print.ui @@ -214,6 +214,10 @@ group:Advanced Infill options setting:label_width$8:width$5:fill_smooth_distribution setting:label_width$26:label$Spacing between ironing lines:width$5:sidetext_width$7:fill_smooth_width end_line + line:Small Area Infill Flow Compensation (beta) + setting:label$_:sidetext_width$0:small_area_infill_flow_compensation + setting:label_:small_area_infill_flow_compensation_model + end_line group:title_width$19:Ironing post-process (This will go on top of infills and perimeters) line:Enable ironing post-process setting:label$_:sidetext_width$0:ironing diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index a342f208878..6fd62ddbab8 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -118,7 +118,7 @@ int CLI::run(int argc, char **argv) boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), GCODEVIEWER_APP_CMD); #endif // _WIN32 - const std::vector &load_configs = m_config.option("load", true)->values; + const std::vector &load_configs = m_config.option("load", true)->get_values(); const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option>("config_compatibility", true)->value; // load config files supplied via --load @@ -245,7 +245,7 @@ int CLI::run(int argc, char **argv) // and not the fff defaults. double w = sla_print_config.display_width.get_float(); double h = sla_print_config.display_height.get_float(); - sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }; + sla_print_config.bed_shape.set({ Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }); sla_print_config.apply(m_print_config, true); m_print_config.apply(sla_print_config, true); @@ -294,7 +294,7 @@ int CLI::run(int argc, char **argv) } } else if (opt_key == "duplicate_grid") { - std::vector &ints = m_config.option("duplicate_grid")->values; + const std::vector &ints = m_config.option("duplicate_grid")->get_values(); const int x = ints.size() > 0 ? ints.at(0) : 1; const int y = ints.size() > 1 ? ints.at(1) : 1; const double distance = fff_print_config.duplicate_distance.value; diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index fd43bf3d2d1..28bc825d527 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -45,7 +45,7 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_spacing , print_object_config(print_object_config) { assert(!print_config.nozzle_diameter.empty()); - this->min_nozzle_diameter = float(*std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end())); + this->min_nozzle_diameter = float(*std::min_element(print_config.nozzle_diameter.get_values().begin(), print_config.nozzle_diameter.get_values().end())); if (const auto &min_feature_size_opt = print_object_config.min_feature_size; min_feature_size_opt.percent) this->min_feature_size = scaled(min_feature_size_opt.value * 0.01 * this->min_nozzle_diameter); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 3e5ffe18195..2ee14d3b07f 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -96,11 +96,17 @@ std::string escape_string_cstyle(const std::string &str) std::string escape_strings_cstyle(const std::vector &strs) { + return escape_strings_cstyle(strs, {}); +} + +std::string escape_strings_cstyle(const std::vector &strs, const std::vector &enables) +{ + assert(strs.size() == enables.size() || enables.empty()); // 1) Estimate the output buffer size to avoid buffer reallocation. size_t outbuflen = 0; for (size_t i = 0; i < strs.size(); ++ i) - // Reserve space for every character escaped + quotes + semicolon. - outbuflen += strs[i].size() * 2 + 3; + // Reserve space for every character escaped + quotes + semicolon + enable. + outbuflen += strs[i].size() * 2 + ((enables.empty() || enables[i]) ? 3 : 4); // 2) Fill in the buffer. std::vector out(outbuflen, 0); char *outptr = out.data(); @@ -108,6 +114,8 @@ std::string escape_strings_cstyle(const std::vector &strs) if (j > 0) // Separate the strings. (*outptr ++) = ';'; + if (!(enables.empty() || enables[j])) + (*outptr ++) = '!'; const std::string &str = strs[j]; // Is the string simple or complex? Complex string contains spaces, tabs, new lines and other // escapable characters. Empty string shall be quoted as well, if it is the only string in strs. @@ -168,7 +176,12 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out) return true; } -bool unescape_strings_cstyle(const std::string &str, std::vector &out) +bool unescape_strings_cstyle(const std::string &str, std::vector &out_values) +{ + std::vector useless; + return unescape_strings_cstyle(str, out_values, useless); +} +bool unescape_strings_cstyle(const std::string &str, std::vector &out_values, std::vector &out_enables) { if (str.empty()) return true; @@ -182,6 +195,11 @@ bool unescape_strings_cstyle(const std::string &str, std::vector &o return true; c = str[i]; } + bool enable = true; + if (c == '!') { + enable = false; + c = str[++i]; + } // Start of a word. std::vector buf; buf.reserve(16); @@ -218,7 +236,8 @@ bool unescape_strings_cstyle(const std::string &str, std::vector &o } } // Store the string into the output vector. - out.push_back(std::string(buf.data(), buf.size())); + out_values.push_back(std::string(buf.data(), buf.size())); + out_enables.push_back(enable); if (i == str.size()) return true; // Skip white spaces. @@ -233,7 +252,8 @@ bool unescape_strings_cstyle(const std::string &str, std::vector &o return false; if (++ i == str.size()) { // Emit one additional empty string. - out.push_back(std::string()); + out_values.push_back(std::string()); + out_enables.push_back(true); return true; } } @@ -256,6 +276,246 @@ std::string escape_ampersand(const std::string& str) return std::string(out.data(), outptr - out.data()); } +Pointfs GraphData::data() const +{ + assert(validate()); + return Pointfs(this->graph_points.begin() + this->begin_idx, this->graph_points.begin() + this->end_idx); +} + +size_t GraphData::data_size() const +{ + assert(validate()); + return this->end_idx - this->begin_idx; +} + +double GraphData::interpolate(double x_value) const{ + double y_value = 0.; + if (this->data_size() < 1) { + // nothing + } else if (this->graph_points.size() == 1 || this->graph_points.front().x() >= x_value) { + y_value = this->graph_points.front().y(); + } else if (this->graph_points.back().x() <= x_value) { + y_value = this->graph_points.back().y(); + } else { + // find first and second datapoint + for (size_t idx = this->begin_idx; idx < this->end_idx; ++idx) { + const auto &data_point = this->graph_points[idx]; + if (data_point.x() == x_value) { + // lucky point + y_value = data_point.y(); + break; + } else if (data_point.x() < x_value) { + // not yet, iterate + } else if (idx == 0) { + y_value = data_point.y(); + break; + } else { + // interpolate + const auto &data_point_before = this->graph_points[idx - 1]; + assert(data_point.x() > data_point_before.x()); + assert(data_point_before.x() < x_value); + assert(data_point.x() > x_value); + if (this->type == GraphData::GraphType::SQUARE) { + y_value = data_point_before.y(); + } else if (this->type == GraphData::GraphType::LINEAR) { + const double interval = data_point.x() - data_point_before.x(); + const double ratio_before = (x_value - data_point_before.x()) / interval; + double mult = data_point_before.y() * (1 - ratio_before) + data_point.y() * ratio_before; + y_value = mult; + } else if (this->type == GraphData::GraphType::SPLINE) { + // Cubic spline interpolation: see https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation#Methods + const bool boundary_first_derivative = true; // true - first derivative is 0 at the leftmost and + // rightmost point false - second ---- || ------- + // TODO: cache (if the caller use my cache). + const int N = end_idx - begin_idx - 1; // last point can be accessed as N, we have N+1 total points + std::vector diag(N + 1); + std::vector mu(N + 1); + std::vector lambda(N + 1); + std::vector h(N + 1); + std::vector rhs(N + 1); + + // let's fill in inner equations + for (int i = 1 + begin_idx; i <= N + begin_idx; ++i) h[i] = this->graph_points[i].x() - this->graph_points[i - 1].x(); + std::fill(diag.begin(), diag.end(), 2.f); + for (int i = 1 + begin_idx; i <= N + begin_idx - 1; ++i) { + mu[i] = h[i] / (h[i] + h[i + 1]); + lambda[i] = 1.f - mu[i]; + rhs[i] = 6 * (float(this->graph_points[i + 1].y() - this->graph_points[i].y()) / + (h[i + 1] * (this->graph_points[i + 1].x() - this->graph_points[i - 1].x())) - + float(this->graph_points[i].y() - this->graph_points[i - 1].y()) / + (h[i] * (this->graph_points[i + 1].x() - this->graph_points[i - 1].x()))); + } + + // now fill in the first and last equations, according to boundary conditions: + if (boundary_first_derivative) { + const float endpoints_derivative = 0; + lambda[0] = 1; + mu[N] = 1; + rhs[0] = (6.f / h[1]) * (float(this->graph_points[begin_idx].y() - this->graph_points[1 + begin_idx].y()) / + (this->graph_points[begin_idx].x() - this->graph_points[1 + begin_idx].x()) - endpoints_derivative); + rhs[N] = (6.f / h[N]) * (endpoints_derivative - float(this->graph_points[N + begin_idx - 1].y() - this->graph_points[N + begin_idx].y()) / + (this->graph_points[N + begin_idx - 1].x() - this->graph_points[N + begin_idx].x())); + } else { + lambda[0] = 0; + mu[N] = 0; + rhs[0] = 0; + rhs[N] = 0; + } + + // the trilinear system is ready to be solved: + for (int i = 1; i <= N; ++i) { + float multiple = mu[i] / diag[i - 1]; // let's subtract proper multiple of above equation + diag[i] -= multiple * lambda[i - 1]; + rhs[i] -= multiple * rhs[i - 1]; + } + // now the back substitution (vector mu contains invalid values from now on): + rhs[N] = rhs[N] / diag[N]; + for (int i = N - 1; i >= 0; --i) rhs[i] = (rhs[i] - lambda[i] * rhs[i + 1]) / diag[i]; + + //now interpolate at our point + size_t curr_idx = idx - begin_idx; + y_value = (rhs[curr_idx - 1] * pow(this->graph_points[idx].x() - x_value, 3) + + rhs[curr_idx] * pow(x_value - this->graph_points[idx - 1].x(), 3)) / + (6 * h[curr_idx]) + + (this->graph_points[idx - 1].y() - rhs[curr_idx - 1] * h[curr_idx] * h[curr_idx] / 6.f) * + (this->graph_points[idx].x() - x_value) / h[curr_idx] + + (this->graph_points[idx].y() - rhs[curr_idx] * h[curr_idx] * h[curr_idx] / 6.f) * + (x_value - this->graph_points[idx - 1].x()) / h[curr_idx]; + } else { + assert(false); + } + break; + } + } + } + return y_value; +} + +bool GraphData::validate() const +{ + if (this->begin_idx < 0 || this->end_idx < 0 || this->end_idx < this->begin_idx) + return false; + if (this->end_idx > this->graph_points.size() && !this->graph_points.empty()) + return false; + if(this->graph_points.empty()) + return this->end_idx == 0 && this->begin_idx == 0; + for (size_t i = 1; i < this->graph_points.size(); ++i) + if (this->graph_points[i - 1].x() > this->graph_points[i].x()) + return false; + return true; +} + +std::string GraphData::serialize() const +{ + std::ostringstream ss; + ss << this->begin_idx; + ss << ":"; + ss << this->end_idx; + ss << ":"; + ss << uint16_t(this->type); + for (const Vec2d &graph_point : this->graph_points) { + ss << ":"; + ss << graph_point.x(); + ss << "x"; + ss << graph_point.y(); + } + return ss.str(); +} + +bool GraphData::deserialize(const std::string &str) +{ + if (size_t pos = str.find('|'); pos != std::string::npos) { + // old format + assert(str.size() > pos + 2); + assert(str[pos+1] == ' '); + assert(str[pos+2] != ' '); + if (str.size() > pos + 1) { + std::string buttons = str.substr(pos + 2); + size_t start = 0; + size_t end_x = buttons.find(' ', start); + size_t end_y= buttons.find(' ', end_x + 1); + while (end_x != std::string::npos && end_y != std::string::npos) { + this->graph_points.emplace_back(); + Vec2d &data_point = this->graph_points.back(); + data_point.x() = std::stod(buttons.substr(start, end_x)); + data_point.y() = std::stod(buttons.substr(end_x + 1, end_y)); + start = end_y + 1; + end_x = buttons.find(' ', start); + end_y= buttons.find(' ', end_x + 1); + } + if (end_x != std::string::npos && end_x + 1 < buttons.size()) { + this->graph_points.emplace_back(); + Vec2d &data_point = this->graph_points.back(); + data_point.x() = std::stod(buttons.substr(start, end_x)); + data_point.y() = std::stod(buttons.substr(end_x + 1, buttons.size())); + } + } + this->begin_idx = 0; + this->end_idx = this->graph_points.size(); + this->type = GraphType::SPLINE; + } else if (size_t pos = str.find(','); pos != std::string::npos) { + //maybe a coStrings with 0,0 values inside, like a coPoints but worse (used by orca's small_area_infill_flow_compensation_model) + std::vector args; + boost::split(args, str, boost::is_any_of(",")); + if (args.size() % 2 == 0) { + for (size_t i = 0; i < args.size(); i += 2) { + this->graph_points.emplace_back(); + Vec2d &data_point = this->graph_points.back(); + args[i].erase(std::remove(args[i].begin(), args[i].end(), '\n'), args[i].end()); + args[i].erase(std::remove(args[i].begin(), args[i].end(), '"'), args[i].end()); + data_point.x() = std::stod(args[i]); + args[i+1].erase(std::remove(args[i+1].begin(), args[i+1].end(), '\n'), args[i+1].end()); + args[i+1].erase(std::remove(args[i+1].begin(), args[i+1].end(), '"'), args[i+1].end()); + data_point.y() = std::stod(args[i+1]); + } + } + this->begin_idx = 0; + this->end_idx = this->graph_points.size(); + this->type = GraphType::SPLINE; + } else { + std::istringstream iss(str); + std::string item; + char sep_point = 'x'; + char sep = ':'; + std::vector values_str; + // get begin_idx + if (std::getline(iss, item, sep)) { + std::istringstream(item) >> this->begin_idx; + } else + return false; + // get end_idx + if (std::getline(iss, item, sep)) { + std::istringstream(item) >> this->end_idx; + } else + return false; + // get type + if (std::getline(iss, item, sep)) { + uint16_t int_type; + std::istringstream(item) >> int_type; + this->type = GraphType(int_type); + } else + return false; + // get points + while (std::getline(iss, item, sep)) { + this->graph_points.emplace_back(); + Vec2d &data_point = this->graph_points.back(); + std::string s_point; + std::istringstream isspoint(item); + if (std::getline(isspoint, s_point, sep_point)) { + std::istringstream(s_point) >> data_point.x(); + } else + return false; + if (std::getline(isspoint, s_point, sep_point)) { + std::istringstream(s_point) >> data_point.y(); + } else + return false; + } + } + //check if data is okay + if (!this->validate()) return false; + return true; +} + void ConfigOptionDeleter::operator()(ConfigOption* p) { delete p; } @@ -307,6 +567,8 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coPoints: return new ConfigOptionPoints(); case coPoint3: return new ConfigOptionPoint3(); // case coPoint3s: return new ConfigOptionPoint3s(); + case coGraph: return new ConfigOptionGraph(); + case coGraphs: return new ConfigOptionGraphs(); case coBool: return new ConfigOptionBool(); case coBools: return new ConfigOptionBools(); case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); @@ -666,7 +928,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con if (optdef->default_value) { // Default value for vectors of booleans used in a "per extruder" context, thus the default contains just a single value. assert(dynamic_cast*>(optdef->default_value.get())); - auto &values = static_cast*>(optdef->default_value.get())->values; + const auto &values = static_cast*>(optdef->default_value.get())->get_values(); if (values.size() == 1 && values.front() == 1) default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToTrue; } @@ -800,8 +1062,8 @@ double ConfigBase::get_computed_value(const t_config_option_key &opt_key, int ex return vector_opt->get_float(idx); if (raw_opt->type() == coFloatsOrPercents) { const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast(raw_opt); - if (!opt_fl_per->values[idx].percent) - return opt_fl_per->values[idx].value; + if (!opt_fl_per->get_at(idx).percent) + return opt_fl_per->get_at(idx).value; const ConfigOptionDef* opt_def = this->get_option_def(opt_key); if (opt_def == nullptr) // maybe a placeholder? @@ -1439,7 +1701,7 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option // Vector values will be chained. Repeated use of a parameter will append the parameter or parameters // to the end of the value. if (opt_base->type() == coBools && value.empty()) - static_cast(opt_base)->values.push_back(!no); + static_cast(opt_base)->set_at(!no, opt_vector->size()); else // Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way // they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape @@ -1600,6 +1862,8 @@ CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercentsNullable) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionGraph) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionGraphs) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBoolsNullable) @@ -1612,12 +1876,14 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionS CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionFloat) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionFloats) @@ -1636,6 +1902,8 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionPoint) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionPoints) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionGraph) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionGraphs) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionBool) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionBools) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionBoolsNullable) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 20dd50e0ca8..b7c3215cdf6 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,116 @@ namespace Slic3r { inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; } inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); } inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (l.value == r.value && int(l.percent) < int(r.percent)); } + + struct GraphData + { + public: + enum GraphType : uint8_t { + SQUARE, + LINEAR, + SPLINE, + COUNT + }; + + GraphData() {} + GraphData(Pointfs graph_data) : graph_points(graph_data) { + begin_idx = 0; + end_idx = graph_data.size(); + } + GraphData(size_t start_idx, size_t stop_idx, Pointfs graph_data) + : graph_points(graph_data), begin_idx(start_idx), end_idx(stop_idx) {} + GraphData(size_t start_idx, size_t stop_idx, GraphType graph_type, Pointfs graph_data) + : graph_points(graph_data), begin_idx(start_idx), end_idx(stop_idx), type(graph_type) {} + + bool operator==(const GraphData &rhs) const throw() { return this->data_size() == rhs.data_size() && this->data() == rhs.data() && this->type == rhs.type; } + bool operator!=(const GraphData &rhs) const throw() { return this->data_size() != rhs.data_size() || this->data() != rhs.data() || this->type != rhs.type; } + bool operator<(const GraphData &rhs) const throw() + { + if (this->data_size() == rhs.data_size()) { + const Pointfs my_data = this->data(); + const Pointfs other_data = rhs.data(); + assert(my_data.size() == other_data.size()); + auto it_this = my_data.begin(); + auto it_other = other_data.begin(); + while (it_this != my_data.end()) { + if(it_this->x() != it_other->x()) + return it_this->x() < it_other->x(); + if(it_this->y() != it_other->y()) + return it_this->y() < it_other->y(); + ++it_this; + ++it_other; + } + return this->type < rhs.type; + } + return this->data_size() < rhs.data_size(); + } + bool operator>(const GraphData &rhs) const throw() + { + if (this->data_size() == rhs.data_size()) { + const Pointfs my_data = this->data(); + const Pointfs other_data = rhs.data(); + assert(my_data.size() == other_data.size()); + auto it_this = my_data.begin(); + auto it_other = other_data.begin(); + while (it_this != my_data.end()) { + if(it_this->x() != it_other->x()) + return it_this->x() > it_other->x(); + if(it_this->y() != it_other->y()) + return it_this->y() > it_other->y(); + ++it_this; + ++it_other; + } + return this->type > rhs.type; + } + return this->data_size() > rhs.data_size(); + } + + // data is the useable part of the graph + Pointfs data() const; + size_t data_size() const; + + double interpolate(double x_value) const; + + //return false if data are not good + bool validate() const; + + //protected: + Pointfs graph_points; + size_t begin_idx = 0; //included + size_t end_idx = 0; //excluded + GraphType type = GraphType::LINEAR; + + std::string serialize() const; + bool deserialize(const std::string &str); + + private: + friend class cereal::access; + template void serialize(Archive &ar) + { + ar(this->begin_idx); + ar(this->end_idx); + ar(this->type); + // does this works? + ar(this->graph_points); + } + }; + + struct GraphSettings + { + std::string title; + std::string description; + std::string y_label; + std::string x_label; + std::string null_label; + double min_x, max_x, step_x; + double min_y, max_y, step_y; + std::string label_min_x; + std::string label_max_x; + std::string label_min_y; + std::string label_max_y; + std::vector allowed_types; + GraphData reset_vals; + }; } namespace std { @@ -49,6 +160,20 @@ namespace std { return v.percent ? seed ^ 0x9e3779b9 : seed; } }; + + template<> struct hash { + std::size_t operator()(const Slic3r::GraphData& v) const noexcept { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash{}(v.begin_idx)); + boost::hash_combine(seed, std::hash{}(v.end_idx)); + boost::hash_combine(seed, std::hash{}(v.type)); + for (const auto &pt : v.graph_points) { + boost::hash_combine(seed, std::hash{}(pt.x())); + boost::hash_combine(seed, std::hash{}(pt.y())); + } + return seed; + } + }; template<> struct hash { std::size_t operator()(const Slic3r::Vec2d& v) const noexcept { @@ -76,8 +201,10 @@ typedef std::vector t_config_option_keys; extern std::string escape_string_cstyle(const std::string &str); extern std::string escape_strings_cstyle(const std::vector &strs); +extern std::string escape_strings_cstyle(const std::vector &strs, const std::vector &enables); extern bool unescape_string_cstyle(const std::string &str, std::string &out); -extern bool unescape_strings_cstyle(const std::string &str, std::vector &out); +extern bool unescape_strings_cstyle(const std::string &str, std::vector &out_values); +extern bool unescape_strings_cstyle(const std::string &str, std::vector &out_values, std::vector &out_enables); extern std::string escape_ampersand(const std::string& str); @@ -212,7 +339,7 @@ class BadOptionValueException : public ConfigurationError // Type of a configuration value. enum ConfigOptionType : uint16_t{ - coVectorType = 0x4000, + coVectorType = 0x4000, // 16384 coNone = 0, // single float coFloat = 1, @@ -246,6 +373,10 @@ enum ConfigOptionType : uint16_t{ coBools = coBool + coVectorType, // a generic enum coEnum = 9, + // a graph of double->double + coGraph = 10, + // a vector of graph of double->double + coGraphs = coGraph + coVectorType, }; enum ConfigOptionMode : uint64_t { @@ -393,15 +524,16 @@ class ConfigOption { // FCO_PHONY: if true, this option doesn't need to be saved (or with empty string), it's a computed value from an other ConfigOption. // FCO_EXTRUDER_ARRAY: set if the ConfigDef has is_extruder_size(). Only apply to ConfigVectorBase and childs // FCO_PLACEHOLDER_TEMP: for PlaceholderParser, to be able to recognise temporary fake ConfigOption (for default_XXX() macro) + // FCO_ENABLED: to see if this option is activated or disabled ( same as 0 or -1 value in the old way) (for single-value options only) uint32_t flags; enum FlagsConfigOption : uint32_t { FCO_PHONY = 1, FCO_EXTRUDER_ARRAY = 1 << 1, FCO_PLACEHOLDER_TEMP = 1 << 2, + FCO_ENABLED = 1 << 3, }; - ConfigOption() : flags(uint32_t(0)) {} - ConfigOption(bool phony) : flags(phony ? uint32_t(FlagsConfigOption::FCO_PHONY) : uint32_t(0)) {} + ConfigOption() : flags(uint32_t(FCO_ENABLED)) { assert(this->flags != 0); } virtual ~ConfigOption() {} @@ -416,8 +548,17 @@ class ConfigOption { virtual bool is_percent(size_t idx = 0) const { return false; } virtual bool get_bool(size_t idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_bool on a non-boolean ConfigOption"); } virtual void set_enum_int(int32_t /* val */) { throw BadOptionTypeException("Calling ConfigOption::set_enum_int on a non-enum ConfigOption"); } - virtual boost::any get_any(int32_t idx = -1) const { throw BadOptionTypeException("Calling ConfigOption::get_any on a raw ConfigOption"); } + virtual boost::any get_any(int32_t idx = -1) const { throw BadOptionTypeException("Calling ConfigOption::get_any on a raw ConfigOption"); } virtual void set_any(boost::any, int32_t idx = -1) { throw BadOptionTypeException("Calling ConfigOption::set_any on a raw ConfigOption"); } + virtual bool is_enabled(size_t idx = -1) const { return (flags & FCO_ENABLED) != 0; } + virtual ConfigOption *set_enabled(bool enabled, size_t idx = -1) + { + if (enabled) + this->flags |= FCO_ENABLED; + else + this->flags &= uint32_t(0xFF ^ FCO_ENABLED); + return this; + } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } virtual size_t hash() const throw() = 0; @@ -428,15 +569,15 @@ class ConfigOption { // A scalar is nil, or all values of a vector are nil if idx < 0. virtual bool is_nil(int32_t idx = -1) const { return false; } bool is_phony() const { return (flags & FCO_PHONY) != 0; } - void set_phony(bool phony) { if (phony) this->flags |= FCO_PHONY; else this->flags &= uint8_t(0xFF ^ FCO_PHONY); } + ConfigOption* set_phony(bool phony) { if (phony) this->flags |= FCO_PHONY; else this->flags &= uint8_t(0xFF ^ FCO_PHONY); return this; } // Is this option overridden by another option? // An option overrides another option if it is not nil and not equal. - virtual bool overriden_by(const ConfigOption *rhs) const { + virtual bool overriden_by(const ConfigOption *rhs) const { assert(! this->nullable() && ! rhs->nullable()); - return *this != *rhs; + return *this != *rhs && this->is_enabled() == rhs->is_enabled(); } // Apply an override option, possibly a nullable one. - virtual bool apply_override(const ConfigOption *rhs) { + virtual bool apply_override(const ConfigOption *rhs) { if (*this == *rhs) return false; *this = *rhs; @@ -456,7 +597,6 @@ class ConfigOptionSingle : public ConfigOption { public: T value; explicit ConfigOptionSingle(T value) : value(value) {} - explicit ConfigOptionSingle(T value, bool phony) : ConfigOption(phony), value(value) {} operator T() const { return this->value; } boost::any get_any(int32_t idx = -1) const override { return boost::any(value); } void set_any(boost::any anyval, int32_t idx = -1) override { value = boost::any_cast(anyval); } @@ -475,14 +615,23 @@ class ConfigOptionSingle : public ConfigOption { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); - return this->value == static_cast*>(&rhs)->value; + return this->value == static_cast*>(&rhs)->value + && this->is_enabled() == rhs.is_enabled() + && this->is_phony() == rhs.is_phony(); + // should compare all flags? } bool operator==(const T &rhs) const throw() { return this->value == rhs; } bool operator!=(const T &rhs) const throw() { return this->value != rhs; } bool operator< (const T &rhs) const throw() { return this->value < rhs; } - size_t hash() const throw() override { return std::hash{}(this->value); } + size_t hash() const throw() override { + std::hash hasher; + size_t seed = 0; + boost::hash_combine(seed, this->is_enabled()); + boost::hash_combine(seed, hasher(this->value)); + return seed; + } private: friend class cereal::access; @@ -492,6 +641,8 @@ class ConfigOptionSingle : public ConfigOption { // Value of a vector valued option (bools, ints, floats, strings, points) class ConfigOptionVectorBase : public ConfigOption { public: + virtual std::string serialize() const override = 0; + virtual bool deserialize(const std::string &str, bool append = false) override = 0; // Currently used only to initialize the PlaceholderParser. virtual std::vector vserialize() const = 0; // Set from a vector of ConfigOptions. @@ -519,6 +670,35 @@ class ConfigOptionVectorBase : public ConfigOption { bool is_extruder_size() const { return (flags & FCO_EXTRUDER_ARRAY) != 0; } ConfigOptionVectorBase* set_is_extruder_size(bool is_extruder_size = true) { if (is_extruder_size) this->flags |= FCO_EXTRUDER_ARRAY; else this->flags &= uint8_t(0xFF ^ FCO_EXTRUDER_ARRAY); + assert(this->flags != 0); + return this; + } + + + bool is_enabled(size_t idx = 0) const override { + assert (m_enabled.size() == size()); + return idx >= 0 && idx < m_enabled.size() ? m_enabled[idx] : ConfigOption::is_enabled(); + } + + bool has_same_enabled(const ConfigOptionVectorBase &rhs) const + { + return this->m_enabled == rhs.m_enabled; + } + ConfigOption *set_enabled(bool enabled, size_t idx = 0) override + { + assert (m_enabled.size() == size()); + // reset evrything, use the default. + if (idx < 0) { + for(size_t i=0; im_enabled.size(); ++i) + this->m_enabled[i] = enabled; + ConfigOption::set_enabled(enabled); + return this; + } + // can't enable something that doesn't exist + if(idx >= size()) + return this; + // set our value + m_enabled[idx] = enabled; return this; } @@ -528,8 +708,23 @@ class ConfigOptionVectorBase : public ConfigOption { protected: + // Is the size of the vector, so every bit is set. + // If at least one is true, then the default (from configoption flag) is also true, else false. + std::vector m_enabled; // Used to verify type compatibility when assigning to / from a scalar ConfigOption. ConfigOptionType scalar_type() const { return static_cast(this->type() - coVectorType); } + + void set_default_enabled() + { + if (!m_enabled.empty()) { + bool has_enabled = false; + for (size_t i = 0; !has_enabled && i < m_enabled.size(); ++i) { has_enabled = m_enabled[i]; } + ConfigOption::set_enabled(has_enabled); + } + } + + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->flags); ar(this->m_enabled); } }; // Value of a vector valued option (bools, ints, floats, strings, points), template @@ -538,32 +733,36 @@ class ConfigOptionVector : public ConfigOptionVectorBase { private: void set_default_from_values() { - assert(!values.empty()); - if (!values.empty()) - default_value = values.front(); + assert(!m_values.empty()); + if (!m_values.empty()) + default_value = m_values.front(); } protected: // this default is used to fill this vector when resized. It's not the default of a setting, for it please use the - // ConfigOptionDef. + // ConfigOptionDef. It's not even serialized or put in the undo/redo. T default_value; + std::vector m_values; public: - std::vector values; ConfigOptionVector() {} - explicit ConfigOptionVector(const T& default_val) : default_value(default_val) {} - explicit ConfigOptionVector(size_t n, const T &value) : values(n, value), default_value(value) {} - explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) { set_default_from_values(); } - explicit ConfigOptionVector(const std::vector &values) : values(values) { set_default_from_values(); } - explicit ConfigOptionVector(std::vector &&values) : values(std::move(values)) { set_default_from_values(); } - + explicit ConfigOptionVector(const T& default_val) : default_value(default_val) { assert (m_enabled.size() == size()); } + explicit ConfigOptionVector(size_t n, const T &value) : m_values(n, value), default_value(value) { this->m_enabled.resize(m_values.size(), ConfigOption::is_enabled()); assert (m_enabled.size() == size()); } + explicit ConfigOptionVector(std::initializer_list il) : m_values(std::move(il)) { set_default_from_values(); this->m_enabled.resize(m_values.size(), ConfigOption::is_enabled()); assert (m_enabled.size() == size()); } + explicit ConfigOptionVector(const std::vector &values) : m_values(values) { set_default_from_values(); this->m_enabled.resize(m_values.size(), ConfigOption::is_enabled()); assert (m_enabled.size() == size()); } + explicit ConfigOptionVector(std::vector &&values) : m_values(std::move(values)) { set_default_from_values(); this->m_enabled.resize(m_values.size(), ConfigOption::is_enabled()); assert (m_enabled.size() == size()); } + + const std::vector &get_values() const { return m_values; } + void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); - this->values = static_cast*>(rhs)->values; + this->m_values = static_cast*>(rhs)->m_values; + this->m_enabled = static_cast*>(rhs)->m_enabled; this->flags = rhs->flags; + assert (m_enabled.size() == this->m_values.size()); } // Set from a vector of ConfigOptions. @@ -572,19 +771,44 @@ class ConfigOptionVector : public ConfigOptionVectorBase // This function is useful to collect values for multiple extrder / filament settings. void set(const std::vector &rhs) override { - this->values.clear(); - this->values.reserve(rhs.size()); + this->m_values.clear(); + this->m_values.reserve(rhs.size()); + this->m_enabled.clear(); + this->m_enabled.reserve(rhs.size()); for (const ConfigOption *opt : rhs) { if (opt->type() == this->type()) { + assert(dynamic_cast*>(opt) != nullptr); auto other = static_cast*>(opt); - if (other->values.empty()) + if (other->m_values.empty()) throw ConfigurationError("ConfigOptionVector::set(): Assigning from an empty vector"); - this->values.emplace_back(other->values.front()); - } else if (opt->type() == this->scalar_type()) - this->values.emplace_back(static_cast*>(opt)->value); + this->m_values.emplace_back(other->get_at(0)); + this->m_enabled.push_back(other->is_enabled(0)); + if (other->is_enabled(0)) + ConfigOption::set_enabled(true); + } else if (opt->type() == this->scalar_type()) { + this->m_values.emplace_back(static_cast *>(opt)->value); + this->m_enabled.push_back(opt->is_enabled()); + if (opt->is_enabled()) + ConfigOption::set_enabled(true); + } else throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type"); } + assert (m_enabled.size() == size()); + } + + // Set from a vector of values, all enabled (if default is enabled) + void set(const std::vector &rhs) + { + this->m_values.clear(); + this->m_values.reserve(rhs.size()); + this->m_enabled.clear(); + this->m_enabled.reserve(rhs.size()); + for (const T &val : rhs) { + this->m_values.push_back(val); + this->m_enabled.push_back(ConfigOption::is_enabled()); + } + assert (m_enabled.size() == size()); } // Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions. @@ -592,45 +816,60 @@ class ConfigOptionVector : public ConfigOptionVectorBase void set_at(const ConfigOption *rhs, size_t i, size_t j) override { // Fill with default value up to the needed position - if (this->values.size() <= i) { + if (this->m_values.size() <= i) { // Resize this vector, fill in the new vector fields with the copy of the first field. - this->values.resize(i + 1, this->default_value); + this->m_values.resize(i + 1, this->default_value); + this->m_enabled.resize(i + 1, ConfigOption::is_enabled()); } if (rhs->type() == this->type()) { // Assign the first value of the rhs vector. auto other = static_cast*>(rhs); - if (other->values.empty()) + if (other->empty()) throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector"); - this->values[i] = other->get_at(j); - } else if (rhs->type() == this->scalar_type()) - this->values[i] = static_cast*>(rhs)->value; + this->m_values[i] = other->get_at(j); + this->m_enabled[i] = other->is_enabled(j); + ConfigOption::set_enabled(other->is_enabled(-1)); + } else if (rhs->type() == this->scalar_type()) { + auto other = static_cast*>(rhs); + this->m_values[i] = other->value; + this->m_enabled[i] = other->is_enabled(); + set_default_enabled(); + } else throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type"); + assert (m_enabled.size() == size()); } void set_at(T val, size_t i) { // Fill with default value up to the needed position - if (this->values.size() <= i) { + if (this->m_values.size() <= i) { // Resize this vector, fill in the new vector fields with the copy of the first field. - this->values.resize(i + 1, this->default_value); + this->m_values.resize(i + 1, this->default_value); + this->m_enabled.resize(i + 1, ConfigOption::is_enabled()); } - this->values[i] = val; + this->m_values[i] = val; + assert (m_enabled.size() == size()); } const T& get_at(size_t i) const { - //assert(! this->values.empty()); - return (i < this->values.size()) ? this->values[i] : (this->values.empty()? default_value : this->values.front()); + //assert(! this->m_values.empty()); + assert (m_enabled.size() == size()); + return (i < this->m_values.size()) ? this->m_values[i] : + (this->m_values.empty() ? default_value : this->m_values.front()); } T& get_at(size_t i) { return const_cast(std::as_const(*this).get_at(i)); } - boost::any get_any(int32_t idx = -1) const override { return idx < 0 ? boost::any(values) : boost::any(get_at(idx)); } + boost::any get_any(int32_t idx = -1) const override { return idx < 0 ? boost::any(this->m_values) : boost::any(get_at(idx)); } void set_any(boost::any anyval, int32_t idx = -1) override { - if (idx < 0) - values = boost::any_cast>(anyval); - else - set_at(boost::any_cast(anyval), idx); + if (idx < 0) { + this->m_values = boost::any_cast>(anyval); + this->m_enabled.resize(this->m_values.size(), ConfigOption::is_enabled()); + } else { + set_at(boost::any_cast(anyval), idx); + } + assert (m_enabled.size() == size()); } // Resize this vector by duplicating the /*last*/first or default value. @@ -638,33 +877,57 @@ class ConfigOptionVector : public ConfigOptionVectorBase void resize(size_t n, const ConfigOption *opt_default = nullptr) override { assert(opt_default == nullptr || opt_default->is_vector()); + assert(n >= 0); // assert(opt_default == nullptr || dynamic_cast>(opt_default)); - // assert(! this->values.empty() || opt_default != nullptr); - if (n == 0) - this->values.clear(); - else if (n < this->values.size()) - this->values.erase(this->values.begin() + n, this->values.end()); - else if (n > this->values.size()) { - if (this->values.empty()) { + // assert(! this->m_values.empty() || opt_default != nullptr); + if (n == 0) { + this->m_values.clear(); + this->m_enabled.clear(); + } else if (n < this->m_values.size()) { + assert (this->m_enabled.size() == this->m_values.size()); + this->m_values.erase(this->m_values.begin() + n, this->m_values.end()); + this->m_enabled.erase(this->m_enabled.begin() + n, this->m_enabled.end()); + } else if (n > this->m_values.size()) { + if (this->m_values.empty()) { if (opt_default == nullptr) - this->values.resize(n, this->default_value); - if (opt_default->type() != this->type()) + this->m_values.resize(n, this->default_value); + else if (opt_default->type() != this->type()) throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type."); - if(auto other = static_cast*>(opt_default); other->values.empty()) - this->values.resize(n, other->default_value); + else if(auto other = static_cast*>(opt_default); other->m_values.empty()) + this->m_values.resize(n, other->default_value); else - this->values.resize(n, other->values.front()); + this->m_values.resize(n, other->get_at(0)); } else { - // Resize by duplicating the /*last*/first value. - this->values.resize(n, this->values./*back*/front()); + // Resize by duplicating the first value. + this->m_values.resize(n, this->get_at(0)); } + this->m_enabled.resize(n, ConfigOption::is_enabled()); + } + assert(m_enabled.size() == size()); + } + + // Resize this vector by duplicating the given value + void resize(size_t n, const T &resize_value) + { + assert(n >= 0); + if (n == 0) { + this->m_values.clear(); + this->m_enabled.clear(); + } else if (n < this->m_values.size()) { + assert(this->m_enabled.size() == this->m_values.size()); + this->m_values.erase(this->m_values.begin() + n, this->m_values.end()); + this->m_enabled.erase(this->m_enabled.begin() + n, this->m_enabled.end()); + } else if (n > this->m_values.size()) { + this->m_values.resize(n, resize_value); + this->m_enabled.resize(n, ConfigOption::is_enabled()); } + assert(m_enabled.size() == size()); } // Clear the values vector. - void clear() override { this->values.clear(); } - size_t size() const override { return this->values.size(); } - bool empty() const override { return this->values.empty(); } + void clear() override { this->m_values.clear(); this->m_enabled.clear(); } + size_t size() const override { return this->m_values.size(); } + bool empty() const override { return this->m_values.empty(); } // get the stored default value for filling empty vector. // If you use it, double check if you shouldn't instead use the ConfigOptionDef.defaultvalue, which is the default value of a setting. // currently, it's used to try to have a meaningful value for a Field if the default value is Nil @@ -675,22 +938,26 @@ class ConfigOptionVector : public ConfigOptionVectorBase if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionVector: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); - return this->values == static_cast*>(&rhs)->values; + return this->has_same_enabled(*static_cast *>(&rhs)) && + this->m_values == static_cast *>(&rhs)->m_values; } - bool operator==(const std::vector &rhs) const throw() { return this->values == rhs; } - bool operator!=(const std::vector &rhs) const throw() { return this->values != rhs; } + bool operator==(const std::vector &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->m_values == rhs; } + bool operator!=(const std::vector &rhs) const throw() { return this->is_enabled() != rhs.is_enabled() || this->m_values != rhs; } size_t hash() const throw() override { std::hash hasher; + std::hash hasher_b; size_t seed = 0; - for (const auto &v : this->values) + for (const auto &v : this->m_values) boost::hash_combine(seed, hasher(v)); + for (bool b : this->m_enabled) + boost::hash_combine(seed, hasher_b(b)); return seed; } // Is this option overridden by another option? - // An option overrides another option if it is not nil and not equal. + // An option overrides another option if it is not nil and not equal bool overriden_by(const ConfigOption *rhs) const override { if (this->nullable()) throw ConfigurationError("Cannot override a nullable ConfigOption."); @@ -699,11 +966,12 @@ class ConfigOptionVector : public ConfigOptionVectorBase auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) // Overridding a non-nullable object with another non-nullable object. - return this->values != rhs_vec->values; + return this->m_values != rhs_vec->m_values && this->m_enabled != rhs_vec->m_enabled; size_t i = 0; size_t cnt = std::min(this->size(), rhs_vec->size()); for (; i < cnt; ++ i) - if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i]) + if (! rhs_vec->is_nil(i) && + (this->m_values[i] != rhs_vec->m_values[i] || this->is_enabled(i) != rhs_vec->is_enabled(i))) return true; for (; i < rhs_vec->size(); ++ i) if (! rhs_vec->is_nil(i)) @@ -719,8 +987,9 @@ class ConfigOptionVector : public ConfigOptionVectorBase auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { // Overridding a non-nullable object with another non-nullable object. - if (this->values != rhs_vec->values) { - this->values = rhs_vec->values; + if (this->m_values != rhs_vec->m_values || this->m_enabled != rhs_vec->m_enabled) { + this->m_values = rhs_vec->m_values; + this->m_enabled = rhs_vec->m_enabled; return true; } return false; @@ -729,14 +998,16 @@ class ConfigOptionVector : public ConfigOptionVectorBase size_t cnt = std::min(this->size(), rhs_vec->size()); bool modified = false; for (; i < cnt; ++ i) - if (! rhs_vec->is_nil(i) && this->values[i] != rhs_vec->values[i]) { - this->values[i] = rhs_vec->values[i]; + if (! rhs_vec->is_nil(i) && this->m_values[i] != rhs_vec->m_values[i]) { + this->m_values[i] = rhs_vec->m_values[i]; + this->m_enabled[i] = rhs_vec->m_enabled[i]; modified = true; } for (; i < rhs_vec->size(); ++ i) if (! rhs_vec->is_nil(i)) { - this->values.resize(i + 1, this->default_value); - this->values[i] = rhs_vec->values[i]; + this->m_values.resize(i + 1, this->default_value); + this->m_values[i] = rhs_vec->m_values[i]; + this->m_enabled[i] = rhs_vec->m_enabled[i]; modified = true; } return modified; @@ -744,7 +1015,7 @@ class ConfigOptionVector : public ConfigOptionVectorBase private: friend class cereal::access; - template void serialize(Archive & ar) { ar(this->flags); ar(this->values); } + template void serialize(Archive & ar) { ar(this->m_values); ar(cereal::base_class(this)); } }; class ConfigOptionFloat : public ConfigOptionSingle @@ -752,18 +1023,19 @@ class ConfigOptionFloat : public ConfigOptionSingle public: ConfigOptionFloat() : ConfigOptionSingle(0) {} explicit ConfigOptionFloat(double _value) : ConfigOptionSingle(_value) {} - explicit ConfigOptionFloat(double _value, bool _phony) : ConfigOptionSingle(_value, _phony) {} static ConfigOptionType static_type() { return coFloat; } ConfigOptionType type() const override { return static_type(); } double get_float(size_t idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } - bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value; return ss.str(); } @@ -771,7 +1043,12 @@ class ConfigOptionFloat : public ConfigOptionSingle bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - std::istringstream iss(str); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + std::istringstream iss(this->is_enabled() ? str : str.substr(1)); iss >> this->value; return !iss.fail(); } @@ -800,29 +1077,36 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector static ConfigOptionType static_type() { return coFloats; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); } - bool operator==(const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); } - bool operator< (const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_lower(this->values, rhs.values); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionFloatsTempl(*this); } + bool operator==(const ConfigOptionFloatsTempl &rhs) const throw() + { + return this->m_enabled == rhs.m_enabled && vectors_equal(this->m_values, rhs.m_values); + } + bool operator<(const ConfigOptionFloatsTempl &rhs) const throw() + { + return this->m_enabled < rhs.m_enabled && vectors_lower(this->m_values, rhs.m_values); + } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); - return vectors_equal(this->values, static_cast*>(&rhs)->values); + return this->has_same_enabled(*static_cast *>(&rhs)) && + vectors_equal(this->m_values, static_cast *>(&rhs)->get_values()); } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } // A scalar is nil, or all values of a vector are nil. - bool is_nil(int32_t idx = -1) const override + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { - for (double v : this->values) + for (double v : this->m_values) if (!std::isnan(v) && v != NIL_VALUE()) return false; return true; } else { - return idx < int32_t(values.size()) ? (std::isnan(this->values[idx]) || NIL_VALUE() == this->values[idx]) : - values.empty() ? (std::isnan(this->default_value) || NIL_VALUE() == this->default_value) : - (std::isnan(this->values.front()) || NIL_VALUE() == this->values.front()); + return idx < int32_t(this->size()) ? (std::isnan(this->m_values[idx]) || NIL_VALUE() == this->m_values[idx]) : + this->empty() ? (std::isnan(this->default_value) || NIL_VALUE() == this->default_value) : + (std::isnan(this->m_values.front()) || NIL_VALUE() == this->m_values.front()); } } double get_float(size_t idx = 0) const override { return get_at(idx); } @@ -835,22 +1119,24 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector std::string serialize() const override { + assert(this->m_values.size() == this->m_enabled.size()); std::ostringstream ss; - for (const double &v : this->values) { - if (&v != &this->values.front()) + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + if (idx > 0) ss << ","; - serialize_single_value(ss, v); + this->serialize_single_value(ss, this->m_values[idx], this->m_enabled[idx]); } return ss.str(); } std::vector vserialize() const override { + assert(this->m_values.size() == this->m_enabled.size()); std::vector vv; - vv.reserve(this->values.size()); - for (const double v : this->values) { + vv.reserve(this->m_values.size()); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { std::ostringstream ss; - serialize_single_value(ss, v); + this->serialize_single_value(ss, this->m_values[idx], this->m_enabled[idx]); vv.push_back(ss.str()); } return vv; @@ -858,30 +1144,42 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector bool deserialize(const std::string &str, bool append = false) override { - if (! append) - this->values.clear(); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { boost::trim(item_str); + bool enabled = true; + if (!item_str.empty() && item_str.front() == '!') { + enabled = false; + item_str = item_str.substr(1); + boost::trim(item_str); + } if (item_str == NIL_STR_VALUE) { if (NULLABLE) - this->values.push_back(NIL_VALUE()); + this->m_values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); double value; iss >> value; - this->values.push_back(value); + this->m_values.push_back(value); } + this->m_enabled.push_back(enabled); } + set_default_enabled(); + assert(this->m_values.size() == this->m_enabled.size()); return true; } ConfigOptionFloatsTempl& operator=(const ConfigOption *opt) { this->set(opt); + assert(this->m_values.size() == this->m_enabled.size()); return *this; } @@ -889,16 +1187,18 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector // Special "nil" value to be stored into the vector if this->supports_nil(). //please use is_nil & create_nil, to better support nan static double NIL_VALUE() { return std::numeric_limits::quiet_NaN(); } - void serialize_single_value(std::ostringstream &ss, const double v) const { - if (std::isfinite(v)) - ss << v; - else if (std::isnan(v) || v == NIL_VALUE()) { - if (NULLABLE) - ss << NIL_STR_VALUE; - else - throw ConfigurationError("Serializing NaN"); - } else - throw ConfigurationError("Serializing invalid number"); + void serialize_single_value(std::ostringstream &ss, const double v, const bool enabled) const { + if (!enabled) + ss << "!"; + if (std::isfinite(v)) + ss << v; + else if (std::isnan(v) || v == NIL_VALUE()) { + if (NULLABLE) + ss << NIL_STR_VALUE; + else + throw ConfigurationError("Serializing NaN"); + } else + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -946,11 +1246,14 @@ class ConfigOptionInt : public ConfigOptionSingle int32_t get_int(size_t idx = 0) const override { return this->value; } double get_float(size_t idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } - bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; } + bool operator==(const ConfigOptionInt &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator<(const ConfigOptionInt &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value; return ss.str(); } @@ -958,7 +1261,12 @@ class ConfigOptionInt : public ConfigOptionSingle bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - std::istringstream iss(str); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + std::istringstream iss(this->is_enabled() ? str : str.substr(1)); iss >> this->value; return !iss.fail(); } @@ -987,10 +1295,10 @@ class ConfigOptionIntsTempl : public ConfigOptionVector static ConfigOptionType static_type() { return coInts; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionIntsTempl(*this); } ConfigOptionIntsTempl& operator= (const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->values == rhs.values; } - bool operator< (const ConfigOptionIntsTempl &rhs) const throw() { return this->values < rhs.values; } + bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->m_enabled == rhs.m_enabled && this->m_values == rhs.m_values; } + bool operator< (const ConfigOptionIntsTempl &rhs) const throw() { return this->m_enabled < rhs.m_enabled || this->m_values < rhs.m_values; } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). @@ -999,14 +1307,14 @@ class ConfigOptionIntsTempl : public ConfigOptionVector bool is_nil(int32_t idx = -1) const override { if (idx < 0) { - for (int32_t v : this->values) + for (int32_t v : this->m_values) if (v != NIL_VALUE()) return false; return true; } else { - return idx < int32_t(values.size()) ? NIL_VALUE() == this->values[idx] : - values.empty() ? NIL_VALUE() == this->default_value : - NIL_VALUE() == this->values.front(); + return idx < int32_t(this->size()) ? NIL_VALUE() == this->m_values[idx] : + this->empty() ? NIL_VALUE() == this->default_value : + NIL_VALUE() == this->get_at(0); } } int32_t get_int(size_t idx = 0) const override { return get_at(idx); } @@ -1015,10 +1323,10 @@ class ConfigOptionIntsTempl : public ConfigOptionVector std::string serialize() const override { std::ostringstream ss; - for (const int32_t &v : this->values) { - if (&v != &this->values.front()) - ss << ","; - serialize_single_value(ss, v); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + if (idx > 0) + ss << ","; + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); } return ss.str(); } @@ -1026,10 +1334,10 @@ class ConfigOptionIntsTempl : public ConfigOptionVector std::vector vserialize() const override { std::vector vv; - vv.reserve(this->values.size()); - for (const int32_t v : this->values) { + vv.reserve(this->m_values.size()); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { std::ostringstream ss; - serialize_single_value(ss, v); + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); vv.push_back(ss.str()); } return vv; @@ -1037,36 +1345,49 @@ class ConfigOptionIntsTempl : public ConfigOptionVector bool deserialize(const std::string &str, bool append = false) override { - if (! append) - this->values.clear(); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { - boost::trim(item_str); + bool enabled = true; + boost::trim(item_str); + if (!item_str.empty() && item_str.front() == '!') { + enabled = false; + item_str = item_str.substr(1); + boost::trim(item_str); + } if (item_str == NIL_STR_VALUE) { if (NULLABLE) - this->values.push_back(NIL_VALUE()); + this->m_values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); int32_t value; iss >> value; - this->values.push_back(value); + this->m_values.push_back(value); } + this->m_enabled.push_back(enabled); } + set_default_enabled(); + assert(this->m_values.size() == this->m_enabled.size()); return true; } private: - void serialize_single_value(std::ostringstream &ss, const int32_t v) const { - if (v == NIL_VALUE()) { - if (NULLABLE) - ss << NIL_STR_VALUE; - else - throw ConfigurationError("Serializing NaN"); - } else - ss << v; + void serialize_single_value(std::ostringstream &ss, const int32_t v, bool enabled) const { + if (!enabled) + ss << "!"; + if (v == NIL_VALUE()) { + if (NULLABLE) + ss << NIL_STR_VALUE; + else + throw ConfigurationError("Serializing NaN"); + } else + ss << v; } friend class cereal::access; @@ -1086,19 +1407,26 @@ class ConfigOptionString : public ConfigOptionSingle ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionString(*this); } ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionString &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionString &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionString &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionString &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } bool empty() const { return this->value.empty(); } std::string serialize() const override { + if (!this->is_enabled()) + return std::string("!") + escape_string_cstyle(this->value); return escape_string_cstyle(this->value); } bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - return unescape_string_cstyle(str, this->value); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + return unescape_string_cstyle(this->is_enabled() ? str : str.substr(1), this->value); } private: @@ -1113,33 +1441,46 @@ class ConfigOptionStrings : public ConfigOptionVector ConfigOptionStrings() : ConfigOptionVector() {} explicit ConfigOptionStrings(const std::string& value) : ConfigOptionVector(value) {} explicit ConfigOptionStrings(size_t n, const std::string& value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} explicit ConfigOptionStrings(const std::vector &values) : ConfigOptionVector(values) {} explicit ConfigOptionStrings(std::vector &&values) : ConfigOptionVector(std::move(values)) {} - explicit ConfigOptionStrings(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} static ConfigOptionType static_type() { return coStrings; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionStrings(*this); } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; } - bool operator< (const ConfigOptionStrings &rhs) const throw() { return this->values < rhs.values; } + bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->m_enabled == rhs.m_enabled && this->m_values == rhs.m_values; } + bool operator< (const ConfigOptionStrings &rhs) const throw() { return this->m_enabled < rhs.m_enabled || this->m_values < rhs.m_values; } bool is_nil(int32_t idx = 0) const override { return false; } std::string serialize() const override { - return escape_strings_cstyle(this->values); + if (this->m_enabled.empty() && !this->m_values.empty()) { + std::vector filled; + filled.resize(this->m_values.size(), ConfigOption::is_enabled(0)); + return escape_strings_cstyle(this->m_values, filled); + } + return escape_strings_cstyle(this->m_values, this->m_enabled); } std::vector vserialize() const override { - return this->values; + return this->m_values; } bool deserialize(const std::string &str, bool append = false) override { - if (! append) - this->values.clear(); - return unescape_strings_cstyle(str, this->values); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } + assert(this->m_enabled.size() == this->m_values.size()); + bool success = unescape_strings_cstyle(str, this->m_values, this->m_enabled); + if (success) { + set_default_enabled(); + } + assert(this->m_values.size() == this->m_enabled.size()); + return success; } private: @@ -1152,14 +1493,13 @@ class ConfigOptionPercent : public ConfigOptionFloat public: ConfigOptionPercent() : ConfigOptionFloat(0) {} explicit ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {} - explicit ConfigOptionPercent(double _value, bool _phony) : ConfigOptionFloat(_value, _phony) {} static ConfigOptionType static_type() { return coPercent; } ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } ConfigOptionPercent& operator= (const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPercent &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionPercent &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionPercent &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionPercent &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100.; } bool is_percent(size_t idx = 0) const override { return true; } @@ -1167,6 +1507,8 @@ class ConfigOptionPercent : public ConfigOptionFloat std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value; std::string s(ss.str()); s += "%"; @@ -1176,8 +1518,13 @@ class ConfigOptionPercent : public ConfigOptionFloat bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } // don't try to parse the trailing % since it's optional - std::istringstream iss(str); + std::istringstream iss(this->is_enabled() ? str : str.substr(1)); iss >> this->value; return !iss.fail(); } @@ -1200,22 +1547,28 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl static ConfigOptionType static_type() { return coPercents; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionPercentsTempl(*this); } ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPercentsTempl &rhs) const throw() { return ConfigOptionFloatsTempl::vectors_equal(this->values, rhs.values); } - bool operator< (const ConfigOptionPercentsTempl &rhs) const throw() { return ConfigOptionFloatsTempl::vectors_lower(this->values, rhs.values); } + bool operator==(const ConfigOptionPercentsTempl &rhs) const throw() + { + return this->m_enabled == rhs.m_enabled && ConfigOptionFloatsTempl::vectors_equal(this->m_values, rhs.m_values); + } + bool operator<(const ConfigOptionPercentsTempl &rhs) const throw() + { + return this->m_enabled < rhs.m_enabled || ConfigOptionFloatsTempl::vectors_lower(this->m_values, rhs.m_values); + } double get_abs_value(size_t i, double ratio_over) const { return this->is_nil(i) ? 0 : ratio_over * this->get_at(i) / 100; } bool is_percent(size_t idx = 0) const override { return true; } std::string serialize() const override { std::ostringstream ss; - for (const double &v : this->values) { - if (&v != &this->values.front()) - ss << ","; - this->serialize_single_value(ss, v); - if (! (std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) - ss << "%"; + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + if (idx > 0) + ss << ","; + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); + if (! (std::isnan(this->m_values[idx]) || this->m_values[idx] == ConfigOptionFloatsTempl::NIL_VALUE())) + ss << "%"; } std::string str = ss.str(); return str; @@ -1224,12 +1577,12 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl std::vector vserialize() const override { std::vector vv; - vv.reserve(this->values.size()); - for (const double v : this->values) { + vv.reserve(this->m_values.size()); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { std::ostringstream ss; - this->serialize_single_value(ss, v); - if (! (std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) - ss << "%"; + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); + if (! (std::isnan(this->m_values[idx]) || this->m_values[idx] == ConfigOptionFloatsTempl::NIL_VALUE())) + ss << "%"; vv.push_back(ss.str()); } return vv; @@ -1252,7 +1605,6 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent bool percent; ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {} explicit ConfigOptionFloatOrPercent(double _value, bool _percent) : ConfigOptionPercent(_value), percent(_percent) {} - explicit ConfigOptionFloatOrPercent(double _value, bool _percent, bool _phony) : ConfigOptionPercent(_value, _phony), percent(_percent) {} static ConfigOptionType static_type() { return coFloatOrPercent; } ConfigOptionType type() const override { return static_type(); } @@ -1266,11 +1618,11 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent return *this == *static_cast(&rhs); } bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value == rhs.value && this->percent == rhs.percent; } + { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value && this->percent == rhs.percent; } size_t hash() const throw() override { size_t seed = std::hash{}(this->value); return this->percent ? seed ^ 0x9e3779b9 : seed; } bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } + { return this->is_enabled() == rhs.is_enabled() || this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } double get_abs_value(double ratio_over) const { return this->percent ? (ratio_over * this->value / 100) : this->value; } @@ -1295,6 +1647,8 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value; std::string s(ss.str()); if (this->percent) s += "%"; @@ -1304,8 +1658,13 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } this->percent = str.find_first_of("%") != std::string::npos; - std::istringstream iss(str); + std::istringstream iss(this->is_enabled() ? str : str.substr(1)); iss >> this->value; return !iss.fail(); } @@ -1328,30 +1687,38 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalues, rhs.values); } - bool operator==(const ConfigOption &rhs) const override { + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionFloatsOrPercentsTempl(*this); } + bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() + { + return this->m_enabled == rhs.m_enabled && vectors_equal(this->m_values, rhs.m_values); + } + bool operator==(const ConfigOption &rhs) const override + { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); - assert(dynamic_cast*>(&rhs)); - return vectors_equal(this->values, static_cast*>(&rhs)->values); + assert(dynamic_cast *>(&rhs)); + return this->has_same_enabled(*static_cast *>(&rhs)) && + vectors_equal(this->m_values, static_cast *>(&rhs)->get_values()); + } + bool operator<(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() + { + return this->m_enabled < rhs.m_enabled || vectors_lower(this->m_values, rhs.m_values); } - bool operator< (const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_lower(this->values, rhs.values); } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } // A scalar is nil, or all values of a vector are nil. - bool is_nil(int32_t idx = -1) const override + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { - for (const FloatOrPercent &v : this->values) + for (const FloatOrPercent &v : this->m_values) if (!(std::isnan(v.value) || v.value == NIL_VALUE().value || v.value > std::numeric_limits::max())) return false; return true; } else { - return idx < int32_t(values.size()) ? (std::isnan(this->values[idx].value) || NIL_VALUE() == this->values[idx]) : - values.empty() ? (std::isnan(this->default_value.value) || NIL_VALUE() == this->default_value) : - (std::isnan(this->values.front().value) || NIL_VALUE() == this->values.front()); + return idx < int32_t(this->size()) ? (std::isnan(this->m_values[idx].value) || NIL_VALUE() == this->m_values[idx]) : + this->empty() ? (std::isnan(this->default_value.value) || NIL_VALUE() == this->default_value) : + (std::isnan(this->m_values.front().value) || NIL_VALUE() == this->m_values.front()); } } double get_abs_value(size_t i, double ratio_over) const { @@ -1379,10 +1746,10 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalues) { - if (&v != &this->values.front()) + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + if (idx > 0) ss << ","; - serialize_single_value(ss, v); + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); } return ss.str(); } @@ -1390,10 +1757,10 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector vserialize() const override { std::vector vv; - vv.reserve(this->values.size()); - for (const FloatOrPercent &v : this->values) { + vv.reserve(this->m_values.size()); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { std::ostringstream ss; - serialize_single_value(ss, v); + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); vv.push_back(ss.str()); } return vv; @@ -1401,15 +1768,23 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalues.clear(); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } std::istringstream is(str); std::string item_str; while (std::getline(is, item_str, ',')) { boost::trim(item_str); + bool enabled = true; + if (!item_str.empty() && item_str.front() == '!') { + enabled = false; + item_str = item_str.substr(1); + boost::trim(item_str); + } if (item_str == NIL_STR_VALUE) { if (NULLABLE) - this->values.push_back(NIL_VALUE()); + this->m_values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { @@ -1417,9 +1792,12 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector> value; - this->values.push_back({ value, percent }); + this->m_values.push_back({ value, percent }); } + this->m_enabled.push_back(enabled); } + set_default_enabled(); + assert(this->m_values.size() == this->m_enabled.size()); return true; } @@ -1433,18 +1811,20 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorsupports_nil(). static FloatOrPercent NIL_VALUE() { return FloatOrPercent{ std::numeric_limits::max(), false }; } - void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const { - if (std::isfinite(v.value)) { - ss << v.value; - if (v.percent) - ss << "%"; - } else if (std::isnan(v.value) || v.value == NIL_VALUE().value || v.value > std::numeric_limits::max()) { - if (NULLABLE) - ss << NIL_STR_VALUE; - else - throw ConfigurationError("Serializing NaN"); - } else - throw ConfigurationError("Serializing invalid number"); + void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v, bool enabled) const { + if (!enabled) + ss << "!"; + if (std::isfinite(v.value)) { + ss << v.value; + if (v.percent) + ss << "%"; + } else if (std::isnan(v.value) || v.value == NIL_VALUE().value || v.value > std::numeric_limits::max()) { + if (NULLABLE) + ss << NIL_STR_VALUE; + else + throw ConfigurationError("Serializing NaN"); + } else + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -1491,12 +1871,14 @@ class ConfigOptionPoint : public ConfigOptionSingle ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPoint &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionPoint &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionPoint &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionPoint &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value(0); ss << "x"; ss << this->value(1); @@ -1506,8 +1888,14 @@ class ConfigOptionPoint : public ConfigOptionSingle bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + Vec2d point(Vec2d::Zero()); - std::istringstream iss(str); + std::istringstream iss(this->is_enabled() ? str : str.substr(1)); std::string coord_str; char sep = 'x'; // compatibility withy old ',' separator @@ -1541,21 +1929,29 @@ class ConfigOptionPoints : public ConfigOptionVector static ConfigOptionType static_type() { return coPoints; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionPoints(*this); } ConfigOptionPoints& operator= (const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; } + bool operator==(const ConfigOptionPoints &rhs) const throw() + { + return this->m_enabled == rhs.m_enabled && this->m_values == rhs.m_values; + } bool operator< (const ConfigOptionPoints &rhs) const throw() - { return std::lexicographical_compare(this->values.begin(), this->values.end(), rhs.values.begin(), rhs.values.end(), [](const auto &l, const auto &r){ return l < r; }); } + { return this->m_enabled < rhs.m_enabled || + std::lexicographical_compare(this->m_values.begin(), this->m_values.end(), rhs.m_values.begin(), + rhs.m_values.end(), [](const auto &l, const auto &r) { return l < r; }); + } bool is_nil(int32_t idx = 0) const override { return false; } std::string serialize() const override { std::ostringstream ss; - for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << (*it)(0); + for (size_t idx = 0 ; idx < this->m_values.size(); ++idx) { + if (idx != 0) ss << ","; + if (!m_enabled[idx]) + ss << "!"; + ss << this->m_values[idx].x(); ss << "x"; - ss << (*it)(1); + ss << this->m_values[idx].y(); } return ss.str(); } @@ -1563,7 +1959,7 @@ class ConfigOptionPoints : public ConfigOptionVector std::vector vserialize() const override { std::vector vv; - for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + for (Pointfs::const_iterator it = this->m_values.begin(); it != this->m_values.end(); ++it) { std::ostringstream ss; ss << *it; vv.push_back(ss.str()); @@ -1573,11 +1969,19 @@ class ConfigOptionPoints : public ConfigOptionVector bool deserialize(const std::string &str, bool append = false) override { - if (! append) - this->values.clear(); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { + boost::trim(point_str); + bool enabled = true; + if (!point_str.empty() && point_str.front() == '!') { + enabled = false; + point_str = point_str.substr(1); + } Vec2d point(Vec2d::Zero()); std::istringstream iss(point_str); std::string coord_str; @@ -1587,8 +1991,11 @@ class ConfigOptionPoints : public ConfigOptionVector std::istringstream(coord_str) >> point(1); } } - this->values.push_back(point); + this->m_values.push_back(point); + this->m_enabled.push_back(enabled); } + set_default_enabled(); + assert(this->m_values.size() == this->m_enabled.size()); return true; } @@ -1596,16 +2003,16 @@ class ConfigOptionPoints : public ConfigOptionVector friend class cereal::access; template void save(Archive& archive) const { archive(flags); - size_t cnt = this->values.size(); + size_t cnt = this->m_values.size(); archive(cnt); - archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt); + archive.saveBinary((const char*)this->m_values.data(), sizeof(Vec2d) * cnt); } template void load(Archive& archive) { archive(flags); size_t cnt; archive(cnt); - this->values.assign(cnt, Vec2d()); - archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt); + this->m_values.assign(cnt, Vec2d()); + archive.loadBinary((char*)this->m_values.data(), sizeof(Vec2d) * cnt); } }; @@ -1619,13 +2026,15 @@ class ConfigOptionPoint3 : public ConfigOptionSingle ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); } ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionPoint3 &rhs) const throw() { return this->value == rhs.value; } + bool operator==(const ConfigOptionPoint3 &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } bool operator< (const ConfigOptionPoint3 &rhs) const throw() - { return this->value.x() < rhs.value.x() || (this->value.x() == rhs.value.x() && (this->value.y() < rhs.value.y() || (this->value.y() == rhs.value.y() && this->value.z() < rhs.value.z()))); } + { return this->is_enabled() < rhs.is_enabled() || this->value.x() < rhs.value.x() || (this->value.x() == rhs.value.x() && (this->value.y() < rhs.value.y() || (this->value.y() == rhs.value.y() && this->value.z() < rhs.value.z()))); } std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value(0); ss << ","; ss << this->value(1); @@ -1634,9 +2043,17 @@ class ConfigOptionPoint3 : public ConfigOptionSingle return ss.str(); } - bool deserialize(const std::string &str, bool append = false) override + bool deserialize(const std::string &str_raw, bool append = false) override { UNUSED(append); + if (!str_raw.empty() && str_raw.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + + Vec2d point(Vec2d::Zero()); + std::string str = (this->is_enabled() ? str_raw : str_raw.substr(1)); char dummy; return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 3 || sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 3; @@ -1647,6 +2064,149 @@ class ConfigOptionPoint3 : public ConfigOptionSingle template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; +class ConfigOptionGraph : public ConfigOptionSingle +{ +public: + ConfigOptionGraph() : ConfigOptionSingle(GraphData()) {} + explicit ConfigOptionGraph(const GraphData &value) : ConfigOptionSingle(value) {} + + static ConfigOptionType static_type() { return coGraph; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { return new ConfigOptionGraph(*this); } + ConfigOptionGraph& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionGraph &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionGraph &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } + + std::string serialize() const override + { + std::ostringstream ss; + if(!this->is_enabled()) + ss << "!"; + ss << this->value.serialize(); + return ss.str(); + } + + bool deserialize(const std::string &str, bool append = false) override + { + UNUSED(append); + GraphData data; + bool enabled = true; + if (!str.empty() && str.front() == '!') { + enabled = false; + } + bool ok = data.deserialize(enabled ? str : str.substr(1)); + if (!ok) + return false; + this->set_enabled(enabled); + this->value = data; + return true; + } + + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } +}; + + +class ConfigOptionGraphs : public ConfigOptionVector +{ +public: + ConfigOptionGraphs() : ConfigOptionVector() {} + explicit ConfigOptionGraphs(const GraphData &value) : ConfigOptionVector(value) {} + explicit ConfigOptionGraphs(size_t n, const GraphData& value) : ConfigOptionVector(n, value) {} + explicit ConfigOptionGraphs(std::initializer_list il) : ConfigOptionVector(std::move(il)) {} + explicit ConfigOptionGraphs(const std::vector &values) : ConfigOptionVector(values) {} + + static ConfigOptionType static_type() { return coGraphs; } + ConfigOptionType type() const override { return static_type(); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionGraphs(*this); } + ConfigOptionGraphs& operator=(const ConfigOption *opt) { this->set(opt); return *this; } + bool operator==(const ConfigOptionGraphs &rhs) const throw() { return this->m_enabled == rhs.m_enabled && this->m_values == rhs.m_values; } + bool operator<(const ConfigOptionGraphs &rhs) const throw() + { + return this->m_enabled < rhs.m_enabled || std::lexicographical_compare(this->m_values.begin(), this->m_values.end(), rhs.m_values.begin(), + rhs.m_values.end(), [](const auto &l, const auto &r) { return l < r; }); + } + bool is_nil(int32_t idx = 0) const override { return false; } + + std::string serialize() const override + { + std::ostringstream ss; + for (size_t idx = 0; idx < size(); ++idx) { + const GraphData &graph = this->m_values[idx]; + if (idx != 0) ss << ","; + if (!this->is_enabled(idx)) ss << "!"; + ss << graph.serialize(); + } + return ss.str(); + } + + std::vector vserialize() const override + { + std::vector vv; + for (size_t idx = 0; idx < size(); ++idx) { + const GraphData &graph = this->m_values[idx]; + std::ostringstream ss; + if (!this->is_enabled(idx)) ss << "!"; + ss << graph.serialize(); + vv.push_back(ss.str()); + } + return vv; + } + + bool deserialize(const std::string &str, bool append = false) override + { + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } + std::istringstream is(str); + std::string graph_str; + char sep = ','; + if (str.find(';') != std::string::npos) + sep = ';'; + while (std::getline(is, graph_str, sep)) { + boost::trim(graph_str); + bool enabled = true; + if (!graph_str.empty() && graph_str.front() == '!') { + enabled = false; + graph_str = graph_str.substr(1); + boost::trim(graph_str); + } + GraphData graph; + bool ok = graph.deserialize(graph_str); + if (ok) { + this->m_values.push_back(std::move(graph)); + this->m_enabled.push_back(enabled); + } + } + set_default_enabled(); + return true; + } + +private: + // use the string representation for cereal archive, as it's convenient. + // TODO: try to save/load the vector of pair of double and the two bits. + friend class cereal::access; + template void save(Archive& archive) const { + archive(flags); + std::string serialized = this->serialize(); + size_t cnt = serialized.size(); + archive(cnt); + archive.saveBinary((const char*)serialized.data(), sizeof(char) * cnt); + } + template void load(Archive& archive) { + archive(flags); + size_t cnt; + archive(cnt); + std::string serialized; + serialized.assign(cnt, char()); + archive.loadBinary((char*)serialized.data(), sizeof(char) * cnt); + deserialize(serialized, false); + } +}; + class ConfigOptionBool : public ConfigOptionSingle { public: @@ -1660,22 +2220,29 @@ class ConfigOptionBool : public ConfigOptionSingle double get_float(size_t idx = 0) const override { return this->value ? 1. : 0.; } ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionBool &rhs) const throw() { return int(this->value) < int(rhs.value); } + bool operator==(const ConfigOptionBool &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() &&this->value == rhs.value; } + bool operator< (const ConfigOptionBool &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || int(this->value) < int(rhs.value); } std::string serialize() const override { - return std::string(this->value ? "1" : "0"); + return std::string(this->is_enabled() ? "" : "!") + std::string(this->value ? "1" : "0"); } bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - if (str == "1") { + if (str.empty()) + return false; + if (str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + if (str.back() == '1') { this->value = true; return true; } - if (str == "0") { + if (str.back() == '0') { this->value = false; return true; } @@ -1694,17 +2261,29 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector ConfigOptionBoolsTempl() : ConfigOptionVector() {} explicit ConfigOptionBoolsTempl(bool default_value) : ConfigOptionVector(default_value) {} explicit ConfigOptionBoolsTempl(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} - explicit ConfigOptionBoolsTempl(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } - explicit ConfigOptionBoolsTempl(std::initializer_list il) { values.reserve(il.size()); for (unsigned char b : il) values.emplace_back(b); } + explicit ConfigOptionBoolsTempl(std::initializer_list il) + { + this->m_values.reserve(il.size()); + for (bool b : il) this->m_values.emplace_back((unsigned char) b); + this->m_enabled.resize(this->m_values.size(), ConfigOption::is_enabled()); + assert(m_enabled.size() == size()); + } + explicit ConfigOptionBoolsTempl(std::initializer_list il) + { + this->m_values.reserve(il.size()); + for (unsigned char b : il) this->m_values.emplace_back(b); + this->m_enabled.resize(this->m_values.size(), ConfigOption::is_enabled()); + assert(m_enabled.size() == size()); + } explicit ConfigOptionBoolsTempl(const std::vector& vec) : ConfigOptionVector(vec) {} explicit ConfigOptionBoolsTempl(std::vector&& vec) : ConfigOptionVector(std::move(vec)) {} static ConfigOptionType static_type() { return coBools; } ConfigOptionType type() const override { return static_type(); } - ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); } + ConfigOption* clone() const override { assert(this->m_values.size() == this->m_enabled.size()); return new ConfigOptionBoolsTempl(*this); } ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionBoolsTempl &rhs) const throw() { return this->values == rhs.values; } - bool operator< (const ConfigOptionBoolsTempl &rhs) const throw() { return this->values < rhs.values; } + bool operator==(const ConfigOptionBoolsTempl &rhs) const throw() { return this->m_enabled == rhs.m_enabled && this->m_values == rhs.m_values; } + bool operator< (const ConfigOptionBoolsTempl &rhs) const throw() { return this->m_enabled < rhs.m_enabled || this->m_values < rhs.m_values; } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). @@ -1713,14 +2292,14 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector bool is_nil(int32_t idx = -1) const override { if (idx < 0) { - for (uint8_t v : this->values) + for (uint8_t v : this->m_values) if (v != NIL_VALUE()) return false; return true; } else { - return idx < int32_t(values.size()) ? NIL_VALUE() == this->values[idx] : - values.empty() ? NIL_VALUE() == this->default_value : - NIL_VALUE() == this->values.front(); + return idx < int32_t(this->size()) ? NIL_VALUE() == this->m_values[idx] : + this->empty() ? NIL_VALUE() == this->default_value : + NIL_VALUE() == this->m_values.front(); } } bool get_bool(size_t idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0; } @@ -1730,20 +2309,20 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector std::string serialize() const override { std::ostringstream ss; - for (const unsigned char &v : this->values) { - if (&v != &this->values.front()) - ss << ","; - this->serialize_single_value(ss, v); - } + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + if (idx > 0) + ss << ","; + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); + } return ss.str(); } std::vector vserialize() const override { std::vector vv; - for (const unsigned char v : this->values) { - std::ostringstream ss; - this->serialize_single_value(ss, v); + for (size_t idx = 0;idx < this->m_values.size(); ++idx) { + std::ostringstream ss; + this->serialize_single_value(ss, this->m_values[idx], this->is_enabled(idx)); vv.push_back(ss.str()); } return vv; @@ -1751,13 +2330,21 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, ConfigHelpers::DeserializationSubstitution substitution) { - if (! append) - this->values.clear(); + if (!append) { + this->m_values.clear(); + this->m_enabled.clear(); + } std::istringstream is(str); std::string item_str; bool substituted = false; while (std::getline(is, item_str, ',')) { boost::trim(item_str); + bool enabled = true; + if (!item_str.empty() && item_str.front() == '!') { + enabled = false; + item_str = item_str.substr(1); + boost::trim(item_str); + } unsigned char new_value = 0; if (item_str == NIL_STR_VALUE) { if (NULLABLE) @@ -1773,8 +2360,11 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector substituted = true; } else return ConfigHelpers::DeserializationResult::Failed; - this->values.push_back(new_value); + this->m_values.push_back(new_value); + this->m_enabled.push_back(enabled); } + set_default_enabled(); + assert(this->m_values.size() == this->m_enabled.size()); return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded; } @@ -1784,15 +2374,17 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector } protected: - void serialize_single_value(std::ostringstream &ss, const unsigned char v) const { - if (v == NIL_VALUE()) { - if (NULLABLE) - ss << NIL_STR_VALUE; - else - throw ConfigurationError("Serializing NaN"); - } else - ss << (v ? "1" : "0"); - } + void serialize_single_value(std::ostringstream &ss, const unsigned char v, bool enabled) const { + if (!enabled) + ss << "!"; + if (v == NIL_VALUE()) { + if (NULLABLE) + ss << NIL_STR_VALUE; + else + throw ConfigurationError("Serializing NaN"); + } else + ss << (v ? "1" : "0"); + } private: friend class cereal::access; @@ -1819,8 +2411,8 @@ class ConfigOptionEnum : public ConfigOptionSingle ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionEnum(*this); } ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionEnum &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionEnum &rhs) const throw() { return int(this->value) < int(rhs.value); } + bool operator==(const ConfigOptionEnum &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionEnum &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || int(this->value) < int(rhs.value); } int32_t get_int(size_t idx = 0) const override { return int32_t(this->value); } void set_enum_int(int32_t val) override { this->value = T(val); } // special case for get/set any: use a int like for ConfigOptionEnumGeneric, to simplify @@ -1832,7 +2424,7 @@ class ConfigOptionEnum : public ConfigOptionSingle if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionEnum: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - return this->value == (T)rhs.get_int(); + return this->is_enabled() == rhs.is_enabled() && this->value == (T)rhs.get_int(); } void set(const ConfigOption *rhs) override { @@ -1847,13 +2439,18 @@ class ConfigOptionEnum : public ConfigOptionSingle { const t_config_enum_names& names = ConfigOptionEnum::get_enum_names(); assert(static_cast(this->value) < int(names.size())); - return names[static_cast(this->value)]; + return std::string(this->is_enabled() ? "" : "!") + names[static_cast(this->value)]; } bool deserialize(const std::string &str, bool append = false) override { UNUSED(append); - return from_string(str, this->value); + if (!str.empty() && str.front() == '!') { + this->set_enabled(false); + } else { + this->set_enabled(true); + } + return from_string(this->is_enabled() ? str : str.substr(1), this->value); } static bool has(T value) @@ -1895,15 +2492,15 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); } ConfigOptionEnumGeneric& operator= (const ConfigOption *opt) { this->set(opt); return *this; } - bool operator==(const ConfigOptionEnumGeneric &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionEnumGeneric &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionEnumGeneric &rhs) const throw() { return this->is_enabled() == rhs.is_enabled() && this->value == rhs.value; } + bool operator< (const ConfigOptionEnumGeneric &rhs) const throw() { return this->is_enabled() < rhs.is_enabled() || this->value < rhs.value; } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - return this->value == rhs.get_int(); + return this->is_enabled() == rhs.is_enabled() && this->value == rhs.get_int(); } void set_enum_int(int32_t val) override { this->value = val; } @@ -1917,10 +2514,13 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt std::string serialize() const override { + std::string prefix; + if (!this->is_enabled()) + prefix = "!"; for (const auto &kvp : *this->keys_map) if (kvp.second == this->value) - return kvp.first; - return std::string(); + return prefix + kvp.first; + return prefix; } bool deserialize(const std::string &str, bool append = false) override @@ -2007,6 +2607,8 @@ class ConfigOptionDef case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; } case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); opt->set_is_extruder_size(this->is_vector_extruder); return opt; } case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; } + case coGraph: { auto opt = new ConfigOptionGraph(); archive(*opt); return opt; } + case coGraphs: { auto opt = new ConfigOptionGraphs(); archive(*opt); opt->set_is_extruder_size(this->is_vector_extruder); return opt; } case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); opt->set_is_extruder_size(this->is_vector_extruder); return opt; } case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } @@ -2040,6 +2642,8 @@ class ConfigOptionDef case coPoint: archive(*static_cast(opt)); break; case coPoints: archive(*static_cast(opt)); break; case coPoint3: archive(*static_cast(opt)); break; + case coGraph: archive(*static_cast(opt)); break; + case coGraphs: archive(*static_cast(opt)); break; case coBool: archive(*static_cast(opt)); break; case coBools: archive(*static_cast(opt)); break; case coEnum: archive(*static_cast(opt)); break; @@ -2093,6 +2697,8 @@ class ConfigOptionDef bool readonly = false; // Can be phony. if not present at laoding, mark it as phony. Also adapt the gui to look for phony status. bool can_phony = false; + // Can be enabled/disabled by a check box. + bool can_enable = false; // Height of a multiline GUI text box. int height = -1; // Optional width of an input field. @@ -2129,6 +2735,9 @@ class ConfigOptionDef // For enums (when type == coEnum). Maps enum_values to enums. // Initialized by ConfigOptionEnum::get_enum_values() const t_config_enum_values *enum_keys_map = nullptr; + + // Initialized by ConfigOptionEnum::get_enum_values() + std::shared_ptr graph_settings; // for scripted gui widgets // true if it's not a real option but a simplified/composite one that use angelscript for interaction. @@ -2137,6 +2746,8 @@ class ConfigOptionDef std::vector depends_on; // from Option bool has_enum_value(const std::string &value) const { + if (!value.empty() && value.front() == '!') + return has_enum_value(value.substr(1)); for (const std::string &v : enum_values) if (v == value) return true; diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 38335847f6b..ff283af8628 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -15,14 +15,13 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint auto *colorprint_heights = config->option("colorprint_heights"); if (colorprint_heights == nullptr) return; - if (info.gcodes.empty() && ! colorprint_heights->values.empty()) { + if (info.gcodes.empty() && ! colorprint_heights->empty()) { // Convert the old colorprint_heighs only if there is no equivalent data in a new format. const std::vector& colors = ColorPrintColors::get(); - const auto& colorprint_values = colorprint_heights->values; info.gcodes.clear(); - info.gcodes.reserve(colorprint_values.size()); + info.gcodes.reserve(colorprint_heights->size()); int i = 0; - for (auto val : colorprint_values) + for (auto val : colorprint_heights->get_values()) info.gcodes.emplace_back(Item{ val, ColorChange, 1, colors[(++i)%7] }); info.mode = SingleExtruder; diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index e12a5cd0b36..3c99851b4ec 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -26,7 +26,7 @@ Mill::Mill(uint16_t mill_id, GCodeConfig* config) : Tool(mill_id, config) { m_mill_id = mill_id; - m_id = mill_id + (uint16_t)config->retract_length.values.size(); + m_id = mill_id + (uint16_t)config->retract_length.size(); } double Tool::extrude(double dE) @@ -215,7 +215,7 @@ double Extruder::retract_length() const assert(!m_config->retract_length.is_nil()); assert(m_config->retract_length.get_at(m_id) < std::numeric_limits::max()); assert(m_config->retract_length.get_at(m_id) > -std::numeric_limits::max()); - assert(m_config->retract_length.values.size() > m_id); + assert(m_config->retract_length.size() > m_id); return m_config->retract_length.get_at(m_id); } @@ -224,7 +224,7 @@ double Extruder::retract_lift() const assert(!m_config->retract_lift.is_nil()); assert(m_config->retract_lift.get_at(m_id) < std::numeric_limits::max()); assert(m_config->retract_lift.get_at(m_id) > -std::numeric_limits::max()); - assert(m_config->retract_lift.values.size() > m_id); + assert(m_config->retract_lift.size() > m_id); return m_config->retract_lift.get_at(m_id); } @@ -244,7 +244,7 @@ double Extruder::retract_restart_extra() const assert(!m_config->retract_restart_extra.is_nil()); assert(m_config->retract_restart_extra.get_at(m_id) < std::numeric_limits::max()); assert(m_config->retract_restart_extra.get_at(m_id) > -std::numeric_limits::max()); - assert(m_config->retract_restart_extra.values.size() > m_id); + assert(m_config->retract_restart_extra.size() > m_id); return m_config->retract_restart_extra.get_at(m_id); } @@ -258,7 +258,7 @@ double Extruder::retract_restart_extra_toolchange() const assert(!m_config->retract_restart_extra_toolchange.is_nil()); assert(m_config->retract_restart_extra_toolchange.get_at(m_id) < std::numeric_limits::max()); assert(m_config->retract_restart_extra_toolchange.get_at(m_id) > -std::numeric_limits::max()); - assert(m_config->retract_restart_extra_toolchange.values.size() > m_id); + assert(m_config->retract_restart_extra_toolchange.size() > m_id); return m_config->retract_restart_extra_toolchange.get_at(m_id); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 98c82035867..4bcf2e1a1a9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -86,7 +86,9 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(config->gap_fill_acceleration); RETURN_COMPARE_NON_EQUAL(config->gap_fill_speed); RETURN_COMPARE_NON_EQUAL(config->print_extrusion_multiplier); - RETURN_COMPARE_NON_EQUAL(config->region_gcode.value); + RETURN_COMPARE_NON_EQUAL(config->region_gcode.value) + RETURN_COMPARE_NON_EQUAL(config->small_area_infill_flow_compensation.value) + RETURN_COMPARE_NON_EQUAL(config->small_area_infill_flow_compensation_model.value); } if (config == nullptr || rhs.config == nullptr || max_sparse_infill_spacing == 0) RETURN_COMPARE_NON_EQUAL(flow.width()); @@ -115,6 +117,8 @@ struct SurfaceFillParams : FillParams || config->gap_fill_speed != rhs.config->gap_fill_speed || config->print_extrusion_multiplier != rhs.config->print_extrusion_multiplier || config->region_gcode != rhs.config->region_gcode + || config->small_area_infill_flow_compensation != rhs.config->small_area_infill_flow_compensation + || config->small_area_infill_flow_compensation_model != rhs.config->small_area_infill_flow_compensation_model )) return false; // then check params @@ -153,8 +157,8 @@ float compute_fill_angle(const PrintRegionConfig ®ion_config, size_t layer_id float angle = 0; if (!region_config.fill_angle_template.empty()) { // fill pattern: replace fill angle - size_t idx = layer_id % region_config.fill_angle_template.values.size(); - angle = region_config.fill_angle_template.values[idx]; + size_t idx = layer_id % region_config.fill_angle_template.size(); + angle = region_config.fill_angle_template.get_at(idx); } else { angle = region_config.fill_angle.value; } @@ -923,7 +927,7 @@ void Layer::make_ironing() // Create the ironing extrusions for regions object()->print()->config().nozzle_diameter.values[ironing_params.extruder - 1]; + double nozzle_dmr = this->object()->print()->config().nozzle_diameter.get_at(ironing_params.extruder - 1); const PrintRegionConfig& region_config = ironing_params.layerm->region().config(); if (ironing_params.just_infill) { // Just infill. diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 52412405c9c..76cc2207a0f 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -293,7 +293,7 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob std::vector region_fill_data; region_fill_data.reserve(print_object.num_printing_regions()); bool build_octree = false; - const std::vector &nozzle_diameters = print_object.print()->config().nozzle_diameter.values; + const std::vector &nozzle_diameters = print_object.print()->config().nozzle_diameter.get_values(); double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index df22f09cc2a..f23479f56c5 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -29,7 +29,7 @@ Generator::Generator(const PrintObject &print_object, const coordf_t fill_densit const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &object_config = print_object.config(); const PrintRegionConfig ®ion_config = print_object.shared_regions()->all_regions.front()->config(); - const std::vector &nozzle_diameters = print_config.nozzle_diameter.values; + const std::vector &nozzle_diameters = print_config.nozzle_diameter.get_values(); double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); // const int infill_extruder = region_config.infill_extruder.value; const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); diff --git a/src/libslic3r/Format/BBConfig.cpp b/src/libslic3r/Format/BBConfig.cpp index 78e81ae23a8..ce47ca5f956 100644 --- a/src/libslic3r/Format/BBConfig.cpp +++ b/src/libslic3r/Format/BBConfig.cpp @@ -585,8 +585,9 @@ void custom_gcode_transform(DynamicPrintConfig &print_config) } for (std::string opt_key : {"end_filament_gcode", "start_filament_gcode"}) { auto opt = print_config.opt(opt_key); - if (opt != nullptr) - for (std::string &custom_gcode : opt->values) { + if (opt != nullptr) { + std::vector custom_gcode_values = opt->get_values(); + for (std::string &custom_gcode : custom_gcode_values) { // check & replace setting name for (auto &entry : key_translation_map) { boost::replace_all(custom_gcode, entry.first, entry.second); @@ -596,6 +597,8 @@ void custom_gcode_transform(DynamicPrintConfig &print_config) boost::replace_all(custom_gcode, entry.first, entry.second); } } + opt->set(custom_gcode_values); + } } } diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 68c775b4e4b..eb2c62b4c12 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -2676,7 +2676,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //skip this file return; } - preset_name = filament_names->values[0]; + preset_name = filament_names->get_at(0); } else if (type == Preset::TYPE_PRINTER) { print_name = dynamic_cast < ConfigOptionString* > (config.option("printer_settings_id")); @@ -7570,7 +7570,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) // ConfigOptionInts *first_layer_print_sequence_opt = plate_data->config.option("first_layer_print_sequence"); // if (first_layer_print_sequence_opt != nullptr) { // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FIRST_LAYER_PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\""; -// const std::vector& values = first_layer_print_sequence_opt->values; +// const std::vector& values = first_layer_print_sequence_opt->get_values(); // for (int i = 0; i < values.size(); ++i) { // stream << values[i]; // if (i != (values.size() - 1)) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a56dff4dc1b..cb6a86fa744 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -630,15 +630,15 @@ std::vector GCode::collect_layers_to_print(const PrintObjec double support_layer_height_min = 1000000.; const ConfigOptionFloatsOrPercents& min_layer_height = object.print()->config().min_layer_height; const ConfigOptionFloats& nozzle_diameter = object.print()->config().nozzle_diameter; - for(int extr_id = 0; extr_id < min_layer_height.values.size(); ++extr_id) - support_layer_height_min = std::min(support_layer_height_min, std::max(nozzle_diameter.values[extr_id]/40, min_layer_height.get_abs_value(extr_id, nozzle_diameter.values[extr_id]))); + for(int extr_id = 0; extr_id < min_layer_height.size(); ++extr_id) + support_layer_height_min = std::min(support_layer_height_min, std::max(nozzle_diameter.get_at(extr_id)/40, min_layer_height.get_abs_value(extr_id, nozzle_diameter.get_at(extr_id)))); gap_over_supports += support_layer_height_min; }*/ std::vector> warning_ranges; //check for max nozzle diameter - const std::vector& nozzle_diameters = object.print()->config().nozzle_diameter.values; + const std::vector& nozzle_diameters = object.print()->config().nozzle_diameter.get_values(); std::set exctruder_ids = object.object_extruders(); double max_nozzle = 0; for (uint16_t id : exctruder_ids) { @@ -884,28 +884,28 @@ namespace DoExport { if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause Print G-code")), GCodeWriter::get_default_pause_gcode(config)); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value); if (ret.size() < MAX_TAGS_COUNT) { - for (const std::string& value : config.start_filament_gcode.values) { + for (const std::string& value : config.start_filament_gcode.get_values()) { check(_(L("Filament Start G-code")), value); if (ret.size() == MAX_TAGS_COUNT) break; } } if (ret.size() < MAX_TAGS_COUNT) { - for (const std::string& value : config.end_filament_gcode.values) { + for (const std::string& value : config.end_filament_gcode.get_values()) { check(_(L("Filament End G-code")), value); if (ret.size() == MAX_TAGS_COUNT) break; } } if (ret.size() < MAX_TAGS_COUNT) { - for (const std::string& value : print.config().milling_toolchange_start_gcode.values) { + for (const std::string& value : print.config().milling_toolchange_start_gcode.get_values()) { check(_(L("Milling Start G-code")), value); if (ret.size() == MAX_TAGS_COUNT) break; } } if (ret.size() < MAX_TAGS_COUNT) { - for (const std::string& value : print.config().milling_toolchange_end_gcode.values) { + for (const std::string& value : print.config().milling_toolchange_end_gcode.get_values()) { check(_(L("Milling End G-code")), value); if (ret.size() == MAX_TAGS_COUNT) break; @@ -1431,7 +1431,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene m_processor.process_string(gcode, this->m_throw_if_canceled); } - if (! print.config().gcode_substitutions.values.empty()) { + if (! print.config().gcode_substitutions.empty()) { m_find_replace = make_unique(print.config()); file.set_find_replace(m_find_replace.get(), false); } @@ -1506,7 +1506,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene const ConfigOptionBool* thumbnails_with_bed = print.full_print_config().option("thumbnails_with_bed"); if (thumbnails_format != nullptr && thumbnails_format->value == GCodeThumbnailsFormat::BIQU) GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, - print.full_print_config().option("thumbnails")->values, + print.full_print_config().option("thumbnails")->get_values(), thumbnails_with_bed ? thumbnails_with_bed->value : false, thumbnails_format->value, true, @@ -1524,7 +1524,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, - print.full_print_config().option("thumbnails")->values, + print.full_print_config().option("thumbnails")->get_values(), thumbnails_with_bed ? thumbnails_with_bed->value : false, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, thumbnails_tag_with_format ? thumbnails_tag_with_format->value : false, @@ -1671,7 +1671,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene std::vector print_object_instances_ordering; std::vector::const_iterator print_object_instance_sequential_active; bool has_milling = false; - if (!config().milling_diameter.values.empty()) { + if (!config().milling_diameter.empty()) { for (const PrintObject* obj : print.objects()) { for (const Layer *layer : obj->layers()) { for (const LayerRegion *lr : layer->regions()) { @@ -1767,7 +1767,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). m_placeholder_parser.set("bounding_box", new ConfigOptionFloats({ global_bounding_box.min.x(), global_bounding_box.min.y(), global_bounding_box.min.z(), global_bounding_box.max.x(), global_bounding_box.max.y(), global_bounding_box.max.z() })); { - BoundingBoxf bbox(print.config().bed_shape.values); + BoundingBoxf bbox(print.config().bed_shape.get_values()); m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); @@ -1779,10 +1779,10 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene // therefore it does NOT encompass the initial purge line. // It does NOT encompass MMU/MMU2 starting (wipe) areas. auto pts = std::make_unique(); - pts->values.reserve(print.first_layer_convex_hull().size()); - for (const Point &pt : print.first_layer_convex_hull().points) - pts->values.emplace_back(unscale(pt)); - BoundingBoxf bbox(pts->values); + pts->resize(print.first_layer_convex_hull().size()); + for (size_t idx = 0; idx < print.first_layer_convex_hull().points.size(); ++idx) + pts->set_at(unscale(print.first_layer_convex_hull().points[idx]), idx); + BoundingBoxf bbox(pts->get_values()); m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); @@ -1840,9 +1840,9 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene BoundingBoxf bbox = bbvisitor.get_bb(); Polygon first_layer_hull = bbvisitor.get_hull(); auto pts = std::make_unique(); - pts->values.reserve(first_layer_hull.size()); - for (const Point& pt : first_layer_hull.points) - pts->values.emplace_back(unscale(pt)); + pts->resize(first_layer_hull.size()); + for (size_t idx = 0; idx < first_layer_hull.points.size(); ++idx) + pts->set_at(unscale(first_layer_hull.points[idx]), idx); m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); @@ -1896,7 +1896,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene } else { DynamicConfig config; config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - file.preamble_to_put_start_layer.append(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); + file.preamble_to_put_start_layer.append(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.get_at(initial_extruder_id), initial_extruder_id, &config)); preamble_to_put_start_layer.append("\n"); } */ @@ -2164,7 +2164,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); file.writeln(this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); } else { - for (const std::string& end_gcode : print.config().end_filament_gcode.values) { + for (const std::string& end_gcode : print.config().end_filament_gcode.get_values()) { int extruder_id = (uint16_t)(&end_gcode - &print.config().end_filament_gcode.get_at(0)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); config.set_key_value("previous_extruder", new ConfigOptionInt(extruder_id)); @@ -2228,7 +2228,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, - print.full_print_config().option("thumbnails")->values, + print.full_print_config().option("thumbnails")->get_values(), thumbnails_with_bed ? thumbnails_with_bed->value : false, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, thumbnails_tag_with_format ? thumbnails_tag_with_format->value: false, @@ -2523,8 +2523,8 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std } }; if (current_extruder_id >= 0 && current_extruder_id < config().filament_colour.size()) { - func_add_colour("filament_colour_int", config().filament_colour.values[current_extruder_id]); - func_add_colour("extruder_colour_int", config().extruder_colour.values[current_extruder_id]); + func_add_colour("filament_colour_int", config().filament_colour.get_at(current_extruder_id)); + func_add_colour("extruder_colour_int", config().extruder_colour.get_at(current_extruder_id)); } // should be the same as Vec2d gcode_pos = point_to_gcode(m_last_pos); @@ -3694,7 +3694,7 @@ LayerResult GCode::process_layer( //add milling post-process if enabled - if (!config().milling_diameter.values.empty()) { + if (!config().milling_diameter.empty()) { bool milling_ok = false; for (const LayerToPrint& ltp : layers) { if (ltp.object_layer != nullptr) { @@ -3714,7 +3714,7 @@ LayerResult GCode::process_layer( //switch to mill gcode += "; milling ok\n"; uint32_t current_extruder_filament = m_writer.tool()->id(); - uint32_t milling_extruder_id = uint32_t(config().nozzle_diameter.values.size()); + uint32_t milling_extruder_id = uint32_t(config().nozzle_diameter.size()); m_writer.toolchange(milling_extruder_id); m_placeholder_parser.set("current_extruder", milling_extruder_id); // Append the filament start G-code. @@ -5595,16 +5595,41 @@ std::vector cut_corner_cache = { 0.252510726678311,0.262777267777188,0.27352986689699,0.284799648665007,0.296620441746888,0.309029079319231,0.322065740515038,0.335774339512048,0.350202970204428,0.365404415947691, 0.381436735764648,0.398363940736199,0.416256777189962,0.435193636891737,0.455261618934834 }; - -void GCode::_extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment) { +void GCode::_extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, + ExtrusionRole role) { if (line.a.coincides_with_epsilon(line.b)) { assert(false); // todo: investigate if it happens (it happens in perimeters) return; } + std::string comment_copy = comment; + double unscaled_line_length = unscaled(line.length()); + double extrusion_value = e_per_mm * unscaled_line_length; + // small_area_infill_flow_compensation + // this is only done in _extrude_line and not in _extrude_line_cut_corner because _extrude_line_cut_corner doesn't apply to solid infill, but only for external perimeters. + if (!this->on_first_layer() && (role == ExtrusionRole::erSolidInfill || role == ExtrusionRole::erTopSolidInfill) && + m_config.small_area_infill_flow_compensation.value && + m_config.small_area_infill_flow_compensation_model.value.data_size() > 1) { + GraphData graph = m_config.small_area_infill_flow_compensation_model.value; + assert(graph.begin_idx >= 0 && graph.begin_idx + 1 < graph.end_idx && graph.end_idx <= graph.graph_points.size()); + // ensure it start at length = 0, and ensure it ends with a compensation of 1. + graph.graph_points[graph.begin_idx].x() = 0; + graph.graph_points[graph.end_idx - 1].y() = 1; + //interpolate and verify + double new_extrusion_value = extrusion_value * graph.interpolate(unscaled_line_length); + assert(new_extrusion_value > 0.0); + if (new_extrusion_value != extrusion_value) { + extrusion_value = (new_extrusion_value > 0.0) ? new_extrusion_value : 0.0; + if (m_config.gcode_comments) { + comment_copy += Slic3r::format(_(L(" | Old Flow Value: %0.5f Length: %0.5f")), extrusion_value, + unscaled_line_length); + } + } + } + // end small_area_infill_flow_compensation gcode_str += m_writer.extrude_to_xy( this->point_to_gcode(line.b), - e_per_mm * unscaled(line.length()), - comment); + extrusion_value, + comment_copy); } void GCode::_extrude_line_cut_corner(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, Point& last_pos, const double path_width) { @@ -5700,27 +5725,16 @@ double GCode::compute_e_per_mm(double path_mm3_per_mm) { * m_writer.tool()->e_per_mm3() // inside is the filament_extrusion_multiplier * this->config().print_extrusion_multiplier.get_abs_value(1); // extrusion mult per speed - std::string str = this->config().extruder_extrusion_multiplier_speed.get_at(this->m_writer.tool()->id()); - if (str.size() > 2 && !(str.at(0) == '0' && str.at(1) == ' ')) { - assert(e_per_mm > 0); - double current_speed = this->writer().get_speed(); - std::vector extrusion_mult; - std::stringstream stream{str}; - double parsed = 0.f; - while (stream >> parsed) - extrusion_mult.push_back(parsed); - int idx_before = int(current_speed/10); - if (idx_before >= extrusion_mult.size() - 1) { - // last or after the last - e_per_mm *= extrusion_mult.back(); - } else { - assert(idx_before + 1 < extrusion_mult.size()); - float percent_before = 1 - (current_speed/10 - idx_before); - double mult = extrusion_mult[idx_before] * percent_before; - mult += extrusion_mult[idx_before+1] * (1-percent_before); - e_per_mm *= (mult / 2); + if (this->config().extruder_extrusion_multiplier_speed.is_enabled()) { + GraphData eems_graph = this->config().extruder_extrusion_multiplier_speed.get_at(this->m_writer.tool()->id()); + if (eems_graph.data_size() > 0 && this->config().extruder_extrusion_multiplier_speed.is_enabled(this->m_writer.tool()->id())) { + assert(e_per_mm > 0); + double current_speed = this->writer().get_speed(); + if (eems_graph.data_size() > 0) { + e_per_mm *= eems_graph.interpolate(current_speed); + } + assert(e_per_mm > 0); } - assert(e_per_mm > 0); } // first layer mult if (this->m_layer->bottom_z() < EPSILON) @@ -5756,7 +5770,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri for (const Line& line : path.polyline.lines()) { if (path.role() != erExternalPerimeter || config().external_perimeter_cut_corners.value == 0) { // normal & legacy pathcode - _extrude_line(gcode, line, e_per_mm, comment); + _extrude_line(gcode, line, e_per_mm, comment, path.role()); } else { _extrude_line_cut_corner(gcode, line, e_per_mm, comment, last_pos, path.width); } @@ -5774,7 +5788,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri const Line line = Line(path.polyline.get_points()[point_index - 1], path.polyline.get_points()[point_index]); if (path.role() != erExternalPerimeter || config().external_perimeter_cut_corners.value == 0) { // normal & legacy pathcode - _extrude_line(gcode, line, e_per_mm, comment); + _extrude_line(gcode, line, e_per_mm, comment, path.role()); } else { _extrude_line_cut_corner(gcode, line, e_per_mm, comment, last_pos, path.width); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 2399a0cdd09..09d30fa6269 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -79,7 +79,7 @@ class WipeTowerIntegration { m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), - m_extruder_offsets(print_config.extruder_offset.values), + m_extruder_offsets(print_config.extruder_offset.get_values()), m_priming(priming), m_tool_changes(tool_changes), m_final_purge(final_purge), @@ -519,7 +519,7 @@ class GCode : ExtrusionVisitorConst { double compute_e_per_mm(double path_mm3_per_mm); std::string _extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); - void _extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment); + void _extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, ExtrusionRole role); void _extrude_line_cut_corner(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, Point& last_pos, const double path_width); std::string _before_extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); double_t _compute_speed_mm_per_sec(const ExtrusionPath &path, double speed, std::string *comment); diff --git a/src/libslic3r/GCode/FanMover.cpp b/src/libslic3r/GCode/FanMover.cpp index 951de6107e0..f124425fd7b 100644 --- a/src/libslic3r/GCode/FanMover.cpp +++ b/src/libslic3r/GCode/FanMover.cpp @@ -279,7 +279,7 @@ void FanMover::_process_ACTIVATE_EXTRUDER(const std::string_view cmd) std::string_view extruder_name = cmd.substr(extruder_pos_start, extruder_pos_end-extruder_pos_start); // we have a "name". It may be whatever or "extruder" + X for (const Extruder &extruder : m_writer.extruders()) { - if (m_writer.config.tool_name.values[extruder.id()] == extruder_name) { + if (m_writer.config.tool_name.get_at(extruder.id()) == extruder_name) { m_current_extruder = static_cast(extruder.id()); return; } diff --git a/src/libslic3r/GCode/FindReplace.hpp b/src/libslic3r/GCode/FindReplace.hpp index 9bc0a481cff..0e5f6422ad9 100644 --- a/src/libslic3r/GCode/FindReplace.hpp +++ b/src/libslic3r/GCode/FindReplace.hpp @@ -9,7 +9,7 @@ namespace Slic3r { class GCodeFindReplace { public: - GCodeFindReplace(const PrintConfig &print_config) : GCodeFindReplace(print_config.gcode_substitutions.values) {} + GCodeFindReplace(const PrintConfig &print_config) : GCodeFindReplace(print_config.gcode_substitutions.get_values()) {} GCodeFindReplace(const std::vector &gcode_substitutions); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6ad1f7b498a..f2a630b8749 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -63,14 +63,14 @@ const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; static void set_option_value(ConfigOptionFloats& option, size_t id, float value) { - if (id < option.values.size()) - option.values[id] = static_cast(value); + if (id < option.size()) + option.set_at(static_cast(value), id); }; static float get_option_value(const ConfigOptionFloats& option, size_t id) { - return option.values.empty() ? 0.0f : - ((id < option.values.size()) ? static_cast(option.values[id]) : static_cast(option.values.back())); + return option.empty() ? 0.0f : + ((id < option.size()) ? static_cast(option.get_at(id)) : static_cast(option.get_at(option.size()-1))); } static float estimated_acceleration_distance(float initial_rate, float target_rate, float acceleration) @@ -878,7 +878,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_flavor = config.gcode_flavor; - size_t extruders_count = config.nozzle_diameter.values.size(); + size_t extruders_count = config.nozzle_diameter.size(); m_result.extruders_count = extruders_count; m_extruder_offsets.resize(extruders_count); @@ -901,9 +901,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config) } // takes colors from config if (config.extruder_colour.size() == extruders_count) - m_result.extruder_colors = config.extruder_colour.values; + m_result.extruder_colors = config.extruder_colour.get_values(); if (config.filament_colour.size() == extruders_count) - m_result.filament_colors = config.filament_colour.values; + m_result.filament_colors = config.filament_colour.get_values(); if (config.machine_limits_usage.value < MachineLimitsUsage::Limits) { @@ -914,21 +914,23 @@ void GCodeProcessor::apply_config(const PrintConfig& config) } if (m_flavor == gcfRepRap) { // RRF does not support setting min feedrates. Set them to zero. - m_time_processor.machine_limits.machine_min_travel_rate.values.assign(m_time_processor.machine_limits.machine_min_travel_rate.size(), 0.); - m_time_processor.machine_limits.machine_min_extruding_rate.values.assign(m_time_processor.machine_limits.machine_min_extruding_rate.size(), 0.); + for (size_t i = 0; i < m_time_processor.machine_limits.machine_min_travel_rate.size(); ++i) { + m_time_processor.machine_limits.machine_min_travel_rate.set_at(0., i); + m_time_processor.machine_limits.machine_min_extruding_rate.set_at(0., i); + } } } // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // are considered to be active for the single extruder multi-material printers only. - m_time_processor.filament_load_times.resize(config.filament_load_time.values.size()); - for (size_t i = 0; i < config.filament_load_time.values.size(); ++i) { - m_time_processor.filament_load_times[i] = static_cast(config.filament_load_time.values[i]); + m_time_processor.filament_load_times.resize(config.filament_load_time.size()); + for (size_t i = 0; i < config.filament_load_time.size(); ++i) { + m_time_processor.filament_load_times[i] = static_cast(config.filament_load_time.get_at(i)); } - m_time_processor.filament_unload_times.resize(config.filament_unload_time.values.size()); - for (size_t i = 0; i < config.filament_unload_time.values.size(); ++i) { - m_time_processor.filament_unload_times[i] = static_cast(config.filament_unload_time.values[i]); + m_time_processor.filament_unload_times.resize(config.filament_unload_time.size()); + for (size_t i = 0; i < config.filament_unload_time.size(); ++i) { + m_time_processor.filament_unload_times[i] = static_cast(config.filament_unload_time.get_at(i)); } // With MM setups like Prusa MMU2, the filaments may be expected to be parked at the beginning. @@ -1001,7 +1003,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionPoints* bed_shape = config.option("bed_shape"); if (bed_shape != nullptr) - m_result.bed_shape = bed_shape->values; + m_result.bed_shape = bed_shape->get_values(); const ConfigOptionString* print_settings_id = config.option("print_settings_id"); if (print_settings_id != nullptr) @@ -1009,20 +1011,20 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionStrings* filament_settings_id = config.option("filament_settings_id"); if (filament_settings_id != nullptr) - m_result.settings_ids.filament = filament_settings_id->values; + m_result.settings_ids.filament = filament_settings_id->get_values(); const ConfigOptionString* printer_settings_id = config.option("printer_settings_id"); if (printer_settings_id != nullptr) m_result.settings_ids.printer = printer_settings_id->value; - m_result.extruders_count = config.option("nozzle_diameter")->values.size(); + m_result.extruders_count = config.option("nozzle_diameter")->size(); const ConfigOptionFloats* filament_diameters = config.option("filament_diameter"); if (filament_diameters != nullptr) { m_result.filament_diameters.clear(); - m_result.filament_diameters.resize(filament_diameters->values.size()); - for (size_t i = 0; i < filament_diameters->values.size(); ++i) { - m_result.filament_diameters[i] = static_cast(filament_diameters->values[i]); + m_result.filament_diameters.resize(filament_diameters->size()); + for (size_t i = 0; i < filament_diameters->size(); ++i) { + m_result.filament_diameters[i] = static_cast(filament_diameters->get_at(i)); } } @@ -1035,9 +1037,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionFloats* filament_densities = config.option("filament_density"); if (filament_densities != nullptr) { m_result.filament_densities.clear(); - m_result.filament_densities.resize(filament_densities->values.size()); - for (size_t i = 0; i < filament_densities->values.size(); ++i) { - m_result.filament_densities[i] = static_cast(filament_densities->values[i]); + m_result.filament_densities.resize(filament_densities->size()); + for (size_t i = 0; i < filament_densities->size(); ++i) { + m_result.filament_densities[i] = static_cast(filament_densities->get_at(i)); } } @@ -1049,9 +1051,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionPoints* extruder_offset = config.option("extruder_offset"); if (extruder_offset != nullptr) { - m_extruder_offsets.resize(extruder_offset->values.size()); - for (size_t i = 0; i < extruder_offset->values.size(); ++i) { - Vec2f offset = extruder_offset->values[i].cast(); + m_extruder_offsets.resize(extruder_offset->size()); + for (size_t i = 0; i < extruder_offset->size(); ++i) { + Vec2f offset = extruder_offset->get_at(i).cast(); m_extruder_offsets[i] = { offset(0), offset(1), 0.0f }; } } @@ -1068,9 +1070,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionStrings* filament_colour = config.option("filament_colour"); // takes colors from config if (extruder_colour != nullptr) - m_result.extruder_colors = extruder_colour->values; + m_result.extruder_colors = extruder_colour->get_values(); if (filament_colour != nullptr) - m_result.filament_colors = filament_colour->values; + m_result.filament_colors = filament_colour->get_values(); // try to replace missing values with filament colors if (filament_colour != nullptr && filament_colour != nullptr && m_result.extruder_colors.size() == m_result.filament_colors.size()) { @@ -1107,17 +1109,17 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionFloats* filament_load_time = config.option("filament_load_time"); if (filament_load_time != nullptr) { - m_time_processor.filament_load_times.resize(filament_load_time->values.size()); - for (size_t i = 0; i < filament_load_time->values.size(); ++i) { - m_time_processor.filament_load_times[i] = static_cast(filament_load_time->values[i]); + m_time_processor.filament_load_times.resize(filament_load_time->size()); + for (size_t i = 0; i < filament_load_time->size(); ++i) { + m_time_processor.filament_load_times[i] = static_cast(filament_load_time->get_at(i)); } } const ConfigOptionFloats* filament_unload_time = config.option("filament_unload_time"); if (filament_unload_time != nullptr) { - m_time_processor.filament_unload_times.resize(filament_unload_time->values.size()); - for (size_t i = 0; i < filament_unload_time->values.size(); ++i) { - m_time_processor.filament_unload_times[i] = static_cast(filament_unload_time->values[i]); + m_time_processor.filament_unload_times.resize(filament_unload_time->size()); + for (size_t i = 0; i < filament_unload_time->size(); ++i) { + m_time_processor.filament_unload_times[i] = static_cast(filament_unload_time->get_at(i)); } } @@ -1129,59 +1131,59 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (use_machine_limits) {// && (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfRepRap)) { const ConfigOptionFloats* machine_max_acceleration_x = config.option("machine_max_acceleration_x"); if (machine_max_acceleration_x != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values; + m_time_processor.machine_limits.machine_max_acceleration_x.set(machine_max_acceleration_x->get_values()); const ConfigOptionFloats* machine_max_acceleration_y = config.option("machine_max_acceleration_y"); if (machine_max_acceleration_y != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values; + m_time_processor.machine_limits.machine_max_acceleration_y.set(machine_max_acceleration_y->get_values()); const ConfigOptionFloats* machine_max_acceleration_z = config.option("machine_max_acceleration_z"); if (machine_max_acceleration_z != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values; + m_time_processor.machine_limits.machine_max_acceleration_z.set(machine_max_acceleration_z->get_values()); const ConfigOptionFloats* machine_max_acceleration_e = config.option("machine_max_acceleration_e"); if (machine_max_acceleration_e != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values; + m_time_processor.machine_limits.machine_max_acceleration_e.set(machine_max_acceleration_e->get_values()); const ConfigOptionFloats* machine_max_feedrate_x = config.option("machine_max_feedrate_x"); if (machine_max_feedrate_x != nullptr) - m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values; + m_time_processor.machine_limits.machine_max_feedrate_x.set(machine_max_feedrate_x->get_values()); const ConfigOptionFloats* machine_max_feedrate_y = config.option("machine_max_feedrate_y"); if (machine_max_feedrate_y != nullptr) - m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values; + m_time_processor.machine_limits.machine_max_feedrate_y.set(machine_max_feedrate_y->get_values()); const ConfigOptionFloats* machine_max_feedrate_z = config.option("machine_max_feedrate_z"); if (machine_max_feedrate_z != nullptr) - m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values; + m_time_processor.machine_limits.machine_max_feedrate_z.set(machine_max_feedrate_z->get_values()); const ConfigOptionFloats* machine_max_feedrate_e = config.option("machine_max_feedrate_e"); if (machine_max_feedrate_e != nullptr) - m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values; + m_time_processor.machine_limits.machine_max_feedrate_e.set(machine_max_feedrate_e->get_values()); const ConfigOptionFloats* machine_max_jerk_x = config.option("machine_max_jerk_x"); if (machine_max_jerk_x != nullptr) - m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values; + m_time_processor.machine_limits.machine_max_jerk_x.set(machine_max_jerk_x->get_values()); const ConfigOptionFloats* machine_max_jerk_y = config.option("machine_max_jerk_y"); if (machine_max_jerk_y != nullptr) - m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values; + m_time_processor.machine_limits.machine_max_jerk_y.set(machine_max_jerk_y->get_values()); const ConfigOptionFloats* machine_max_jerk_z = config.option("machine_max_jerkz"); if (machine_max_jerk_z != nullptr) - m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values; + m_time_processor.machine_limits.machine_max_jerk_z.set(machine_max_jerk_z->get_values()); const ConfigOptionFloats* machine_max_jerk_e = config.option("machine_max_jerk_e"); if (machine_max_jerk_e != nullptr) - m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values; + m_time_processor.machine_limits.machine_max_jerk_e.set(machine_max_jerk_e->get_values()); const ConfigOptionFloats* machine_max_acceleration_extruding = config.option("machine_max_acceleration_extruding"); if (machine_max_acceleration_extruding != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values; + m_time_processor.machine_limits.machine_max_acceleration_extruding.set(machine_max_acceleration_extruding->get_values()); const ConfigOptionFloats* machine_max_acceleration_retracting = config.option("machine_max_acceleration_retracting"); if (machine_max_acceleration_retracting != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values; + m_time_processor.machine_limits.machine_max_acceleration_retracting.set(machine_max_acceleration_retracting->get_values()); // Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead. @@ -1189,24 +1191,24 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) ? "machine_max_acceleration_extruding" : "machine_max_acceleration_travel"); if (machine_max_acceleration_travel != nullptr) - m_time_processor.machine_limits.machine_max_acceleration_travel.values = machine_max_acceleration_travel->values; + m_time_processor.machine_limits.machine_max_acceleration_travel.set(machine_max_acceleration_travel->get_values()); if (std::set{gcfMarlinLegacy, gcfMarlinFirmware, gcfLerdge, gcfRepetier, gcfRepRap}.count(m_flavor) > 0) { const ConfigOptionFloats* machine_min_extruding_rate = config.option("machine_min_extruding_rate"); if (machine_min_extruding_rate != nullptr) - m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values; + m_time_processor.machine_limits.machine_min_extruding_rate.set(machine_min_extruding_rate->get_values()); if (m_flavor == gcfRepRap) { // RRF does not support setting min feedrates. Set zero. - m_time_processor.machine_limits.machine_min_extruding_rate.values.assign(m_time_processor.machine_limits.machine_min_extruding_rate.size(), 0.); + m_time_processor.machine_limits.machine_min_extruding_rate.set(std::vector(m_time_processor.machine_limits.machine_min_extruding_rate.size(), 0.)); } } const ConfigOptionFloats* machine_min_travel_rate = config.option("machine_min_travel_rate"); if (machine_min_travel_rate != nullptr) { - m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values; + m_time_processor.machine_limits.machine_min_travel_rate.set(machine_min_travel_rate->get_values()); if (m_flavor == gcfRepRap) { // RRF does not support setting min feedrates. Set zero. - m_time_processor.machine_limits.machine_min_travel_rate.values.assign(m_time_processor.machine_limits.machine_min_travel_rate.size(), 0.); + m_time_processor.machine_limits.machine_min_travel_rate.set(std::vector(m_time_processor.machine_limits.machine_min_travel_rate.size(), 0.)); } } } @@ -1226,7 +1228,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) { const ConfigOptionBool* silent_mode = config.option("silent_mode"); if (silent_mode != nullptr) { - if (silent_mode->value && m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1) + if (silent_mode->value && m_time_processor.machine_limits.machine_max_acceleration_x.size() > 1) enable_stealth_time_estimator(true); } } diff --git a/src/libslic3r/GCode/PostProcessor.cpp b/src/libslic3r/GCode/PostProcessor.cpp index 6e08ff14ebf..4029409ad4e 100644 --- a/src/libslic3r/GCode/PostProcessor.cpp +++ b/src/libslic3r/GCode/PostProcessor.cpp @@ -261,7 +261,7 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: if (// likely running in SLA mode post_process == nullptr || // no post-processing script - post_process->values.empty()) + post_process->empty()) return false; std::string path; @@ -321,7 +321,7 @@ bool run_post_process_scripts(std::string &src_path, bool make_copy, const std:: remove_output_name_file(); try { - for (const std::string &scripts : post_process->values) { + for (const std::string &scripts : post_process->get_values()) { std::vector lines; boost::split(lines, scripts, boost::is_any_of("\r\n")); for (std::string script : lines) { diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index 763df78c286..1b9ed4be245 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -44,13 +44,13 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_ // Calculate filamet crossections for the multiple extruders. m_filament_crossections.clear(); - for (double r : config.filament_diameter.values) { + for (double r : config.filament_diameter.get_values()) { double a = 0.25f*M_PI*r*r; m_filament_crossections.push_back(float(a)); } m_extruder_names.clear(); - for (const std::string & str: config.tool_name.values) { + for (const std::string & str: config.tool_name.get_values()) { m_extruder_names.push_back(str); } diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 91ecff72ed6..2d14e2d9615 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -77,10 +77,10 @@ uint16_t LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const static double calc_max_layer_height(const PrintConfig &config, double max_object_layer_height) { double max_layer_height = std::numeric_limits::max(); - for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) { - double mlh = config.max_layer_height.get_abs_value(i, config.nozzle_diameter.values[i]); + for (size_t i = 0; i < config.nozzle_diameter.size(); ++ i) { + double mlh = config.max_layer_height.get_abs_value(i, config.nozzle_diameter.get_at(i)); if (mlh == 0.) - mlh = 0.75 * config.nozzle_diameter.values[i]; + mlh = 0.75 * config.nozzle_diameter.get_at(i); max_layer_height = std::min(max_layer_height, mlh); } // The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 9797f884736..b13b659aa9d 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -654,7 +654,7 @@ WipeTower::WipeTower(const PrintConfig& config, const PrintObjectConfig& default } // Read absolute value of first layer speed, if given as percentage, // it is taken over wipe_tower_speed. - m_first_layer_speed = config.get_abs_value("first_layer_speed", m_speed); + m_first_layer_speed = default_object_config.first_layer_speed.get_abs_value(m_speed); if (m_first_layer_speed == 0.f) { // just to make sure autospeed doesn't break it. m_first_layer_speed = m_speed; } @@ -669,7 +669,7 @@ WipeTower::WipeTower(const PrintConfig& config, const PrintObjectConfig& default m_set_extruder_trimpot = config.high_current_on_filament_swap; } // Calculate where the priming lines should be - very naive test not detecting parallelograms etc. - const std::vector& bed_points = config.bed_shape.values; + const std::vector& bed_points = config.bed_shape.get_values(); BoundingBoxf bb(bed_points); m_bed_width = float(bb.size().x()); m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed); @@ -776,23 +776,30 @@ std::vector WipeTower::prime( // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0]. // box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area); - float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f); + float prime_section_width = std::min((m_bed_shape == CircularBed ? 0.45f : 0.9f) * m_bed_width / tools.size(), 60.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); if (m_bed_shape == CircularBed) { cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f); float total_width_half = tools.size() * prime_section_width / 2.f; - cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f)))); + if (m_config->priming_position.value == Vec2d(0,0)) { + cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f)))); + } else { + cleaning_box.translate(m_config->priming_position.value.x(), m_config->priming_position.value.y()); + } + } else { + if (m_config->priming_position.value == Vec2d(0,0)) { + cleaning_box.translate(m_bed_bottom_left); + } else { + cleaning_box.translate(m_config->priming_position.value.x(), m_config->priming_position.value.y()); + } } - else - cleaning_box.translate(m_bed_bottom_left); - std::vector results; // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector. for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { size_t old_tool = m_current_tool; - WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.values, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.get_values(), m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool); @@ -888,7 +895,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool) (tool != (unsigned int)(-1) ? wipe_area+m_depth_traversed-0.5f*m_perimeter_width : m_wipe_tower_depth-m_perimeter_width)); - WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.values, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.get_values(), m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -1330,7 +1337,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() size_t old_tool = m_current_tool; - WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.values, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_config->tool_name.get_values(), m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -1435,7 +1442,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() *Flow::extrusion_spacing_option("brim", brim_region_config), (float)m_nozzle_diameter, (float)m_layer_height, - (m_current_tool < m_config->nozzle_diameter.values.size()) ? m_object_config->get_computed_value("filament_max_overlap", m_current_tool) : 1 + (m_current_tool < m_config->nozzle_diameter.size()) ? m_object_config->get_computed_value("filament_max_overlap", m_current_tool) : 1 ); const double spacing = brim_flow.spacing(); // How many perimeters shall the brim have? diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 0dfa90f206f..2f032b385c6 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -448,11 +448,11 @@ std::string GCodeWriter::toolchange(uint16_t tool_id) if (this->multiple_extruders) { if (FLAVOR_IS(gcfKlipper)) { //check if we can use the tool_name field or not - if (tool_id > 0 && tool_id < this->config.tool_name.values.size() && !this->config.tool_name.values[tool_id].empty() + if (tool_id > 0 && tool_id < this->config.tool_name.size() && !this->config.tool_name.get_at(tool_id).empty() // NOTE: this will probably break if there's more than 10 tools, as it's relying on the // ASCII character table. - && this->config.tool_name.values[tool_id][0] != static_cast(('0' + tool_id))) { - gcode << this->toolchange_prefix() << this->config.tool_name.values[tool_id]; + && this->config.tool_name.get_at(tool_id)[0] != static_cast(('0' + tool_id))) { + gcode << this->toolchange_prefix() << this->config.tool_name.get_at(tool_id); } else { gcode << this->toolchange_prefix() << "extruder"; if (tool_id > 0) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index c406ed76e7d..6992517c1ac 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -319,8 +319,8 @@ void Layer::make_milling_post_process() { if (config.milling_post_process == other_config.milling_post_process && config.milling_extra_size == other_config.milling_extra_size && (config.milling_after_z == other_config.milling_after_z || - this->bottom_z() > std::min(config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.values[0]), - other_config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.values[0])))) { + this->bottom_z() > std::min(config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.get_at(0)), + other_config.milling_after_z.get_abs_value(this->object()->print()->config().milling_diameter.get_at(0))))) { layerms.push_back(other_layerm); done[it - m_regions.begin()] = true; } diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index e07a474b12d..dc7506015ab 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -65,7 +65,7 @@ std::string to_string_nozero(double value, int32_t max_precision) { double intpart; if (modf(value, &intpart) == 0.0) { //shortcut for int - return std::to_string(intpart); + return std::to_string(int64_t(intpart)); } else { std::stringstream ss; //first, get the int part, to see how many digit it takes diff --git a/src/libslic3r/Milling/MillingPostProcess.cpp b/src/libslic3r/Milling/MillingPostProcess.cpp index c08c27ca1c3..98e2e4b1d21 100644 --- a/src/libslic3r/Milling/MillingPostProcess.cpp +++ b/src/libslic3r/Milling/MillingPostProcess.cpp @@ -139,9 +139,9 @@ namespace Slic3r { bool MillingPostProcess::can_be_milled(const Layer* layer) { double max_first_layer = 0; - for (double diam : this->print_config->nozzle_diameter.values) + for (double diam : this->print_config->nozzle_diameter.get_values()) max_first_layer = std::max(max_first_layer, config->milling_after_z.get_abs_value(this->object_config->first_layer_height.get_abs_value(diam))); - return !print_config->milling_diameter.values.empty() && config->milling_post_process + return !print_config->milling_diameter.empty() && config->milling_post_process && layer->bottom_z() >= max_first_layer; } diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 724995e2d37..e143f272707 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -773,8 +773,8 @@ namespace client return vector_opt->get_float(int(current_extruder_id)); if (raw_opt->type() == coFloatsOrPercents) { const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast(raw_opt); - if (!opt_fl_per->values[current_extruder_id].percent) - return opt_fl_per->values[current_extruder_id].value; + if (!opt_fl_per->get_at(current_extruder_id).percent) + return opt_fl_per->get_at(current_extruder_id).value; const ConfigOptionDef* opt_def = print_config_def.get(opt_key); if (!opt_def->ratio_over.empty() && opt_def->ratio_over != "depends") @@ -934,13 +934,14 @@ namespace client if (!vector_opt->is_extruder_size()) ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); } - const ConfigOptionDef* opt_def; + const ConfigOptionDef* opt_def = nullptr; switch (opt.opt->type()) { case coFloat: output.set_d(opt.opt->get_float()); break; case coInt: output.set_i(opt.opt->get_int()); break; case coString: output.set_s(static_cast(opt.opt)->value); break; case coPercent: output.set_d(opt.opt->get_float()); break; - case coPoint: output.set_s(opt.opt->serialize()); break; + case coPoint: + case coGraph: case coEnum: output.set_s(opt.opt->serialize()); break; case coBool: output.set_b(opt.opt->get_bool()); break; case coFloatOrPercent: @@ -956,7 +957,7 @@ namespace client opt_def = print_config_def.get(opt_key); assert(opt_def != nullptr); double v = opt.opt->get_float() * 0.01; // percent to ratio - for (;;) { + if (opt_def) for (;;) { const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); if (opt_parent == nullptr) ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); @@ -985,7 +986,7 @@ namespace client } case coInts: opt_def = print_config_def.get(opt_key); - if (opt_def->is_vector_extruder) { + if (opt_def && opt_def->is_vector_extruder) { output.set_i(int(((ConfigOptionVectorBase*)opt.opt)->get_float(int(ctx->current_extruder_id)))); break; } else @@ -1008,18 +1009,24 @@ namespace client case coStrings: vector_opt = static_cast(opt.opt); if (vector_opt->is_extruder_size()) { - output.set_s(((ConfigOptionStrings*)opt.opt)->values[ctx->current_extruder_id]); + output.set_s(((ConfigOptionStrings*)opt.opt)->get_at(ctx->current_extruder_id)); break; } else ctx->throw_exception("Unknown scalar variable type", opt.it_range); case coPoints: vector_opt = static_cast(opt.opt); if (vector_opt->is_extruder_size()) { - output.set_s(to_string(((ConfigOptionPoints*)opt.opt)->values[ctx->current_extruder_id])); + output.set_s(to_string(((ConfigOptionPoints*)opt.opt)->get_at(ctx->current_extruder_id))); + break; + }else + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + case coGraphs: + vector_opt = static_cast(opt.opt); + if (vector_opt->is_extruder_size()) { + output.set_s(((ConfigOptionGraphs*)opt.opt)->get_at(ctx->current_extruder_id).serialize()); break; }else ctx->throw_exception("Unknown scalar variable type", opt.it_range); - //TODO: coFloatOrPercents default: ctx->throw_exception("Unknown scalar variable type", opt.it_range); } @@ -1044,13 +1051,14 @@ namespace client ctx->throw_exception("Indexing an empty vector variable", opt.it_range); size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); switch (opt.opt->type()) { - case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; - case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; - case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coFloatsOrPercents: output.set_d(static_cast(opt.opt)->values[idx].value); break; - case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; - case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; + case coFloats: output.set_d(static_cast(opt.opt)->get_at(idx)); break; + case coInts: output.set_i(static_cast(opt.opt)->get_at(idx)); break; + case coStrings: output.set_s(static_cast(opt.opt)->get_at(idx)); break; + case coPercents: output.set_d(static_cast(opt.opt)->get_at(idx)); break; + case coFloatsOrPercents: output.set_d(static_cast(opt.opt)->get_at(idx).value); break; + case coPoints: output.set_s(to_string(static_cast(opt.opt)->get_at(idx))); break; + case coGraphs: output.set_s(static_cast(opt.opt)->get_at(idx).serialize()); break; + case coBools: output.set_b(static_cast(opt.opt)->get_at(idx) != 0); break; default: ctx->throw_exception("Unknown vector variable type", opt.it_range); } @@ -1800,11 +1808,11 @@ void PlaceholderParser::parse_custom_variables(const ConfigOptionString& custom_ void PlaceholderParser::parse_custom_variables(const ConfigOptionStrings& filament_custom_variables) { std::map> name2var_array; - const std::vector empty_array(filament_custom_variables.values.size()); + const std::vector empty_array(filament_custom_variables.size()); - for (int extruder_id = 0; extruder_id < filament_custom_variables.values.size(); ++extruder_id) + for (int extruder_id = 0; extruder_id < filament_custom_variables.size(); ++extruder_id) { - std::string raw_text = filament_custom_variables.values[extruder_id]; + std::string raw_text = filament_custom_variables.get_at(extruder_id); boost::erase_all(raw_text, "\r"); std::vector lines; boost::algorithm::split(lines, raw_text, boost::is_any_of("\n")); @@ -1823,7 +1831,7 @@ void PlaceholderParser::parse_custom_variables(const ConfigOptionStrings& filame } } } - append_custom_variables(name2var_array, uint16_t(filament_custom_variables.values.size())); + append_custom_variables(name2var_array, uint16_t(filament_custom_variables.size())); } } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 668257eea6f..6c62a83846c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -298,11 +298,11 @@ void Preset::normalize(DynamicPrintConfig &config) auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); if (nozzle_diameter != nullptr) // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values. - config.set_num_extruders((unsigned int)nozzle_diameter->values.size()); + config.set_num_extruders((unsigned int)nozzle_diameter->size()); if (config.option("filament_diameter") != nullptr) { // This config contains single or multiple filament presets. // Ensure that the filament preset vector options contain the correct number of values. - size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->values.size(); + size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->size(); const auto &defaults = FullPrintConfig::defaults(); for (const std::string &key : Preset::filament_options()) { if (key == "compatible_prints" || key == "compatible_printers") @@ -318,13 +318,13 @@ void Preset::normalize(DynamicPrintConfig &config) auto *opt = config.option(key, false); assert(opt == nullptr || opt->type() == coStrings); if (opt != nullptr && opt->type() == coStrings) - static_cast(opt)->values.resize(n, std::string()); + static_cast(opt)->resize(n, std::string()); } } auto *milling_diameter = dynamic_cast(config.option("milling_diameter")); if (milling_diameter != nullptr) // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values. - config.set_num_milling((unsigned int)milling_diameter->values.size()); + config.set_num_milling((unsigned int)milling_diameter->size()); if (const auto *gap_fill_speed = config.option("gap_fill_speed", false); gap_fill_speed && gap_fill_speed->value <= 0.) { // Legacy conversion. If the gap fill speed is zero, it means the gap fill is not enabled. // Set the new gap_fill_enabled value, so that it will show up in the UI as disabled. @@ -368,7 +368,7 @@ bool is_compatible_with_print(const PresetWithVendorProfile &preset, const Prese return false; auto &condition = preset.preset.compatible_prints_condition(); auto *compatible_prints = dynamic_cast(preset.preset.config.option("compatible_prints")); - bool has_compatible_prints = compatible_prints != nullptr && ! compatible_prints->values.empty(); + bool has_compatible_prints = compatible_prints != nullptr && ! compatible_prints->empty(); if (! has_compatible_prints && ! condition.empty()) { try { return PlaceholderParser::evaluate_boolean_expression(condition, active_print.preset.config); @@ -379,8 +379,8 @@ bool is_compatible_with_print(const PresetWithVendorProfile &preset, const Prese } } return preset.preset.is_default || active_print.preset.name.empty() || ! has_compatible_prints || - std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.preset.name) != - compatible_prints->values.end(); + std::find(compatible_prints->get_values().begin(), compatible_prints->get_values().end(), active_print.preset.name) != + compatible_prints->get_values().end(); } bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config) @@ -390,7 +390,7 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre return false; auto &condition = preset.preset.compatible_printers_condition(); auto *compatible_printers = dynamic_cast(preset.preset.config.option("compatible_printers")); - bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty(); + bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->empty(); if (! has_compatible_printers && ! condition.empty()) { try { return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.preset.config, extra_config); @@ -401,8 +401,8 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre } } return preset.preset.is_default || active_printer.preset.name.empty() || ! has_compatible_printers || - std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.name) != - compatible_printers->values.end(); + std::find(compatible_printers->get_values().begin(), compatible_printers->get_values().end(), active_printer.preset.name) != + compatible_printers->get_values().end(); } bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer) @@ -411,10 +411,10 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name)); const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter"); if (opt) - config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size())); + config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->size())); opt = active_printer.preset.config.option("milling_diameter"); if (opt) - config.set_key_value("num_milling", new ConfigOptionInt((int)static_cast(opt)->values.size())); + config.set_key_value("num_milling", new ConfigOptionInt((int)static_cast(opt)->size())); return is_compatible_with_printer(preset, active_printer, &config); } @@ -679,6 +679,7 @@ static std::vector s_Preset_print_options { "bridge_overlap_min", "first_layer_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin", + "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", // compensation "first_layer_size_compensation", "first_layer_size_compensation_layers", @@ -694,6 +695,7 @@ static std::vector s_Preset_print_options { "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "wipe_tower_speed", "wipe_tower_wipe_starting_speed", "wipe_tower_brim_width", + "priming_position", "mmu_segmented_region_max_width", "single_extruder_multi_material_priming", "wipe_tower_no_sparse_layers", @@ -737,7 +739,7 @@ static std::vector s_Preset_print_options { "milling_speed", //Arachne "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width", }; static std::vector s_Preset_filament_options { @@ -1488,10 +1490,10 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name)); const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter"); if (opt) - config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size())); + config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->size())); opt = active_printer.preset.config.option("milling_diameter"); if (opt) - config.set_key_value("num_milling", new ConfigOptionInt((int)static_cast(opt)->values.size())); + config.set_key_value("num_milling", new ConfigOptionInt((int)static_cast(opt)->size())); bool some_compatible = false; if(m_idx_selected < m_num_default_presets && unselect_if_incompatible != PresetSelectCompatibleType::Never) m_idx_selected = size_t(-1); @@ -1536,11 +1538,11 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& { const T* opt_init = static_cast(other.option(opt_key)); const T* opt_cur = static_cast(this_c.option(opt_key)); - int opt_init_max_id = opt_init->values.size() - 1; - for (int i = 0; i < int(opt_cur->values.size()); i++) + int opt_init_max_id = opt_init->size() - 1; + for (int i = 0; i < int(opt_cur->size()); i++) { int init_id = i <= opt_init_max_id ? i : 0; - if (opt_init_max_id < 0 || opt_cur->values[i] != opt_init->values[init_id]) + if (opt_init_max_id < 0 || opt_cur->get_at(i) != opt_init->get_at(init_id)) vec.emplace_back(opt_key + "#" + std::to_string(i)); } } @@ -1931,10 +1933,7 @@ static void update_preset_name_option(const std::set& preset_names, void PhysicalPrinter::update_preset_names_in_config() { if (!preset_names.empty()) { - std::vector& values = config.option("preset_names")->values; - values.clear(); - for (auto preset : preset_names) - values.push_back(preset); + config.option("preset_names")->set(std::vector(preset_names.begin(), preset_names.end())); // temporary workaround for compatibility with older Slicer update_preset_name_option(preset_names, config); @@ -1962,7 +1961,7 @@ void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) { config.apply_only(new_config, printer_options(), false); - const std::vector& values = config.option("preset_names")->values; + const std::vector& values = config.option("preset_names")->get_values(); if (values.empty()) preset_names.clear(); diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 98adfb2e1d8..d471c03f71d 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -57,7 +57,7 @@ PresetBundle::PresetBundle() : this->fff_prints.default_preset().compatible_printers_condition(); this->fff_prints.default_preset().inherits(); - this->filaments.default_preset().config.option("filament_settings_id", true)->values = { "" }; + this->filaments.default_preset().config.option("filament_settings_id", true)->set({""}); this->filaments.default_preset().compatible_printers_condition(); this->filaments.default_preset().inherits(); // Set all the nullable values to nils. @@ -643,7 +643,7 @@ DynamicPrintConfig PresetBundle::full_fff_config() const out.apply(this->project_config); auto *nozzle_diameter = dynamic_cast(out.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); + size_t num_extruders = nozzle_diameter->size(); // Collect the "compatible_printers_condition" and "inherits" values over all presets (print, filaments, printers) into a single vector. std::vector compatible_printers_condition; std::vector compatible_prints_condition; @@ -713,7 +713,7 @@ DynamicPrintConfig PresetBundle::full_fff_config() const } out.option("print_settings_id", true)->value = this->fff_prints.get_selected_preset_name(); - out.option("filament_settings_id", true)->values = this->filament_presets; + out.option("filament_settings_id", true)->set(this->filament_presets); out.option("printer_settings_id", true)->value = this->printers.get_selected_preset_name(); out.option("physical_printer_settings_id", true)->value = this->physical_printers.get_selected_printer_name(); @@ -868,21 +868,21 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool if (opt_compatible != nullptr) { assert(opt_compatible->type() == coStrings); if (opt_compatible->type() == coStrings) - static_cast(opt_compatible)->values.clear(); + static_cast(opt_compatible)->clear(); } } size_t num_extruders = (printer_technology == ptFFF) ? - std::min(config.option("nozzle_diameter" )->values.size(), - config.option("filament_diameter")->values.size()) : + std::min(config.option("nozzle_diameter" )->size(), + config.option("filament_diameter")->size()) : // 1 SLA material 1; // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which // accumulate values over all presets (print, filaments, printers). // These values will be distributed into their particular presets when loading. - std::vector compatible_printers_condition_values = std::move(config.option("compatible_printers_condition_cummulative", true)->values); - std::vector compatible_prints_condition_values = std::move(config.option("compatible_prints_condition_cummulative", true)->values); - std::vector inherits_values = std::move(config.option("inherits_cummulative", true)->values); + std::vector compatible_printers_condition_values = std::move(config.option("compatible_printers_condition_cummulative", true)->get_values()); + std::vector compatible_prints_condition_values = std::move(config.option("compatible_prints_condition_cummulative", true)->get_values()); + std::vector inherits_values = std::move(config.option("inherits_cummulative", true)->get_values()); std::string &compatible_printers_condition = Preset::compatible_printers_condition(config); std::string &compatible_prints_condition = Preset::compatible_prints_condition(config); std::string &inherits = Preset::inherits(config); @@ -934,7 +934,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 3) Now load the filaments. If there are multiple filament presets, split them and load them. auto old_filament_profile_names = config.option("filament_settings_id", true); - old_filament_profile_names->values.resize(num_extruders, std::string()); + old_filament_profile_names->resize(num_extruders, std::string()); if (num_extruders <= 1) { // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. @@ -942,9 +942,10 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool compatible_printers_condition = compatible_printers_condition_values[1]; compatible_prints_condition = compatible_prints_condition_values.front(); Preset *loaded = nullptr; - if (is_external) - loaded = this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config).first; - else { + if (is_external) { + assert(!old_filament_profile_names->empty()); + loaded = this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->get_at(0), config).first; + } else { // called from Config Wizard. loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config); loaded->save(); @@ -982,7 +983,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool cfg.opt_string("inherits", true) = inherits_values[i + 1]; // Load all filament presets, but only select the first one in the preset dialog. auto [loaded, modified] = this->filaments.load_external_preset(name_or_path, name, - (i < int(old_filament_profile_names->values.size())) ? old_filament_profile_names->values[i] : "", + (i < int(old_filament_profile_names->size())) ? old_filament_profile_names->get_at(i) : "", std::move(cfg), i == 0 ? PresetCollection::LoadAndSelect::Always : @@ -1544,7 +1545,7 @@ void PresetBundle::update_multi_material_filament_presets() // Verify and select the filament presets. auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); + size_t num_extruders = nozzle_diameter->size(); // Verify validity of the current filament presets. for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; @@ -1552,19 +1553,20 @@ void PresetBundle::update_multi_material_filament_presets() this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator): - std::vector old_matrix = this->project_config.option("wiping_volumes_matrix")->values; + std::vector old_matrix = this->project_config.option("wiping_volumes_matrix")->get_values(); size_t old_number_of_extruders = size_t(sqrt(old_matrix.size())+EPSILON); if (num_extruders != old_number_of_extruders) { - // First verify if purging volumes presets for each extruder matches number of extruders - std::vector& extruders = this->project_config.option("wiping_volumes_extruders")->values; - while (extruders.size() < 2*num_extruders) { - extruders.push_back(extruders.size()>1 ? extruders[0] : 50.); // copy the values from the first extruder - extruders.push_back(extruders.size()>1 ? extruders[1] : 50.); - } - while (extruders.size() > 2*num_extruders) { - extruders.pop_back(); - extruders.pop_back(); - } + // First verify if purging volumes presets for each extruder matches number of extruders + std::vector extruders = this->project_config.option("wiping_volumes_extruders")->get_values(); + while (extruders.size() < 2*num_extruders) { + extruders.push_back(extruders.size()>1 ? extruders[0] : 50.); // copy the values from the first extruder + extruders.push_back(extruders.size()>1 ? extruders[1] : 50.); + } + while (extruders.size() > 2*num_extruders) { + extruders.pop_back(); + extruders.pop_back(); + } + this->project_config.option("wiping_volumes_extruders")->set(extruders); std::vector new_matrix; for (unsigned int i=0;iproject_config.option("wiping_volumes_matrix")->values = new_matrix; + this->project_config.option("wiping_volumes_matrix")->set(new_matrix); } } @@ -1697,7 +1699,7 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri { assert(printer_preset.config.has("default_print_profile")); assert(printer_preset.config.has("default_filament_profile")); - const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; + const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->get_values(); this->fff_prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_print_if_incompatible, PreferedPrintProfileMatch(this->fff_prints.get_selected_idx() == size_t(-1) ? nullptr : &this->fff_prints.get_edited_preset(), printer_preset.config.opt_string("default_print_profile"))); const PresetWithVendorProfile print_preset_with_vendor_profile = this->fff_prints.get_edited_preset_with_vendor_profile(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cbf6f656965..40dad440ad5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -287,6 +287,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "first_layer_temperature" || opt_key == "gcode_flavor" || opt_key == "high_current_on_filament_swap" + || opt_key == "priming_position" || opt_key == "single_extruder_multi_material" || opt_key == "temperature" || opt_key == "wipe_tower" @@ -615,7 +616,7 @@ double Print::get_object_first_layer_height(const PrintObject& object) const { } object_first_layer_height = 1000000000; for (uint16_t extruder_id : object_extruders) { - double nozzle_diameter = config().nozzle_diameter.values[extruder_id]; + double nozzle_diameter = config().nozzle_diameter.get_at(extruder_id); object_first_layer_height = std::min(object_first_layer_height, object.config().first_layer_height.get_abs_value(nozzle_diameter)); } } @@ -974,7 +975,7 @@ Flow Print::brim_flow(size_t extruder_id, const PrintObjectConfig& brim_config) *Flow::extrusion_spacing_option("brim", tempConf), (float)m_config.nozzle_diameter.get_at(extruder_id), (float)get_first_layer_height(), - (extruder_id < m_config.nozzle_diameter.values.size()) ? brim_config.get_computed_value("filament_max_overlap", extruder_id) : 1 + (extruder_id < m_config.nozzle_diameter.size()) ? brim_config.get_computed_value("filament_max_overlap", extruder_id) : 1 ); } @@ -993,7 +994,7 @@ Flow Print::skirt_flow(size_t extruder_id, bool first_layer/*=false*/) const } //get object first layer extruder diam for (uint16_t extruder_id : object_extruders) { - double nozzle_diameter = config().nozzle_diameter.values[extruder_id]; + double nozzle_diameter = config().nozzle_diameter.get_at(extruder_id); max_nozzle_diam = std::max(max_nozzle_diam, nozzle_diameter); } } @@ -1027,7 +1028,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const if (model_object->volumes.size() < 2) return; -// size_t extruders = m_config.nozzle_diameter.values.size(); +// size_t extruders = m_config.nozzle_diameter.size(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { ModelVolume *volume = model_object->volumes[volume_id]; //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. @@ -1551,7 +1552,7 @@ bool Print::has_wipe_tower() const return ! m_config.spiral_vase.value && m_config.wipe_tower.value && - m_config.nozzle_diameter.values.size() > 1; + m_config.nozzle_diameter.size() > 1; } const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double nozzle_diameter) const @@ -1576,7 +1577,7 @@ void Print::_make_wipe_tower() return; // Get wiping matrix to get number of extruders and convert vector to vector: - std::vector wiping_matrix(cast(m_config.wiping_volumes_matrix.values)); + std::vector wiping_matrix(cast(m_config.wiping_volumes_matrix.get_values())); // Extract purging volumes for each extruder pair: std::vector> wipe_volumes; const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e0a16ddcaf6..741682dfeaa 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1480,7 +1480,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(105, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(105, true)); def = this->add("external_perimeter_extrusion_spacing", coFloatOrPercent); def->label = L("External perimeters"); @@ -1497,7 +1497,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("external_perimeter_extrusion_change_odd_layers", coFloatOrPercent); def->label = L("External perimeters"); @@ -1719,13 +1719,31 @@ void PrintConfigDef::init_fff_params() def->is_vector_extruder = true; def->set_default_value(new ConfigOptionStrings{ "" }); - def = this->add("extruder_extrusion_multiplier_speed", coStrings); + def = this->add("extruder_extrusion_multiplier_speed", coGraphs); def->label = L("Extrusion multipler"); def->tooltip = L("This string is edited by a Dialog and contains extusion multiplier for different speeds."); def->mode = comExpert | comSuSi; def->is_vector_extruder = true; - def->set_default_value(new ConfigOptionStrings { "0 1 1 1 1 1 1 1 1 1 1 1|" - " 10 1. 20 1. 30 1. 40 1. 60 1. 80 1. 120 1. 160 1. 240 1. 320 1. 480 1. 640 1. 960 1. 1280 1." }); + def->set_default_value(new ConfigOptionGraphs( GraphData(0,10, GraphData::GraphType::LINEAR, + {{10,1.},{20,1.},{30,1.},{40,1.},{60,1.},{80,1.},{120,1.},{160,1.},{240,1.},{320,1.},{480,1.},{640,1.},{960,1.},{1280,1.}} + ))); + def->graph_settings = std::make_shared(); + def->graph_settings->title = L("Extrusion multiplier per extrusion speed"); + def->graph_settings->description = L("Choose the extrusion multipler value for multiple speeds.\nYou can add/remove points with a right clic."); + def->graph_settings->x_label = L("Print speed (mm/s)"); + def->graph_settings->y_label = L("Extrusion multiplier"); + def->graph_settings->null_label = L("No compensation"); + def->graph_settings->label_min_x = L("Graph min speed"); + def->graph_settings->label_max_x = L("Graph max speed"); + def->graph_settings->label_min_y = L("Minimum flow"); + def->graph_settings->label_max_y = L("Maximum flow"); + def->graph_settings->min_x = 10; + def->graph_settings->max_x = 2000; + def->graph_settings->step_x = 1.; + def->graph_settings->min_y = 0.1; + def->graph_settings->max_y = 2; + def->graph_settings->step_y = 0.1; + def->graph_settings->allowed_types = {GraphData::GraphType::LINEAR, GraphData::GraphType::SQUARE}; def = this->add("extruder_offset", coPoints); def->label = L("Extruder offset"); @@ -1811,7 +1829,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comExpert | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("extrusion_spacing", coFloatOrPercent); def->label = L("Default extrusion spacing"); @@ -1827,7 +1845,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); #if 0 //not used anymore, to remove !! @DEPRECATED (replaces by default_fan_speed) @@ -2492,6 +2510,42 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("%"); def->set_default_value(new ConfigOptionPercent(10)); + def = this->add("small_area_infill_flow_compensation", coBool); + def->label = L("Enable small area flow compensation"); + def->category = OptionCategory::infill; + def->tooltip = L("Enable flow compensation for small infill areas." + "\nFirst layer is always disabled, to not compromise adhesion."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("small_area_infill_flow_compensation_model", coGraph); + def->label = L("Flow Compensation Model"); + def->category = OptionCategory::infill; + def->tooltip = L("Flow Compensation Model, used to adjust the flow for small solid infill " + "lines. The model is a graph of flow correction factors (between 0 and 1) per extrusion length (in mm)." + "\nThe first point length has to be 0mm. the last point need to have a flow correction of 1."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionGraph(GraphData(0,10, GraphData::GraphType::SPLINE, + {{0,0},{0.2,0.44},{0.4,0.61},{0.6,0.7},{0.8,0.76},{1.5,0.86},{2,0.89},{3,0.92},{5,0.95},{10,1}} + ))); + def->graph_settings = std::make_shared(); + def->graph_settings->title = L("Flow Compensation Model"); + def->graph_settings->description = def->tooltip; + def->graph_settings->x_label = L("Length of an extrusion (mm)"); + def->graph_settings->y_label = L("Flow correction (ratio between 0 and 1)"); + def->graph_settings->null_label = L("No values"); + def->graph_settings->label_min_x = ""; + def->graph_settings->label_max_x = L("Maximum length"); + def->graph_settings->label_min_y = L("Minimum ratio"); + def->graph_settings->label_max_y = L("Maximum ratio"); + def->graph_settings->min_x = 0; + def->graph_settings->max_x = 100; + def->graph_settings->step_x = 0.1; + def->graph_settings->min_y = 0; + def->graph_settings->max_y = 1; + def->graph_settings->step_y = 0.01; + def->graph_settings->allowed_types = {GraphData::GraphType::LINEAR, GraphData::GraphType::SPLINE, GraphData::GraphType::SQUARE}; + def = this->add("first_layer_acceleration", coFloatOrPercent); def->label = L("Max"); def->full_label = L("First layer acceleration"); @@ -2548,7 +2602,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(140, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(140, true)); def = this->add("first_layer_extrusion_spacing", coFloatOrPercent); def->label = L("First layer"); @@ -2564,7 +2618,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("first_layer_height", coFloatOrPercent); def->label = L("First layer height"); @@ -3162,7 +3216,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("infill_extrusion_change_odd_layers", coFloatOrPercent); def->label = L("Infill"); @@ -3191,7 +3245,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); def = this->add("infill_fan_speed", coInts); def->label = L("Internal Infill fan speed"); @@ -4231,7 +4285,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("perimeter_extrusion_spacing", coFloatOrPercent); def->label = L("Perimeters"); @@ -4249,7 +4303,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); def = this->add("perimeter_extrusion_change_odd_layers", coFloatOrPercent); def->label = L("Perimeters"); @@ -4393,6 +4447,17 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comPrusa; def->set_default_value(new ConfigOptionStrings()); + def = this->add("priming_position", coPoint); + def->label = L("Priming position"); + def->full_label = L("Priming position"); + def->tooltip = L("Coordinates of the left front corner of the priming patch." + "\nIf set to 0,0 then the position is computed automatically."); + //TODO: enable/disable + def->category = OptionCategory::customgcode; + def->sidetext = L("mm"); + def->mode = comAdvancedE | comSuSi; + def->set_default_value(new ConfigOptionPoint(Vec2d(0,0))); + def = this->add("printer_custom_variables", coString); def->label = L("Custom variables"); def->full_label = L("Custom Printer variables"); @@ -5140,7 +5205,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("solid_infill_extrusion_change_odd_layers", coFloatOrPercent); def->label = L("Infill"); @@ -5169,7 +5234,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(100, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(100, true)); def = this->add("solid_infill_fan_speed", coInts); def->label = L("Solid Infill fan speed"); @@ -5328,15 +5393,6 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("wipe_tower_no_sparse_layers", coBool); - def->label = L("No sparse layers (EXPERIMENTAL)"); - def->category = OptionCategory::mmsetup; - def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. " - "On layers with a toolchange, extruder will travel downward to print the wipe tower. " - "User is responsible for ensuring there is no collision with the print."); - def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionBool(false)); def = this->add("solid_infill_acceleration", coFloatOrPercent); def->label = L("Solid "); @@ -6050,7 +6106,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comPrusa; - def->set_default_value(new ConfigOptionFloatOrPercent(105, true, false)); + def->set_default_value(new ConfigOptionFloatOrPercent(105, true)); def = this->add("top_infill_extrusion_spacing", coFloatOrPercent); def->label = L("Top solid spacing"); @@ -6065,7 +6121,7 @@ void PrintConfigDef::init_fff_params() def->precision = 6; def->can_phony = true; def->mode = comAdvancedE | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0, false, true)); + def->set_default_value((new ConfigOptionFloatOrPercent(0, false))->set_phony(true)); def = this->add("top_solid_infill_acceleration", coFloatOrPercent); def->label = L("Top solid "); @@ -6377,6 +6433,14 @@ void PrintConfigDef::init_fff_params() def->aliases = { "wipe_tower_brim" }; // SuperSlicer 2.3 and before def->set_default_value(new ConfigOptionFloatOrPercent(2,false)); + def = this->add("wipe_tower_no_sparse_layers", coBool); + def->label = L("No sparse layers (EXPERIMENTAL)"); + def->category = OptionCategory::mmsetup; + def->tooltip = L("If enabled, the wipe tower will not be printed on layers with no toolchanges. " + "On layers with a toolchange, extruder will travel downward to print the wipe tower. " + "User is responsible for ensuring there is no collision with the print."); + def->mode = comAdvancedE | comPrusa; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("wipe_tower_x", coFloat); def->label = L("X"); def->full_label = L("Wipe tower X"); @@ -6385,6 +6449,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionFloat(180.)); + def = this->add("wipe_tower_y", coFloat); def->label = L("Y"); def->full_label = L("Wipe tower Y"); @@ -8353,7 +8418,6 @@ std::unordered_set prusa_export_to_remove_keys = { "gcode_precision_e", "gcode_precision_xyz", "hole_size_compensation", -"hole_size_compensations_curve", "hole_size_threshold", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", @@ -8418,6 +8482,7 @@ std::unordered_set prusa_export_to_remove_keys = { "perimeter_reverse", "perimeter_round_corners", "perimeters_hole", +"priming_position", "print_extrusion_multiplier", "print_first_layer_temperature", "print_custom_variables", @@ -8450,6 +8515,8 @@ std::unordered_set prusa_export_to_remove_keys = { "skirt_brim", "skirt_distance_from_brim", "skirt_extrusion_width", +"small_area_infill_flow_compensation", +"small_area_infill_flow_compensation_model", "small_perimeter_max_length", "small_perimeter_min_length", "solid_fill_pattern", @@ -8598,9 +8665,9 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& if (SupportZDistanceType::zdNone == dist_type) { value = "0"; } else { - double val = all_conf.option("support_material_contact_distance_top")->get_abs_value(all_conf.option("nozzle_diameter")->values.front()); + double val = all_conf.option("support_material_contact_distance_top")->get_abs_value(all_conf.option("nozzle_diameter")->get_at(0)); if (SupportZDistanceType::zdFilament == dist_type) { // not exact but good enough effort - val += all_conf.option("nozzle_diameter")->values.front(); + val += all_conf.option("nozzle_diameter")->get_at(0); val -= all_conf.get_computed_value("layer_height", 0); } value = boost::lexical_cast(val); @@ -8618,9 +8685,9 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& if (SupportZDistanceType::zdNone == dist_type) { value = "0"; } else { - double val = all_conf.option("support_material_contact_distance_bottom")->get_abs_value(all_conf.option("nozzle_diameter")->values.front()); + double val = all_conf.option("support_material_contact_distance_bottom")->get_abs_value(all_conf.option("nozzle_diameter")->get_at(0)); if (SupportZDistanceType::zdFilament == dist_type) { // not exact but good enough effort - val += all_conf.option("nozzle_diameter")->values.front(); + val += all_conf.option("nozzle_diameter")->get_at(0); val -= all_conf.get_computed_value("layer_height", 0); } value = boost::lexical_cast(val); @@ -8644,7 +8711,7 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& const ConfigOptionFloatOrPercent* opt = all_conf.option(opt_key); if (opt->is_phony() || opt->percent) { if (opt->percent) { - ConfigOptionFloat opt_temp{ opt->get_abs_value(all_conf.option("nozzle_diameter")->values.front()) }; + ConfigOptionFloat opt_temp{ opt->get_abs_value(all_conf.option("nozzle_diameter")->get_at(0)) }; value = opt_temp.serialize(); } else { //bypass the phony kill switch from Config::opt_serialize @@ -8855,7 +8922,7 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) if (config->option("parallel_objects_step")->get_float() > 0 || co_opt && co_opt->value) { double skirt_dist = 0; try { - std::vector vals = dynamic_cast(config->option("nozzle_diameter"))->values; + std::vector vals = dynamic_cast(config->option("nozzle_diameter"))->get_values(); double max_nozzle_diam = 0; for (double val : vals) max_nozzle_diam = std::fmax(max_nozzle_diam, val); @@ -8948,10 +9015,10 @@ void DynamicPrintConfig::normalize_fdm() { // this should be actually done only on the spiral layers instead of all auto* opt = this->opt("retract_layer_change", true); - opt->values.assign(opt->values.size(), false); // set all values to false + opt->set(std::vector(opt->size(), false)); // set all values to false // Disable retract on layer change also for filament overrides. auto* opt_n = this->opt("filament_retract_layer_change", true); - opt_n->values.assign(opt_n->values.size(), false); // Set all values to false. + opt_n->set(std::vector(opt_n->size(), false)); // Set all values to false. } { this->opt("top_solid_layers", true)->value = 0; @@ -8993,17 +9060,17 @@ void handle_legacy_sla(DynamicPrintConfig& config) if (config.has(corr)) { if (std::string corr_x = corr + "_x"; !config.has(corr_x)) { auto* opt = config.opt(corr_x, true); - opt->value = config.opt(corr)->values[0]; + opt->value = config.opt(corr)->get_at(0); } if (std::string corr_y = corr + "_y"; !config.has(corr_y)) { auto* opt = config.opt(corr_y, true); - opt->value = config.opt(corr)->values[0]; + opt->value = config.opt(corr)->get_at(0); } if (std::string corr_z = corr + "_z"; !config.has(corr_z)) { auto* opt = config.opt(corr_z, true); - opt->value = config.opt(corr)->values[1]; + opt->value = config.opt(corr)->get_at(1); } } } @@ -9124,7 +9191,7 @@ std::set DynamicPrintConfig::value_changed(const t_co if (layer_height_option && spacing_option && nozzle_diameter_option) { //compute spacing with current height and change the width double max_nozzle_diameter = 0; - for (double dmr : nozzle_diameter_option->values) + for (double dmr : nozzle_diameter_option->get_values()) max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); double spacing_value = spacing_option->get_abs_value(max_nozzle_diameter); float overlap_ratio = 1; @@ -9270,7 +9337,7 @@ std::set DynamicPrintConfig::value_changed(const t_co if (layer_height_option && width_option && nozzle_diameter_option) { //compute spacing with current height and change the width float max_nozzle_diameter = 0; - for (double dmr : nozzle_diameter_option->values) + for (double dmr : nozzle_diameter_option->get_values()) max_nozzle_diameter = std::max(max_nozzle_diameter, (float)dmr); ConfigOptionFloatOrPercent* spacing_option = nullptr; try { @@ -9474,12 +9541,12 @@ std::string validate(const FullPrintConfig& cfg) return "Invalid value for --first-layer-height"; // --filament-diameter - for (double fd : cfg.filament_diameter.values) + for (double fd : cfg.filament_diameter.get_values()) if (fd < 1) return "Invalid value for --filament-diameter"; // --nozzle-diameter - for (double nd : cfg.nozzle_diameter.values) + for (double nd : cfg.nozzle_diameter.get_values()) if (nd < 0.005) return "Invalid value for --nozzle-diameter"; @@ -9506,7 +9573,7 @@ std::string validate(const FullPrintConfig& cfg) return "--use-firmware-retraction is only supported by Marlin 1&2, Smoothie, Sprinter, Reprap, Repetier, Machinekit, Repetier, Klipper? and Lerdge firmware"; if (cfg.use_firmware_retraction.value) - for (unsigned char wipe : cfg.wipe.values) + for (unsigned char wipe : cfg.wipe.get_values()) if (wipe) return "--use-firmware-retraction is not compatible with --wipe"; @@ -9556,7 +9623,7 @@ std::string validate(const FullPrintConfig& cfg) return "Invalid value for --extruder-clearance-height"; // --extrusion-multiplier - for (double em : cfg.extrusion_multiplier.values) + for (double em : cfg.extrusion_multiplier.get_values()) if (em <= 0) return "Invalid value for --extrusion-multiplier"; @@ -9587,7 +9654,7 @@ std::string validate(const FullPrintConfig& cfg) // extrusion widths { double max_nozzle_diameter = 0.; - for (double dmr : cfg.nozzle_diameter.values) + for (double dmr : cfg.nozzle_diameter.get_values()) max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); const char *widths[] = { "", "external_perimeter_", "perimeter_", "infill_", "solid_infill_", "top_infill_", "support_material_", "first_layer_", "skirt_" }; for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { @@ -9621,14 +9688,14 @@ std::string validate(const FullPrintConfig& cfg) } case coPercents: case coFloats: - for (double v : static_cast*>(opt)->values) + for (double v : static_cast*>(opt)->get_values()) if (v < optdef->min || v > optdef->max) { out_of_range = true; break; } break; case coFloatsOrPercents: - for (FloatOrPercent v : static_cast*>(opt)->values) + for (FloatOrPercent v : static_cast*>(opt)->get_values()) if (v.value < optdef->min || v.value > optdef->max) { out_of_range = true; break; @@ -9641,7 +9708,7 @@ std::string validate(const FullPrintConfig& cfg) break; } case coInts: - for (int v : static_cast*>(opt)->values) + for (int v : static_cast*>(opt)->get_values()) if (v < optdef->min || v > optdef->max) { out_of_range = true; break; @@ -9953,15 +10020,15 @@ Points get_bed_shape(const DynamicPrintConfig &config) return {}; } - return to_points(bed_shape_opt->values); + return to_points(bed_shape_opt->get_values()); } Points get_bed_shape(const PrintConfig &cfg) { - return to_points(cfg.bed_shape.values); + return to_points(cfg.bed_shape.get_values()); } -Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.bed_shape.values); } +Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.bed_shape.get_values()); } } // namespace Slic3r diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5cca571ff41..099a2716b89 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -952,6 +952,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, print_retract_length)) ((ConfigOptionFloat, print_retract_lift)) ((ConfigOptionString, region_gcode)) + ((ConfigOptionBool, small_area_infill_flow_compensation)) + ((ConfigOptionGraph, small_area_infill_flow_compensation_model)) ((ConfigOptionFloatOrPercent, small_perimeter_speed)) ((ConfigOptionFloatOrPercent, small_perimeter_min_length)) ((ConfigOptionFloatOrPercent, small_perimeter_max_length)) @@ -1053,7 +1055,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionString, end_gcode)) ((ConfigOptionStrings, end_filament_gcode)) ((ConfigOptionFloat, extra_loading_move)) - ((ConfigOptionStrings, extruder_extrusion_multiplier_speed)) + ((ConfigOptionGraphs, extruder_extrusion_multiplier_speed)) ((ConfigOptionPercents, extruder_fan_offset)) ((ConfigOptionFloats, extruder_temperature_offset)) ((ConfigOptionString, extrusion_axis)) @@ -1245,6 +1247,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInts, overhangs_fan_speed)) ((ConfigOptionInts, perimeter_fan_speed)) ((ConfigOptionStrings, post_process)) + ((ConfigOptionPoint, priming_position)) ((ConfigOptionString, print_custom_variables)) ((ConfigOptionString, printer_custom_variables)) ((ConfigOptionString, printer_model)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c029b894982..20fc7fc317a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1031,6 +1031,8 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() || opt_key == "seam_notch_outer" || opt_key == "seam_travel_cost" || opt_key == "seam_visibility" + || opt_key == "small_area_infill_flow_compensation" + || opt_key == "small_area_infill_flow_compensation_model" || opt_key == "small_perimeter_speed" || opt_key == "small_perimeter_min_length" || opt_key == "small_perimeter_max_length" @@ -1339,7 +1341,7 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() //check if small enough double max_nozzle_diam = 0; for (uint16_t extruder_id : object_extruders()) { - max_nozzle_diam = std::max(max_nozzle_diam, print()->config().nozzle_diameter.values[extruder_id]); + max_nozzle_diam = std::max(max_nozzle_diam, print()->config().nozzle_diameter.get_at(extruder_id)); } coordf_t min_width = scale_d(max_nozzle_diam) / region->config().fill_density.get_abs_value(1.); ExPolygons smalls = offset_ex(intersect, -min_width); @@ -2660,7 +2662,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau if (config().first_layer_height.percent) { object_first_layer_height = 1000000000; for (uint16_t extruder_id : object_extruders()) { - double nozzle_diameter = print()->config().nozzle_diameter.values[extruder_id]; + double nozzle_diameter = print()->config().nozzle_diameter.get_at(extruder_id); object_first_layer_height = std::fmin(object_first_layer_height, config().first_layer_height.get_abs_value(nozzle_diameter)); } } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 22c2739f532..21ea8023e85 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1068,13 +1068,13 @@ Vec3d SLAPrint::relative_correction() const { Vec3d corr(1., 1., 1.); - if(printer_config().relative_correction.values.size() >= 2) { + if(printer_config().relative_correction.size() >= 2) { corr.x() = printer_config().relative_correction_x.value; corr.y() = printer_config().relative_correction_y.value; corr.z() = printer_config().relative_correction_z.value; } - if(material_config().material_correction.values.size() >= 2) { + if(material_config().material_correction.size() >= 2) { corr.x() *= material_config().material_correction_x.value; corr.y() *= material_config().material_correction_y.value; corr.z() *= material_config().material_correction_z.value; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 2b6318bce3b..5d3dd1f538e 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -94,7 +94,7 @@ std::shared_ptr SlicingParameters::create_from_config( for (uint16_t extruder_id : object_extruders) { if (print_config.nozzle_diameter.size() <= extruder_id) break; - double nozzle_diameter = print_config.nozzle_diameter.values[extruder_id]; + double nozzle_diameter = print_config.nozzle_diameter.get_at(extruder_id); first_layer_height = std::min(first_layer_height, object_config.first_layer_height.get_abs_value(nozzle_diameter)); } if (first_layer_height == 1000000000.) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index be84a97c838..f947923285a 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -360,7 +360,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_support_params.support_layer_height_min = 1000000.; const ConfigOptionFloatsOrPercents& min_layer_height = m_print_config->min_layer_height; const ConfigOptionFloats& nozzle_diameter = m_print_config->nozzle_diameter; - for (int extr_id = 0; extr_id < min_layer_height.values.size(); ++extr_id) { + for (int extr_id = 0; extr_id < min_layer_height.size(); ++extr_id) { double min_from_extr = min_layer_height.get_abs_value(extr_id, nozzle_diameter.get_at(extr_id)); if(min_from_extr > 0) m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, min_from_extr); @@ -370,7 +370,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, layer->height); } if (m_support_params.support_layer_height_min >= 1000000.) { - for (int extr_id = 0; extr_id < min_layer_height.values.size(); ++extr_id) { + for (int extr_id = 0; extr_id < min_layer_height.size(); ++extr_id) { m_support_params.support_layer_height_min = std::min(m_support_params.support_layer_height_min, nozzle_diameter.get_at(extr_id) / 10); } } diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 25c31bfd64b..d2f3f8667e7 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -387,7 +387,7 @@ std::tuple Bed3D::detect_type(const const Preset* curr = &bundle->printers.get_selected_preset(); while (curr != nullptr) { if (curr->config.has("bed_shape")) { - if (shape == dynamic_cast(curr->config.option("bed_shape"))->values) { + if (shape == dynamic_cast(curr->config.option("bed_shape"))->get_values()) { std::string model_filename = PresetUtils::system_printer_bed_model(*curr); std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr); if (!model_filename.empty() && !texture_filename.empty()) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 531d7fcc7ab..79a22c4a4a4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1040,7 +1040,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con if (filamemts_opt == nullptr) return; - unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + unsigned int colors_count = std::max((unsigned int)extruders_opt->size(), (unsigned int)filamemts_opt->size()); if (colors_count == 0) return; colors.resize(colors_count); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 14c1db6cdc2..1b1b841cd44 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -206,7 +206,7 @@ void BackgroundSlicingProcess::process_sla() const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path); ThumbnailsList thumbnails = this->render_thumbnails( - ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); + ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->get_values(), true, true, true, true}); Zipper zipper(export_path); m_sla_archive->export_print(zipper, *m_sla_print); @@ -805,7 +805,7 @@ void BackgroundSlicingProcess::prepare_upload() m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); ThumbnailsList thumbnails = this->render_thumbnails( - ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); + ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->get_values(), true, true, true, true}); // true, false, true, true); // renders also supports and pad Zipper zipper{source_path.string()}; m_sla_archive->export_print(zipper, *m_sla_print, m_upload_job.upload_data.upload_path.string()); diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index ec877bd4c14..63ef634c397 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -23,8 +23,8 @@ namespace GUI { BedShape::BedShape(const ConfigOptionPoints& points) { - m_build_volume = { points.values, 0. }; - } + m_build_volume = { points.get_values(), 0. }; +} static std::string get_option_label(BedShape::Parameter param) { @@ -175,7 +175,7 @@ const std::string BedShapePanel::EMPTY_STRING = ""; void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { wxGetApp().UpdateDarkUI(this); - m_shape = default_pt.values; + m_shape = default_pt.get_values(); m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; @@ -440,7 +440,7 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points) // Copy the polygon to the canvas, make a copy of the array, if custom shape is selected if (shape.is_custom()) - m_loaded_shape = points.values; + m_loaded_shape = points.get_values(); update_shape(); diff --git a/src/slic3r/GUI/CalibrationBedDialog.cpp b/src/slic3r/GUI/CalibrationBedDialog.cpp index 79c456ca45a..ba681f5fc94 100644 --- a/src/slic3r/GUI/CalibrationBedDialog.cpp +++ b/src/slic3r/GUI/CalibrationBedDialog.cpp @@ -57,11 +57,11 @@ void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { /// --- scale --- //model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter = printerConfig->option("nozzle_diameter"); - assert(nozzle_diameter->values.size() > 0); - float xyScale = nozzle_diameter->values[0] / 0.4; + assert(nozzle_diameter->size() > 0); + float xyScale = nozzle_diameter->get_at(0) / 0.4; //scale z with the first_layer_height const ConfigOptionFloatOrPercent* first_layer_height = printConfig->option("first_layer_height"); - float zscale = first_layer_height->get_abs_value(nozzle_diameter->values[0]) / 0.2; + float zscale = first_layer_height->get_abs_value(nozzle_diameter->get_at(0)) / 0.2; //do scaling if (xyScale < 0.9 || 1.2 < xyScale) { for (size_t i = 0; i < 5; i++) @@ -73,7 +73,7 @@ void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { /// --- rotate --- const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); - if (bed_shape->values.size() == 4) { + if (bed_shape->size() == 4) { model.objects[objs_idx[0]]->rotate(PI / 4, { 0,0,1 }); model.objects[objs_idx[1]]->rotate(5 * PI / 4, { 0,0,1 }); model.objects[objs_idx[3]]->rotate(3 * PI / 4, { 0,0,1 }); @@ -87,15 +87,15 @@ void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { //three first will stay with this orientation (top left, middle, bottom right) //last two with 90deg (top left, middle, bottom right) //get position for patches - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; float offsetx = 10 + 10 * xyScale; float offsety = 10 + 10 * xyScale; - if (bed_shape->values.size() > 4) { + if (bed_shape->size() > 4) { offsetx = bed_size.x() / 2 - bed_size.x() * 1.414 / 4 + 10 * xyScale; offsety = bed_size.y() / 2 - bed_size.y() * 1.414 / 4 + 10 * xyScale; } - bool large_enough = bed_shape->values.size() == 4 ? + bool large_enough = bed_shape->size() == 4 ? (bed_size.x() > offsetx * 3 && bed_size.y() > offsety * 3) : (bed_size.x() > offsetx * 2 + 10 * xyScale && bed_size.y() > offsety * 2 + 10 * xyScale); if (!large_enough){ @@ -124,7 +124,7 @@ void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { //disable ironing post-process model.objects[objs_idx[i]]->config.set_key_value("ironing", new ConfigOptionBool(false)); } - if (bed_shape->values.size() == 4) { + if (bed_shape->size() == 4) { model.objects[objs_idx[0]]->config.set_key_value("fill_angle", new ConfigOptionFloat(90)); model.objects[objs_idx[1]]->config.set_key_value("fill_angle", new ConfigOptionFloat(90)); model.objects[objs_idx[2]]->config.set_key_value("fill_angle", new ConfigOptionFloat(45)); diff --git a/src/slic3r/GUI/CalibrationBridgeDialog.cpp b/src/slic3r/GUI/CalibrationBridgeDialog.cpp index 30a98d55a08..35440027ee8 100644 --- a/src/slic3r/GUI/CalibrationBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationBridgeDialog.cpp @@ -91,8 +91,8 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool /// --- scale --- // model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float z_scale = nozzle_diameter / 0.4; //do scaling if (z_scale < 0.9 || 1.2 < z_scale) { @@ -128,8 +128,8 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool const float brim_width = std::max(print_config->option("brim_width")->value, nozzle_diameter * 5.); const ConfigOptionFloat* extruder_clearance_radius = print_config->option("extruder_clearance_radius"); const ConfigOptionPoints* bed_shape = printer_config->option("bed_shape"); - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; float offsety = 2 + 10 * 1 + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 2.5 * z_scale }); for (int i = 1; i < nb_items; i++) { diff --git a/src/slic3r/GUI/CalibrationCubeDialog.cpp b/src/slic3r/GUI/CalibrationCubeDialog.cpp index 067fb45c155..65bb5c27363 100644 --- a/src/slic3r/GUI/CalibrationCubeDialog.cpp +++ b/src/slic3r/GUI/CalibrationCubeDialog.cpp @@ -73,8 +73,8 @@ void CalibrationCubeDialog::create_geometry(std::string calibration_path) { /// --- scale --- //model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printerConfig->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float cube_size = 30; if (calibration_path == "xyzCalibration_cube.amf") cube_size = 20; @@ -90,8 +90,8 @@ void CalibrationCubeDialog::create_geometry(std::string calibration_path) { /// --- translate --- const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); /// --- custom config --- diff --git a/src/slic3r/GUI/CalibrationFlowDialog.cpp b/src/slic3r/GUI/CalibrationFlowDialog.cpp index 2dddeb0edf4..66970abd8ed 100644 --- a/src/slic3r/GUI/CalibrationFlowDialog.cpp +++ b/src/slic3r/GUI/CalibrationFlowDialog.cpp @@ -77,8 +77,8 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { /// --- scale --- // model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printerConfig->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float xyScale = nozzle_diameter / 0.4; //scale z to have 6 layers const ConfigOptionFloatOrPercent* first_layer_height_setting = print_config->option("first_layer_height"); @@ -140,8 +140,8 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { const ConfigOptionFloat* extruder_clearance_radius = print_config->option("extruder_clearance_radius"); const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); const double brim_width = nozzle_diameter * 3.5; - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; float offsetx = 3 + 20 * xyScale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); float offsety = 3 + 20 * xyScale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, zscale / 2 }); diff --git a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp index a5297cb9a89..dbd4f89aee4 100644 --- a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp @@ -73,8 +73,8 @@ void CalibrationOverBridgeDialog::create_geometry(bool over_bridge) { /// --- scale --- // model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float xyz_scale = (0.2 + nozzle_diameter) / 0.6; //do scaling if (xyz_scale < 0.9 || 1.2 < xyz_scale) { @@ -110,8 +110,8 @@ void CalibrationOverBridgeDialog::create_geometry(bool over_bridge) { const ConfigOptionPoints* bed_shape = printer_config->option("bed_shape"); const float brim_width = print_config->option("brim_width")->get_float(); const float skirt_width = print_config->option("skirts")->get_int() == 0 ? 0 : print_config->option("skirt_distance")->get_float() + print_config->option("skirts")->get_int() * nozzle_diameter * 2; - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; float offsetx = 3 + 30 * xyz_scale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); float offsety = 3 + 25 * xyz_scale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, (nozzle_diameter / 0.4) }); diff --git a/src/slic3r/GUI/CalibrationPressureAdvDialog.cpp b/src/slic3r/GUI/CalibrationPressureAdvDialog.cpp index e624a5e92ea..09c0831f81c 100644 --- a/src/slic3r/GUI/CalibrationPressureAdvDialog.cpp +++ b/src/slic3r/GUI/CalibrationPressureAdvDialog.cpp @@ -364,8 +364,8 @@ struct ExtrusionSettings {// think a struct is better instead of all the maps ? // --- scale --- //models is created for nozzles from 0.1-2mm walls should be nozzle_size*4 spaced, scale xy model by widths down is futher const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - double nozzle_diameter = nozzle_diameter_config->values[0];//get extruderID too? + assert(nozzle_diameter_config->size() > 0); + double nozzle_diameter = nozzle_diameter_config->get_at(0);//get extruderID too? double first_layer_height = print_config->get_abs_value("first_layer_height", nozzle_diameter); double base_layer_height = print_config->get_computed_value("layer_height",0); GCodeFlavor flavor = printer_config->option>("gcode_flavor")->value; diff --git a/src/slic3r/GUI/CalibrationRetractionDialog.cpp b/src/slic3r/GUI/CalibrationRetractionDialog.cpp index d4ed5b2f792..b630c07e849 100644 --- a/src/slic3r/GUI/CalibrationRetractionDialog.cpp +++ b/src/slic3r/GUI/CalibrationRetractionDialog.cpp @@ -82,14 +82,14 @@ void CalibrationRetractionDialog::remove_slowdown(wxCommandEvent& event_args) { const ConfigOptionFloats *fil_conf = filament_config->option("slowdown_below_layer_time"); ConfigOptionFloats *new_fil_conf = new ConfigOptionFloats(5); - new_fil_conf->values = fil_conf->values; - new_fil_conf->values[0] = 0; + new_fil_conf->set(fil_conf); + new_fil_conf->set_at(0, 0); new_filament_config.set_key_value("slowdown_below_layer_time", new_fil_conf); fil_conf = filament_config->option("fan_below_layer_time"); new_fil_conf = new ConfigOptionFloats(60); - new_fil_conf->values = fil_conf->values; - new_fil_conf->values[0] = 0; + new_fil_conf->set(fil_conf); + new_fil_conf->set_at(0, 0); new_filament_config.set_key_value("fan_below_layer_time", new_fil_conf); this->gui_app->get_tab(Preset::TYPE_FFF_FILAMENT)->load_config(new_filament_config); @@ -158,8 +158,8 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) { /// --- scale --- // model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float xyScale = nozzle_diameter / 0.4; //scale z to have 6 layers const ConfigOptionFloatOrPercent* first_layer_height_setting = print_config->option("first_layer_height"); @@ -202,8 +202,8 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) { const ConfigOptionFloat* extruder_clearance_radius = print_config->option("extruder_clearance_radius"); const ConfigOptionPoints* bed_shape = printer_config->option("bed_shape"); const float brim_width = std::max(print_config->option("brim_width")->value, nozzle_diameter * 5.); - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; float offset = 4 + 26 * scale * 1 + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); if (nb_items == 1) { model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, zscale_number }); diff --git a/src/slic3r/GUI/CalibrationTempDialog.cpp b/src/slic3r/GUI/CalibrationTempDialog.cpp index 49ec3c35c40..7708c13111f 100644 --- a/src/slic3r/GUI/CalibrationTempDialog.cpp +++ b/src/slic3r/GUI/CalibrationTempDialog.cpp @@ -72,7 +72,7 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { // -- get temps const ConfigOptionInts* temperature_config = filament_config->option("temperature"); const int first_layer_temperature = filament_config->option("temperature")->get_at(0); - assert(temperature_config->values.size() >= 1); + assert(temperature_config->size() >= 1); long nb_items_up = 1; if (!nb_up->GetValue().ToLong(&nb_items_up)) { nb_items_up = 2; @@ -81,7 +81,7 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { if (!nb_down->GetValue().ToLong(&nb_items_down)) { nb_items_down = 2; } - int16_t temperature = 5 * (temperature_config->values[0] / 5); + int16_t temperature = 5 * (temperature_config->get_at(0) / 5); long step_temp = 1; if (!steps->GetValue().ToLong(&step_temp)) { step_temp = 10; @@ -93,8 +93,8 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { /// --- scale --- //model is created for a 0.4 nozzle, scale xy with nozzle size. const ConfigOptionFloats* nozzle_diameter_config = printer_config->option("nozzle_diameter"); - assert(nozzle_diameter_config->values.size() > 0); - float nozzle_diameter = nozzle_diameter_config->values[0]; + assert(nozzle_diameter_config->size() > 0); + float nozzle_diameter = nozzle_diameter_config->get_at(0); float xyzScale = nozzle_diameter / 0.4; //do scaling if (xyzScale < 0.9 || 1.1 < xyzScale) { @@ -146,8 +146,8 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { bool autocenter = gui_app->app_config->get("autocenter") == "1"; if (!autocenter) { const ConfigOptionPoints* bed_shape = printer_config->option("bed_shape"); - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 5 * xyzScale - 5 }); } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index e5d7d5273ad..8b0cbe11f67 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -434,6 +434,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("fill_angle", (have_infill || has_solid_infill) && ((ConfigOptionVectorBase*)config->option("fill_angle_template"))->size() == 0); + + toggle_field("small_area_infill_flow_compensation", has_solid_infill); + bool have_small_area_infill_flow_compensation = has_solid_infill && config->opt_bool("small_area_infill_flow_compensation"); + toggle_field("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); + toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill); toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index c5359966556..ec18e65d2c8 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1898,8 +1898,8 @@ const std::string& Materials::get_vendor(const Preset *preset) const const std::string& Materials::get_filament_type(const Preset *preset) { const auto *opt = preset->config.opt("filament_type"); - if (opt != nullptr && opt->values.size() > 0) { - return opt->values[0]; + if (opt != nullptr && opt->size() > 0) { + return opt->get_at(0); } else { return UNKNOWN; } diff --git a/src/slic3r/GUI/CreateMMUTiledCanvas.cpp b/src/slic3r/GUI/CreateMMUTiledCanvas.cpp index 8be76fcc546..c638c77646b 100644 --- a/src/slic3r/GUI/CreateMMUTiledCanvas.cpp +++ b/src/slic3r/GUI/CreateMMUTiledCanvas.cpp @@ -1476,8 +1476,8 @@ void CreateMMUTiledCanvas::create_color_tab(wxPanel* tab) //row of available colors //group_colors->append_single_option_line(group_colors->get_option("available_colors")); ConfigOptionStrings* available_colors = m_config.option("available_colors"); - for (int i = 0; i < available_colors->values.size(); i++) { - MywxColourPickerCtrl::add_color_bt(available_colors->values[i], color_row_sizer); + for (int i = 0; i < available_colors->size(); i++) { + MywxColourPickerCtrl::add_color_bt(available_colors->get_at(i), color_row_sizer); } tab->Refresh(); color_sizer->Add(color_row_sizer, wxGBPosition(2, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 2); @@ -1753,8 +1753,8 @@ void CreateMMUTiledCanvas::create_geometry(wxCommandEvent& event_args) { ///// --- translate --- //const DynamicPrintConfig* printerConfig = this->m_gui_app->get_tab(Preset::TYPE_PRINTER)->get_config(); //const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); - //Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - //Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + //Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + //Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; //model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); //update colors @@ -1763,9 +1763,9 @@ void CreateMMUTiledCanvas::create_geometry(wxCommandEvent& event_args) { //{m_impl=L"#800040" m_convertedToChar={m_str=0x0000000000000000 m_len=0 } } const ConfigOptionStrings* color_conf = printer_config->option("extruder_colour"); ConfigOptionStrings* new_color_conf = static_cast(color_conf->clone()); - for(int idx_col = 0; idx_col < this->m_used_colors.size() && idx_col < new_color_conf->values.size(); idx_col++){ + for(int idx_col = 0; idx_col < this->m_used_colors.size() && idx_col < new_color_conf->size(); idx_col++){ wxColour col = this->m_used_colors[idx_col]->get_printed_color(use_spool_colors); - new_color_conf->values[idx_col] = "#" + AppConfig::int2hex(col.GetRGB()); + new_color_conf->get_at(idx_col) = "#" + AppConfig::int2hex(col.GetRGB()); } new_Printer_config.set_key_value("extruder_colour", new_color_conf); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 11efa48133d..0325d8c336f 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -5,6 +5,7 @@ #include "BitmapComboBox.hpp" #include "format.hpp" +#include "GraphDialog.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "I18N.hpp" @@ -417,7 +418,7 @@ std::pair any_to_wxstring(const boost::any &value, const ConfigO ConfigOptionStrings reader; reader.set_any(value, opt_idx); std::string good_str; - for (std::string &s : reader.values) { + for (std::string s : reader.get_values()) { //ensure the separator isn't inside, not escaped. if (s.find((opt.multiline ? '\n' : ';')) != std::string::npos) { if (opt.multiline) { @@ -554,7 +555,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = if (m_opt_idx < 0) { ConfigOptionInts reader; need_update = get_vector_value(str, reader); - m_value = reader.values; + m_value = reader.get_values(); break; } // else: one int on m_opt_idx, done below case coInt: { @@ -566,7 +567,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = if (m_opt_idx < 0) { ConfigOptionBools reader; need_update = get_vector_value(str, reader); - m_value = reader.values; + m_value = reader.get_values(); break; } // else: one bool on m_opt_idx, done below case coBool: { @@ -584,7 +585,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = if (m_opt_idx < 0) { ConfigOptionFloats reader; need_update = get_vector_value(str, reader); - m_value = reader.values; + m_value = reader.get_values(); break; } case coPercent: @@ -708,7 +709,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = if (m_opt_idx < 0) { // not used yet ConfigOptionFloatsOrPercents reader; need_update = get_vector_value(str, reader); - m_value = reader.values; + m_value = reader.get_values(); break; } case coFloatOrPercent: { @@ -722,7 +723,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = const DynamicPrintConfig &printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; const std::vector &nozzle_diameters = - printer_config.option("nozzle_diameter")->values; + printer_config.option("nozzle_diameter")->get_values(); double nozzle_diameter = 0; for (double diameter : nozzle_diameters) nozzle_diameter = std::max(nozzle_diameter, diameter); @@ -801,7 +802,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = const DynamicPrintConfig &printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; const std::vector &nozzle_diameters = - printer_config.option("nozzle_diameter")->values; + printer_config.option("nozzle_diameter")->get_values(); double nozzle_diameter = 0; for (double diameter : nozzle_diameters) nozzle_diameter = std::max(nozzle_diameter, diameter); @@ -2098,6 +2099,119 @@ void ColourPicker::sys_color_changed() #endif } + +void GraphButton::BUILD() +{ + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); + if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit); + if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); + + assert(m_opt.type == coGraph || m_opt.type == coGraphs); + if (m_opt.type == coGraphs) + current_value = m_opt.get_default_value()->get_at(m_opt_idx); + if (m_opt.type == coGraph) + current_value = m_opt.get_default_value()->value; + + wxButton* bt_widget = new wxButton(m_parent, wxID_ANY, _L("Edit graph"), wxDefaultPosition, size); + if (parent_is_custom_ctrl && m_opt.height < 0) + opt_height = (double)bt_widget->GetSize().GetHeight() / m_em_unit; + bt_widget->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + if (!wxOSX) bt_widget->SetBackgroundStyle(wxBG_STYLE_PAINT); + + wxGetApp().UpdateDarkUI(bt_widget); + + // recast as a wxWindow to fit the calling convention + window = dynamic_cast(bt_widget); + + //window->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), window->GetId()); + + bt_widget->Bind(wxEVT_BUTTON, ([this](wxCommandEvent &e) { + GraphSettings settings; + assert(m_opt.graph_settings); + if (m_opt.graph_settings) { + settings = *m_opt.graph_settings; + } else { + settings.title = m_opt.full_label.empty() ? m_opt.label : m_opt.full_label; + settings.description = m_opt.tooltip; + settings.x_label = ""; + settings.y_label = ""; + settings.null_label = L("No values"); + settings.label_min_x = L("Minimum x"); + settings.label_max_x = L("Maximum x"); + settings.label_min_y = L("Minimum y"); + settings.label_max_y = L("Maximum y"); + settings.min_x = 0; + settings.max_x = 1000; + settings.step_x = 0.1; + settings.min_y = 0; + settings.max_y = 1000; + settings.step_y = 0.1; + settings.allowed_types = {GraphData::GraphType::LINEAR, GraphData::GraphType::SPLINE, GraphData::GraphType::SQUARE}; + } + if (this->m_opt.type == coGraphs) + settings.reset_vals = m_opt.get_default_value()->get_at(m_opt_idx); + if (this->m_opt.type == coGraph) + settings.reset_vals = m_opt.get_default_value()->value; + GraphDialog dlg(this->window, current_value, settings); + if (dlg.ShowModal() == wxID_OK) { + m_value = current_value = dlg.get_data(); + this->on_change_field(); + } + })); + this->set_tooltip(current_value.serialize()); +} + +void GraphButton::set_any_value(const boost::any &value, bool change_event) +{ + // can be ConfigOptionDef::GUIType::color + m_disable_change_event = !change_event; + if (this->m_opt.type == coGraphs && m_opt_idx >= 0) { + assert(false); // shouldn't happen. or need to be tested + std::vector graphs = boost::any_cast>(value); + assert(!graphs.empty()); + if (!graphs.empty()) { + assert(m_opt_idx m_opt.type == coGraph || this->m_opt.type == coGraphs) { + m_value = current_value = boost::any_cast(value); + } + m_disable_change_event = false; +} + +boost::any& GraphButton::get_value() +{ + m_value = current_value; + return m_value; +} + +void GraphButton::msw_rescale() +{ + Field::msw_rescale(); + + wxButton* field = dynamic_cast(window); + auto size = wxSize(def_width() * m_em_unit, wxDefaultCoord); + if (m_opt.height >= 0) + size.SetHeight(m_opt.height * m_em_unit); + else if (parent_is_custom_ctrl && opt_height > 0) + size.SetHeight(lround(opt_height * m_em_unit)); + if (m_opt.width >= 0) size.SetWidth(m_opt.width * m_em_unit); + if (parent_is_custom_ctrl) + field->SetSize(size); + else + field->SetMinSize(size); + +} + +void GraphButton::sys_color_changed() +{ +#ifdef _WIN32 + if (wxWindow* win = this->getWindow()) + if (wxButton* bt = dynamic_cast(win)) + wxGetApp().UpdateDarkUI(bt); +#endif +} + void PointCtrl::BUILD() { auto temp = new wxBoxSizer(wxHORIZONTAL); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index db0c6aafc34..192d6f8504f 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -457,6 +457,27 @@ class ColourPicker : public Field { wxWindow* getWindow() override { return window; } }; +class GraphButton : public Field { + using Field::Field; + GraphData current_value; +public: + GraphButton(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} + GraphButton(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} + ~GraphButton() {} + + wxWindow* window{ nullptr }; + void BUILD() override; + + void set_any_value(const boost::any &value, bool change_event = false) override; + boost::any& get_value() override; + void msw_rescale() override; + void sys_color_changed() override; + + void enable() override { dynamic_cast(window)->Enable(); } + void disable() override{ dynamic_cast(window)->Disable(); } + wxWindow* getWindow() override { return window; } +}; + class PointCtrl : public Field { using Field::Field; public: diff --git a/src/slic3r/GUI/FreeCADDialog.cpp b/src/slic3r/GUI/FreeCADDialog.cpp index d51962667e7..ae97c3dd4f7 100644 --- a/src/slic3r/GUI/FreeCADDialog.cpp +++ b/src/slic3r/GUI/FreeCADDialog.cpp @@ -987,8 +987,8 @@ void FreeCADDialog::create_geometry(wxCommandEvent& event_args) { /// --- translate --- const DynamicPrintConfig* printerConfig = this->gui_app->get_tab(Preset::TYPE_PRINTER)->get_config(); const ConfigOptionPoints* bed_shape = printerConfig->option("bed_shape"); - Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); - Vec2d bed_min = BoundingBoxf(bed_shape->values).min; + Vec2d bed_size = BoundingBoxf(bed_shape->get_values()).size(); + Vec2d bed_min = BoundingBoxf(bed_shape->get_values()).min; model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); //update plater diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c78a159886e..d85941c57e1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2660,7 +2660,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const size_t extruders_count = config.nozzle_diameter.size(); if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) { //FIXME using first nozzle diameter instead of the "right" one. - const WipeTowerData& wipe_tower_data = print.wipe_tower_data(extruders_count, config.nozzle_diameter.values.front()); + const WipeTowerData& wipe_tower_data = print.wipe_tower_data(extruders_count, config.nozzle_diameter.get_at(0)); const float depth = wipe_tower_data.depth; const float brim_width = wipe_tower_data.brim_width; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5fec69be56b..1af0500a64d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -276,9 +276,9 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const const ConfigOptionFloats* nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); min_height = std::numeric_limits::max(); max_height = 0.f; - assert(extruders_min_height->values.size() == extruders_max_height->values.size()); - assert(extruders_min_height->values.size() == nozzle_diameter->values.size()); - for (size_t idx_extruder = 0; idx_extruder < extruders_min_height->values.size(); ++idx_extruder) { + assert(extruders_min_height->size() == extruders_max_height->size()); + assert(extruders_min_height->size() == nozzle_diameter->size()); + for (size_t idx_extruder = 0; idx_extruder < extruders_min_height->size(); ++idx_extruder) { min_height = std::min(min_height, float(extruders_min_height->get_abs_value(idx_extruder, nozzle_diameter->get_float(idx_extruder)))); max_height = std::max(max_height, float(extruders_max_height->get_abs_value(idx_extruder, nozzle_diameter->get_float(idx_extruder)))); } @@ -2163,7 +2163,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (printer_technology == ptFFF && m_config->has("nozzle_diameter")) { // Should the wipe tower be visualized ? - unsigned int extruders_count = (unsigned int)m_config->option("nozzle_diameter")->values.size(); + unsigned int extruders_count = (unsigned int)m_config->option("nozzle_diameter")->size(); bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; bool co = dynamic_cast(m_config->option("complete_objects"))->value; @@ -2180,8 +2180,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const Print *print = m_process->fff_print(); //FIXME use real nozzle diameter - float depth = print->wipe_tower_data(extruders_count, m_config->option("nozzle_diameter")->values.front()).depth; - float brim_width = print->wipe_tower_data(extruders_count, m_config->option("nozzle_diameter")->values.front()).brim_width; + float depth = print->wipe_tower_data(extruders_count, m_config->option("nozzle_diameter")->get_at(0)).depth; + float brim_width = print->wipe_tower_data(extruders_count, m_config->option("nozzle_diameter")->get_at(0)).brim_width; int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 57d201fcef9..e603dfdd46a 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -202,14 +202,25 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio break; case coBools: if (conf_substitution.new_value->nullable()) - for (const char v : static_cast(conf_substitution.new_value.get())->values) + for (const char v : static_cast(conf_substitution.new_value.get())->get_values()) new_val += std::string(v == ConfigOptionBoolsNullable::NIL_VALUE() ? "nil" : v ? "true" : "false") + ", "; else - for (const char v : static_cast(conf_substitution.new_value.get())->values) + for (const char v : static_cast(conf_substitution.new_value.get())->get_values()) new_val += std::string(v ? "true" : "false") + ", "; if (! new_val.empty()) new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end()); break; + case coGraph: + if (auto opt = dynamic_cast(conf_substitution.new_value.get())) { + new_val = opt->value.serialize(); + } else assert(false); + break; + case coGraphs: + if (auto opts = dynamic_cast(conf_substitution.new_value.get())) { + for (const GraphData &graph : opts->get_values()) + new_val += graph.serialize() + ", "; + } else assert(false); + break; default: assert(false); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 66f6881c74a..902f4798aaa 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3135,7 +3135,7 @@ int GUI_App::extruders_cnt() const { const Preset& preset = preset_bundle->printers.get_selected_preset(); return preset.printer_technology() == ptSLA ? 1 : - preset.config.option("nozzle_diameter")->values.size(); + preset.config.option("nozzle_diameter")->size(); } // extruders count from edited printer preset @@ -3143,7 +3143,7 @@ int GUI_App::extruders_edited_cnt() const { const Preset& preset = preset_bundle->printers.get_edited_preset(); return preset.printer_technology() == ptSLA ? 1 : - preset.config.option("nozzle_diameter")->values.size(); + preset.config.option("nozzle_diameter")->size(); } wxString GUI_App::current_language_code_safe() const diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 2e0ff17f978..7d4eb855cee 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2014,7 +2014,7 @@ void ObjectList::split() if (!get_volume_by_item(item, volume)) return; DynamicPrintConfig& config = printer_config(); const ConfigOption *nozzle_dmtrs_opt = config.option("nozzle_diameter", false); - const auto nozzle_dmrs_cnt = (nozzle_dmtrs_opt == nullptr) ? size_t(1) : dynamic_cast(nozzle_dmtrs_opt)->values.size(); + const auto nozzle_dmrs_cnt = (nozzle_dmtrs_opt == nullptr) ? size_t(1) : dynamic_cast(nozzle_dmtrs_opt)->size(); if (!volume->is_splittable()) { wxMessageBox(_(L("The selected object couldn't be split because it contains only one part."))); return; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0b834ad3fe1..4dd7a6b9758 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1027,7 +1027,7 @@ void Preview::load_print_as_fff(bool keep_z_range) { const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); - unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + unsigned int colors_count = std::max((unsigned int)extruders_opt->size(), (unsigned int)filamemts_opt->size()); unsigned char rgb[3]; for (unsigned int i = 0; i < colors_count; ++i) diff --git a/src/slic3r/GUI/GraphDialog.cpp b/src/slic3r/GUI/GraphDialog.cpp index c8a895e1a65..f9f63e9e7ea 100644 --- a/src/slic3r/GUI/GraphDialog.cpp +++ b/src/slic3r/GUI/GraphDialog.cpp @@ -1,12 +1,15 @@ -#include -#include #include "GraphDialog.hpp" + #include "BitmapCache.hpp" #include "GUI.hpp" -#include "I18N.hpp" #include "GUI_App.hpp" +#include "I18N.hpp" #include "MsgDialog.hpp" +#include +#include +#include + #include #include @@ -21,16 +24,17 @@ int ITEM_WIDTH() { return scale(6); } static void update_ui(wxWindow *window) { Slic3r::GUI::wxGetApp().UpdateDarkUI(window); } -GraphDialog::GraphDialog(wxWindow *parent, const std::string ¶meters) + +GraphDialog::GraphDialog(wxWindow *parent, const GraphData ¶meters, const GraphSettings &settings) : wxDialog(parent, wxID_ANY, - _(L("Extrusion multiplier per extrusion speed")), + _(settings.title), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/) { update_ui(this); - m_panel_graph = new GraphPanel(this, parameters); + m_panel_graph = new GraphPanel(this, parameters, settings); // Not found another way of getting the background colours of GraphDialog, GraphPanel and Chart correct than // setting them all explicitely. Reading the parent colour yielded colour that didn't really match it, no @@ -59,7 +63,8 @@ GraphDialog::GraphDialog(wxWindow *parent, const std::string ¶meters) this->Bind( wxEVT_BUTTON, [this](wxCommandEvent &) { - m_output_data = m_panel_graph->get_parameters(); + m_output_data = m_panel_graph->get_data(); + m_disabled = m_panel_graph->is_disabled(); EndModal(wxID_OK); }, wxID_OK); @@ -75,214 +80,462 @@ GraphDialog::GraphDialog(wxWindow *parent, const std::string ¶meters) #endif +double text_enter_event(wxSpinCtrlDouble *widget, + double & last_val, + const double & curr_min, + const double & curr_max, + double min, + double max, + double step, + wxCommandEvent & evt) +{ + // m_widget_max_x->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + double old_val = last_val; + last_val = std::atof(evt.GetString().c_str()); + last_val = int32_t(last_val / step) * step; + last_val = std::min(std::max(last_val, min), max); + if (curr_min >= curr_max) + last_val = old_val; + widget->SetValue(last_val); + double precision = 0.000001f; + while (precision < (curr_max - curr_min)) precision *= 10; + return precision / 1000; +} +double spin_move(wxSpinCtrlDouble * widget, + double & last_val, + const double & curr_min, + const double & curr_max, + double min, + double max, + double step, + wxSpinDoubleEvent &evt) +{ + double old_val = last_val; + const double evt_dbl_val = std::atof(evt.GetString().c_str()); + // if second call, from our SetValue, ignore. + if (std::abs(evt_dbl_val - last_val) < 0.0000001) + return 0; + double incr = step; + while (last_val > incr * 20) incr *= 10; + if (evt_dbl_val > last_val) { + last_val += incr; + } else { + last_val -= incr; + } + last_val = std::min(std::max(last_val, min), max); + if (curr_min >= curr_max) + last_val = old_val; + widget->SetValue(last_val); + float precision = 0.000001f; + while (precision < (curr_max - curr_min)) precision *= 10; + return precision / 1000; +} -GraphPanel::GraphPanel(wxWindow *parent, const std::string ¶meters) +GraphPanel::GraphPanel(wxWindow *parent, GraphData data, const GraphSettings &settings) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize /*,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED*/) { + assert(data.validate()); update_ui(this); auto sizer_chart = new wxBoxSizer(wxVERTICAL); - - std::stringstream stream{parameters}; - //stream >> m_graph_line_width_multiplicator >> m_graph_step_multiplicator; - int Graph_speed_size = 0; - float dummy = 0.f; - float min = 2.f; - float max = 0.f; - while (stream >> dummy) { - ++Graph_speed_size; - if (dummy > 0 && dummy <= 2) { - min = std::min(min, dummy); - max = std::max(max, dummy); + + // safe + if (data.end_idx == 0) { + if (data.graph_points.size() > 0) { + data.begin_idx = 0; + data.end_idx = data.graph_points.size(); } } - stream.clear(); - stream.get(); - if (min >= max) { - max = 1.2f; - min = 0.9f; + //int Graph_speed_size = 0; + //float dummy = 0.f; + m_last_min_y = 2.f; + m_last_max_y = 0.f; + //while (stream >> dummy) { + // ++Graph_speed_size; + // if (dummy > 0 && dummy <= 2) { + // min = std::min(min, dummy); + // max = std::max(max, dummy); + // } + //} + //stream.clear(); + //stream.get(); + + // min & max + Pointfs &data_points = data.data(); + if (!data_points.empty()) { + for (Vec2d &point : data_points) { + m_last_min_y = std::min(m_last_min_y, (point.y())); + m_last_max_y = std::max(m_last_max_y, (point.y())); + } + if (m_last_min_y >= m_last_max_y) { + m_last_max_y = std::max(m_last_min_y, m_last_max_y) * 1.1f; + m_last_min_y = std::min(m_last_min_y, m_last_max_y) * 0.9f; + } else { + m_last_min_y = int(m_last_min_y * 10 - 1 + EPSILON) / 10.f; + m_last_max_y = int(1.9f + m_last_max_y * 10 - EPSILON) / 10.f; + } + if (m_last_max_y == m_last_min_y) { + m_last_max_y = settings.max_y; + } } else { - min = int(min * 10 - 1 + EPSILON) / 10.f; - max = int(1.9f + max * 10 - EPSILON) / 10.f; + m_last_min_y = settings.min_y; + m_last_max_y = settings.max_y; + } + + if (!data_points.empty()) { + if (data_points.size() == 1) { + m_last_min_x = settings.min_x; + } else { + m_last_min_x = std::max(settings.min_x, data_points.begin()->x() - settings.step_x); + } + if (data_points.rbegin()->x() <= m_last_min_x + settings.step_x) { + m_last_max_x = settings.max_x; + } else { + m_last_max_x = std::min(settings.max_x, data_points.rbegin()->x() + settings.step_x); + } + } else { + m_last_min_x = settings.min_x; + m_last_max_x = settings.max_x; } std::vector> buttons; - float x = 0.f; - float y = 0.f; - while (stream >> x >> y) buttons.push_back(std::make_pair(x, y)); + for (const Vec2d &point : data.graph_points) buttons.emplace_back(float(point.x()), float(point.y())); m_chart = new Chart(this, wxRect(scale(1), scale(1), scale(64), scale(36)), buttons, scale(1)); m_chart->set_manual_points_manipulation(true); - m_chart->set_xy_range(0, min, Graph_speed_size * 10.f, max); - m_chart->set_x_label(_L("Print speed") + " ("+_L("mm/s")+")", 1.f); - m_chart->set_y_label(_L("Extrusion multiplier"), 0.001f); - m_chart->set_no_point_label(_L("No compensation")); + double precision = 0.000001f; + while (precision < (m_last_max_x - m_last_min_x)) precision *= 10; + m_chart->set_x_label(_(settings.x_label), precision / 1000); + precision = 0.000001f; + while (precision < (m_last_max_y - m_last_min_y)) precision *= 10; + m_chart->set_y_label(_(settings.y_label), precision / 1000); + m_chart->set_no_point_label(_(settings.null_label)); + m_chart->set_type(data.type); #ifdef _WIN32 update_ui(m_chart); #else m_chart->SetBackgroundColour(parent->GetBackgroundColour()); // see comment in GraphDialog constructor #endif sizer_chart->Add(new wxStaticText(this, wxID_ANY, - _L("Choose the extrusion multipler value for multiple speeds.\nYou can add/remove points with a right clic."))); + _(settings.description))); sizer_chart->Add(m_chart, 0, wxALL, 5); - - m_last_speed = Graph_speed_size * 10; - m_widget_speed = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH(), -1), - style | wxTE_PROCESS_ENTER, 10, 2000, m_last_speed); - // note: wxTE_PROCESS_ENTER allow the wxSpinCtrl to receive wxEVT_TEXT_ENTER events + + if (!settings.label_min_x.empty()) + m_widget_min_x = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(ITEM_WIDTH(), -1), style | wxTE_PROCESS_ENTER, settings.min_x, + settings.max_x, m_last_min_x, settings.step_x); + if (!settings.label_max_x.empty()) + m_widget_max_x = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(ITEM_WIDTH(), -1), style | wxTE_PROCESS_ENTER, settings.min_x, + settings.max_x, m_last_max_x, settings.step_x); + // note: wxTE_PROCESS_ENTER allow the wxSpinCtrl to receive wxEVT_TEXT_ENTER events + + if (!settings.label_min_y.empty()) + m_widget_min_y = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(ITEM_WIDTH(), -1), style | wxTE_PROCESS_ENTER, settings.min_y, + settings.max_y, m_last_min_y, settings.step_y); + if (!settings.label_max_y.empty()) + m_widget_max_y = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(ITEM_WIDTH(), -1), style | wxTE_PROCESS_ENTER, settings.min_y, + settings.max_y, m_last_max_y, settings.step_y); - m_widget_min_flow = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH(), -1), - style, 0.1, 2, min, 0.1f); - m_widget_max_flow = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH(), -1), - style, 0.1, 2, max, 0.1f); + m_chart->set_xy_range(m_last_min_x, m_last_min_y, m_last_max_x, m_last_max_y); #ifdef _WIN32 - update_ui(m_widget_speed); - update_ui(m_widget_min_flow); - update_ui(m_widget_max_flow); + if (!settings.label_min_x.empty()) + update_ui(m_widget_min_x); + if (!settings.label_max_x.empty()) + update_ui(m_widget_max_x); + if (!settings.label_min_y.empty()) + update_ui(m_widget_min_y); + if (!settings.label_max_y.empty()) + update_ui(m_widget_max_y); #endif // line for speed max & reset + if (!settings.label_min_x.empty() || !settings.label_max_x.empty()) { + wxBoxSizer *size_line = new wxBoxSizer(wxHORIZONTAL); + if (!settings.label_min_x.empty()) { + size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_(settings.label_min_x) + " :")), 0, wxALIGN_CENTER_VERTICAL); + size_line->Add(m_widget_min_x); + size_line->AddSpacer(20); + } + if (!settings.label_max_x.empty()) { + size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_(settings.label_max_x) + " :")), 0, wxALIGN_CENTER_VERTICAL); + size_line->Add(m_widget_max_x); + } + sizer_chart->Add(size_line); + } + + //line for y min & max + if (!settings.label_min_y.empty() || !settings.label_max_y.empty()) { + wxBoxSizer *size_line = new wxBoxSizer(wxHORIZONTAL); + if (!settings.label_min_y.empty()) { + size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_(settings.label_min_y) + " :")), 0, wxALIGN_CENTER_VERTICAL); + size_line->Add(m_widget_min_y); + size_line->AddSpacer(20); + } + + if (!settings.label_max_y.empty()) { + size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_(settings.label_max_y) + " :")), 0, wxALIGN_CENTER_VERTICAL); + size_line->Add(m_widget_max_y); + } + sizer_chart->Add(size_line); + } + + wxBoxSizer *size_line = new wxBoxSizer(wxHORIZONTAL); - size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_L("Graph max speed") + " :")), 0, wxALIGN_CENTER_VERTICAL); - size_line->Add(m_widget_speed); - size_line->AddSpacer(80); wxButton *bt_reset = new wxButton(this, wxID_ANY, _L("Reset")); - bt_reset->SetToolTip(_L("Reset all values to 1. Also reset all points to defaults.")); + bt_reset->SetToolTip(_L("Reset all points to defaults.")); size_line->Add(bt_reset); - sizer_chart->Add(size_line); - - //line for y min & max - size_line = new wxBoxSizer(wxHORIZONTAL); - size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_L("Minimum flow") + " :")), 0, wxALIGN_CENTER_VERTICAL); - size_line->Add(m_widget_min_flow); size_line->AddSpacer(20); - size_line->Add(new wxStaticText(this, wxID_ANY, wxString(_L("Maximum flow") + " :")), 0, wxALIGN_CENTER_VERTICAL); - size_line->Add(m_widget_max_flow); + wxButton *bt_type = new wxButton(this, wxID_ANY, _L("Change Type")); + bt_type->SetToolTip(_L("Change the graph type into square, linear or spline.")); + size_line->Add(bt_type); sizer_chart->Add(size_line); sizer_chart->SetSizeHints(this); SetSizer(sizer_chart); - bt_reset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { + bt_reset->Bind(wxEVT_BUTTON, ([this, reset_vals = settings.reset_vals](wxCommandEvent& e) { std::vector> buttons;// = m_chart->get_buttons(); - //for (std::pair &button : buttons) { - // button.second = 1.f; - //} - //buttons.emplace_back(5,1.f); - buttons.emplace_back(10,1.f); - //buttons.emplace_back(15,1.f); - buttons.emplace_back(20,1.f); - buttons.emplace_back(30,1.f); - buttons.emplace_back(40,1.f); - buttons.emplace_back(60,1.f); - buttons.emplace_back(80,1.f); - buttons.emplace_back(120,1.f); - buttons.emplace_back(160,1.f); - buttons.emplace_back(240,1.f); - buttons.emplace_back(480,1.f); - buttons.emplace_back(640,1.f); - buttons.emplace_back(960,1.f); - buttons.emplace_back(1280,1.f); + for (const auto &x2y: reset_vals.graph_points) { + buttons.emplace_back(float(x2y.x()), float(x2y.y())); + } m_chart->set_buttons(buttons); + m_chart->set_xy_range( + reset_vals.begin_idx >= 0 && reset_vals.begin_idx < reset_vals.graph_points.size() ? reset_vals.graph_points[reset_vals.begin_idx].x() : std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + reset_vals.end_idx >= 0 && reset_vals.end_idx < reset_vals.graph_points.size() ? reset_vals.graph_points[reset_vals.end_idx].x() : std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()); })); + + bt_type->Bind(wxEVT_BUTTON, ([this, allowed_types = settings.allowed_types](wxCommandEvent& e) { + if(allowed_types.empty()) + m_chart->set_type(GraphData::GraphType((uint8_t(m_chart->get_type()) + 1) % GraphData::GraphType::COUNT)); + else { + auto it_search = std::find(allowed_types.begin(), allowed_types.end(), m_chart->get_type()); + if (it_search == allowed_types.end() || (it_search + 1) == allowed_types.end()) { + m_chart->set_type(allowed_types.front()); + } else { + m_chart->set_type(*(it_search + 1)); + } - m_widget_speed->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent &) { - int old_speed = m_last_speed; - m_last_speed = 10 * ((m_widget_speed->GetValue() + 5) / 10); - m_last_speed = std::min(std::max(m_last_speed, 20), 2000); - m_widget_speed->SetValue(m_last_speed); - if (old_speed < m_last_speed) { - if (old_speed < 1000 && m_last_speed >= 1000) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 100.f); - else if (old_speed < 100 && m_last_speed >= 100) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 10.f); - } else { - if (old_speed >= 100 && m_last_speed < 100) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 1.f); - else if (old_speed >= 1000 && m_last_speed < 1000) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 10.f); - } - m_chart->set_xy_range(0, -1, m_last_speed, -1); - }); - m_widget_speed->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent &evt) { - assert(evt.GetInt() != m_last_speed); - int incr = 10; - if (m_last_speed >= 60) - incr = 20; - if (m_last_speed >= 100) - incr = 50; - if (m_last_speed >= 300) - incr = 100; - if (m_last_speed >= 600) - incr = 200; - if (m_last_speed >= 1000) - incr = 500; - if (evt.GetInt() > m_last_speed) { - if (m_last_speed < 100 && m_last_speed + incr >= 100) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 10.f); - else if (m_last_speed < 1000 && m_last_speed + incr >= 1000) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 100.f); - m_last_speed += incr; - } else { - if (m_last_speed >= 100 && m_last_speed - incr < 100) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 1.f); - else if (m_last_speed >= 1000 && m_last_speed - incr < 1000) - m_chart->set_x_label(_L("Print speed") + " (" + _L("mm/s") + ")", 10.f); - m_last_speed -= incr; } - m_last_speed = std::min(std::max(m_last_speed, 20), 2000); - m_widget_speed->SetValue(m_last_speed); - m_chart->set_xy_range(0, 0, m_last_speed, -1); - }); - // thses don't work, i don't know why. - //m_widget_speed->Bind(wxEVT_SPIN_UP, [this](wxSpinEvent &evt) { + })); + + if (!settings.label_max_x.empty()) { + m_widget_max_x->Bind(wxEVT_TEXT_ENTER, [this, settings](wxCommandEvent &evt) { + // m_widget_max_x->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + // double old_speed = m_last_max_x; + // m_last_max_x = std::atof(evt.GetString().c_str()); + // m_last_max_x = int32_t(m_last_max_x / settings.step_x) * settings.step_x; + // m_last_max_x = std::min(std::max(m_last_max_x, settings.min_x), settings.max_x); + // m_last_max_x = std::max(m_last_max_x, m_last_min_x + settings.step_x); + // m_widget_max_x->SetValue(m_last_max_x); + // double precision = 0.000001f; + // while(precision < (m_last_max_x - m_last_min_x)) precision *= 10; + // m_chart->set_x_label(_(settings.x_label), float(precision/1000)); + // m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + // std::numeric_limits::quiet_NaN()); + double new_precision = text_enter_event(m_widget_max_x, m_last_max_x, m_last_min_x, m_last_max_x, + settings.min_x, settings.max_x, settings.step_x, evt); + if (new_precision != 0) { + m_chart->set_x_label(_(settings.x_label), new_precision); + m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + std::numeric_limits::quiet_NaN()); + } + }); + // m_widget_max_x->Bind(wxEVT_TEXT, [this, settings](wxCommandEvent &evt) { + m_widget_max_x->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + // const double evt_dbl_val = std::atof(evt.GetString().c_str()); + //// if second call, from our SetValue, ignore. + // if(std::abs(evt_dbl_val - m_last_max_x) < 0.0000001) return; + // double incr = settings.step_x; + // while (m_last_max_x > incr * 20) + // incr *= 10; + // if (evt_dbl_val > m_last_max_x) { + // m_last_max_x += incr; + //} else { + // m_last_max_x -= incr; + //} + // m_last_max_x = std::min(std::max(m_last_max_x, settings.min_x), settings.max_x); + // m_last_max_x = std::max(m_last_max_x, m_last_min_x + settings.step_x); + // float precision = 0.000001f; + // while(precision < (m_last_max_x - m_last_min_x)) precision *= 10; + double new_precision = spin_move(m_widget_max_x, m_last_max_x, m_last_min_x, m_last_max_x, settings.min_x, + settings.max_x, settings.step_x, evt); + if (new_precision != 0) { + m_chart->set_x_label(_(settings.x_label), new_precision); + m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + std::numeric_limits::quiet_NaN()); + } + }); + } + if (!settings.label_min_x.empty()) { + m_widget_min_x->Bind(wxEVT_TEXT_ENTER, [this, settings](wxCommandEvent &evt) { + // double old_speed = m_last_min_x; + // m_last_min_x = std::atof(evt.GetString().c_str()); + // m_last_min_x = int32_t(m_last_min_x / settings.step_x) * settings.step_x; + // m_last_min_x = std::min(std::max(m_last_min_x, settings.min_x), settings.max_x); + // m_last_min_x = std::min(m_last_min_x, m_last_max_x - settings.step_x); + // m_widget_min_x->SetValue(m_last_min_x); + // double precision = 0.000001f; + // while(precision < (m_last_max_x - m_last_min_x)) precision *= 10; + // m_chart->set_x_label(_(settings.x_label), float(precision/1000)); + // m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + // std::numeric_limits::quiet_NaN()); + double new_precision = text_enter_event(m_widget_min_x, m_last_min_x, m_last_min_x, m_last_max_x, + settings.min_x, settings.max_x, settings.step_x, evt); + if (new_precision != 0) { + m_chart->set_x_label(_(settings.x_label), new_precision); + m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + std::numeric_limits::quiet_NaN()); + } + }); + // m_widget_min_x->Bind(wxEVT_TEXT, [this, settings](wxCommandEvent &evt) { + m_widget_min_x->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + // double evt_dbl_val = std::atof(evt.GetString().c_str()); + //// if second call, from our SetValue, ignore. + // if(std::abs(evt_dbl_val - m_last_min_x) < 0.0000001) return; + // double incr = settings.step_x; + // while (m_last_min_x > incr * 20) + // incr *= 10; + // if (evt_dbl_val > m_last_min_x) { + // m_last_min_x += incr; + //} else { + // m_last_min_x -= incr; + //} + // m_last_min_x = std::min(std::max(m_last_min_x, settings.min_x), settings.max_x); + // m_last_min_x = std::min(m_last_min_x, m_last_max_x - settings.step_x); + // float precision = 0.000001f; + // while(precision < (m_last_max_x - m_last_min_x)) precision *= 10; + // m_chart->set_x_label(_(settings.x_label), precision / 1000); + // m_widget_min_x->SetValue(m_last_min_x); + // m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + // std::numeric_limits::quiet_NaN()); + double new_precision = spin_move(m_widget_min_x, m_last_min_x, m_last_min_x, m_last_max_x, settings.min_x, + settings.max_x, settings.step_x, evt); + if (new_precision != 0) { + m_chart->set_x_label(_(settings.x_label), new_precision); + m_chart->set_xy_range(m_last_min_x, std::numeric_limits::quiet_NaN(), m_last_max_x, + std::numeric_limits::quiet_NaN()); + } + }); + } + // these work for wxSpinCtrl but not for wxSpinCtrlDouble + //m_widget_max_y->Bind(wxEVT_SPINCTRL, [this, settings](wxSpinEvent &evt) { // this one works + //m_widget_max_y->Bind(wxEVT_SPIN_UP, [this](wxSpinEvent &evt) { // std::cout<<"up"; // }); - //m_widget_speed->Bind(wxEVT_SPIN_DOWN, [this](wxSpinEvent &evt) { + //m_widget_max_y->Bind(wxEVT_SPIN_DOWN, [this](wxSpinEvent &evt) { // std::cout<<"down"; // }); - m_widget_min_flow->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { - m_chart->set_xy_range(-1, m_widget_min_flow->GetValue(), -1, m_widget_max_flow->GetValue()); - }); - m_widget_min_flow->Bind(wxEVT_CHAR, [](wxKeyEvent &) {}); // do nothing - prevents the user to change the value - m_widget_max_flow->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { - m_chart->set_xy_range(-1, m_widget_min_flow->GetValue(), -1, m_widget_max_flow->GetValue()); - }); - m_widget_max_flow->Bind(wxEVT_CHAR, [](wxKeyEvent &) {}); // do nothing - prevents the user to change the value - Bind(EVT_WIPE_TOWER_CHART_CHANGED, [this](wxCommandEvent &) { - int nb_samples = m_chart->get_speed(10.f).size(); - m_last_speed = 10 * nb_samples; - m_widget_speed->SetValue(m_last_speed); - }); + //m_widget_min_y->Bind(wxEVT_TEXT, [this, settings](wxCommandEvent &) { + // double precision = 0.000001f; + // while(precision < (m_widget_max_y->GetValue() - m_widget_min_y->GetValue())) precision *= 10; + // m_chart->set_y_label(_(settings.y_label), float(precision/1000)); + // m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_widget_min_y->GetValue(), std::numeric_limits::quiet_NaN(), m_widget_max_y->GetValue()); + //}); + //m_widget_min_y->Bind(wxEVT_CHAR, [](wxKeyEvent &) {}); // do nothing - prevents the user to change the value + //m_widget_max_y->Bind(wxEVT_TEXT, [this, settings](wxCommandEvent &) { + // double precision = 0.000001f; + // while(precision < (m_widget_max_y->GetValue() - m_widget_min_y->GetValue())) precision *= 10; + // m_chart->set_y_label(_(settings.y_label), float(precision/1000)); + // m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_widget_min_y->GetValue(), std::numeric_limits::quiet_NaN(), m_widget_max_y->GetValue()); + //}); + if (!settings.label_min_y.empty()) { + m_widget_min_y->Bind(wxEVT_TEXT_ENTER, [this, settings](wxCommandEvent &evt) { + double new_precision = text_enter_event(m_widget_min_y, m_last_min_y, m_last_min_y, m_last_max_y, + settings.min_y, settings.max_y, settings.step_y, evt); + if (new_precision != 0) { + m_chart->set_y_label(_(settings.y_label), new_precision); + m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_last_min_y, + std::numeric_limits::quiet_NaN(), m_last_max_y); + } + }); + m_widget_min_y->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + double new_precision = spin_move(m_widget_min_y, m_last_min_y, m_last_min_y, m_last_max_y, settings.min_y, + settings.max_y, settings.step_y, evt); + if (new_precision != 0) { + m_chart->set_y_label(_(settings.y_label), new_precision); + m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_last_min_y, + std::numeric_limits::quiet_NaN(), m_last_max_y); + } + }); + } + + if (!settings.label_max_y.empty()) { + m_widget_max_y->Bind(wxEVT_TEXT_ENTER, [this, settings](wxCommandEvent &evt) { + double new_precision = text_enter_event(m_widget_max_y, m_last_max_y, m_last_min_y, m_last_max_y, + settings.min_y, settings.max_y, settings.step_y, evt); + if (new_precision != 0) { + m_chart->set_y_label(_(settings.y_label), new_precision); + m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_last_min_y, + std::numeric_limits::quiet_NaN(), m_last_max_y); + } + }); + m_widget_max_y->Bind(wxEVT_SPINCTRLDOUBLE, [this, settings](wxSpinDoubleEvent &evt) { + double new_precision = spin_move(m_widget_max_y, m_last_max_y, m_last_min_y, m_last_max_y, settings.min_y, + settings.max_y, settings.step_y, evt); + if (new_precision != 0) { + m_chart->set_y_label(_(settings.y_label), new_precision); + m_chart->set_xy_range(std::numeric_limits::quiet_NaN(), m_last_min_y, + std::numeric_limits::quiet_NaN(), m_last_max_y); + } + }); + } + //m_widget_max_y->Bind(wxEVT_CHAR, [](wxKeyEvent &) {}); // do nothing - prevents the user to change the value + //Bind(EVT_SLIC3R_CHART_CHANGED, [this](wxCommandEvent &) { + // //int nb_samples = m_chart->get_value_samples(10.f).size(); + // //m_last_max_x = 10 * nb_samples; + // //m_widget_max_x->SetValue(m_last_max_x); + //}); Refresh(true); // erase background } -std::string GraphPanel::get_parameters() +bool GraphPanel::is_disabled() { - std::vector flow_rates = m_chart->get_speed(10.f); - std::vector> buttons = m_chart->get_buttons(); + GraphData data = get_data(); + if (data.graph_points.empty()) + return true; + if (data.begin_idx == data.end_idx) + return true; + if (data.end_idx == size_t(-1)) + return true; + return false; +} - //write string - std::stringstream stream; - //stream << m_graph_line_width_multiplicator << " " << m_graph_step_multiplicator; - //if all are at 1, then set the first to 0 so it's "disabled" - bool disabled = true; - for (const float &flow_rate : flow_rates) - if (flow_rate != 1.) - disabled = false; - for (size_t i = 0; i < flow_rates.size() ; i++) { - const float &flow_rate = flow_rates[i]; - if (0 == i) { - stream << (disabled ? 0.f : flow_rate); - } else { - stream << " " << flow_rate; +GraphData GraphPanel::get_data() +{ + const std::vector> &buttons = m_chart->get_buttons(); + GraphData data; + data.type = m_chart->get_type(); + data.begin_idx = size_t(-1); + data.end_idx = size_t(-1); + assert(m_chart->get_max_x() > m_chart->get_min_x()); + for (size_t idx = 0; idx < buttons.size(); ++idx) { + const std::pair &pt = buttons[idx]; + data.graph_points.emplace_back(pt.first, pt.second); + if (data.begin_idx == size_t(-1) && m_chart->get_min_x() <= pt.first) { + data.begin_idx = idx; + } + if (m_chart->get_max_x() >= pt.first) { + data.end_idx = idx + 1; } } - stream << "|"; - for (const auto &button : buttons) stream << " " << button.first << " " << button.second; - return stream.str(); + if (data.graph_points.empty()) { + data.begin_idx = 0; + data.end_idx = 0; + } + if (size_t(-1) == data.begin_idx || size_t(-1) == data.end_idx) { + data.begin_idx = 0; + data.end_idx = 0; + } + assert(data.end_idx >= data.begin_idx); + assert(data.validate()); + return data; } }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/GraphDialog.hpp b/src/slic3r/GUI/GraphDialog.hpp index eb20f4a93fc..60646ac5a15 100644 --- a/src/slic3r/GUI/GraphDialog.hpp +++ b/src/slic3r/GUI/GraphDialog.hpp @@ -7,6 +7,7 @@ #include #include +#include "libslic3r/Config.hpp" // for GraphSettings #include "RammingChart.hpp" namespace Slic3r { namespace GUI { @@ -14,26 +15,33 @@ namespace Slic3r { namespace GUI { class GraphPanel : public wxPanel { public: - GraphPanel(wxWindow *parent, const std::string &data); - std::string get_parameters(); + GraphPanel(wxWindow *parent, GraphData data,const GraphSettings &settings); + GraphData get_data(); + bool is_disabled(); private: - Chart * m_chart = nullptr; - wxSpinCtrl * m_widget_speed = nullptr; - wxSpinCtrlDouble *m_widget_min_flow = nullptr; - wxSpinCtrlDouble *m_widget_max_flow = nullptr; - int m_last_speed = 120; + Chart * m_chart = nullptr; + wxSpinCtrlDouble *m_widget_min_x = nullptr; + wxSpinCtrlDouble *m_widget_max_x = nullptr; + wxSpinCtrlDouble *m_widget_min_y = nullptr; + wxSpinCtrlDouble *m_widget_max_y = nullptr; + double m_last_min_x = 0.f; + double m_last_max_x = 1.f; + double m_last_min_y = 0.f; + double m_last_max_y = 1.f; }; class GraphDialog : public wxDialog { public: - GraphDialog(wxWindow *parent, const std::string ¶meters); - std::string get_parameters() { return m_output_data; } + GraphDialog(wxWindow *parent, const GraphData ¶meters, const GraphSettings &settings); + GraphData get_data() { return m_output_data; } + bool is_disabled() { return m_disabled; } private: GraphPanel *m_panel_graph = nullptr; - std::string m_output_data; + GraphData m_output_data; + bool m_disabled = false; }; }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 5d5ada87604..daec062b5db 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -171,7 +171,7 @@ TagCheckResult tag_check_material(const std::string& tag) if (wxGetApp().app_config->get("filament_type").find(tag)) { const Preset& preset = tab->m_presets->get_edited_preset(); const auto* opt = preset.config.opt("filament_type"); - if (opt->values[0] == tag) + if (opt->get_at(0) == tag) return TagCheckAffirmative; return TagCheckNegative; } @@ -182,7 +182,7 @@ TagCheckResult tag_check_material(const std::string& tag) //if (wxGetApp().app_config->get("material_type").find(tag)) { const Preset& preset = tab->m_presets->get_edited_preset(); const auto* opt = preset.config.opt("material_type"); - if (opt->values[0] == tag) + if (opt->get_at(0) == tag) return TagCheckAffirmative; return TagCheckNegative; //} diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b1deae00296..5a96894850b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1195,7 +1195,7 @@ void MainFrame::init_tabpanel() // Show a correct number of filament fields. // nozzle_diameter is undefined when SLA printer is selected if (full_config.has("nozzle_diameter")) { - m_plater->on_extruders_change(full_config.option("nozzle_diameter")->values.size()); + m_plater->on_extruders_change(full_config.option("nozzle_diameter")->size()); } } } @@ -2181,7 +2181,7 @@ void MainFrame::quick_slice(const int qs) auto input_file_basename = get_base_name(input_file); wxGetApp().app_config->update_skein_dir(get_dir_name(input_file)); - auto bed_shape = Slic3r::Polygon::new_scale(config.option("bed_shape")->values); + auto bed_shape = Slic3r::Polygon::new_scale(config.option("bed_shape")->get_values()); // auto print_center = Slic3r::Pointf->new_unscale(bed_shape.bounding_box().center()); // // auto sprint = new Slic3r::Print::Simple( diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index b2cdbb82276..dcde96ca493 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -25,7 +25,7 @@ const t_field& OptionsGroup::build_field(const Option& opt) { return build_field(opt.opt_id, opt.opt); } const t_field& OptionsGroup::build_field(const t_config_option_key& id) { - const ConfigOptionDef& opt = m_options.at(id).opt; + const ConfigOptionDef& opt = m_options.at(id).opt; return build_field(id, opt); } @@ -54,11 +54,11 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case coFloatOrPercent: case coFloat: case coFloats: - case coPercent: + case coPercent: case coPercents: case coFloatsOrPercents: - case coString: - case coStrings: + case coString: + case coStrings: m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); break; case coBools: @@ -67,28 +67,32 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); break; } - case coBool: + case coBool: m_fields.emplace(id, CheckBox::Create(this->ctrl_parent(), opt, id)); - break; + break; case coInts: if (id.find('#') == std::string::npos) { // string field with vector serialization m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); break; } - case coInt: + case coInt: m_fields.emplace(id, SpinCtrl::Create(this->ctrl_parent(), opt, id)); - break; + break; case coEnum: m_fields.emplace(id, Choice::Create(this->ctrl_parent(), opt, id)); - break; + break; case coPoint: case coPoints: m_fields.emplace(id, PointCtrl::Create(this->ctrl_parent(), opt, id)); - break; + break; + case coGraph: + case coGraphs: + m_fields.emplace(id, GraphButton::Create(this->ctrl_parent(), opt, id)); + break; case coNone: assert(false); break; default: - throw Slic3r::LogicError("This control doesn't exist till now"); break; + throw Slic3r::LogicError("This control doesn't exist till now"); break; } } // Grab a reference to fields for convenience @@ -697,10 +701,10 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, auto it_opt_map = m_opt_map.find(opt_key); if (opt_key == "extruders_count") { auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); - value = int(nozzle_diameter->values.size()); + value = int(nozzle_diameter->size()); } else if (opt_key == "milling_count") { auto *milling_diameter = dynamic_cast(config.option("milling_diameter")); - value = int(milling_diameter->values.size()); + value = int(milling_diameter->size()); } else if (it_opt != m_options.end() && it_opt->second.opt.is_script) { // when a scripted key is reset, reset its deps // call the reset function if it exits diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f95f146b174..3eed33cb041 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -438,9 +438,9 @@ void FreqChangedParams::init() ->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent &e) { auto &project_config = wxGetApp().preset_bundle->project_config; const std::vector &init_matrix = - (project_config.option("wiping_volumes_matrix"))->values; + (project_config.option("wiping_volumes_matrix"))->get_values(); const std::vector &init_extruders = - (project_config.option("wiping_volumes_extruders"))->values; + (project_config.option("wiping_volumes_extruders"))->get_values(); const std::vector extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); @@ -451,10 +451,10 @@ void FreqChangedParams::init() if (dlg.ShowModal() == wxID_OK) { std::vector matrix = dlg.get_matrix(); std::vector extruders = dlg.get_extruders(); - (project_config.option("wiping_volumes_matrix"))->values = - std::vector(matrix.begin(), matrix.end()); - (project_config.option("wiping_volumes_extruders")) - ->values = std::vector(extruders.begin(), extruders.end()); + (project_config.option("wiping_volumes_matrix"))->set( + std::vector(matrix.begin(), matrix.end())); + (project_config.option("wiping_volumes_extruders"))->set( + std::vector(extruders.begin(), extruders.end())); wxGetApp().plater()->update_project_dirty_from_presets(); wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); } @@ -968,7 +968,7 @@ void Sidebar::update_presets(Preset::Type preset_type) case Preset::TYPE_FFF_FILAMENT: { const size_t extruder_cnt = print_tech != ptFFF ? 1 : - dynamic_cast(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); + dynamic_cast(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->size(); const size_t filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); if (filament_cnt == 1) { @@ -2460,7 +2460,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ auto *nozzle_dmrs = config->opt("nozzle_diameter"); - bool one_by_one = input_files.size() == 1 || printer_technology == ptSLA; // || nozzle_dmrs->values.size() <= 1; // removed by bb (toa llow multi-import on a single extruder printer. + bool one_by_one = input_files.size() == 1 || printer_technology == ptSLA; // || nozzle_dmrs->size() <= 1; // removed by bb (toa llow multi-import on a single extruder printer. if (! one_by_one) { for (const auto &path : input_files) { if (std::regex_match(path.string(), pattern_bundle)) { @@ -2724,7 +2724,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ "the file be loaded as a single object having multiple parts?") + "\n", _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { - model.convert_multipart_object(nozzle_dmrs->values.size()); + model.convert_multipart_object(nozzle_dmrs->size()); } } } @@ -2773,14 +2773,14 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (new_model != nullptr && new_model->objects.size() > 1) { //wxMessageDialog msg_dlg(q, _L( - MessageDialog msg_dlg(q, nozzle_dmrs->values.size() > 1 ? _L( + MessageDialog msg_dlg(q, nozzle_dmrs->size() > 1 ? _L( "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" "these files to represent a single object having multiple parts?") + "\n": _L("Load these files as a single object with multiple parts?\n"), _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { - new_model->convert_multipart_object(nozzle_dmrs->values.size()); + new_model->convert_multipart_object(nozzle_dmrs->size()); } auto loaded_idxs = load_model_objects(new_model->objects); @@ -2880,7 +2880,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode coord_t min_obj_distance = static_cast(6/SCALING_FACTOR); const auto *bed_shape_opt = config->opt("bed_shape"); assert(bed_shape_opt); - auto& bedpoints = bed_shape_opt->values; + auto& bedpoints = bed_shape_opt->get_values(); Polyline bed; bed.points.reserve(bedpoints.size()); for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); @@ -5915,9 +5915,9 @@ void Plater::export_gcode(bool prefer_removable) std::string str_material = ""; if (printer_technology() == ptFFF) { const ConfigOptionStrings* filaments = fff_print().full_print_config().opt("filament_settings_id"); - assert(filaments->values.size() == fff_print().config().filament_type.values.size()); - for (int i = 0; i < filaments->values.size(); i++) { - str_material += "\n" + format(_L("'%1%' of type %2%"), filaments->values[i], fff_print().config().filament_type.values[i]); + assert(filaments->size() == fff_print().config().filament_type.size()); + for (int i = 0; i < filaments->size(); i++) { + str_material += "\n" + format(_L("'%1%' of type %2%"), filaments->get_at(i), fff_print().config().filament_type.get_at(i)); } } else if (printer_technology() == ptSLA) { str_material = format(_L(" resin '%1%'"), sla_print().full_print_config().opt_string("sla_material_settings_id")); @@ -6705,7 +6705,7 @@ bool Plater::update_filament_colors_in_full_config() for (const std::string& filament_preset : filament_presets) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); - p->config->option("filament_colour")->values = filament_colors; + p->config->option("filament_colour")->set(filament_colors); return true; } @@ -6716,7 +6716,7 @@ void Plater::on_config_change(const DynamicConfig &config) std::vector diff = p->config->diff(config); for (const std::string& opt_key : diff) { if (opt_key == "nozzle_diameter") { - if (p->config->option(opt_key)->values.size() > config.option(opt_key)->values.size()) { + if (p->config->option(opt_key)->size() > config.option(opt_key)->size()) { //lower number of extuders, please don't try to display the old gcode. p->reset_gcode_toolpaths(); p->gcode_result.reset(); @@ -6759,7 +6759,7 @@ void Plater::on_config_change(const DynamicConfig &config) } else if(opt_key == "extruder_colour") { update_scheduled = true; - if (p->config->option("filament_colour")->values.size() < config.option(opt_key)->values.size()) { + if (p->config->option("filament_colour")->size() < config.option(opt_key)->size()) { const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; std::vector filament_colors; @@ -6768,7 +6768,7 @@ void Plater::on_config_change(const DynamicConfig &config) for (const std::string& filament_preset : filament_presets) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); - p->config->option("filament_colour")->values = filament_colors; + p->config->option("filament_colour")->set(filament_colors); } p->sidebar->obj_list()->update_extruder_colors(); } @@ -6797,7 +6797,7 @@ void Plater::on_config_change(const DynamicConfig &config) void Plater::set_bed_shape() const { - set_bed_shape(p->config->option("bed_shape")->values, + set_bed_shape(p->config->option("bed_shape")->get_values(), p->config->option("max_print_height")->value, p->config->option("bed_custom_texture")->value, p->config->option("bed_custom_model")->value); @@ -6814,7 +6814,7 @@ void Plater::force_filament_colors_update() DynamicPrintConfig* config = p->config; const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; if (filament_presets.size() > 1 && - p->config->option("filament_colour")->values.size() == filament_presets.size()) + p->config->option("filament_colour")->size() == filament_presets.size()) { const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; std::vector filament_colors; @@ -6823,8 +6823,8 @@ void Plater::force_filament_colors_update() for (const std::string& filament_preset : filament_presets) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); - if (config->option("filament_colour")->values != filament_colors) { - config->option("filament_colour")->values = filament_colors; + if (config->option("filament_colour")->get_values() != filament_colors) { + config->option("filament_colour")->set(filament_colors); update_scheduled = true; } } @@ -6861,11 +6861,11 @@ std::vector Plater::get_extruder_colors_from_plater_config(const GC if (!config->has("extruder_colour")) // in case of a SLA print return extruder_colors; - extruder_colors = (config->option("extruder_colour"))->values; + extruder_colors = (config->option("extruder_colour"))->get_values(); if (!wxGetApp().plater()) return extruder_colors; - const std::vector& filament_colours = (p->config->option("filament_colour"))->values; + const std::vector& filament_colours = (p->config->option("filament_colour"))->get_values(); for (size_t i = 0; i < extruder_colors.size(); ++i) if (extruder_colors[i] == "" && i < filament_colours.size()) extruder_colors[i] = filament_colours[i]; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 9b68f386b12..5a0f25b9b55 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -699,7 +699,7 @@ void PlaterPresetComboBox::change_extruder_color() // get current color DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); auto colors = static_cast(cfg->option("extruder_colour")->clone()); - wxColour clr(colors->values[m_extruder_idx]); + wxColour clr(colors->get_at(m_extruder_idx)); if (!clr.IsOk()) clr = wxColour(0,0,0); // Don't set alfa to transparence @@ -711,7 +711,7 @@ void PlaterPresetComboBox::change_extruder_color() dialog.CenterOnParent(); if (dialog.ShowModal() == wxID_OK) { - colors->values[m_extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + colors->set_at(dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), m_extruder_idx); DynamicPrintConfig cfg_new = *cfg; cfg_new.set_key_value("extruder_colour", colors); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 2b2937bbf90..abf1b19c4a3 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -569,7 +569,7 @@ std::string PresetHints::recommended_extrusion_width(const PresetBundle& preset_ const DynamicPrintConfig& print_config = preset_bundle.fff_prints.get_edited_preset().config; const DynamicPrintConfig& printer_config = preset_bundle.printers.get_edited_preset().config; - int nb_nozzles = printer_config.option("nozzle_diameter")->values.size(); + int nb_nozzles = printer_config.option("nozzle_diameter")->size(); double nozzle_diameter = 0; for(int i=0; i< nb_nozzles; i++) diff --git a/src/slic3r/GUI/RammingChart.cpp b/src/slic3r/GUI/RammingChart.cpp index f906809609f..9110e75f134 100644 --- a/src/slic3r/GUI/RammingChart.cpp +++ b/src/slic3r/GUI/RammingChart.cpp @@ -6,7 +6,31 @@ #include "GUI_App.hpp" #include "I18N.hpp" -wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent); +wxDEFINE_EVENT(EVT_SLIC3R_CHART_CHANGED, wxCommandEvent); + +void Chart::set_x_label(const wxString &label, float incr) +{ + m_x_legend = label; + m_x_legend_incr = incr; + for (int i = 0; i < 6; i++) { + if (std::round(m_x_legend_incr * std::pow(10, i)) > 0) { + m_x_precision = i; + break; + } + } +} + +void Chart::set_y_label(const wxString &label, float incr) +{ + m_y_legend = label; + m_y_legend_incr = incr; + for (int i = 0; i < 6; i++) { + if (std::round(m_y_legend_incr * std::pow(10, i)) > 0) { + m_y_precision = i; + break; + } + } +} void Chart::draw() { wxAutoBufferedPaintDC dc(this); // unbuffered DC caused flickering on win @@ -24,10 +48,9 @@ void Chart::draw() { #endif dc.DrawRectangle(m_rect); - if (visible_area.m_width < 0.499) { - dc.DrawText(m_no_point_legend, wxPoint(m_rect.GetLeft() + m_rect.GetWidth() / 2 - legend_side, + if (m_line_to_draw.empty()) { + dc.DrawText(m_no_point_legend, wxPoint(m_rect.GetLeft() + m_rect.GetWidth() / 2 - 2*legend_side, m_rect.GetBottom() - m_rect.GetHeight() / 2)); - return; } @@ -43,9 +66,11 @@ void Chart::draw() { dc.SetPen( wxPen( wxColor(0,0,0), 1 ) ); #endif for (unsigned int i=0; im_type == Slic3r::GraphData::GraphType::SPLINE) { + dc.DrawLine(m_rect.GetLeft()+i, (m_line_to_draw)[i], m_rect.GetLeft()+i+1, (m_line_to_draw)[i+1]); + } else if (this->m_type == Slic3r::GraphData::GraphType::LINEAR) { dc.DrawLine(m_rect.GetLeft()+i, (m_line_to_draw)[i], m_rect.GetLeft()+i+1, (m_line_to_draw)[i+1]); - else { + } else { dc.DrawLine(m_rect.GetLeft()+i, (m_line_to_draw)[i], m_rect.GetLeft()+i+1, (m_line_to_draw)[i]); dc.DrawLine(m_rect.GetLeft()+i+1,(m_line_to_draw)[i], m_rect.GetLeft()+i+1, (m_line_to_draw)[i+1]); } @@ -55,31 +80,35 @@ void Chart::draw() { // draw draggable buttons dc.SetBrush(*wxBLUE_BRUSH); #ifdef _WIN32 - dc.SetPen(wxPen(GetForegroundColour())); + dc.SetPen(wxPen(GetForegroundColour())); #else - dc.SetPen( wxPen( wxColor(0,0,0), 1 ) ); + dc.SetPen( wxPen( wxColor(0,0,0), 1 ) ); #endif - for (auto& button : m_buttons) { - //dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side)); - dc.DrawCircle(math_to_screen(button.get_pos()),side/2.); - //dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10)); + for (size_t idx = 0; idx < m_buttons.size(); ++idx) { + auto& button = m_buttons[idx]; + if (m_dragged != &button && button_idx_hover != idx) { + if (visible_area.GetLeft() <= button.get_pos().m_x && button.get_pos().m_x <= visible_area.GetRight() && + visible_area.GetTop() <= button.get_pos().m_y && button.get_pos().m_y <= visible_area.GetBottom()) { + dc.DrawCircle(math_to_screen(button.get_pos()), side / 2.); + } + } + } + if (m_dragged || button_idx_hover < m_buttons.size()) { + dc.SetBrush(orange_brush); + dc.DrawCircle(math_to_screen(m_dragged ? m_dragged->get_pos() : m_buttons[button_idx_hover].get_pos()),side/2.); } // draw x-axis: float last_mark = -10000; - for (float math_x = int(visible_area.m_x * 10) / 10; math_x < (visible_area.m_x + visible_area.m_width); math_x += m_x_legend_incr) { + for (float math_x = int(visible_area.m_x * 10) / 10.f; math_x < (visible_area.m_x + visible_area.m_width); math_x += m_x_legend_incr) { int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x; int y = m_rect.GetBottom(); if (x-last_mark < legend_side) continue; dc.DrawLine(x,y+3,x,y-3); - if (m_x_legend_incr == int(m_x_legend_incr)) { + if (m_x_precision == 0) { dc.DrawText(wxString()<get_pos().m_x); - float pos_y = float(m_dragged->get_pos().m_y); + wxPoint2DDouble pos; + if (m_dragged) { + pos = m_dragged->get_pos(); + } else if (m_mouse_hover_point.m_x == 0 && m_mouse_hover_point.m_y == 0) { + pos = screen_to_math(m_previous_mouse); + } else { + pos = m_mouse_hover_point; + } // show on bottom right // TODO: compute legend height instead of '3 * scale_unit' wxPoint ptx = math_to_screen(wxPoint2DDouble(visible_area.m_x + visible_area.m_width, visible_area.m_y)); @@ -128,8 +160,8 @@ void Chart::draw() { ptx.y -= 1*legend_side; pty.x -= 1*legend_side; pty.y -= 0.5*legend_side; - dc.DrawText(wxString().Format(wxT("x: %.3f"), pos_x), ptx); - dc.DrawText(wxString().Format(wxT("y: %.3f"), pos_y), pty); + dc.DrawText(wxString().Format(wxT("x: %.3f"), pos.m_x), ptx); + dc.DrawText(wxString().Format(wxT("y: %.3f"), pos.m_y), pty); } } @@ -138,7 +170,7 @@ void Chart::mouse_right_button_clicked(wxMouseEvent& event) { return; wxPoint point = event.GetPosition(); int button_index = which_button_is_clicked(point); - if (button_index != -1 && m_buttons.size() > 2) { + if (button_index != -1) { m_buttons.erase(m_buttons.begin() + button_index); recalculate_line(); } else { @@ -166,25 +198,44 @@ void Chart::mouse_clicked(wxMouseEvent& event) { if ( button_index != -1) { m_dragged = &m_buttons[button_index]; m_previous_mouse = point; + m_mouse_hover_point = m_dragged->get_pos(); Refresh(); } } void Chart::mouse_moved(wxMouseEvent& event) { - if (!event.Dragging() || !m_dragged) return; - wxPoint pos = event.GetPosition(); + wxPoint pos = event.GetPosition(); wxRect rect = m_rect; + size_t button_idx_hover_old = button_idx_hover; + button_idx_hover = size_t(-1); rect.Deflate(side/2.); if (!(rect.Contains(pos))) { // the mouse left chart area mouse_left_window(event); return; } - int delta_x = pos.x - m_previous_mouse.x; - int delta_y = pos.y - m_previous_mouse.y; - m_dragged->move(fixed_x ? 0 : double(delta_x) / m_rect.GetWidth() * visible_area.m_width, - -double(delta_y) / m_rect.GetHeight() * visible_area.m_height); - m_previous_mouse = pos; + if (event.Dragging() && m_dragged) { + int delta_x = pos.x - m_previous_mouse.x; + int delta_y = pos.y - m_previous_mouse.y; + m_dragged->move(fixed_x ? 0 : double(delta_x) / m_rect.GetWidth() * visible_area.m_width, + -double(delta_y) / m_rect.GetHeight() * visible_area.m_height); + m_previous_mouse = pos; + m_mouse_hover_point.m_x = 0; + m_mouse_hover_point.m_y = 0; + } else { + int idx_bt = which_button_is_clicked(pos); + if (idx_bt < 0) { + m_previous_mouse = pos; + m_mouse_hover_point.m_x = 0; + m_mouse_hover_point.m_y = 0; + } else { + button_idx_hover = size_t(idx_bt); + assert(button_idx_hover < m_buttons.size()); + m_previous_mouse = math_to_screen(m_buttons[button_idx_hover].get_pos()); + m_mouse_hover_point = m_buttons[button_idx_hover].get_pos(); + } + } recalculate_line(); + Refresh(); } void Chart::mouse_double_clicked(wxMouseEvent& event) { @@ -201,8 +252,46 @@ void Chart::mouse_double_clicked(wxMouseEvent& event) { void Chart::mouse_left_window(wxMouseEvent &e) { - mouse_released(e); -} + button_idx_hover = size_t(-1); + if (m_dragged != nullptr) { + wxPoint2DDouble dblpoint = screen_to_math(e.GetPosition()); + double exit_left = (dblpoint.m_x - visible_area.m_x) / visible_area.m_width; + double exit_right = (visible_area.m_x + visible_area.m_width - dblpoint.m_x) / visible_area.m_width; + double exit_bottom = (dblpoint.m_y - visible_area.m_y) / visible_area.m_height; + double exit_top = (visible_area.m_y + visible_area.m_height - dblpoint.m_y) / visible_area.m_height; + bool is_exit_left = exit_left < exit_right; + bool is_exit_exit_bottom = exit_bottom < exit_top; + bool is_exit_side = (is_exit_left ? exit_left : exit_right) < (is_exit_exit_bottom ? exit_bottom : exit_top); + if (!fixed_x) { + wxDouble m_x = m_dragged->get_pos().m_x; + // check if exit by left / right + if (is_exit_side) { + if (is_exit_left) { + m_x = visible_area.m_x; + } else { + m_x = visible_area.m_x + visible_area.m_width; + } + } + m_x = int((m_x + this->m_x_legend_incr / 2) / this->m_x_legend_incr) * this->m_x_legend_incr; + m_dragged->move(m_x - m_dragged->get_pos().m_x, 0); + } + wxDouble m_y = m_dragged->get_pos().m_y; + // check if exit by top / bo + if (!is_exit_side) { + if (is_exit_exit_bottom) { + m_y = visible_area.m_y; + } else { + m_y = visible_area.m_y + visible_area.m_height; + } + } + m_y = int((m_y + this->m_y_legend_incr / 2) / this->m_y_legend_incr) * this->m_y_legend_incr; + m_dragged->move(0, m_y - m_dragged->get_pos().m_y); + m_previous_mouse = math_to_screen(m_dragged->get_pos()); + m_dragged = nullptr; + recalculate_line(); + } + this->Refresh(); +} void Chart::mouse_released(wxMouseEvent &) { if (m_dragged != nullptr) { @@ -212,6 +301,7 @@ void Chart::mouse_released(wxMouseEvent &) } float m_y = int((m_dragged->get_pos().m_y + this->m_y_legend_incr / 2) / this->m_y_legend_incr) * this->m_y_legend_incr; m_dragged->move(0, m_y - m_dragged->get_pos().m_y); + m_previous_mouse = math_to_screen(m_dragged->get_pos()); m_dragged = nullptr; recalculate_line(); } @@ -222,14 +312,16 @@ void Chart::recalculate_line() { m_total_volume = 0.f; std::vector points; + size_t before_area_idx = 0; for (auto& but : m_buttons) { - points.push_back(wxPoint(math_to_screen(but.get_pos()))); - if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back(); - if (points.size()>1 && points.back().x > m_rect.GetRight()) { - points.pop_back(); - break; + before_area_idx += visible_area.m_x <= but.get_pos().m_x ? 1 : 0; + if (visible_area.m_x <= but.get_pos().m_x && but.get_pos().m_x <= visible_area.m_x + visible_area.m_width) { + points.push_back(wxPoint(math_to_screen(but.get_pos()))); } } + if (points.empty() && before_area_idx < m_buttons.size()) { + points.push_back(wxPoint(math_to_screen(m_buttons[before_area_idx].get_pos()))); + } // The calculation wouldn't work in case the ramming is to be turned off completely. if (points.size()>1) { @@ -262,8 +354,7 @@ void Chart::recalculate_line() { mu[N] = 1; rhs[0] = (6.f/h[1]) * (float(points[0].y-points[1].y)/(points[0].x-points[1].x) - endpoints_derivative); rhs[N] = (6.f/h[N]) * (endpoints_derivative - float(points[N-1].y-points[N].y)/(points[N-1].x-points[N].x)); - } - else { + } else { lambda[0] = 0; mu[N] = 0; rhs[0] = 0; @@ -281,29 +372,35 @@ void Chart::recalculate_line() { for (int i=N-1;i>=0;--i) rhs[i] = (rhs[i]-lambda[i]*rhs[i+1])/diag[i]; - unsigned int i=1; + size_t curr_idx = 1; float y=0.f; - for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) { - if (splines) { - if (i x) - y = points[0].y; - else - if (points[N].x < x) - y = points[N].y; + if (this->m_type == Slic3r::GraphData::GraphType::SPLINE) { + y = (rhs[curr_idx - 1] * pow(points[curr_idx].x - x, 3) + rhs[curr_idx] * pow(x - points[curr_idx - 1].x, 3)) / (6 * h[curr_idx]) + + (points[curr_idx - 1].y - rhs[curr_idx - 1] * h[curr_idx] * h[curr_idx] / 6.f) * (points[curr_idx].x - x) / h[curr_idx] + + (points[curr_idx].y - rhs[curr_idx] * h[curr_idx] * h[curr_idx] / 6.f) * (x - points[curr_idx - 1].x) / h[curr_idx]; + m_line_to_draw.push_back(y); + } else if (this->m_type == Slic3r::GraphData::GraphType::LINEAR) { + assert(points[curr_idx - 1].x < points[curr_idx].x); + float ratio = float(x - points[curr_idx - 1].x) / + (points[curr_idx].x - points[curr_idx - 1].x); + y = (1 - ratio) * points[curr_idx - 1].y + ratio * points[curr_idx].y; + m_line_to_draw.push_back(y); + } else /*if (this->m_type == Slic3r::GraphData::GraphType::SQUARE)*/ { + if (points.back().x == x) + m_line_to_draw.push_back(points[curr_idx].y); else - y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) + - (points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] + - (points[i].y -rhs[i] *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i]; - m_line_to_draw.push_back(y); - } - else { - float x_math = screen_to_math(wxPoint(x,0)).m_x; - if (i+2<=points.size() && m_buttons[i+1].get_pos().m_x-0.125 < x_math) - ++i; - m_line_to_draw.push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[i].get_pos().m_y)).y); + m_line_to_draw.push_back(points[curr_idx - 1].y); + } } m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1); @@ -312,32 +409,32 @@ void Chart::recalculate_line() { } } else if (points.size() == 1) { - for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) { - float x_math = screen_to_math(wxPoint(x,0)).m_x; - m_line_to_draw.push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[0].get_pos().m_y)).y); - m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1); - m_line_to_draw.back() = std::min(m_line_to_draw.back(), m_rect.GetBottom()-1); - m_total_volume += (m_rect.GetBottom() - m_line_to_draw.back()) * (visible_area.m_width / m_rect.GetWidth()) * (visible_area.m_height / m_rect.GetHeight()); + m_line_to_draw.push_back(points.front().y); + m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1); + m_line_to_draw.back() = std::min(m_line_to_draw.back(), m_rect.GetBottom()-1); + for (int x = m_rect.GetLeft() + 1; x <= m_rect.GetRight(); ++x) { + m_line_to_draw.push_back(m_line_to_draw.back()); } + m_total_volume += (m_rect.GetBottom() - m_line_to_draw.back()) * (visible_area.m_width) * (visible_area.m_height / m_rect.GetHeight()); } - wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED)); + wxPostEvent(this->GetParent(), wxCommandEvent(EVT_SLIC3R_CHART_CHANGED)); Refresh(); } -std::vector Chart::get_speed(float sampling) const { - std::vector speeds_out; +std::vector Chart::get_value_samples(float sampling) const { + std::vector smaples; const int number_of_samples = std::round( visible_area.m_width / sampling); - if (number_of_samples>0) { + if (number_of_samples > 0 && !m_line_to_draw.empty()) { const int dx = (m_line_to_draw.size()-1) / number_of_samples; for (int j=0;j> Chart::get_buttons() const { diff --git a/src/slic3r/GUI/RammingChart.hpp b/src/slic3r/GUI/RammingChart.hpp index 0d3bfec96c8..5155a63d523 100644 --- a/src/slic3r/GUI/RammingChart.hpp +++ b/src/slic3r/GUI/RammingChart.hpp @@ -1,24 +1,26 @@ #ifndef RAMMING_CHART_H_ #define RAMMING_CHART_H_ +#include + #include #include #ifndef WX_PRECOMP #include #endif -wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent); +wxDECLARE_EVENT(EVT_SLIC3R_CHART_CHANGED, wxCommandEvent); class Chart : public wxWindow { - public: + Chart(wxWindow* parent, wxRect rect,const std::vector>& initial_buttons, int scale_unit=10) : wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize()), scale_unit(scale_unit), legend_side(5*scale_unit) { SetBackgroundStyle(wxBG_STYLE_PAINT); - m_rect = wxRect(wxPoint(legend_side,0),rect.GetSize()-wxSize(legend_side,legend_side)); + m_rect = wxRect(wxPoint(2 * legend_side, 0), rect.GetSize() - wxSize(2 * legend_side, legend_side)); visible_area = wxRect2DDouble(0.0, 0.0, 20., 20.); m_buttons.clear(); if (initial_buttons.size()>0) @@ -27,24 +29,32 @@ class Chart : public wxWindow { recalculate_line(); } void set_xy_range(float min_x, float min_y, float max_x, float max_y) { - if (min_x >= 0 && max_x > min_x) { + if (!std::isnan(min_x)) { visible_area.SetLeft(min_x); + } + if (!std::isnan(max_x) && max_x > visible_area.GetLeft()) { visible_area.SetRight(max_x); } - if (min_y >= 0 && max_y > min_y) { + if (!std::isnan(min_y)) { visible_area.SetTop(min_y); + } + if (!std::isnan(max_y) && max_y > visible_area.GetTop()) { visible_area.SetBottom(max_y); } recalculate_line(); } void set_manual_points_manipulation(bool manip) { m_manual_points_manipulation = manip; } - void set_x_label(const wxString &label, float incr = 0.1f) { m_x_legend = label; m_x_legend_incr = incr; } - void set_y_label(const wxString &label, float incr = 0.1f) { m_y_legend = label; m_y_legend_incr = incr; } + void set_x_label(const wxString &label, float incr = 0.1f); + void set_y_label(const wxString &label, float incr = 0.1f); void set_no_point_label(const wxString &label) { m_no_point_legend = label; } + void set_type(Slic3r::GraphData::GraphType type) { m_type = type; recalculate_line(); } + Slic3r::GraphData::GraphType get_type() const { return m_type; } + float get_volume() const { return m_total_volume; } - float get_max_x() const { return visible_area.m_width; } + float get_max_x() const { return visible_area.m_x + visible_area.m_width; } + float get_min_x() const { return visible_area.m_x; } - std::vector get_speed(float sampling) const; //returns sampled ramming speed + std::vector get_value_samples(float sampling) const; //returns sampled values std::vector> get_buttons() const; // returns buttons position void set_buttons(std::vector>); @@ -64,18 +74,22 @@ class Chart : public wxWindow { private: static const bool fixed_x = true; - static const bool splines = true; static const int side = 10; // side of draggable button + Slic3r::GraphData::GraphType m_type = Slic3r::GraphData::GraphType::LINEAR; const int scale_unit; int legend_side; wxString m_x_legend; wxString m_y_legend; float m_x_legend_incr = 0.1f; + int m_x_precision = 0; float m_y_legend_incr = 1.f; + int m_y_precision = 0; wxString m_no_point_legend; bool m_manual_points_manipulation = false; + wxBrush orange_brush = wxBrush(wxColour(255,150,0), wxBRUSHSTYLE_SOLID); + class ButtonToDrag { public: bool operator<(const ButtonToDrag& a) const { return m_pos.m_x < a.m_pos.m_x; } @@ -108,12 +122,19 @@ class Chart : public wxWindow { int which_button_is_clicked(const wxPoint& point) const { if (!m_rect.Contains(point)) return -1; - for (unsigned int i=0;i m_buttons; std::vector m_line_to_draw; wxRect2DDouble visible_area; ButtonToDrag* m_dragged = nullptr; + size_t button_idx_hover = size_t(-1); float m_total_volume = 0.f; }; diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index 1693ac08b6e..3e10680e46c 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -235,7 +235,7 @@ void _set_float(DynamicPrintConfig& conf, const ConfigOption* opt, std::string& } else if (opt->type() == ConfigOptionType::coFloats) { ConfigOptionFloats* new_opt = static_cast(opt->clone()); double new_val = round(f_val); - if (!new_opt->values.empty()) { + if (!new_opt->empty()) { // only update if difference is significant double old_value = idx < 0 ? new_opt->get_float(0) : new_opt->get_float(idx); if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) @@ -258,7 +258,7 @@ void _set_float(DynamicPrintConfig& conf, const ConfigOption* opt, std::string& } else if (opt->type() == ConfigOptionType::coPercents) { ConfigOptionPercents* new_opt = static_cast(opt->clone()); double percent_f = floor(f_val * 100000. + 0.5) / 1000.; - if (!new_opt->values.empty()) { + if (!new_opt->empty()) { // only update if difference is significant double old_value = idx < 0 ? new_opt->get_float(0) : new_opt->get_float(idx); if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) @@ -282,7 +282,7 @@ void _set_float(DynamicPrintConfig& conf, const ConfigOption* opt, std::string& } else if (opt->type() == ConfigOptionType::coFloatsOrPercents) { ConfigOptionFloatsOrPercents* new_opt = static_cast(opt->clone()); double new_val = round(f_val); - if (!new_opt->values.empty() && !new_opt->values.front().percent) { + if (!new_opt->empty() && !new_opt->get_at(0).percent) { // only update if difference is significant double old_value = idx < 0 ? new_opt->get_float(0) : new_opt->get_float(idx); if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) @@ -334,9 +334,9 @@ void _set_percent(DynamicPrintConfig& conf, const ConfigOption* opt, std::string conf.set_key_value(key, new ConfigOptionFloat(percent_f / 100.)); } else if (opt->type() == ConfigOptionType::coFloats) { ConfigOptionFloats* new_opt = static_cast(opt->clone()); - if (!new_opt->values.empty()) { + if (!new_opt->empty()) { // only update if difference is significant - double old_value = new_opt->values.front() * 100; + double old_value = new_opt->get_at(0) * 100; if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } @@ -354,9 +354,9 @@ void _set_percent(DynamicPrintConfig& conf, const ConfigOption* opt, std::string conf.set_key_value(key, new ConfigOptionPercent(percent_f)); } else if (opt->type() == ConfigOptionType::coPercents) { ConfigOptionPercents* new_opt = static_cast(opt->clone()); - if (!new_opt->values.empty()) { + if (!new_opt->empty()) { // only update if difference is significant - double old_value = new_opt->values.front(); + double old_value = new_opt->get_at(0); if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } @@ -376,9 +376,9 @@ void _set_percent(DynamicPrintConfig& conf, const ConfigOption* opt, std::string conf.set_key_value(key, new ConfigOptionFloatOrPercent(percent_f, true)); } else if (opt->type() == ConfigOptionType::coFloatsOrPercents) { ConfigOptionFloatsOrPercents* new_opt = static_cast(opt->clone()); - if (!new_opt->values.empty() && new_opt->values.front().percent) { + if (!new_opt->empty() && new_opt->get_at(0).percent) { // only update if difference is significant - double old_value = new_opt->values.front().value; + double old_value = new_opt->get_at(0).value; if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 99f385841da..18ac0456057 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -70,10 +70,10 @@ template void change_opt_key(std::string& opt_key, DynamicPrintConfig* config, int& cnt) { T* opt_cur = static_cast(config->option(opt_key)); - cnt = opt_cur->values.size(); + cnt = opt_cur->size(); return; - if (opt_cur->values.size() > 0) + if (opt_cur->size() > 0) opt_key += "#" + std::to_string(0); } @@ -127,10 +127,10 @@ static std::string get_key(const std::string& opt_key, Preset::Type type) void change_opt_keyFoP(std::string& opt_key, DynamicPrintConfig* config, int& cnt) { ConfigOptionFloatsOrPercents* opt_cur = static_cast(config->option(opt_key)); - cnt = opt_cur->values.size(); + cnt = opt_cur->size(); return; - if (opt_cur->values.size() > 0) + if (opt_cur->size() > 0) opt_key += "#" + std::to_string(0); } const GroupAndCategory& OptionsSearcher::get_group_and_category(const std::string& opt_key, ConfigOptionMode tags) const @@ -207,6 +207,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty //case coFloatsOrPercents:change_opt_key(opt_key, config, cnt); break; case coFloatsOrPercents:change_opt_keyFoP(opt_key, config, cnt); break; case coPoints: change_opt_key(opt_key, config, cnt); break; + case coGraphs: change_opt_key(opt_key, config, cnt); break; default: break; } @@ -583,18 +584,6 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Ty size_t pos_hash = opt_key.find('#'); if (pos_hash == std::string::npos) { auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key), type, idx })); -#ifdef _DEBUG - if (options[it - options.begin()].opt_key_with_idx() != opt_key) { - std::wstring wopt_key = boost::nowide::widen(opt_key); - for (const Option &opt : options) { - if (opt.key == wopt_key) { - if (opt.type == type) { - std::cout << "found\n"; - } - } - } - } -#endif assert(it != options.end()); return options[it - options.begin()]; } else { @@ -602,17 +591,6 @@ const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Ty std::string opt_idx = opt_key.substr(pos_hash + 1); idx = atoi(opt_idx.c_str()); auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(raw_opt_key), type, idx })); -#ifdef _DEBUG - if (options[it - options.begin()].opt_key_with_idx() != opt_key) { - for (const Option &opt : options) { - if (opt.opt_key_with_idx() == opt_key) { - if (opt.type == type) { - std::cout << "found\n"; - } - } - } - } -#endif assert(it != options.end()); return options[it - options.begin()]; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c16edfe4dd5..ec56dcf2c77 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -846,7 +846,7 @@ template 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++) + for (size_t i = 0; i < opt_cur->size(); i++) map.emplace(opt_key /* + "#" + std::to_string(i)*/, std::pair{i, value}); } @@ -870,6 +870,7 @@ 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; + case coGraphs: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; default: m_options_list.emplace(opt_key, std::pair{-1, m_opt_status_value}); break; } } @@ -916,6 +917,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; + case coGraphs: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; default: m_options_list.emplace(opt_key, std::pair{0, m_opt_status_value}); break; } } @@ -1054,7 +1056,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) 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); - bool is_empty = m_config_base->option("compatible_printers")->values.empty(); + bool is_empty = m_config_base->option("compatible_printers")->empty(); m_compatible_printers.checkbox->SetValue(is_empty); is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); } @@ -1064,7 +1066,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) 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); - bool is_empty = m_config_base->option("compatible_prints")->values.empty(); + bool is_empty = m_config_base->option("compatible_prints")->empty(); m_compatible_prints.checkbox->SetValue(is_empty); is_empty ? m_compatible_prints.btn->Disable() : m_compatible_prints.btn->Enable(); } @@ -1457,7 +1459,7 @@ void Tab::update_wiping_button_visibility() { if (m_preset_bundle->printers.get_selected_preset().printer_technology() != ptFFF) return; // ys_FIXME bool wipe_tower_enabled = dynamic_cast( (m_preset_bundle->fff_prints.get_edited_preset().config ).option("wipe_tower"))->value; - bool multiple_extruders = dynamic_cast((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1; + bool multiple_extruders = dynamic_cast((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->size() > 1; auto wiping_dialog_button = wxGetApp().sidebar().get_wiping_dialog_button(); if (wiping_dialog_button) { @@ -1667,7 +1669,7 @@ void Tab::update_preset_description_line() { //FIXME add prefered_sla_material_profile for SLA const std::string &default_print_profile = preset.config.opt_string("default_print_profile"); - const std::vector &default_filament_profiles = preset.config.option("default_filament_profile")->values; + const std::vector &default_filament_profiles = preset.config.option("default_filament_profile")->get_values(); if (!default_print_profile.empty()) description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile; if (!default_filament_profiles.empty()) @@ -1898,7 +1900,7 @@ std::vector Tab::create_pages(std::string setting_type_nam assert(m_config); if (boost::any_cast(value) && tab->m_extruders_count > 1) { SuppressBackgroundProcessingUpdate sbpu; - std::vector nozzle_diameters = static_cast(m_config->option("nozzle_diameter"))->values; + std::vector nozzle_diameters = static_cast(m_config->option("nozzle_diameter"))->get_values(); const double frst_diam = nozzle_diameters[0]; for (auto cur_diam : nozzle_diameters) { @@ -2336,10 +2338,8 @@ std::vector Tab::create_pages(std::string setting_type_nam // hack (see FreqChangedParams::init() in plater.cpp) current_line.label_tooltip = full_line; } else if (full_line == "update_nozzle_diameter") { - current_group - ->m_on_change = set_or_add(current_group->m_on_change, [this, - idx_page](const t_config_option_key &opt_key, - boost::any value) { + current_group->m_on_change = set_or_add(current_group->m_on_change, [this, idx_page] + (const t_config_option_key &opt_key, boost::any value) { TabPrinter *tab = nullptr; if ((tab = dynamic_cast(this)) == nullptr) return; @@ -2348,7 +2348,7 @@ std::vector Tab::create_pages(std::string setting_type_nam SuppressBackgroundProcessingUpdate sbpu; const double new_nd = boost::any_cast(value); std::vector nozzle_diameters = - static_cast(m_config_base->option("nozzle_diameter"))->values; + static_cast(m_config_base->option("nozzle_diameter"))->get_values(); // if value was changed if (fabs(nozzle_diameters[idx_page == 0 ? 1 : 0] - new_nd) > EPSILON) { @@ -2568,26 +2568,6 @@ std::vector Tab::create_pages(std::string setting_type_nam return sizer; }; current_group->append_line(thisline); - } else if (boost::starts_with(full_line, "extruder_extrusion_multiplier_speed")) { - // don't forget the idx_page as it's on the extruder page. - Line thisline = current_group->create_single_option_line("extruder_extrusion_multiplier_speed", "", idx_page); - thisline.widget = [this](wxWindow *parent) { - auto dialog_btn = new wxButton(parent, wxID_ANY, _L("Extrusion multiplier per speed") + dots, - wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - dialog_btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - wxGetApp().UpdateDarkUI(dialog_btn); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(dialog_btn); - - dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent &e) { - GraphDialog dlg(this, (m_config_base->option("extruder_extrusion_multiplier_speed"))->get_at(0)); - if (dlg.ShowModal() == wxID_OK) - //(m_config_base->option("extruder_extrusion_multiplier_speed"))->get_at(0) = dlg.get_parameters(); - load_key_value("extruder_extrusion_multiplier_speed", dlg.get_parameters(), false, 0); - })); - return sizer; - }; - current_group->append_line(thisline); } else if (full_line == "bed_shape") { TabPrinter *tab = nullptr; if ((tab = dynamic_cast(this)) == nullptr) @@ -2691,12 +2671,11 @@ std::vector Tab::create_pages(std::string setting_type_nam assert(m_config); btn->Bind(wxEVT_BUTTON, [this, idx_page](wxCommandEvent &e) { std::vector colors = - static_cast(m_config->option("extruder_colour"))->values; + static_cast(m_config->option("extruder_colour"))->get_values(); colors[idx_page] = ""; DynamicPrintConfig new_conf = *m_config; - new_conf.set_key_value("extruder_colour", - (new ConfigOptionStrings(colors))->set_is_extruder_size(true)); + new_conf.set_key_value("extruder_colour", (new ConfigOptionStrings(colors))->set_is_extruder_size(true)); load_config(new_conf); update_dirty(); @@ -3229,18 +3208,18 @@ void TabPrinter::build_fff() m_pages.reserve(30); auto* nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); - m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); + m_initial_extruders_count = m_extruders_count = nozzle_diameter->size(); wxGetApp().sidebar().update_objects_list_extruder_column(m_initial_extruders_count); auto* milling_diameter = dynamic_cast(m_config->option("milling_diameter")); - m_initial_milling_count = m_milling_count = milling_diameter->values.size(); + m_initial_milling_count = m_milling_count = milling_diameter->size(); const Preset* parent_preset = m_printer_technology == ptSLA ? nullptr // just for first build, if SLA printer preset is selected : m_presets->get_selected_preset_parent(); m_sys_extruders_count = parent_preset == nullptr ? 0 : - static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); + static_cast(parent_preset->config.option("nozzle_diameter"))->size(); m_sys_milling_count = parent_preset == nullptr ? 0 : - static_cast(parent_preset->config.option("milling_diameter"))->values.size(); + static_cast(parent_preset->config.option("milling_diameter"))->size(); append(this->m_pages, create_pages("printer_fff.ui")); @@ -3272,7 +3251,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count) m_preset_bundle->update_multi_material_filament_presets(); is_count_changed = true; } else if (m_extruders_count == 1 && - m_preset_bundle->project_config.option("wiping_volumes_matrix")->values.size() > 1) + m_preset_bundle->project_config.option("wiping_volumes_matrix")->size() > 1) m_preset_bundle->update_multi_material_filament_presets(); @@ -3288,9 +3267,9 @@ void TabPrinter::extruders_count_changed(size_t extruders_count) on_value_change("extruders_count", (int)extruders_count); //update default tool_name => not used, no need to do that //ConfigOptionStrings* names = this->m_config->option("tool_name"); - //for (size_t ss = 0; ss < names->values.size(); ss++) - // if (names->values[ss] == "") - // names->values[ss] = std::to_string(ss); + //for (size_t ss = 0; ss < names->size(); ss++) + // if (names->get_at(ss) == "") + // names->get_at(ss) = std::to_string(ss); //update gui wxGetApp().sidebar().update_objects_list_extruder_column(extruders_count); } @@ -3543,13 +3522,13 @@ void TabPrinter::on_preset_loaded() { // update the extruders count field auto *nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); - size_t extruders_count = nozzle_diameter->values.size(); + size_t extruders_count = nozzle_diameter->size(); // update the GUI field according to the number of nozzle diameters supplied extruders_count_changed(extruders_count); //same for milling auto* milling_diameter = dynamic_cast(m_config->option("milling_diameter")); - size_t milling_count = milling_diameter->values.size(); + size_t milling_count = milling_diameter->size(); milling_count_changed(milling_count); } @@ -3750,8 +3729,8 @@ void TabPrinter::toggle_options() DynamicPrintConfig new_conf = *m_config; if (dialog.ShowModal() == wxID_YES) { auto wipe = static_cast(m_config->option("wipe")->clone()); - for (size_t w = 0; w < wipe->values.size(); w++) - wipe->values[w] = false; + for (size_t w = 0; w < wipe->size(); w++) + wipe->get_at(w) = false; new_conf.set_key_value("wipe", wipe); } else { new_conf.set_key_value("use_firmware_retraction", new ConfigOptionBool(false)); @@ -3799,24 +3778,24 @@ void TabPrinter::toggle_options() int64_t z_step_Mlong = (int64_t)(z_step * 1000000.); DynamicPrintConfig new_conf; bool has_changed = false; - const std::vector& nozzle_diameters = m_config->option("nozzle_diameter")->values; - const std::vector& min_layer_height = m_config->option("min_layer_height")->values; + const std::vector& nozzle_diameters = m_config->option("nozzle_diameter")->get_values(); + const std::vector& min_layer_height = m_config->option("min_layer_height")->get_values(); for (int i = 0; i < min_layer_height.size(); i++) { if(!min_layer_height[i].percent) if (min_layer_height[i].value != 0 && (int64_t)(min_layer_height[i].value * 1000000.) % z_step_Mlong != 0) { if (!has_changed) new_conf = *m_config; - new_conf.option("min_layer_height")->values[i].value = std::max(z_step, Slic3r::check_z_step(min_layer_height[i].value, z_step)); + new_conf.option("min_layer_height")->set_at(FloatOrPercent{std::max(z_step, Slic3r::check_z_step(min_layer_height[i].value, z_step)), false}, i); has_changed = true; } } - std::vector max_layer_height = m_config->option("max_layer_height")->values; + std::vector max_layer_height = m_config->option("max_layer_height")->get_values(); for (int i = 0; i < max_layer_height.size(); i++) { if (!max_layer_height[i].percent) if ((int64_t)(max_layer_height[i].value * 1000000.) % z_step_Mlong != 0) { if (!has_changed) new_conf = *m_config; - new_conf.option("max_layer_height")->values[i].value = std::max(z_step, Slic3r::check_z_step(max_layer_height[i].value, z_step)); + new_conf.option("max_layer_height")->get_at(i).value = std::max(z_step, Slic3r::check_z_step(max_layer_height[i].value, z_step)); has_changed = true; } } @@ -3980,13 +3959,13 @@ void Tab::load_current_preset() } on_presets_changed(); if (printer_technology == ptFFF) { - static_cast(this)->m_initial_extruders_count = static_cast(m_presets->get_selected_preset().config.option("nozzle_diameter"))->values.size(); //static_cast(this)->m_extruders_count; + static_cast(this)->m_initial_extruders_count = static_cast(m_presets->get_selected_preset().config.option("nozzle_diameter"))->size(); //static_cast(this)->m_extruders_count; const Preset* parent_preset = m_presets->get_selected_preset_parent(); static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : - static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); + static_cast(parent_preset->config.option("nozzle_diameter"))->size(); static_cast(this)->m_initial_milling_count = static_cast(this)->m_milling_count; static_cast(this)->m_sys_milling_count = parent_preset == nullptr ? 0 : - static_cast(parent_preset->config.option("milling_diameter"))->values.size(); + static_cast(parent_preset->config.option("milling_diameter"))->size(); } } else { @@ -4708,8 +4687,8 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep // Collect and set indices of depending_presets marked as compatible. wxArrayInt selections; auto *compatible_printers = dynamic_cast(m_config_base->option(deps.key_list)); - if (compatible_printers != nullptr || !compatible_printers->values.empty()) - for (auto preset_name : compatible_printers->values) + if (compatible_printers != nullptr || !compatible_printers->empty()) + for (auto preset_name : compatible_printers->get_values()) for (size_t idx = 0; idx < presets.GetCount(); ++idx) if (presets[idx] == preset_name) { selections.Add(idx); @@ -4748,17 +4727,17 @@ void SubstitutionManager::init(DynamicPrintConfig* config, wxWindow* parent, wxF void SubstitutionManager::validate_lenth() { - std::vector& substitutions = m_config->option("gcode_substitutions")->values; - if ((substitutions.size() % 4) != 0) { + size_t substitutions_size = m_config->option("gcode_substitutions")->size(); + if ((substitutions_size % 4) != 0) { WarningDialog(m_parent, "Value of gcode_substitutions parameter will be cut to valid length", "Invalid length of gcode_substitutions parameter").ShowModal(); - substitutions.resize(substitutions.size() - (substitutions.size() % 4)); + m_config->option("gcode_substitutions")->resize(substitutions_size - (substitutions_size % 4)); } } bool SubstitutionManager::is_compatibile_with_ui() { - const std::vector& substitutions = m_config->option("gcode_substitutions")->values; + const std::vector& substitutions = m_config->option("gcode_substitutions")->get_values(); if (int(substitutions.size() / 4) != m_grid_sizer->GetEffectiveRowsCount() - 1) { ErrorDialog(m_parent, "Invalid compatibility between UI and BE", false).ShowModal(); return false; @@ -4768,7 +4747,7 @@ bool SubstitutionManager::is_compatibile_with_ui() bool SubstitutionManager::is_valid_id(int substitution_id, const wxString& message) { - const std::vector& substitutions = m_config->option("gcode_substitutions")->values; + const std::vector& substitutions = m_config->option("gcode_substitutions")->get_values(); if (int(substitutions.size() / 4) < substitution_id) { ErrorDialog(m_parent, message, false).ShowModal(); return false; @@ -4800,8 +4779,9 @@ void SubstitutionManager::delete_substitution(int substitution_id) return; // delete substitution - std::vector& substitutions = m_config->option("gcode_substitutions")->values; + std::vector substitutions = m_config->option("gcode_substitutions")->get_values(); substitutions.erase(std::next(substitutions.begin(), substitution_id * 4), std::next(substitutions.begin(), substitution_id * 4 + 4)); + m_config->option("gcode_substitutions")->set(substitutions); call_ui_update(); // update grid_sizer @@ -4826,9 +4806,10 @@ void SubstitutionManager::add_substitution(int substitution_id, // create new substitution // it have to be added to config too - std::vector& substitutions = m_config->option("gcode_substitutions")->values; + std::vector substitutions = m_config->option("gcode_substitutions")->get_values(); for (size_t i = 0; i < 4; i++) substitutions.push_back(std::string()); + m_config->option("gcode_substitutions")->set(substitutions); call_after_layout = true; } @@ -4928,7 +4909,7 @@ void SubstitutionManager::update_from_config() if (!m_grid_sizer->IsEmpty()) m_grid_sizer->Clear(true); - std::vector& subst = m_config->option("gcode_substitutions")->values; + const std::vector& subst = m_config->option("gcode_substitutions")->get_values(); if (subst.empty()) hide_delete_all_btn(); else @@ -4945,7 +4926,7 @@ void SubstitutionManager::update_from_config() void SubstitutionManager::delete_all() { - m_config->option("gcode_substitutions")->values.clear(); + m_config->option("gcode_substitutions")->clear(); call_ui_update(); if (!m_grid_sizer->IsEmpty()) @@ -4956,20 +4937,21 @@ void SubstitutionManager::delete_all() void SubstitutionManager::edit_substitution(int substitution_id, int opt_pos, const std::string& value) { - std::vector& substitutions = m_config->option("gcode_substitutions")->values; + std::vector substitutions = m_config->option("gcode_substitutions")->get_values(); validate_lenth(); if (!is_compatibile_with_ui() || !is_valid_id(substitution_id, "Invalid substitution_id to edit")) return; substitutions[substitution_id * 4 + opt_pos] = value; + m_config->option("gcode_substitutions")->set(substitutions); call_ui_update(); } bool SubstitutionManager::is_empty_substitutions() { - return m_config->option("gcode_substitutions")->values.empty(); + return m_config->option("gcode_substitutions")->empty(); } // Return a callback to create a TabPrint widget to edit G-code substitutions @@ -5048,10 +5030,10 @@ wxSizer *VectorManager::init(DynamicPrintConfig *config, wxWindow *parent, PageS bool VectorManager::is_compatibile_with_ui() { - const std::vector &values = m_config->option(m_opt_key)->values; - if (int(values.size()) != m_grid_sizer->GetItemCount()) { + size_t values_size = m_config->option(m_opt_key)->size(); + if (int(values_size) != m_grid_sizer->GetItemCount()) { ErrorDialog(m_parent, - std::string("Invalid compatibility between UI and BE: ") + std::to_string(values.size()) + + std::string("Invalid compatibility between UI and BE: ") + std::to_string(values_size) + std::string("=!=") + std::to_string(m_grid_sizer->GetItemCount()), false) .ShowModal(); @@ -5068,8 +5050,9 @@ void VectorManager::pop_back() } // delete substitution - std::vector &values = m_config->option(m_opt_key)->values; - values.pop_back(); + ConfigOptionFloats* opt = m_config->option(m_opt_key); + // pop_back(); + opt->resize(opt->size() - 1); call_ui_update(); @@ -5084,12 +5067,11 @@ void VectorManager::push_back(const std::string &str_value) { bool call_after_layout = false; - std::vector &values = m_config->option(m_opt_key)->values; - if (str_value == "") { // create new substitution // it have to be added to config too - values.push_back(0); + ConfigOptionFloats* opt = m_config->option(m_opt_key); + opt->set_at(0, opt->size()); // push_back() call_after_layout = true; } @@ -5133,7 +5115,7 @@ void VectorManager::update_from_config() switch (m_opt_type) { case coBools: { - std::vector &values = m_config->option(m_opt_key)->values; + const std::vector &values = m_config->option(m_opt_key)->get_values(); if (values.empty()) clear(); else @@ -5141,7 +5123,7 @@ void VectorManager::update_from_config() break; } case coInts: { - std::vector &values = m_config->option(m_opt_key)->values; + const std::vector &values = m_config->option(m_opt_key)->get_values(); if (values.empty()) clear(); else @@ -5149,7 +5131,7 @@ void VectorManager::update_from_config() break; } case coFloats: { - std::vector &values = m_config->option(m_opt_key)->values; + const std::vector &values = m_config->option(m_opt_key)->get_values(); if (values.empty()) clear(); else @@ -5157,7 +5139,7 @@ void VectorManager::update_from_config() break; } case coPercents: { - std::vector &values = m_config->option(m_opt_key)->values; + const std::vector &values = m_config->option(m_opt_key)->get_values(); if (values.empty()) clear(); else @@ -5165,7 +5147,7 @@ void VectorManager::update_from_config() break; } case coFloatsOrPercents: { - std::vector &values = m_config->option(m_opt_key)->values; + const std::vector &values = m_config->option(m_opt_key)->get_values(); if (values.empty()) clear(); else @@ -5199,38 +5181,33 @@ void VectorManager::edit_value(int idx_value, const std::string &str_value) return; switch (m_opt_type) { case coBools: { - std::vector &values = m_config->option(m_opt_key)->values; std::string lower = str_value; boost::to_lower(lower); bool is_val = str_value == "1" || lower == "true"; - values[idx_value] = is_val; + m_config->option(m_opt_key)->set_at(is_val, idx_value); break; } case coInts: { - std::vector &values = m_config->option(m_opt_key)->values; int32_t int_val = (int32_t) string_to_double_decimal_point(str_value); - values[idx_value] = int_val; + m_config->option(m_opt_key)->set_at(int_val, idx_value); break; } case coFloats: { - std::vector &values = m_config->option(m_opt_key)->values; double float_val = string_to_double_decimal_point(str_value); - values[idx_value] = float_val; + m_config->option(m_opt_key)->set_at(float_val, idx_value); break; } case coPercents: { - std::vector &values = m_config->option(m_opt_key)->values; double float_val = string_to_double_decimal_point(str_value); - values[idx_value] = float_val; + m_config->option(m_opt_key)->set_at(float_val, idx_value); break; } case coFloatsOrPercents: { - std::vector &values = m_config->option(m_opt_key)->values; std::string trimed = str_value; boost::trim(trimed); bool has_percent = !trimed.empty() && trimed.back() == '%'; double float_val = string_to_double_decimal_point(has_percent ? trimed.substr(0, trimed.size() - 1) : trimed); - values[idx_value] = FloatOrPercent{float_val, has_percent}; + m_config->option(m_opt_key)->set_at(FloatOrPercent{float_val, has_percent}, idx_value); break; } case coPoints: { @@ -5431,7 +5408,7 @@ void Tab::compatible_widget_reload(PresetDependencies &deps) if (!field) return; - bool has_any = ! m_config_base->option(deps.key_list)->values.empty(); + bool has_any = ! m_config_base->option(deps.key_list)->empty(); has_any ? deps.btn->Enable() : deps.btn->Disable(); deps.checkbox->SetValue(! has_any); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index ee7ed417371..61949061ed3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1074,6 +1074,18 @@ static wxString get_full_label(std::string opt_key, const DynamicPrintConfig& co return opt->full_label.empty() ? opt->label : opt->full_label; } +wxString graph_to_string(const GraphData &graph) +{ + wxString str = ""; + switch (graph.type) { + case GraphData::GraphType::SQUARE: str = _L("Square") + ":"; break; + case GraphData::GraphType::LINEAR: str = _L("Linear") + ":"; break; + case GraphData::GraphType::SPLINE: str = _L("Spline") + ":"; break; + } + for (const Vec2d &pt : graph.data()) { str += format_wxstr(" %1%,%2%", pt.x(), pt.y()); } + return str; +} + static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) { size_t opt_idx = get_id_from_opt_key(opt_key); @@ -1193,7 +1205,7 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& return out; } if (!strings->empty()) - if (opt_idx < strings->values.size()) + if (opt_idx < strings->size()) return from_u8(strings->get_at(opt_idx)); else return from_u8(strings->serialize()); @@ -1209,7 +1221,7 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& case coFloatsOrPercents: { const ConfigOptionFloatsOrPercents* floats_percents = config.opt(opt_key); if (floats_percents) - if(opt_idx < floats_percents->values.size()) + if(opt_idx < floats_percents->size()) return double_to_string(floats_percents->get_at(opt_idx).value, opt->precision) + (floats_percents->get_at(opt_idx).percent ? "%" : ""); else return from_u8(floats_percents->serialize()); @@ -1229,12 +1241,23 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& } const ConfigOptionPoints* opt_pts = config.opt(opt_key); - if (!opt_pts->values.empty()) - if (opt_idx < opt_pts->values.size()) + if (!opt_pts->empty()) + if (opt_idx < opt_pts->size()) return from_u8((boost::format("[%1%]") % ConfigOptionPoint(opt_pts->get_at(opt_idx)).serialize()).str()); else return from_u8(opt_pts->serialize()); } + case coGraph: { + return graph_to_string(config.option(opt_key)->value); + } + case coGraphs: { + const ConfigOptionGraphs* opt_graphs = config.opt(opt_key); + if (!opt_graphs->empty()) + if (opt_idx < opt_graphs->size()) + return graph_to_string(opt_graphs->get_at(opt_idx)); + else + return from_u8(opt_graphs->serialize()); + } default: break; } @@ -1320,10 +1343,10 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres // process changes of extruders count if (type == Preset::TYPE_PRINTER && old_pt == ptFFF && - old_config.opt("extruder_colour")->values.size() != new_config.opt("extruder_colour")->values.size()) { + old_config.opt("extruder_colour")->size() != new_config.opt("extruder_colour")->size()) { wxString local_label = _L("Extruders count"); - wxString old_val = from_u8((boost::format("%1%") % old_config.opt("extruder_colour")->values.size()).str()); - wxString new_val = from_u8((boost::format("%1%") % new_config.opt("extruder_colour")->values.size()).str()); + wxString old_val = from_u8((boost::format("%1%") % old_config.opt("extruder_colour")->size()).str()); + wxString new_val = from_u8((boost::format("%1%") % new_config.opt("extruder_colour")->size()).str()); assert(category_icon_map.find(wxGetApp().get_tab(type)->get_page(0)->title()) != category_icon_map.end()); if(wxGetApp().get_tab(type)->get_page_count() > 0) @@ -1699,7 +1722,7 @@ void DiffPresetDialog::update_tree() // Collect dirty options. const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL); auto dirty_options = type == Preset::TYPE_PRINTER && left_pt == ptFFF && - left_config.opt("extruder_colour")->values.size() < right_congig.opt("extruder_colour")->values.size() ? + left_config.opt("extruder_colour")->size() < right_congig.opt("extruder_colour")->size() ? presets->dirty_options(right_preset, left_preset, deep_compare) : presets->dirty_options(left_preset, right_preset, deep_compare); @@ -1721,10 +1744,10 @@ void DiffPresetDialog::update_tree() // process changes of extruders count if (type == Preset::TYPE_PRINTER && left_pt == ptFFF && - left_config.opt("extruder_colour")->values.size() != right_congig.opt("extruder_colour")->values.size()) { + left_config.opt("extruder_colour")->size() != right_congig.opt("extruder_colour")->size()) { wxString local_label = _L("Extruders count"); - wxString left_val = from_u8((boost::format("%1%") % left_config.opt("extruder_colour")->values.size()).str()); - wxString right_val = from_u8((boost::format("%1%") % right_congig.opt("extruder_colour")->values.size()).str()); + wxString left_val = from_u8((boost::format("%1%") % left_config.opt("extruder_colour")->size()).str()); + wxString right_val = from_u8((boost::format("%1%") % right_congig.opt("extruder_colour")->size()).str()); m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, left_val, right_val, category_icon_map.at("General")); } diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 3353f4f3b1d..7d3c3366065 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -95,6 +95,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) buttons.push_back(std::make_pair(x, y)); m_chart = new Chart(this, wxRect(scale(1),scale(1),scale(48),scale(36)), buttons, scale(1)); + m_chart->set_type(Slic3r::GraphData::GraphType::SPLINE); m_chart->set_xy_range(0, 0, 0.25f * ramming_speed_size, 20.); m_chart->set_x_label(_L("Time") + " ("+_L("s")+")", 0.1f); m_chart->set_y_label(_L("Volumetric speed") + " (" + _L("mm³/s") + ")", 1); @@ -152,7 +153,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(0, 0, m_widget_time->GetValue(), -1);}); m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value - Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_max_x());} ); + Bind(EVT_SLIC3R_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_max_x());} ); Refresh(true); // erase background } @@ -163,7 +164,7 @@ void RammingPanel::line_parameters_changed() { std::string RammingPanel::get_parameters() { - std::vector speeds = m_chart->get_speed(0.25f); + std::vector speeds = m_chart->get_value_samples(0.25f); std::vector> buttons = m_chart->get_buttons(); std::stringstream stream; stream << m_ramming_line_width_multiplicator << " " << m_ramming_step_multiplicator;