diff --git a/resources/calibration/filament_pressure/0.3mf b/resources/calibration/filament_pressure/0.3mf new file mode 100644 index 00000000000..f18107e0e82 Binary files /dev/null and b/resources/calibration/filament_pressure/0.3mf differ diff --git a/resources/calibration/filament_pressure/1.3mf b/resources/calibration/filament_pressure/1.3mf new file mode 100644 index 00000000000..dd8f331777c Binary files /dev/null and b/resources/calibration/filament_pressure/1.3mf differ diff --git a/resources/calibration/filament_pressure/2.3mf b/resources/calibration/filament_pressure/2.3mf new file mode 100644 index 00000000000..fdeb8bbdd07 Binary files /dev/null and b/resources/calibration/filament_pressure/2.3mf differ diff --git a/resources/calibration/filament_pressure/3.3mf b/resources/calibration/filament_pressure/3.3mf new file mode 100644 index 00000000000..04c28fa5af9 Binary files /dev/null and b/resources/calibration/filament_pressure/3.3mf differ diff --git a/resources/calibration/filament_pressure/4.3mf b/resources/calibration/filament_pressure/4.3mf new file mode 100644 index 00000000000..e124482d021 Binary files /dev/null and b/resources/calibration/filament_pressure/4.3mf differ diff --git a/resources/calibration/filament_pressure/5.3mf b/resources/calibration/filament_pressure/5.3mf new file mode 100644 index 00000000000..37135fa6860 Binary files /dev/null and b/resources/calibration/filament_pressure/5.3mf differ diff --git a/resources/calibration/filament_pressure/6.3mf b/resources/calibration/filament_pressure/6.3mf new file mode 100644 index 00000000000..aae82ae2b94 Binary files /dev/null and b/resources/calibration/filament_pressure/6.3mf differ diff --git a/resources/calibration/filament_pressure/7.3mf b/resources/calibration/filament_pressure/7.3mf new file mode 100644 index 00000000000..f21d6fb2593 Binary files /dev/null and b/resources/calibration/filament_pressure/7.3mf differ diff --git a/resources/calibration/filament_pressure/8.3mf b/resources/calibration/filament_pressure/8.3mf new file mode 100644 index 00000000000..8804502d5dc Binary files /dev/null and b/resources/calibration/filament_pressure/8.3mf differ diff --git a/resources/calibration/filament_pressure/9.3mf b/resources/calibration/filament_pressure/9.3mf new file mode 100644 index 00000000000..58fe1bbf199 Binary files /dev/null and b/resources/calibration/filament_pressure/9.3mf differ diff --git a/resources/calibration/filament_pressure/base_plate.3mf b/resources/calibration/filament_pressure/base_plate.3mf new file mode 100644 index 00000000000..4815007b066 Binary files /dev/null and b/resources/calibration/filament_pressure/base_plate.3mf differ diff --git a/resources/calibration/filament_pressure/filament_pressure.html b/resources/calibration/filament_pressure/filament_pressure.html new file mode 100644 index 00000000000..019e9cee723 --- /dev/null +++ b/resources/calibration/filament_pressure/filament_pressure.html @@ -0,0 +1,41 @@ + + + + + Pressure Advance Calibration + + + + + + + + + + + +
+

Pressure/Linear Advance

+
+ + +
Needs:Bed Leveling and Layer Squish to be perfect
+
+
+ +

this test is still in development, beta should work "ok" for now though.
your current print settings will need to be saved before clicking "generate" since it uses the saved values to create the model

+

note: this test will auto pull all your currently loaded config parameters and generate a model for you to print!
for to help with firmware speed/processing limitations it's reccomended to have most extrusion roles similar set to speeds.

+ +

How to tune your printer for Pressure/Linear Advance

+

todo add detaisl here

+

Advice

+

Before doing this test, it's preferable to tune everything else first!
i would reccomended setting XXXX to the same speeds, XXX to a slow speed, and everything else you can send it with.

+

note: having large variance with ER speeds can reduce print quality/dimensional accuracy this is effect is mainly caused by the inner perimeter getting pulled closer to the external perimeter as it cools down, since each perimeter would be at different temperatues.

+

TODO add things about PA and setting first layer correctly
add notes about fan speed and disabling fan speed for this test, or do i have check box in GUI that would auto change relavent UI tab?

+ +

Notes

+

TODO: add cred for andrew ellis testing method

+ + diff --git a/resources/calibration/filament_pressure/pa_border.3mf b/resources/calibration/filament_pressure/pa_border.3mf new file mode 100644 index 00000000000..baf63447955 Binary files /dev/null and b/resources/calibration/filament_pressure/pa_border.3mf differ diff --git a/resources/calibration/filament_pressure/point.3mf b/resources/calibration/filament_pressure/point.3mf new file mode 100644 index 00000000000..5a5e03157c1 Binary files /dev/null and b/resources/calibration/filament_pressure/point.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.10.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.10.3mf new file mode 100644 index 00000000000..a43b1d3d847 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.10.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.20.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.20.3mf new file mode 100644 index 00000000000..de11b883ae1 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.20.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.30.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.30.3mf new file mode 100644 index 00000000000..672eb5b527b Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.30.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.40.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.40.3mf new file mode 100644 index 00000000000..70dd9d89365 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.40.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.50.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.50.3mf new file mode 100644 index 00000000000..7435b5bf9c9 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.50.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.60.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.60.3mf new file mode 100644 index 00000000000..d4414a24389 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.60.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.70.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.70.3mf new file mode 100644 index 00000000000..e3f3e98bab2 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.70.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.80.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.80.3mf new file mode 100644 index 00000000000..802393609dd Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.80.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.90.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.90.3mf new file mode 100644 index 00000000000..c8915c80977 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_0.90.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.00.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.00.3mf new file mode 100644 index 00000000000..5832fc30c0d Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.00.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.10.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.10.3mf new file mode 100644 index 00000000000..06f9975daba Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.10.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.20.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.20.3mf new file mode 100644 index 00000000000..2ba7117cff7 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.20.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.30.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.30.3mf new file mode 100644 index 00000000000..31490cbc46d Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.30.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.40.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.40.3mf new file mode 100644 index 00000000000..317d409fc19 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.40.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.50.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.50.3mf new file mode 100644 index 00000000000..6992e6a0a3a Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.50.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.60.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.60.3mf new file mode 100644 index 00000000000..71c89d2145c Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.60.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.70.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.70.3mf new file mode 100644 index 00000000000..1059221a40c Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.70.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.80.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.80.3mf new file mode 100644 index 00000000000..6ce9f6fc688 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.80.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.90.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.90.3mf new file mode 100644 index 00000000000..cb2cef4769c Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_1.90.3mf differ diff --git a/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_2.00.3mf b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_2.00.3mf new file mode 100644 index 00000000000..31df1df8cc0 Binary files /dev/null and b/resources/calibration/filament_pressure/scaled_with_nozzle_size/90_bend_2.00.3mf differ diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 8eb97365b74..39cb74d0df2 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -12,6 +12,7 @@ src/slic3r/GUI/CalibrationCubeDialog.cpp src/slic3r/GUI/CalibrationFlowDialog.cpp src/slic3r/GUI/CalibrationOverBridgeDialog.cpp src/slic3r/GUI/CalibrationRetractionDialog.cpp +src/slic3r/GUI/CalibrationPressureAdvDialog.cpp src/slic3r/GUI/CalibrationTempDialog.cpp src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp 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 039c1ddf88b..96f4fa6d95c 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -214,11 +214,12 @@ group:sidetext_width$5:Infill angle line:Modifiers setting:label_width$6:width$5:label$increment:fill_angle_increment setting:width$5:fill_angle_cross + setting:width$5:fill_angle_follow_model vector_line:fill_angle_template # setting:fill_angle_template group:sidetext_width$5:Advanced setting:solid_infill_every_layers - line:Solid infill is area below + line:Solid infill if area below setting:label$From region:solid_infill_below_area setting:label$From the whole layer:solid_infill_below_layer_area end_line @@ -237,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 @@ -498,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..7698894054a 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,288 @@ std::string escape_ampersand(const std::string& str) return std::string(out.data(), outptr - out.data()); } +bool GraphData::operator<(const GraphData &rhs) const +{ + 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 GraphData::operator>(const GraphData &rhs) const +{ + 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(); +} + +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 +609,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 +970,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 +1104,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 +1743,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 +1904,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 +1918,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 +1944,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..f8a2105425d 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "libslic3r.h" #include "clonable_ptr.hpp" @@ -18,6 +19,7 @@ #include #include +#include #include #include #include @@ -40,6 +42,79 @@ 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)); } + 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 { return this->data_size() == rhs.data_size() && this->data() == rhs.data() && this->type == rhs.type; } + bool operator!=(const GraphData &rhs) const { return this->data_size() != rhs.data_size() || this->data() != rhs.data() || this->type != rhs.type; } + bool operator<(const GraphData &rhs) const; + bool operator>(const GraphData &rhs) const; + + // 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 +124,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 +165,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 +303,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 +337,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 +488,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 +512,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 +533,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 +561,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 +579,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 +605,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 +634,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 +672,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 +697,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 +735,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 +780,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 +841,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 +902,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 +930,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 +951,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 +962,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 +979,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 +987,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->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 +1007,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 +1041,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 || (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 +1083,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 +1108,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 +1151,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 +1210,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->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 +1225,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 +1259,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_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 +1271,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 +1287,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 +1298,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 +1309,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 +1371,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->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 +1405,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_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 +1457,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->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 +1471,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 +1482,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 +1511,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 || (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 +1541,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 +1569,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 +1582,14 @@ 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)); } + bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw() { + bool this_enabled = this->is_enabled(); + bool rhs_enabled = this->is_enabled(); + return std::tie(this_enabled, this->value, this->percent) < std::tie(rhs_enabled, rhs.value, rhs.percent); + } double get_abs_value(double ratio_over) const { return this->percent ? (ratio_over * this->value / 100) : this->value; } @@ -1295,6 +1614,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 +1625,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 +1654,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 || (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 +1713,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 +1724,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 +1735,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 +1759,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 +1778,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 +1838,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->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 +1855,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 +1896,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 || (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 +1926,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 +1936,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 +1958,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 +1970,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 +1993,19 @@ 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()))); } + { + bool this_enabled = this->is_enabled(); + bool rhs_enabled = this->is_enabled(); + return std::tie(this_enabled, this->value.x(), this->value.y()) < std::tie(rhs_enabled, rhs.value.x(), rhs.value.y()); + } std::string serialize() const override { std::ostringstream ss; + if (!this->is_enabled()) + ss << "!"; ss << this->value(0); ss << ","; ss << this->value(1); @@ -1634,9 +2014,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 +2035,150 @@ 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->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 || (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 +2192,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() || (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 +2233,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_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 +2264,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 +2281,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 +2302,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 +2332,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 +2346,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 +2383,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() || (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 +2396,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 +2411,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 +2464,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->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 +2486,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 +2579,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 +2614,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 +2669,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 +2707,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 +2718,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/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 1beae06780c..2a0ae01b766 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -159,32 +159,54 @@ bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsi p2.append(std::move(p1)); path->polyline.swap(p2); // swap points & fitting result } - } else { - // new paths list starts with the second half of current path - ExtrusionPaths new_paths; - PolylineOrArc p1, p2; - path->polyline.split_at_index(idx, &p1, &p2); - new_paths.reserve(this->paths.size() + 1); - { - ExtrusionPath p = *path; - p.polyline.swap(p2); - if (p.polyline.is_valid()) new_paths.push_back(p); - } - - // then we add all paths until the end of current path list - new_paths.insert(new_paths.end(), path + 1, this->paths.end()); // not including this path - - // then we add all paths since the beginning of current list up to the previous one - new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path - - // finally we add the first half of current path - { - ExtrusionPath p = *path; - p.polyline.swap(p1); - if (p.polyline.is_valid()) new_paths.push_back(p); + } else if (idx > 0) { + if (idx < path->size() - 1) { + // new paths list starts with the second half of current path + ExtrusionPaths new_paths; + PolylineOrArc p1, p2; + path->polyline.split_at_index(idx, &p1, &p2); + new_paths.reserve(this->paths.size() + 1); + { + ExtrusionPath p = *path; + p.polyline.swap(p2); + if (p.polyline.is_valid()) + new_paths.push_back(p); + } + + // then we add all paths until the end of current path list + new_paths.insert(new_paths.end(), path + 1, this->paths.end()); // not including this path + + // then we add all paths since the beginning of current list up to the previous one + new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path + + // finally we add the first half of current path + { + ExtrusionPath p = *path; + p.polyline.swap(p1); + if (p.polyline.is_valid()) + new_paths.push_back(p); + } + // we can now override the old path list with the new one and stop looping + this->paths = std::move(new_paths); + } else { + // last point + assert( (path)->last_point().coincides_with_epsilon(point)); + assert( (path + 1)->first_point().coincides_with_epsilon(point)); + ExtrusionPaths new_paths; + new_paths.reserve(this->paths.size()); + // then we add all paths until the end of current path list + new_paths.insert(new_paths.end(), path + 1, this->paths.end()); // not including this path + // then we add all paths since the beginning of current list up to the previous one + new_paths.insert(new_paths.end(), this->paths.begin(), path + 1); // including this path + // we can now override the old path list with the new one and stop looping + this->paths = std::move(new_paths); } - // we can now override the old path list with the new one and stop looping - this->paths = std::move(new_paths); + } else { + // else first point -> + // if first path - nothign to change. + // else, then impossible as it's also the last point of the previous path. + assert(path == this->paths.begin()); + assert(path->first_point().coincides_with_epsilon(point)); } return true; } @@ -236,8 +258,8 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const const Point *p1 = this->paths[path_idx].polyline.get_points().data() + segment_idx; const Point *p2 = p1; ++ p2; - double d2_1 = (point - *p1).cast().squaredNorm(); - double d2_2 = (point - *p2).cast().squaredNorm(); + double d2_1 = (p - *p1).cast().squaredNorm(); + double d2_2 = (p - *p2).cast().squaredNorm(); const double thr2 = scaled_epsilon * scaled_epsilon; if (d2_1 < d2_2) { if (d2_1 < thr2) @@ -250,8 +272,8 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const // now split path_idx in two parts const ExtrusionPath &path = this->paths[path_idx]; - ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height, can_reverse()); - ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height, can_reverse()); + ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height, path.can_reverse()); + ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height, path.can_reverse()); path.polyline.split_at(p, &p1.polyline, &p2.polyline); if (this->paths.size() == 1) { diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 6577395f17e..45c972bf414 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -57,6 +57,7 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(can_angle_cross); RETURN_COMPARE_NON_EQUAL(density); RETURN_COMPARE_NON_EQUAL(monotonic); + RETURN_COMPARE_NON_EQUAL(max_sparse_infill_spacing); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, connection); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); @@ -70,16 +71,24 @@ struct SurfaceFillParams : FillParams assert(this->config != nullptr); assert(rhs.config != nullptr); if (config != nullptr && rhs.config != nullptr) { + RETURN_COMPARE_NON_EQUAL(config->infill_acceleration); RETURN_COMPARE_NON_EQUAL(config->infill_speed); + RETURN_COMPARE_NON_EQUAL(config->solid_infill_acceleration); RETURN_COMPARE_NON_EQUAL(config->solid_infill_speed); + RETURN_COMPARE_NON_EQUAL(config->top_solid_infill_acceleration); RETURN_COMPARE_NON_EQUAL(config->top_solid_infill_speed); - RETURN_COMPARE_NON_EQUAL(config->ironing_speed); + RETURN_COMPARE_NON_EQUAL(config->default_acceleration); RETURN_COMPARE_NON_EQUAL(config->default_speed); + RETURN_COMPARE_NON_EQUAL(config->bridge_acceleration); RETURN_COMPARE_NON_EQUAL(config->bridge_speed); - RETURN_COMPARE_NON_EQUAL(config->bridge_speed_internal); + RETURN_COMPARE_NON_EQUAL(config->internal_bridge_acceleration); + RETURN_COMPARE_NON_EQUAL(config->internal_bridge_speed); + 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(max_sparse_infill_spacing); + 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()); @@ -92,14 +101,25 @@ struct SurfaceFillParams : FillParams if ((config != nullptr) != (rhs.config != nullptr)) return false; if(config != nullptr && ( - config->infill_speed != rhs.config->infill_speed + config->infill_acceleration != rhs.config->infill_acceleration + || config->infill_speed != rhs.config->infill_speed + || config->solid_infill_acceleration != rhs.config->solid_infill_acceleration || config->solid_infill_speed != rhs.config->solid_infill_speed + || config->top_solid_infill_acceleration != rhs.config->top_solid_infill_acceleration || config->top_solid_infill_speed != rhs.config->top_solid_infill_speed - || config->ironing_speed != rhs.config->ironing_speed + || config->default_acceleration != rhs.config->default_acceleration || config->default_speed != rhs.config->default_speed + || config->bridge_acceleration != rhs.config->bridge_acceleration || config->bridge_speed != rhs.config->bridge_speed - || config->bridge_speed_internal != rhs.config->bridge_speed_internal - || config->gap_fill_speed != rhs.config->gap_fill_speed)) + || config->internal_bridge_acceleration != rhs.config->internal_bridge_acceleration + || config->internal_bridge_speed != rhs.config->internal_bridge_speed + || config->gap_fill_acceleration != rhs.config->gap_fill_acceleration + || 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 return this->extruder == rhs.extruder && @@ -137,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; } @@ -542,6 +562,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->can_angle_cross = surface_fill.params.can_angle_cross; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; + // add object angle if needed + double z_object_angle = this->m_object->trafo().rotation().eulerAngles(0,1,2).z(); + if (this->object()->config().fill_angle_follow_model.value) { + f->angle += z_object_angle; + } + if (surface_fill.params.pattern == ipLightning) dynamic_cast(f.get())->generator = lightning_generator; @@ -700,7 +726,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->fill_surface_extrusion(&surface_fill.surface, surface_fill.params, fills_by_priority[(size_t)surface_fill.params.priority]->set_entities()); #if _DEBUG //check no over or underextrusion if fill_exactly - if(surface_fill.params.fill_exactly && surface_fill.params.density == 1) { + if(surface_fill.params.fill_exactly && surface_fill.params.density == 1 && !surface_fill.params.flow.bridge()) { ExtrusionVolume compute_volume; ExtrusionVolume compute_volume_no_gap_fill(false); const size_t idx_end = fills_by_priority[(size_t)surface_fill.params.priority]->entities().size(); @@ -716,11 +742,25 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: for(auto &t : temp) real_surface += t.area(); assert(compute_volume.volume < unscaled(unscaled(surface_fill.surface.area())) * surface_fill.params.layer_height + EPSILON); double area = unscaled(unscaled(real_surface)); - assert(compute_volume.volume <= area * surface_fill.params.layer_height * 1.001 || f->debug_verify_flow_mult <= 0.8); - if(compute_volume.volume > 0) //can fail for thin regions - assert(compute_volume.volume >= area * surface_fill.params.layer_height * 0.999 || f->debug_verify_flow_mult >= 1.3 || f->debug_verify_flow_mult == 0 // sawtooth output more filament,as it's 3D (debug_verify_flow_mult==0) - || area < std::max(1.,surface_fill.params.config->solid_infill_below_area.value) || area < std::max(1.,surface_fill.params.config->solid_infill_below_layer_area.value)); - } + if(surface_fill.surface.has_pos_top()) + area *= surface_fill.params.config->fill_top_flow_ratio.value; + if(surface_fill.surface.has_pos_bottom() && f->layer_id == 0) + area *= surface_fill.params.config->first_layer_flow_ratio.value; + if(surface_fill.surface.has_mod_bridge() && f->layer_id == 0) + area *= surface_fill.params.config->first_layer_flow_ratio.value; + //TODO: over-bridge mod + if(surface_fill.params.config->over_bridge_flow_ratio.value == 1){ + assert(compute_volume.volume <= area * surface_fill.params.layer_height * 1.001 || f->debug_verify_flow_mult <= 0.8); + if(compute_volume.volume > 0) //can fail for thin regions + assert( + compute_volume.volume >= area * surface_fill.params.layer_height * 0.999 || + f->debug_verify_flow_mult >= 1.3 || + f->debug_verify_flow_mult == + 0 // sawtooth output more filament,as it's 3D (debug_verify_flow_mult==0) + || area < std::max(1., surface_fill.params.config->solid_infill_below_area.value) || + area < std::max(1., surface_fill.params.config->solid_infill_below_layer_area.value)); + } + } #endif } } @@ -765,6 +805,7 @@ void Layer::make_ironing() double line_spacing; // Height of the extrusion, to calculate the extrusion flow from. double height; + double acceleration; double speed; double angle; IroningType type; @@ -786,6 +827,10 @@ void Layer::make_ironing() return true; if (this->height > rhs.height) return false; + if (this->acceleration < rhs.acceleration) + return true; + if (this->acceleration > rhs.acceleration) + return false; if (this->speed < rhs.speed) return true; if (this->speed > rhs.speed) @@ -797,11 +842,12 @@ void Layer::make_ironing() return false; } - bool operator==(const IroningParams &rhs) const { + bool operator==(const IroningParams &rhs) const + { return this->extruder == rhs.extruder && this->just_infill == rhs.just_infill && - this->line_spacing == rhs.line_spacing && this->height == rhs.height && this->speed == rhs.speed && - this->angle == rhs.angle && - this->type == rhs.type; + this->line_spacing == rhs.line_spacing && this->height == rhs.height && + this->acceleration == rhs.acceleration && this->speed == rhs.speed && + this->angle == rhs.angle && this->type == rhs.type; } LayerRegion *layerm = nullptr; @@ -848,6 +894,7 @@ void Layer::make_ironing() ironing_params.just_infill = false; ironing_params.line_spacing = config.ironing_spacing; ironing_params.height = default_layer_height * 0.01 * config.ironing_flowrate; + ironing_params.acceleration = config.ironing_acceleration; ironing_params.speed = config.ironing_speed; if (config.ironing_angle.value >= 0) { ironing_params.angle = float(Geometry::deg2rad(config.ironing_angle.value)); @@ -880,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 ffb22ab40e2..ce47ca5f956 100644 --- a/src/libslic3r/Format/BBConfig.cpp +++ b/src/libslic3r/Format/BBConfig.cpp @@ -96,7 +96,7 @@ void init() //key_translation_map["bridge_angle"] = "bridge_angle"; key_translation_map["bridge_density"] = "bridge_overlap_min"; key_translation_map["bridge_no_support"] = "dont_support_bridges"; - key_translation_map["internal_bridge_speed"] = "bridge_speed_internal"; + //key_translation_map["internal_bridge_speed"] = "internal_bridge_speed"; //key_translation_map["brim_ears"] = "brim_ears"; //key_translation_map["brim_ears_detection_length"] = "brim_ears_detection_length"; //key_translation_map["brim_ears_max_angle"] = "brim_ears_max_angle"; @@ -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 213ccee2774..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; @@ -919,6 +919,16 @@ namespace DoExport { break; } } + if (ret.size() < MAX_TAGS_COUNT) { + std::set per_object_gcodes; + for (const PrintObject *obj : print.objects()) + per_object_gcodes.insert(obj->config().object_gcode.value); + for (const std::string &gcode : per_object_gcodes) { + check(_(L("Per object G-code")), gcode); + if (ret.size() == MAX_TAGS_COUNT) + break; + } + } return ret; } @@ -1096,7 +1106,7 @@ namespace DoExport { excluded.insert(erTopSolidInfill); if (config->option("bridge_speed") != nullptr && config->get_computed_value("bridge_speed") != 0) excluded.insert(erBridgeInfill); - if (config->option("bridge_speed_internal") != nullptr && config->get_computed_value("bridge_speed_internal") != 0) + if (config->option("internal_bridge_speed") != nullptr && config->get_computed_value("internal_bridge_speed") != 0) excluded.insert(erInternalBridgeInfill); if (config->option("support_material_speed") != nullptr && config->get_computed_value("support_material_speed") != 0) excluded.insert(erSupportMaterial); @@ -1421,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); } @@ -1496,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, @@ -1514,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, @@ -1628,6 +1638,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene file.write("M486 T" + std::to_string(nb_items) + "\n"); } if (this->config().gcode_label_objects) { + file.write("; Total objects to print: " + std::to_string(nb_items) + "\n"); file.write_format( "; plater:{\"center\":[%f,%f,%f],\"boundingbox_center\":[%f,%f,%f],\"boundingbox_size\":[%f,%f,%f]}\n", global_bounding_box.center().x(), global_bounding_box.center().y(), 0., global_bounding_box.center().x(), global_bounding_box.center().y(), global_bounding_box.center().z(), @@ -1660,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()) { @@ -1756,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() })); @@ -1768,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() })); @@ -1829,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() })); @@ -1885,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"); } */ @@ -1905,6 +1916,9 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene this->m_throw_if_canceled(); // Collect custom seam data from all objects. + print.set_status(0, L("Computing seam visibility areas: object %s / %s"), + {"1", std::to_string(print.objects().size())}, + PrintBase::SlicingStatus::FORCE_SHOW | PrintBase::SlicingStatus::SECONDARY_STATE); m_seam_placer.init(print, this->m_throw_if_canceled); //activate first extruder is multi-extruder and not in start-gcode @@ -2150,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)); @@ -2214,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, @@ -2509,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); @@ -3055,8 +3069,10 @@ LayerResult GCode::process_layer( const Layer *object_layer = nullptr; const SupportLayer *support_layer = nullptr; const SupportLayer *raft_layer = nullptr; - const size_t layer_id = layers.front().layer()->id(); + /*const*/ size_t layer_id = size_t(-1); for (const LayerToPrint &l : layers) { + if(l.layer()) + layer_id = l.layer()->id(); if (l.object_layer && ! object_layer) object_layer = l.object_layer; if (l.support_layer) { @@ -3067,12 +3083,31 @@ LayerResult GCode::process_layer( } assert(l.layer() == nullptr || layer_id == l.layer()->id()); } + assert(layer_id < layer_count()); + assert(object_layer != nullptr || support_layer != nullptr); const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; + assert(layer_id == layer.id()); LayerResult result { {}, layer.id(), false, last_layer, false}; if (layer_tools.extruders.empty()) // Nothing to extrude. return result; + if (object_layer) { + if (single_object_instance_idx != size_t(-1)) { + size_t nb_layers = object_layer->object()->layer_count(); + m_object_sequentially_printed.insert(object_layer->object()); + print.set_status(int((layer.id() * 100) / nb_layers), + std::string(L("Generating G-code layer %s / %s for object %s / %s")), + std::vector{std::to_string(layer.id()), std::to_string(nb_layers), std::to_string(m_object_sequentially_printed.size()), std::to_string(print.num_object_instances())}, + PrintBase::SlicingStatus::DEFAULT | PrintBase::SlicingStatus::SECONDARY_STATE); + } else { + print.set_status(int((layer.id() * 100) / layer_count()), + std::string(L("Generating G-code layer %s / %s")), + std::vector{std::to_string(layer.id()), std::to_string(layer_count())}, + PrintBase::SlicingStatus::DEFAULT | PrintBase::SlicingStatus::SECONDARY_STATE); + } + } + // Extract 1st object_layer and support_layer of this set of layers with an equal print_z. coordf_t print_z = layer.print_z; bool first_layer = layer_id == 0; @@ -3510,8 +3545,9 @@ LayerResult GCode::process_layer( if (m_config.avoid_crossing_perimeters) m_avoid_crossing_perimeters.init_layer(*m_layer); //print object label to help the printer firmware know where it is (for removing the objects) + m_gcode_label_objects_start = ""; if (this->config().gcode_label_objects) { - m_gcode_label_objects_start = + m_gcode_label_objects_start += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + instance_id + " copy " + instance_copy + @@ -3544,6 +3580,14 @@ LayerResult GCode::process_layer( "\n"; } } + if (!instance_to_print.print_object.config().object_gcode.value.empty()) { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + m_gcode_label_objects_start += this->placeholder_parser_process("object_gcode", + instance_to_print.print_object.config().object_gcode.value, m_writer.tool()->id(), &config) + + "\n"; + } // ask for a bigger lift for travel to object when moving to another object if (single_object_instance_idx == size_t(-1) && !first_object) set_extra_lift(m_last_layer_z, layer.id(), print.config(), m_writer, extruder_id); @@ -3624,7 +3668,7 @@ LayerResult GCode::process_layer( } } // Don't set m_gcode_label_objects_end if you don't had to write the m_gcode_label_objects_start. - if (m_gcode_label_objects_start != "") { + if (!m_gcode_label_objects_start.empty()) { m_gcode_label_objects_start = ""; } else if (this->config().gcode_label_objects) { m_gcode_label_objects_end = std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name @@ -3650,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) { @@ -3670,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. @@ -3764,9 +3808,6 @@ LayerResult GCode::process_layer( BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); - if(object_layer) - print.set_status(int((layer.id() * 100) / layer_count()), std::string(L("Generating G-code layer %s / %s")), std::vector{ std::to_string(layer.id()), std::to_string(layer_count()) }, PrintBase::SlicingStatus::DEFAULT | PrintBase::SlicingStatus::SECONDARY_STATE); - result.gcode = std::move(gcode); result.cooling_buffer_flush = object_layer || raft_layer || last_layer; return result; @@ -4142,6 +4183,18 @@ void GCode::split_at_seam_pos(ExtrusionLoop& loop, bool was_clockwise) if (loop.paths.empty()) return; +#if _DEBUG + ExtrusionLoop old_loop = loop; + for (const ExtrusionPath &path : loop.paths) + for (int i = 1; i < path.polyline.get_points().size(); ++i) + assert(!path.polyline.get_points()[i - 1].coincides_with_epsilon(path.polyline.get_points()[i])); + for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { + assert(it->polyline.size() >= 2); + assert(std::prev(it)->polyline.back() == it->polyline.front()); + } + assert(loop.first_point() == loop.last_point()); +#endif + // SeamPosition seam_position = m_config.seam_position; // if (loop.loop_role() == elrSkirt) // seam_position = spNearest; @@ -5322,6 +5375,41 @@ std::string GCode::extrude_path_3D(const ExtrusionPath3D &path, const std::strin return gcode; } +// Apply region-specific settings +void GCode::apply_region_config(std::string &gcode) { + // modify our fullprintconfig with it. (works as all items avaialable in the regionconfig are present in this config, ie: it write everything region-defined) + m_config.apply(m_region->config()); + // pass our region config to the gcode writer + m_writer.apply_print_region_config(m_region->config()); + // perimeter-only (but won't break anything if done also in infill & ironing): pass needed settings to seam placer. + m_seam_placer.external_perimeters_first = m_region->config().external_perimeters_first.value; + // temperature override from region + if (m_config.print_temperature > 0) { + if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.print_first_layer_temperature.value > 0) { + gcode += m_writer.set_temperature(m_config.print_first_layer_temperature.value, false, m_writer.tool()->id()); + } else { + gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id()); + } + } else if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.first_layer_temperature.get_at(m_writer.tool()->id()) > 0) { + gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, + m_writer.tool()->id()); + } else if (m_config.temperature.get_at(m_writer.tool()->id()) > 0) { // don't set it if disabled + gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, + m_writer.tool()->id()); + } + // apply region_gcode + if (!m_region->config().region_gcode.value.empty()) { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z)); + m_gcode_label_objects_start += this->placeholder_parser_process("region_gcode", + m_region->config().region_gcode.value, + m_writer.tool()->id(), &config) + + "\n"; + } + +} + // Extrude perimeters: Decide where to put seams (hide or align seams). std::string GCode::extrude_perimeters(const Print &print, const std::vector &by_region) { @@ -5331,20 +5419,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig()); - m_writer.apply_print_region_config(m_region->config()); - m_seam_placer.external_perimeters_first = m_region->config().external_perimeters_first.value; - if (m_config.print_temperature > 0) - if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.print_first_layer_temperature.value > 0) - gcode += m_writer.set_temperature(m_config.print_first_layer_temperature.value, false, m_writer.tool()->id()); - else - gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id()); - else if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.first_layer_temperature.get_at(m_writer.tool()->id()) > 0) - gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); - else if (m_config.temperature.get_at(m_writer.tool()->id()) > 0) { // don't set it if disabled - gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, - m_writer.tool()->id()); - } + apply_region_config(gcode); ExtrusionEntitiesPtr extrusions{region.perimeters}; chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); for (const ExtrusionEntity *ee : extrusions) { @@ -5363,17 +5438,7 @@ std::string GCode::extrude_infill(const Print& print, const std::vectorconfig().infill_first == is_infill_first)) { - m_config.apply(m_region->config()); - m_writer.apply_print_region_config(m_region->config()); - if (m_config.print_temperature > 0) - if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.print_first_layer_temperature.value > 0) - gcode += m_writer.set_temperature(m_config.print_first_layer_temperature.value, false, m_writer.tool()->id()); - else - gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id()); - else if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.first_layer_temperature.get_at(m_writer.tool()->id()) > 0) - gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); - else if (m_config.temperature.get_at(m_writer.tool()->id()) > 0) // don't set it if disabled - gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); + apply_region_config(gcode); ExtrusionEntitiesPtr extrusions{ region.infills }; chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); for (const ExtrusionEntity* fill : extrusions) { @@ -5392,17 +5457,7 @@ std::string GCode::extrude_ironing(const Print& print, const std::vectorconfig()); - m_writer.apply_print_region_config(m_region->config()); - if (m_config.print_temperature > 0) - if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.print_first_layer_temperature.value > 0) - gcode += m_writer.set_temperature(m_config.print_first_layer_temperature.value, false, m_writer.tool()->id()); - else - gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id()); - else if (m_layer != nullptr && m_layer->bottom_z() < EPSILON && m_config.first_layer_temperature.get_at(m_writer.tool()->id()) > 0) - gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); - else if (m_config.temperature.get_at(m_writer.tool()->id()) > 0) - gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); + apply_region_config(gcode); ExtrusionEntitiesPtr extrusions{ region.ironings }; chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); for (const ExtrusionEntity* fill : extrusions) { @@ -5540,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) { @@ -5645,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) @@ -5701,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); } @@ -5719,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); } @@ -5772,8 +5841,8 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath &path, double spee speed = m_config.get_computed_value("bridge_speed"); if(comment) *comment = "bridge_speed"; } else if (path.role() == erInternalBridgeInfill) { - speed = m_config.get_computed_value("bridge_speed_internal"); - if(comment) *comment = "bridge_speed_internal"; + speed = m_config.get_computed_value("internal_bridge_speed"); + if(comment) *comment = "internal_bridge_speed"; } else if (path.role() == erOverhangPerimeter) { speed = m_config.get_computed_value("overhangs_speed"); if(comment) *comment = "overhangs_speed"; @@ -5851,8 +5920,8 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath &path, double spee speed = m_config.bridge_speed.get_abs_value(vol_speed); if(comment) *comment = std::string("bridge_speed ") + *comment; } else if (path.role() == erInternalBridgeInfill) { - speed = m_config.bridge_speed_internal.get_abs_value(vol_speed); - if(comment) *comment = std::string("bridge_speed_internal ") + *comment; + speed = m_config.internal_bridge_speed.get_abs_value(vol_speed); + if(comment) *comment = std::string("internal_bridge_speed ") + *comment; } else if (path.role() == erOverhangPerimeter) { speed = m_config.overhangs_speed.get_abs_value(vol_speed); if(comment) *comment = std::string("overhangs_speed ") + *comment; @@ -6095,10 +6164,10 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string } break; case erInternalBridgeInfill: - if (m_config.bridge_internal_acceleration.value > 0) { - double bridge_internal_acceleration = m_config.get_computed_value("bridge_internal_acceleration"); - if (bridge_internal_acceleration > 0) { - acceleration = bridge_internal_acceleration; + if (m_config.internal_bridge_acceleration.value > 0) { + double internal_bridge_acceleration = m_config.get_computed_value("internal_bridge_acceleration"); + if (internal_bridge_acceleration > 0) { + acceleration = internal_bridge_acceleration; break; } } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 89f70520151..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), @@ -390,6 +390,7 @@ class GCode : ExtrusionVisitorConst { // For sequential print, the instance of the object to be printing has to be defined. const size_t single_object_instance_idx); + void apply_region_config(std::string &gcode); std::string extrude_perimeters(const Print &print, const std::vector &by_region); std::string extrude_infill(const Print& print, const std::vector& by_region, bool is_infill_first); std::string extrude_ironing(const Print& print, const std::vector& by_region); @@ -436,6 +437,8 @@ class GCode : ExtrusionVisitorConst { std::string m_delayed_layer_change; // Keeps track of the last extrusion role passed to the processor ExtrusionRole m_last_processor_extrusion_role; + // For Progress bar indicator, in sequential mode (complete objects) + std::set m_object_sequentially_printed; // How many times will change_layer() be called? // change_layer() will update the progress bar. uint32_t m_layer_count; @@ -516,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/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index e88aa6dc0dd..92152ce11bd 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1209,7 +1209,7 @@ static ExPolygons get_boundary(const Layer &layer, std::vector &old_2_new_expoly : old_2_new_expolygons) { - assert(old_2_new_expoly.first.contains(old_2_new_expoly.second.contour.split_at_index(0))); + assert(old_2_new_expoly.first.contains(old_2_new_expoly.second.contour.split_at_index(0)) || old_2_new_expoly.first == old_2_new_expoly.second); boundary.push_back(old_2_new_expoly.second); slice_2_boundary.push_back(std::move(old_2_new_expoly)); } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index f366d24d2ff..3d9d48e0893 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -902,7 +902,7 @@ std::string CoolingBuffer::apply_layer_cooldown( } //set the fan controls default_fan_speed[ExtrusionRole::erBridgeInfill] = EXTRUDER_CONFIG(bridge_fan_speed); - default_fan_speed[ExtrusionRole::erInternalBridgeInfill] = EXTRUDER_CONFIG(bridge_internal_fan_speed); + default_fan_speed[ExtrusionRole::erInternalBridgeInfill] = EXTRUDER_CONFIG(internal_bridge_fan_speed); default_fan_speed[ExtrusionRole::erTopSolidInfill] = EXTRUDER_CONFIG(top_fan_speed); default_fan_speed[ExtrusionRole::erIroning] = default_fan_speed[ExtrusionRole::erTopSolidInfill]; default_fan_speed[ExtrusionRole::erSupportMaterialInterface] = EXTRUDER_CONFIG(support_material_interface_fan_speed); @@ -984,7 +984,7 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_speeds[ExtrusionRole::erBridgeInfill] = fan_speeds[0]; } - // if bridge_internal_fan is disabled, it takes the value of bridge_fan + // if internal_bridge_fan is disabled, it takes the value of bridge_fan if (!fan_control[ExtrusionRole::erInternalBridgeInfill] && fan_control[ExtrusionRole::erBridgeInfill]) { fan_control[ExtrusionRole::erInternalBridgeInfill] = true; fan_speeds[ExtrusionRole::erInternalBridgeInfill] = fan_speeds[ExtrusionRole::erBridgeInfill]; 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/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 5ec022a55e3..7a54df3840d 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -241,45 +241,74 @@ std::vector raycast_visibility(const AABBTreeIndirect::Tree<3, float> &ra return result; } -std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector &lengths, +std::vector calculate_polyline_angles_at_vertices(const PolylineWithEnd &polyline, const std::vector &lengths, float min_arm_length) { - std::vector result(polygon.size()); + std::vector result(polyline.size()); + assert(polyline.size() - 1 == lengths.size()); - if (polygon.size() == 1) { + if (polyline.size() == 1) { result[0] = 0.0f; } size_t idx_prev = 0; size_t idx_curr = 0; size_t idx_next = 0; + size_t idx_start = 0; + size_t idx_end = 0; float distance_to_prev = 0; float distance_to_next = 0; - //push idx_prev far enough back as initialization - while (distance_to_prev < min_arm_length) { - idx_prev = Slic3r::prev_idx_modulo(idx_prev, polygon.size()); - distance_to_prev += lengths[idx_prev]; + bool is_polygon = polyline.front() == polyline.back(); + + //if polygon : start at 0 + if (is_polygon) { + idx_start = 0; + idx_end = polyline.size() -1; // last point is same as first, don't bother. + // push idx_prev far enough back as initialization + while (distance_to_prev < min_arm_length) { + idx_prev = Slic3r::prev_idx_modulo(idx_prev, lengths.size()); + distance_to_prev += lengths[idx_prev]; + } + } else { + //line, start & end are hardcoded + idx_start = 1; + idx_end = polyline.size() -1; + result.front() = polyline.endpoints.first ? -PI / 2 : 0; + result.back() = polyline.endpoints.second ? -PI / 2 : 0; } - for (size_t _i = 0; _i < polygon.size(); ++_i) { + for (size_t _i = idx_start; _i < idx_end; ++_i) { // pull idx_prev to current as much as possible, while respecting the min_arm_length - while (distance_to_prev - lengths[idx_prev] > min_arm_length) { + while (distance_to_prev - lengths[idx_prev] > min_arm_length && idx_prev < lengths.size()) { distance_to_prev -= lengths[idx_prev]; - idx_prev = Slic3r::next_idx_modulo(idx_prev, polygon.size()); + // if polygon, circle, if line, then stop at the end. + if (is_polygon) { + idx_prev = Slic3r::next_idx_modulo(idx_prev, lengths.size()); + } else { + idx_prev++; + } } //push idx_next forward as far as needed - while (distance_to_next < min_arm_length) { + while (distance_to_next < min_arm_length && idx_next < lengths.size()) { distance_to_next += lengths[idx_next]; - idx_next = Slic3r::next_idx_modulo(idx_next, polygon.size()); + // if polygon, circle, if line, then stop at the end. + if (is_polygon) { + idx_next = Slic3r::next_idx_modulo(idx_next, lengths.size()); + } else { + idx_next++; + } } // Calculate angle between idx_prev, idx_curr, idx_next. - const Point &p0 = polygon.points[idx_prev]; - const Point &p1 = polygon.points[idx_curr]; - const Point &p2 = polygon.points[idx_next]; + const Point &p0 = polyline.points[idx_prev]; + const Point &p1 = polyline.points[idx_curr]; + const Point &p2 = polyline.points[idx_next]; result[idx_curr] = float(angle(p1 - p0, p2 - p1)); + if (!is_polygon && polyline.direction == PolylineWithEnd::PolyDir::BOTH) { + result[idx_curr] = std::abs(result[idx_curr]); + } // increase idx_curr by one float curr_distance = lengths[idx_curr]; @@ -287,6 +316,11 @@ std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, distance_to_prev += curr_distance; distance_to_next -= curr_distance; } + + // polygon: repeat angle + if (is_polygon) { + result.back() = result.front(); + } return result; } @@ -419,14 +453,14 @@ struct GlobalModelInfo { } ; -//Extract perimeter polygons of the given layer -Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference, +//Extract perimeter polylines of the given layer +PolylineWithEnds extract_perimeter_polylines(const Layer *layer, const SeamPosition configured_seam_preference, std::vector &corresponding_regions_out, PerimeterGeneratorType perimeter_type) { - Polygons polygons; + PolylineWithEnds polylines; class PerimeterCopy : public ExtrusionVisitorConst { - Polygons* polygons; + PolylineWithEnds* polylines; std::vector* corresponding_regions_out; const LayerRegion* current_layer_region; SeamPosition configured_seam_preference; @@ -434,59 +468,83 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi public: bool also_overhangs = false; bool also_thin_walls = false; - PerimeterCopy(std::vector* regions_out, Polygons* polys, SeamPosition configured_seam, PerimeterGeneratorType perimeter_type) - : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polygons(polys), perimeter_type(perimeter_type) { + PerimeterCopy(std::vector* regions_out, PolylineWithEnds* polys, SeamPosition configured_seam, PerimeterGeneratorType perimeter_type) + : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polylines(polys), perimeter_type(perimeter_type) { } virtual void default_use(const ExtrusionEntity& entity) {}; virtual void use(const ExtrusionPath &path) override { if (perimeter_type == PerimeterGeneratorType::Arachne && path.role() != erThinWall) { - path.polygons_covered_by_width(*polygons, SCALED_EPSILON); - while (corresponding_regions_out->size() < polygons->size()) { + //path.polygons_covered_by_width(*polygons, SCALED_EPSILON); + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(path.polyline.get_points(), true, true, PolylineWithEnd::PolyDir::BOTH); // TODO: more point for arcs + //while (corresponding_regions_out->size() < polylines->size()) { corresponding_regions_out->push_back(current_layer_region); - } + //} } } virtual void use(const ExtrusionLoop& loop) override { - if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && - is_perimeter(loop.paths.front().role())) - || (also_thin_walls && loop.role() == erThinWall)) { - Points p; - loop.collect_points(p); - polygons->emplace_back(std::move(p)); + bool is_ccw = loop.polygon().is_counter_clockwise(); + if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && is_perimeter(loop.paths.front().role())) + || (also_thin_walls && loop.role() == erThinWall)) { + Points pts; + loop.collect_points(pts); + pts.push_back(pts.front()); //polygon + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(std::move(pts), true, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); corresponding_regions_out->push_back(current_layer_region); return; }else { - Points p; + PolylineWithEnds polys; + size_t count_paths_collected = 0; + bool previous_collected = false; + bool current_collected = false; for (const ExtrusionPath &path : loop.paths) { + current_collected = false; if (path.role() == ExtrusionRole::erExternalPerimeter) { - path.collect_points(p); + if(!previous_collected) + polys.emplace_back(false, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); + path.collect_points(polys.back().points); + count_paths_collected++; + current_collected = true; } if (path.role() == ExtrusionRole::erOverhangPerimeter && also_overhangs) { // TODO find a way to search for external overhangs only - path.collect_points(p); + if(!previous_collected) + polys.emplace_back(false, false, is_ccw ? PolylineWithEnd::PolyDir::CCW : PolylineWithEnd::PolyDir::CW); + path.collect_points(polys.back().points); + count_paths_collected++; + current_collected = true; } //if (path.role() == ExtrusionRole::erThinWall && also_thin_walls) { // path.collect_points(p); // TODO: 2.7: reactivate when it's possible to distinguish between thinwalltravel & thinextrusions // // currently, only looking for thinwall-only loop //} + previous_collected = current_collected; } - if (!p.empty()) { // for random seam alignment, extract all perimeters - polygons->emplace_back(std::move(p)); - corresponding_regions_out->push_back(current_layer_region); + if (!polys.empty()) { // for random seam alignment, extract all perimeters + if (count_paths_collected == loop.paths.size()) { + assert(polys.size() == 1); + assert(polys.front().first_point() == polys.front().last_point()); + } + assert(corresponding_regions_out->size() == polylines->size()); + append(*polylines, std::move(polys)); + while (corresponding_regions_out->size() < polylines->size()) { + corresponding_regions_out->push_back(current_layer_region); + } } } } virtual void use(const ExtrusionMultiPath& collection) override { if (perimeter_type == PerimeterGeneratorType::Arachne) { - Polygons polys; - for (const ExtrusionPath& path : collection.paths) { - path.polygons_covered_by_width(polys, SCALED_EPSILON); - } - polys = union_(polys); - append(*polygons, polys); - while (corresponding_regions_out->size() < polygons->size()) { + for (size_t idx = 0; idx < collection.size(); idx++) { + const ExtrusionPath &path = collection.paths[idx]; + assert(corresponding_regions_out->size() == polylines->size()); + polylines->emplace_back(path.polyline.get_points(), + idx == 0 ? true : false, + idx + 1 < collection.size() ? false : true, + PolylineWithEnd::PolyDir::BOTH); // TODO: more points for arcs corresponding_regions_out->push_back(current_layer_region); } } @@ -497,29 +555,28 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } void set_current_layer_region(const LayerRegion *set) { current_layer_region = set; } - } visitor(&corresponding_regions_out, &polygons, configured_seam_preference, perimeter_type); + } visitor(&corresponding_regions_out, &polylines, configured_seam_preference, perimeter_type); for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities()) { visitor.set_current_layer_region(layer_region); ex_entity->visit(visitor); - if (polygons.empty()) { + if (polylines.empty()) { // maybe only thin walls? visitor.also_thin_walls = true; ex_entity->visit(visitor); - if (polygons.empty()) { + if (polylines.empty()) { // can happen if the external is fully an overhang visitor.also_overhangs = true; ex_entity->visit(visitor); visitor.also_overhangs = false; - if (polygons.empty()) { + if (polylines.empty()) { // shouldn't happen - ex_entity->visit(visitor); assert(ex_entity->role() == erThinWall); // no loops // what to do in this case? - Points p; - ex_entity->collect_points(p); - polygons.emplace_back(std::move(p)); + Points pts; + ex_entity->collect_points(pts); + polylines.emplace_back(std::move(pts), true, pts.front() != pts.back(), PolylineWithEnd::PolyDir::BOTH); corresponding_regions_out.push_back(layer_region); } } @@ -527,51 +584,53 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } } - if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point + if (polylines.empty()) { // If there are no perimeter polylines/polygons for whatever reason (disabled perimeters .. ) insert dummy point // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway - polygons.emplace_back(std::vector { Point { 0, 0 } }); + polylines.emplace_back(std::vector{ Point { 0, 0 } }, true, true, PolylineWithEnd::PolyDir::BOTH); corresponding_regions_out.push_back(nullptr); } - assert(corresponding_regions_out.size() == polygons.size()); - return polygons; + assert(corresponding_regions_out.size() == polylines.size()); + return polylines; } -// Insert SeamCandidates created from perimeter polygons in to the result vector. +// Insert SeamCandidates created from perimeter polygons/polylines in to the result vector. // Compute its type (Enfrocer,Blocker), angle, and position -//each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon -// if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions -void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region, +//each SeamCandidate also contains pointer to shared Perimeter structure representing the polyline +// if Custom Seam modifiers are present, oversamples the polyline if necessary to better fit user intentions +void process_perimeter_polylines(const PolylineWithEnd &orig_polyline, float z_coord, const LayerRegion *region, const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) { - if (orig_polygon.size() == 0) { + if (orig_polyline.size() == 0) { return; } - Polygon polygon = orig_polygon; - bool was_clockwise = polygon.make_counter_clockwise(); + PolylineWithEnd polyline = orig_polyline; + bool is_polygon = polyline.first_point() == polyline.last_point(); + assert(!is_polygon || polyline.direction != PolylineWithEnd::PolyDir::BOTH); + if (is_polygon && polyline.direction == PolylineWithEnd::PolyDir::CW) { + polyline.reverse(); + assert(polyline.direction == PolylineWithEnd::PolyDir::CCW); + } float angle_arm_len = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).nozzle_diameter() : 0.5f; std::vector lengths { }; - for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) { - lengths.push_back((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm()); + for (size_t point_idx = 0; point_idx < polyline.size() - 1; ++point_idx) { + lengths.push_back((unscale(polyline.points[point_idx]) - unscale(polyline.points[point_idx + 1])).norm()); } - lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1)); - std::vector polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, - angle_arm_len); + std::vector polyline_angles = calculate_polyline_angles_at_vertices(polyline, lengths, angle_arm_len); result.perimeters.push_back( { }); Perimeter &perimeter = result.perimeters.back(); - std::queue orig_polygon_points { }; - for (size_t index = 0; index < polygon.size(); ++index) { - Vec2f unscaled_p = unscale(polygon[index]).cast(); - orig_polygon_points.emplace(unscaled_p.x(), unscaled_p.y(), z_coord); + std::queue orig_polyline_points { }; + for (size_t index = 0; index < polyline.size(); ++index) { + Vec2f unscaled_p = unscale(polyline.points[index]).cast(); + orig_polyline_points.emplace(unscaled_p.x(), unscaled_p.y(), z_coord); } - Vec3f first = orig_polygon_points.front(); std::queue oversampled_points { }; size_t orig_angle_index = 0; perimeter.start_index = result.points.size(); perimeter.flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f; bool some_point_enforced = false; - while (!orig_polygon_points.empty() || !oversampled_points.empty()) { + while (!orig_polyline_points.empty() || !oversampled_points.empty()) { EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; Vec3f position; float local_ccw_angle = 0; @@ -580,9 +639,16 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const position = oversampled_points.front(); oversampled_points.pop(); } else { - position = orig_polygon_points.front(); - orig_polygon_points.pop(); - local_ccw_angle = was_clockwise ? -polygon_angles[orig_angle_index] : polygon_angles[orig_angle_index]; + position = orig_polyline_points.front(); + orig_polyline_points.pop(); + if (orig_polyline.direction == PolylineWithEnd::PolyDir::BOTH) { + local_ccw_angle = polyline_angles[orig_angle_index]; + } else if (orig_polyline.direction == PolylineWithEnd::PolyDir::CW) { + local_ccw_angle = -polyline_angles[orig_angle_index]; + } else { + assert(orig_polyline.direction == PolylineWithEnd::PolyDir::CCW); + local_ccw_angle = polyline_angles[orig_angle_index]; + } orig_angle_index++; orig_point = true; } @@ -596,10 +662,11 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } some_point_enforced = some_point_enforced || type == EnforcedBlockedSeamPoint::Enforced; - if (orig_point) { - Vec3f pos_of_next = orig_polygon_points.empty() ? first : orig_polygon_points.front(); + if (orig_point && !orig_polyline_points.empty()) { + Vec3f pos_of_next = orig_polyline_points.front(); + Line line(scaled(Vec2f(position.head<2>())), scaled(Vec2f(pos_of_next.head<2>()))); float distance_to_next = (position - pos_of_next).norm(); - if (global_model_info.is_enforced(position, distance_to_next)) { + if (global_model_info.is_enforced( (position + pos_of_next) / 2, distance_to_next / 2)) { Vec3f vec_to_next = (pos_of_next - position).normalized(); float step_size = SeamPlacer::enforcer_oversampling_distance; float step = step_size; @@ -614,6 +681,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } perimeter.end_index = result.points.size(); + assert(!is_polygon || result.points[perimeter.end_index-1].position == result.points[perimeter.start_index].position); if (some_point_enforced) { // We will patches of enforced points (patch: continuous section of enforced points), choose @@ -624,18 +692,26 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size); }; + assert(perimeter.start_index + 2 <= perimeter.end_index); // at least 2 elt std::vector patches_starts_ends; - for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) { + for (size_t i = perimeter.start_index; i < perimeter.end_index - 1; ++i) { if (result.points[i].type != EnforcedBlockedSeamPoint::Enforced && - result.points[next_index(i)].type == EnforcedBlockedSeamPoint::Enforced) { - patches_starts_ends.push_back(next_index(i)); + result.points[i+1].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(i+1); } if (result.points[i].type == EnforcedBlockedSeamPoint::Enforced && - result.points[next_index(i)].type != EnforcedBlockedSeamPoint::Enforced) { - patches_starts_ends.push_back(next_index(i)); + result.points[i+1].type != EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(i+1); } } - //if patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case + // also add start & end that can't be found for polyline + if (!is_polygon && result.points[perimeter.start_index].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.insert(patches_starts_ends.begin(), perimeter.start_index); + } + if (!is_polygon && result.points[perimeter.end_index - 1].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(perimeter.end_index); + } + // If patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case, as there is no center of a circumference if (!patches_starts_ends.empty()) { //if the first point in the patches is not enforced, it marks a patch end. in that case, put it to the end and start on next // to simplify the processing @@ -969,7 +1045,7 @@ void debug_export_points(const std::vector &lay float max_weight = min_weight; for (const SeamCandidate &point : layers[layer_idx].points) { - Vec3i color = value_to_rgbi(-PI, PI, point.local_ccw_angle); + Vec3i32 color = value_to_rgbi(-PI, PI, point.local_ccw_angle); std::string fill = "rgb(" + std::to_string(color.x()) + "," + std::to_string(color.y()) + "," + std::to_string(color.z()) + ")"; angles_svg.draw(scaled(Vec2f(point.position.head<2>())), fill); @@ -1171,9 +1247,9 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, auto unscaled_z = layer->slice_z; std::vector regions; //NOTE corresponding region ptr may be null, if the layer has zero perimeters - Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions, po->config().perimeter_generator.value); - for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { - process_perimeter_polygon(polygons[poly_index], unscaled_z, + PolylineWithEnds polygons_and_lines = extract_perimeter_polylines(layer, configured_seam_preference, regions, po->config().perimeter_generator.value); + for (size_t poly_index = 0; poly_index < polygons_and_lines.size(); ++poly_index) { + process_perimeter_polylines(polygons_and_lines[poly_index], unscaled_z, regions[poly_index], global_model_info, layer_seams); } auto functor = SeamCandidateCoordinateFunctor { layer_seams.points }; @@ -1653,7 +1729,12 @@ void SeamPlacer::init(const Print &print, std::function throw_if_can m_seam_per_object.clear(); this->external_perimeters_first = print.default_region_config().external_perimeters_first; - for (const PrintObject *po : print.objects()) { + for (size_t obj_idx = 0; obj_idx < print.objects().size(); ++ obj_idx) { + const PrintObject *po = print.objects()[obj_idx]; + print.set_status(int((obj_idx * 100) / print.objects().size()), + ("Computing seam visibility areas: object %s / %s"), + {std::to_string(obj_idx + 1), std::to_string(print.objects().size())}, + PrintBase::SlicingStatus::SECONDARY_STATE); throw_if_canceled_func(); SeamPosition configured_seam_preference = po->config().seam_position.value; SeamComparator comparator { configured_seam_preference, *po }; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index e65273f4465..e8c5c579923 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -44,6 +44,30 @@ inline double angle(const Eigen::MatrixBase &v1, const Eigen::MatrixBas struct GlobalModelInfo; struct SeamComparator; +class PolylineWithEnd : public Polyline { +public: + enum PolyDir { + CCW, + CW, + BOTH + }; + /// if true => it's an endpoint, if false it join somthign that can't be used for a seam, so don't use this endpoint. + std::pair endpoints; + PolyDir direction; + + PolylineWithEnd() : endpoints(false, false), direction(PolyDir::BOTH) {} + PolylineWithEnd(bool stopstart, bool stopend, PolyDir is_ccw) : endpoints(stopstart, stopend), direction(is_ccw) {} + PolylineWithEnd(const Points &pts, bool stopstart, bool stopend, PolyDir is_ccw) : Polyline(pts), endpoints(stopstart, stopend), direction(is_ccw) {} + PolylineWithEnd(Points &&pts, bool stopstart, bool stopend, PolyDir is_ccw) : Polyline(pts), endpoints(stopstart, stopend), direction(is_ccw) {} + void reverse() { + Polyline::reverse(); + std::swap(this->endpoints.first, this->endpoints.second); + if (direction != PolyDir::BOTH) + direction = (direction == PolyDir::CCW) ? PolyDir::CW : PolyDir::CCW; + } +}; +typedef std::vector PolylineWithEnds; + enum class EnforcedBlockedSeamPoint { Blocked = 0, Neutral = 1, 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 5e5eaec9b49..6992517c1ac 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -171,6 +171,7 @@ void Layer::make_perimeters() /// if you don't do that, objects will share the same region, and the same settings. if (config.perimeter_extruder == other_config.perimeter_extruder && config.perimeters == other_config.perimeters + && config.external_perimeter_acceleration == other_config.external_perimeter_acceleration && config.external_perimeter_extrusion_width == other_config.external_perimeter_extrusion_width && config.external_perimeter_overlap == other_config.external_perimeter_overlap && config.external_perimeter_speed == other_config.external_perimeter_speed // it os mandatory? can't this be set at gcode.cpp? @@ -182,6 +183,7 @@ void Layer::make_perimeters() && config.extra_perimeters_overhangs == other_config.extra_perimeters_overhangs && config.gap_fill_enabled == other_config.gap_fill_enabled && ((config.gap_fill_speed == other_config.gap_fill_speed) || !config.gap_fill_enabled) + && config.gap_fill_acceleration == other_config.gap_fill_acceleration && config.gap_fill_last == other_config.gap_fill_last && config.gap_fill_flow_match_perimeter == other_config.gap_fill_flow_match_perimeter && config.gap_fill_extension == other_config.gap_fill_extension @@ -196,10 +198,12 @@ void Layer::make_perimeters() && (this->id() == 0 || config.only_one_perimeter_first_layer == other_config.only_one_perimeter_first_layer) && config.only_one_perimeter_top == other_config.only_one_perimeter_top && config.only_one_perimeter_top_other_algo == other_config.only_one_perimeter_top_other_algo + && config.overhangs_acceleration == other_config.overhangs_acceleration && config.overhangs_width_speed == other_config.overhangs_width_speed && config.overhangs_width == other_config.overhangs_width && config.overhangs_reverse == other_config.overhangs_reverse && config.overhangs_reverse_threshold == other_config.overhangs_reverse_threshold + && config.perimeter_acceleration == other_config.perimeter_acceleration && config.perimeter_extrusion_width == other_config.perimeter_extrusion_width && config.perimeter_loop == other_config.perimeter_loop && config.perimeter_loop_seam == other_config.perimeter_loop_seam @@ -211,13 +215,15 @@ void Layer::make_perimeters() && config.small_perimeter_min_length == other_config.small_perimeter_min_length && config.small_perimeter_max_length == other_config.small_perimeter_max_length && config.thin_walls == other_config.thin_walls + && config.thin_walls_acceleration == other_config.thin_walls_acceleration && config.thin_walls_min_width == other_config.thin_walls_min_width && config.thin_walls_overlap == other_config.thin_walls_overlap + && config.thin_walls_speed == other_config.thin_walls_speed && config.thin_perimeters == other_config.thin_perimeters && config.thin_perimeters_all == other_config.thin_perimeters_all - && config.thin_walls_speed == other_config.thin_walls_speed && config.infill_overlap == other_config.infill_overlap && config.perimeter_loop == other_config.perimeter_loop + && config.region_gcode == other_config.region_gcode && config.fuzzy_skin == other_config.fuzzy_skin && config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness @@ -313,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/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 313c96f6497..3dfaa2165d4 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -63,16 +63,16 @@ int MultiPoint::find_point(const Point &point) const return -1; // not found } -int MultiPoint::find_point(const Point &point, double scaled_epsilon) const +int MultiPoint::find_point(const Point &point, coordf_t scaled_epsilon) const { if (scaled_epsilon == 0) return this->find_point(point); - auto dist2_min = std::numeric_limits::max(); - auto eps2 = scaled_epsilon * scaled_epsilon; - int idx_min = -1; + coordf_t dist2_min = std::numeric_limits::max(); + coordf_t eps2 = scaled_epsilon * scaled_epsilon; + int idx_min = -1; for (const Point &pt : this->points) { - double d2 = (pt - point).cast().squaredNorm(); + coordf_t d2 = pt.distance_to_square(point); //(pt - point).cast().squaredNorm(); if (d2 < dist2_min) { idx_min = int(&pt - &this->points.front()); dist2_min = d2; diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index c65e9807cba..33edd409738 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -50,7 +50,7 @@ class MultiPoint int find_point(const Point &point) const; // Return index of the closest point to point closer than scaled_epsilon. // Return -1 if no such point exists. - int find_point(const Point &point, const double scaled_epsilon) const; + int find_point(const Point &point, const coordf_t scaled_epsilon) const; bool has_boundary_point(const Point &point) const; int closest_point_index(const Point &point) const { int idx = -1; diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 724995e2d37..013228f5f82 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 (size_t 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/Point.hpp b/src/libslic3r/Point.hpp index 0d5b8eec490..51ef3d5d2ae 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -179,13 +179,13 @@ class Point : public Vec2crd Point projection_onto(const Line &line) const; Point interpolate(const double percent, const Point &p) const; - double distance_to(const Point &point) const { return (point - *this).cast().norm(); } - double distance_to_square(const Point &point) const { - double dx = double(point.x() - this->x()); - double dy = double(point.y() - this->y()); + coordf_t distance_to(const Point &point) const { return (point - *this).cast().norm(); } + coordf_t distance_to_square(const Point &point) const { + coordf_t dx = double(point.x() - this->x()); + coordf_t dy = double(point.y() - this->y()); return dx*dx + dy*dy; } - double distance_to(const Line &line) const; + coordf_t distance_to(const Line &line) const; bool coincides_with(const Point &point) const { return this->x() == point.x() && this->y() == point.y(); } bool coincides_with_epsilon(const Point &point) const { return std::abs(this->x() - point.x()) < SCALED_EPSILON/2 && std::abs(this->y() - point.y()) < SCALED_EPSILON/2; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 78750f24bfc..4dc23cda34c 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") @@ -314,17 +314,17 @@ void Preset::normalize(DynamicPrintConfig &config) static_cast(opt)->resize(n, defaults.option(key)); } // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately. - for (const std::string &key : { "filament_settings_id" }) { + for (const char *key : {"filament_settings_id"}) { 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); } @@ -517,6 +517,7 @@ static std::vector s_Preset_print_options { "fill_aligned_z", "fill_angle", "fill_angle_cross", + "fill_angle_follow_model", "fill_angle_increment", "fill_angle_template", "bridge_angle", @@ -531,7 +532,7 @@ static std::vector s_Preset_print_options { // speeds "default_speed", "bridge_speed", - "bridge_speed_internal", + "internal_bridge_speed", "brim_speed", "external_perimeter_speed", "first_layer_speed", @@ -567,7 +568,6 @@ static std::vector s_Preset_print_options { "fuzzy_skin_thickness", // acceleration "bridge_acceleration", - "bridge_internal_acceleration", "brim_acceleration", "default_acceleration", "external_perimeter_acceleration", @@ -575,6 +575,7 @@ static std::vector s_Preset_print_options { "first_layer_acceleration_over_raft", "gap_fill_acceleration", "infill_acceleration", + "internal_bridge_acceleration", "ironing_acceleration", "overhangs_acceleration", "perimeter_acceleration", @@ -640,7 +641,9 @@ static std::vector s_Preset_print_options { "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "gcode_substitutions", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", + "ooze_prevention", "standby_temperature_delta", "interface_shells", + "object_gcode", + "region_gcode", // width & spacing "extrusion_spacing", "extrusion_width", @@ -676,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", @@ -691,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", @@ -734,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 { @@ -767,10 +772,10 @@ static std::vector s_Preset_filament_options { "default_fan_speed", "max_fan_speed", "bridge_fan_speed", - "bridge_internal_fan_speed", "external_perimeter_fan_speed", "gap_fill_fan_speed", "infill_fan_speed", + "internal_bridge_fan_speed", "overhangs_fan_speed", "perimeter_fan_speed", "solid_infill_fan_speed", @@ -1485,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); @@ -1533,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)); } } @@ -1928,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); @@ -1959,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 73921321f5d..c4263f1e968 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -61,27 +61,25 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne if (opt_keys.empty()) return false; + // Cache the plenty of parameters, which influence the G-code generator only, // or they are only notes not influencing the generated G-code. static std::unordered_set steps_gcode = { + "allow_empty_layers", "avoid_crossing_perimeters", "avoid_crossing_perimeters_max_detour", "avoid_crossing_not_first_layer", - "avoid_crossing_top", "bed_shape", "bed_temperature", "before_layer_gcode", "between_objects_gcode", - "bridge_acceleration", - "bridge_internal_acceleration", "bridge_fan_speed", - "bridge_internal_fan_speed", - "brim_acceleration", "chamber_temperature", + "color_change_gcode", "colorprint_heights", "complete_objects_sort", - "cooling", - "default_acceleration", + "complete_objects_one_brim", + //"cooling", "default_fan_speed", "deretract_speed", "disable_fan_first_layers", @@ -89,8 +87,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "enforce_retract_first_layer", "end_gcode", "end_filament_gcode", - "external_perimeter_acceleration", - "external_perimeter_cut_corners", "external_perimeter_fan_speed", "extrusion_axis", "extruder_clearance_height", @@ -105,62 +101,59 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "fan_kickstart", "fan_speedup_overhangs", "fan_speedup_time", + "feature_gcode", "fan_percentage", "fan_printer_min_speed", "filament_colour", "filament_custom_variables", "filament_diameter", "filament_density", + "filament_load_time", "filament_notes", "filament_cost", "filament_spool_weight", - "first_layer_acceleration", - "first_layer_acceleration_over_raft", + "filament_unload_time", + "filament_wipe_advanced_pigment", "first_layer_bed_temperature", - "first_layer_flow_ratio", - "first_layer_speed", // ? delete y prusa here in 2.4 - "first_layer_speed_over_raft", - "first_layer_infill_speed", - "first_layer_min_speed", "full_fan_speed_layer", - "gap_fill_acceleration", "gap_fill_fan_speed", - "gap_fill_flow_match_perimeter", - "gap_fill_speed", "gcode_ascii", "gcode_comments", "gcode_filename_illegal_char", "gcode_label_objects", "gcode_precision_xyz", "gcode_precision_e", - "infill_acceleration", + "gcode_substitutions", "infill_fan_speed", - "ironing_acceleration", + "internal_bridge_fan_speed", "layer_gcode", + "lift_min", "max_fan_speed", "max_gcode_per_second", "max_print_height", "max_print_speed", + "max_speed_reduction", "max_volumetric_speed", "min_length", "min_print_speed", + "milling_diameter", "milling_toolchange_end_gcode", "milling_toolchange_start_gcode", - "milling_offset", - "milling_z_offset", - "milling_z_lift", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", "notes", "only_retract_when_crossing_perimeters", "output_filename_format", - "overhangs_acceleration", "overhangs_fan_speed", - "perimeter_acceleration", + "parallel_objects_step", + "pause_print_gcode", "post_process", - "gcode_substitutions", + "print_custom_variables", + "printer_custom_variables", "perimeter_fan_speed", "printer_notes", + "remaining_times", + "remaining_times_type", "retract_before_travel", "retract_before_wipe", "retract_layer_change", @@ -175,9 +168,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "retract_restart_extra", "retract_restart_extra_toolchange", "retract_speed", + "silent_mode", "single_extruder_multi_material_priming", "slowdown_below_layer_time", - "solid_infill_acceleration", "solid_infill_fan_speed", "support_material_acceleration", "support_material_fan_speed", @@ -187,8 +180,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "start_gcode", "start_gcode_manual", "start_filament_gcode", - "thin_walls_acceleration", - "thin_walls_speed", + "template_custom_gcode", "thumbnails", "thumbnails_color", "thumbnails_custom_color", @@ -203,17 +195,16 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "tool_name", "toolchange_gcode", "top_fan_speed", - "top_solid_infill_acceleration", "threads", - "travel_acceleration", - "travel_deceleration_use_target", - "travel_speed", - "travel_speed_z", "use_firmware_retraction", "use_relative_e_distances", "use_volumetric_e", "variable_layer_height", "wipe", + "wipe_advanced", + "wipe_advanced_algo", + "wipe_advanced_multiplier", + "wipe_advanced_nozzle_melted_volume", "wipe_extra_perimeter", "wipe_inside_depth", "wipe_inside_end", @@ -229,6 +220,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { + //this one isn't even use in slicing, only for import. + if (opt_key == "init_z_rotate") + continue; if (steps_gcode.find(opt_key) != steps_gcode.end()) { // These options only affect G-code export or they are just notes without influence on the generated G-code, // so there is nothing to invalidate. @@ -241,10 +235,11 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "min_skirt_length" || opt_key == "ooze_prevention" || opt_key == "skirts" - || opt_key == "skirt_height" || opt_key == "skirt_brim" || opt_key == "skirt_distance" || opt_key == "skirt_distance_from_brim" + || opt_key == "skirt_extrusion_width" + || opt_key == "skirt_height" || opt_key == "wipe_tower_x" || opt_key == "wipe_tower_y" || opt_key == "wipe_tower_rotation_angle" @@ -253,12 +248,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne } else if ( opt_key == "bridge_precision" || opt_key == "filament_shrink" - || opt_key == "first_layer_height" || opt_key == "nozzle_diameter" - || opt_key == "model_precision" || opt_key == "resolution" || opt_key == "resolution_internal" - || opt_key == "slice_closing_radius" // Spiral Vase forces different kind of slicing than the normal model: // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer. // Therefore toggling the Spiral Vase on / off requires complete reslicing. @@ -268,14 +260,13 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne } else if ( opt_key == "complete_objects" || opt_key == "filament_type" - || opt_key == "filament_soluble" - || opt_key == "first_layer_temperature" || opt_key == "filament_loading_speed" || opt_key == "filament_loading_speed_start" || opt_key == "filament_unloading_speed" || opt_key == "filament_unloading_speed_start" || opt_key == "filament_toolchange_delay" || opt_key == "filament_cooling_moves" + || opt_key == "filament_max_wipe_tower_speed" || opt_key == "filament_minimal_purge_on_wipe_tower" || opt_key == "filament_cooling_initial_speed" || opt_key == "filament_cooling_final_speed" @@ -293,9 +284,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "filament_toolchange_part_fan_speed" || opt_key == "filament_dip_insertion_speed" || opt_key == "filament_dip_extraction_speed" //skinnydip params end + || opt_key == "first_layer_temperature" || opt_key == "gcode_flavor" || opt_key == "high_current_on_filament_swap" - || opt_key == "infill_first" + || opt_key == "priming_position" || opt_key == "single_extruder_multi_material" || opt_key == "temperature" || opt_key == "wipe_tower" @@ -303,8 +295,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "wipe_tower_brim_width" || opt_key == "wipe_tower_bridging" || opt_key == "wipe_tower_no_sparse_layers" + || opt_key == "wipe_tower_per_color_wipe" || opt_key == "wipe_tower_speed" || opt_key == "wipe_tower_wipe_starting_speed" + || opt_key == "wiping_volumes_extruders" || opt_key == "wiping_volumes_matrix" || opt_key == "parking_pos_retraction" || opt_key == "cooling_tube_retraction" @@ -312,7 +306,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne || opt_key == "extra_loading_move" || opt_key == "travel_speed" || opt_key == "travel_speed_z" - || opt_key == "first_layer_speed" || opt_key == "z_offset") { steps.emplace_back(psWipeTower); steps.emplace_back(psSkirtBrim); @@ -323,8 +316,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne //FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary. osteps.emplace_back(posSupportMaterial); } else if ( - opt_key == "first_layer_extrusion_width" - || opt_key == "arc_fitting" + opt_key == "arc_fitting" || opt_key == "arc_fitting_tolerance" || opt_key == "min_layer_height" || opt_key == "max_layer_height" @@ -335,6 +327,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne osteps.emplace_back(posSimplifyPath); osteps.emplace_back(posSupportMaterial); steps.emplace_back(psSkirtBrim); + } else if (opt_key == "seam_gap" || opt_key == "seam_gap_external") { + osteps.emplace_back(posInfill); } else if (opt_key == "posSlice") osteps.emplace_back(posSlice); @@ -622,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)); } } @@ -981,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 ); } @@ -1000,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); } } @@ -1034,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. @@ -1558,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 @@ -1583,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); @@ -1794,7 +1788,7 @@ DynamicConfig PrintStatistics::config() const DynamicConfig PrintStatistics::placeholders() { DynamicConfig config; - for (const std::string &key : { + for (const char *key : { "print_time", "normal_print_time", "silent_print_time", "used_filament", "extruded_volume", "total_cost", "total_weight", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament", diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index a353f0b8d0f..ba6e9fe0136 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -46,6 +46,7 @@ namespace FillLightning { using GeneratorPtr = std::unique_ptr; }; // namespace FillLightning + // Print step IDs for keeping track of the print state. // The Print steps are applied in this order. enum PrintStep { @@ -59,6 +60,7 @@ enum PrintStep { // should be refreshed. psSlicingFinished = psSkirtBrim, psGCodeExport, + //TODO: psGCodeLoader (for params that are only used for time display and such) psCount, }; diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 255cb0bf305..4c9e0dd1987 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -420,7 +420,8 @@ class PrintBase : public ObjectBase SLICING_ENDED = 1 << 6, GCODE_ENDED = 1 << 7, MAIN_STATE = 1 << 8, - SECONDARY_STATE = 1 << 9 + SECONDARY_STATE = 1 << 9, + FORCE_SHOW = 1 << 10 }; // Bitmap of FlagBits unsigned int flags; @@ -443,19 +444,25 @@ class PrintBase : public ObjectBase } void set_status(int percent, const std::string& message, const std::vector& args, unsigned int flags = SlicingStatus::DEFAULT) const { //check if it need an update. Avoid doing a gui update each ms. - if ((flags & SlicingStatus::SECONDARY_STATE) != 0 && message != m_last_status_message) { - std::chrono::time_point current_time = std::chrono::system_clock::now(); - if ((static_cast>(current_time - PrintBase::m_last_status_update)).count() > 0.002 && PrintBase::m_last_status_percent != percent) { - PrintBase::m_last_status_update = current_time; - PrintBase::m_last_status_percent = percent; + { + std::lock_guard lock(m_last_status_mutex); + if ((flags & SlicingStatus::FORCE_SHOW) == 0 && (flags & SlicingStatus::SECONDARY_STATE) != 0 && + message != m_last_status_message) { + std::chrono::time_point current_time = std::chrono::system_clock::now(); + if ((static_cast>(current_time - PrintBase::m_last_status_update)) + .count() > 0.002 && + PrintBase::m_last_status_percent != percent) { + PrintBase::m_last_status_update = current_time; + PrintBase::m_last_status_percent = percent; + } else { + // don't update if it's for the secondary and already done in less than 200ms + return; + } } else { - //don't update if it's for the secondary and already done in less than 200ms - return; + PrintBase::m_last_status_percent = -1; } - } else { - PrintBase::m_last_status_percent = -1; + m_last_status_message = message; } - m_last_status_message = message; if ((flags & SlicingStatus::FlagBits::MAIN_STATE) == 0 && (flags & SlicingStatus::FlagBits::SECONDARY_STATE) == 0) flags = flags | SlicingStatus::FlagBits::MAIN_STATE; if (m_status_callback) { @@ -549,6 +556,7 @@ class PrintBase : public ObjectBase m_last_status_update = {}; inline static int m_last_status_percent = -1; inline static std::string m_last_status_message = ""; + inline static std::mutex m_last_status_mutex; private: std::atomic m_cancel_status; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a9444b68fab..a671206198a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -712,20 +712,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionFloatOrPercent(0,false)); - def = this->add("bridge_internal_acceleration", coFloatOrPercent); - def->label = L("Internal bridges "); - def->full_label = L("Internal bridges acceleration"); - def->category = OptionCategory::speed; - def->tooltip = L("This is the acceleration your printer will use for internal bridges. " - "\nCan be a % of the default acceleration" - "\nSet zero to use bridge acceleration for internal bridges."); - def->sidetext = L("mm/s² or %"); - def->ratio_over = "bridge_acceleration"; - def->min = 0; - def->max_literal = { -200, false }; - def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0,false)); - def = this->add("bridge_angle", coFloat); def->label = L("Bridging"); def->full_label = L("Bridging angle"); @@ -751,19 +737,6 @@ void PrintConfigDef::init_fff_params() def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ 100 }); - def = this->add("bridge_internal_fan_speed", coInts); - def->label = L("Infill bridges fan speed"); - def->category = OptionCategory::cooling; - def->tooltip = L("This fan speed is enforced during all infill bridges. It won't slow down the fan if it's currently running at a higher speed." - "\nSet to -1 to disable this override (Internal bridges will use Bridges fan speed)." - "\nCan be disabled by disable_fan_first_layers and increased by low layer time."); - def->sidetext = L("%"); - def->min = -1; - def->max = 100; - def->mode = comAdvancedE | comSuSi; - def->is_vector_extruder = true; - def->set_default_value(new ConfigOptionInts{ -1 }); - def = this->add("bridge_type", coEnum); def->label = L("Bridge flow baseline"); def->category = OptionCategory::width; @@ -865,17 +838,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionFloatOrPercent(60, true)); - def = this->add("bridge_speed_internal", coFloatOrPercent); - def->label = L("Internal bridges"); - def->full_label = L("Internal bridge speed"); - def->category = OptionCategory::speed; - def->tooltip = L("Speed for printing the bridges that support the top layer.\nCan be a % of the bridge speed."); - def->sidetext = L("mm/s or %"); - def->ratio_over = "bridge_speed"; - def->min = 0; - def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(150,true)); - def = this->add("brim_inside_holes", coBool); def->label = L("Brim inside holes"); def->category = OptionCategory::skirtBrim; @@ -1518,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"); @@ -1535,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"); @@ -1757,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"); @@ -1849,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"); @@ -1865,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) @@ -2336,6 +2316,13 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionBool(true)); + def = this->add("fill_angle_follow_model", coBool); + def->label = L("Rotate with object"); + def->category = OptionCategory::infill; + def->tooltip = L("If your object has a z-rotation, then the infill will also be rotated by this value."); + def->mode = comAdvancedE | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("fill_angle_increment", coFloat); def->label = L("Fill"); def->full_label = L("Fill angle increment"); @@ -2523,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"); @@ -2579,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"); @@ -2595,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"); @@ -2784,7 +2807,7 @@ void PrintConfigDef::init_fff_params() def = this->add("gap_fill_flow_match_perimeter", coPercent); def->label = L("Cap with perimeter flow"); def->full_label = L("Gapfill: cap speed with perimeter flow"); - def->category = OptionCategory::output; + def->category = OptionCategory::speed; def->tooltip = L("A percentage of the perimeter flow (mm3/s) is used as a limit for the gap fill flow, and so the gapfill may reduce its speed when the gap fill extrusions became too thick." " This allow you to use a high gapfill speed, to print the thin gapfill quickly and reduce the difference in flow rate for the gapfill." "\nSet zero to deactivate."); @@ -3193,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"); @@ -3222,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"); @@ -3306,6 +3329,47 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comPrusa; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("internal_bridge_acceleration", coFloatOrPercent); + def->label = L("Internal bridges "); + def->full_label = L("Internal bridges acceleration"); + def->category = OptionCategory::speed; + def->tooltip = L("This is the acceleration your printer will use for internal bridges. " + "\nCan be a % of the default acceleration" + "\nSet zero to use bridge acceleration for internal bridges."); + def->sidetext = L("mm/s² or %"); + def->ratio_over = "bridge_acceleration"; + def->min = 0; + def->max_literal = { -200, false }; + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionFloatOrPercent(0,false)); + def->aliases = { "bridge_internal_acceleration" }; + + def = this->add("internal_bridge_fan_speed", coInts); + def->label = L("Infill bridges fan speed"); + def->category = OptionCategory::cooling; + def->tooltip = L("This fan speed is enforced during all infill bridges. It won't slow down the fan if it's currently running at a higher speed." + "\nSet to -1 to disable this override (Internal bridges will use Bridges fan speed)." + "\nCan be disabled by disable_fan_first_layers and increased by low layer time."); + def->sidetext = L("%"); + def->min = -1; + def->max = 100; + def->mode = comAdvancedE | comSuSi; + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionInts{ -1 }); + def->aliases = { "bridge_internal_fan_speed" }; + + def = this->add("internal_bridge_speed", coFloatOrPercent); + def->label = L("Internal bridges"); + def->full_label = L("Internal bridge speed"); + def->category = OptionCategory::speed; + def->tooltip = L("Speed for printing the bridges that support the top layer.\nCan be a % of the bridge speed."); + def->sidetext = L("mm/s or %"); + def->ratio_over = "bridge_speed"; + def->min = 0; + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionFloatOrPercent(150,true)); + def->aliases = { "bridge_speed_internal" }; + def = this->add("mmu_segmented_region_max_width", coFloat); def->label = L("Maximum width of a segmented region"); def->tooltip = L("Maximum width of a segmented region. Zero disables this feature."); @@ -3325,7 +3389,7 @@ void PrintConfigDef::init_fff_params() def = this->add("ironing_acceleration", coFloatOrPercent); def->label = L("Ironing"); def->full_label = L("Ironing acceleration"); - def->category = OptionCategory::speed; + def->category = OptionCategory::ironing; def->tooltip = L("This is the acceleration your printer will use for ironing. " "\nCan be a % of the top solid infill acceleration" "\nSet zero to use top solid infill acceleration for ironing."); @@ -3923,6 +3987,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionString{ "" }); + def = this->add("object_gcode", coString); + def->label = L("Per object G-code"); + def->category = OptionCategory::advanced; + def->tooltip = L("This code is inserted each layer, when the object began to print (just after the label if any)." + " It's main advantage is when you use it as a object modifer (right click on a model)." + "\nSpecial variables: 'layer_num','layer_z'"); + def->multiline = true; + def->full_width = true; + def->height = 10; + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionString("")); + def = this->add("only_one_perimeter_first_layer", coBool); def->label = L("Only one perimeter on First layer"); def->category = OptionCategory::perimeter; @@ -4209,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"); @@ -4227,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"); @@ -4371,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"); @@ -4520,6 +4607,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); + def = this->add("region_gcode", coString); + def->label = L("Per region G-code"); + def->category = OptionCategory::output; + def->tooltip = L("This code is inserted when a region is starting to print something (infill, perimeter, ironing)." + " It's main advantage is when you use it as a object modifer(right click on a model to add it there)" + "\nSpecial variables: 'layer_num','layer_z'"); + def->multiline = true; + def->full_width = true; + def->height = 10; + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionString("")); + def = this->add("resolution", coFloat); def->label = L("Slice resolution"); def->category = OptionCategory::slicing; @@ -5106,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"); @@ -5135,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"); @@ -5294,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 "); @@ -6016,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"); @@ -6031,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 "); @@ -6343,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"); @@ -6351,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"); @@ -7818,17 +7917,19 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va // In PrusaSlicer 2.3.0-alpha0 the "monotonic" infill was introduced, which was later renamed to "monotonous". if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern" - || opt_key == "solid_fill_pattern" || opt_key == "bridge_fill_pattern" || opt_key == "support_material_interface_pattern")) + || opt_key == "solid_fill_pattern" || opt_key == "bridge_fill_pattern" || opt_key == "support_material_interface_pattern")) { value = "monotonic"; + } // some changes has occurs between rectilineargapfill and monotonicgapfill. Set them at the right value for each type if (value == "rectilineargapfill" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern") ) value = "monotonicgapfill"; - if (opt_key == "fill_pattern" || opt_key == "support_material_interface_pattern") - if (value == "rectilineargapfill") + if (opt_key == "fill_pattern" || opt_key == "support_material_interface_pattern") { + if (value == "rectilineargapfill") { value = "rectilinear"; - else if (value == "monotonicgapfill") + } else if (value == "monotonicgapfill") { value = "monotonic"; - + } + } if (ignore.find(opt_key) != ignore.end()) { opt_key = ""; return; @@ -7838,9 +7939,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va opt_key = "raft_first_layer_density"; value = "100"; } - if (boost::starts_with(opt_key, "thin_perimeters") && value == "1") + if (boost::starts_with(opt_key, "thin_perimeters") && value == "1") { value = "100%"; - + } if ("fan_always_on" == opt_key) { if (value != "1") { //min_fan_speed is already converted to default_fan_speed, just has to deactivate it if not always_on @@ -7869,8 +7970,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } } } - if (remove_unkown_keys) + if (remove_unkown_keys) { opt_key = ""; + } return; } } @@ -8050,13 +8152,13 @@ void _convert_from_prusa(CONFIG_CLASS& conf, const DynamicPrintConfig& global_co // set phony entries if (with_phony) { for (auto &[opt_key_width, opt_key_spacing] : - {std::pair{"extrusion_width", "extrusion_spacing"}, - std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, - std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, - std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, - std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, - std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, - std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { + {std::pair{"extrusion_width", "extrusion_spacing"}, + std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, + std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, + std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, + std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, + std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, + std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { // if prusa has defined a width, or if the conf has a default spacing that need to be overwritten if (conf.option(opt_key_width) != nullptr || conf.option(opt_key_spacing) != nullptr) { ConfigOption *opt_new = print_config_def.get(opt_key_spacing)->default_value.get()->clone(); @@ -8157,13 +8259,13 @@ void _deserialize_maybe_from_prusa(const std::map{"extrusion_width", "extrusion_spacing"}, - std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, - std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, - std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, - std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, - std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, - std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { + {std::pair{"extrusion_width", "extrusion_spacing"}, + std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, + std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, + std::pair{"first_layer_extrusion_width", "first_layer_extrusion_spacing"}, + std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, + std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, + std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { const ConfigOption *opt_width = config.option(opt_key_width); const ConfigOption *opt_spacing = config.option(opt_key_spacing); if (opt_width && opt_spacing) { @@ -8226,12 +8328,9 @@ std::unordered_set prusa_export_to_remove_keys = { "avoid_crossing_not_first_layer", "avoid_crossing_top", "bridge_fill_pattern", -"bridge_internal_acceleration", -"bridge_internal_fan_speed", "bridge_precision", "bridge_overlap", "bridge_overlap_min", -"bridge_speed_internal", "bridge_type", "bridged_infill_margin", "brim_acceleration", @@ -8295,6 +8394,7 @@ std::unordered_set prusa_export_to_remove_keys = { "fill_aligned_z", "fill_angle_increment", "fill_angle_cross", +"fill_angle_follow_model", "fill_angle_template", "fill_smooth_distribution", "fill_smooth_width", @@ -8321,7 +8421,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", @@ -8337,6 +8436,9 @@ std::unordered_set prusa_export_to_remove_keys = { "infill_extrusion_spacing", "infill_fan_speed", "init_z_rotate", +"internal_bridge_acceleration", +"internal_bridge_fan_speed", +"internal_bridge_speed", "ironing_acceleration", "ironing_angle", "lift_min", @@ -8358,6 +8460,7 @@ std::unordered_set prusa_export_to_remove_keys = { "min_width_top_surface", "model_precision", "no_perimeter_unsupported_algo", +"object_gcode", "only_one_perimeter_top_other_algo", "only_one_perimeter_top", "only_one_perimeter_first_layer", @@ -8382,6 +8485,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", @@ -8393,6 +8497,7 @@ std::unordered_set prusa_export_to_remove_keys = { "printhost_client_cert_password", "raft_layer_height", "raft_interface_layer_height", +"region_gcode", "remaining_times_type", "resolution_internal", "retract_lift_first_layer", @@ -8413,6 +8518,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", @@ -8423,7 +8530,6 @@ std::unordered_set prusa_export_to_remove_keys = { "solid_infill_overlap", "start_gcode_manual", "solid_infill_below_layer_area", -"solid_infill_below_thickness", "solid_infill_below_width", "support_material_angle_height", "support_material_acceleration", @@ -8519,6 +8625,10 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& } else if ("seam_position" == opt_key) { if ("cost" == value) { value = "nearest"; + }else if ("allrandom" == value) { + value = "random"; + }else if ("contiguous" == value) { + value = "aligned"; } } else if ("first_layer_size_compensation" == opt_key) { opt_key = "elefant_foot_compensation"; @@ -8531,13 +8641,14 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& } } else if ("elephant_foot_min_width" == opt_key) { opt_key = "elefant_foot_min_width"; - } else if("first_layer_acceleration" == opt_key) { + } else if("first_layer_acceleration" == opt_key || "first_layer_acceleration_over_raft" == opt_key) { if (value.find("%") != std::string::npos) { // can't support %, so we uese the default accel a baseline for half-assed conversion value = std::to_string(all_conf.get_abs_value(opt_key, all_conf.get_computed_value("default_acceleration"))); } } else if ("infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key - || "overhangs_speed" == opt_key || "ironing_speed" == opt_key || "perimeter_speed" == opt_key || "infill_speed" == opt_key || "bridge_speed" == opt_key || "support_material_speed" == opt_key + || "overhangs_speed" == opt_key || "ironing_speed" == opt_key || "perimeter_speed" == opt_key + || "infill_speed" == opt_key || "bridge_speed" == opt_key || "support_material_speed" == opt_key || "max_print_speed" == opt_key ) { // remove '%' @@ -8562,9 +8673,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); @@ -8582,9 +8693,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); @@ -8608,7 +8719,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 @@ -8657,6 +8768,20 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& new_entries["fan_always_on"] = "1"; } } + + // compute max & min height from % to flat value + if ("min_layer_height" == opt_key || "max_layer_height" == opt_key) { + ConfigOptionFloats computed_opt; + const ConfigOptionFloatsOrPercents *current_opt = all_conf.option(opt_key); + const ConfigOptionFloats *nozzle_diameters = all_conf.option("nozzle_diameter"); + assert(current_opt && nozzle_diameters); + assert(current_opt->size() == nozzle_diameters->size()); + for (int i = 0; i < current_opt->size(); i++) { + computed_opt.set_at(current_opt->get_abs_value(i, nozzle_diameters->get_at(i)), i); + } + assert(computed_opt.size() == nozzle_diameters->size()); + value = computed_opt.serialize(); + } if ("thumbnails" == opt_key) { // add format to thumbnails @@ -8668,8 +8793,6 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& std::string coma = ""; for (std::string &size : sizes) { //if first or second dimension is 0: ignore. - size_t test1 = size.find("0x"); - size_t test2 = size.find("x0"); if (size.find("0x") == 0 || size.find("x0") + 2 == size.size()) continue; assert(size.find('/') == std::string::npos); @@ -8719,8 +8842,10 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector const ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key) const { - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif const ConfigOption *opt = conf->optptr(opt_key); if (opt) return opt; @@ -8730,8 +8855,10 @@ const ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_k ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key, bool create) { assert(!create); - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif ConfigOption *opt = conf->optptr(opt_key); if (opt) return opt; @@ -8743,8 +8870,10 @@ t_config_option_keys MultiPtrPrintConfig::keys() const assert(false); // shouldn't need ot call that t_config_option_keys keys; - for (auto conf : storages) { + for (ConfigBase *conf : storages) { +#ifdef _DEBUG assert(conf->exists()); +#endif append(keys, conf->keys()); } return keys; @@ -8816,10 +8945,10 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) double base_dist = 0; //std::cout << "START min_object_distance =>" << base_dist << "\n"; const ConfigOptionBool* co_opt = config->option("complete_objects"); - if (config->option("parallel_objects_step")->get_float() > 0 || co_opt && co_opt->value) { + 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); @@ -8912,10 +9041,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; @@ -8957,17 +9086,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); } } } @@ -9088,7 +9217,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; @@ -9234,7 +9363,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 { @@ -9438,12 +9567,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"; @@ -9470,7 +9599,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"; @@ -9520,7 +9649,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"; @@ -9551,7 +9680,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) { @@ -9585,14 +9714,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; @@ -9605,7 +9734,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; @@ -9917,15 +10046,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 f2eba2b63db..099a2716b89 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -706,6 +706,7 @@ protected: \ PRINT_CONFIG_CLASS_DEFINE( PrintObjectConfig, + ((ConfigOptionFloatOrPercent, brim_acceleration)) ((ConfigOptionBool, brim_inside_holes)) ((ConfigOptionFloat, brim_width)) ((ConfigOptionFloat, brim_width_interior)) @@ -718,17 +719,23 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, brim_speed)) //((ConfigOptionEnum, brim_type)) ((ConfigOptionBool, clip_multipart_objects)) + ((ConfigOptionString, object_gcode)) ((ConfigOptionBool, dont_support_bridges)) ((ConfigOptionPercent, external_perimeter_cut_corners)) //((ConfigOptionBool, exact_last_layer_height)) ((ConfigOptionFloatOrPercent, extrusion_width)) ((ConfigOptionFloatOrPercent, extrusion_spacing)) + ((ConfigOptionBool, fill_angle_follow_model)) + ((ConfigOptionFloatOrPercent, first_layer_acceleration)) ((ConfigOptionFloatOrPercent, first_layer_acceleration_over_raft)) ((ConfigOptionFloatOrPercent, first_layer_height)) ((ConfigOptionFloatOrPercent, first_layer_extrusion_width)) ((ConfigOptionFloatOrPercent, first_layer_extrusion_spacing)) + ((ConfigOptionFloatOrPercent, first_layer_infill_speed)) + ((ConfigOptionFloat, first_layer_min_speed)) ((ConfigOptionFloat, first_layer_size_compensation)) /* elefant_foot_compensation */ ((ConfigOptionInt, first_layer_size_compensation_layers)) + ((ConfigOptionFloatOrPercent, first_layer_speed)) ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) ((ConfigOptionFloat, hole_size_compensation)) ((ConfigOptionFloat, hole_size_threshold)) @@ -815,6 +822,7 @@ PRINT_CONFIG_CLASS_DEFINE( PrintRegionConfig, ((ConfigOptionBool, avoid_crossing_top)) + ((ConfigOptionFloatOrPercent, bridge_acceleration)) ((ConfigOptionFloat, bridge_angle)) ((ConfigOptionEnum, bridge_fill_pattern)) ((ConfigOptionEnum, bridge_type)) @@ -827,15 +835,16 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, bottom_fill_pattern)) ((ConfigOptionFloatOrPercent, bridged_infill_margin)) ((ConfigOptionFloatOrPercent, bridge_speed)) - ((ConfigOptionFloatOrPercent, bridge_speed_internal)) ((ConfigOptionFloat, curve_smoothing_precision)) ((ConfigOptionFloat, curve_smoothing_cutoff_dist)) ((ConfigOptionFloat, curve_smoothing_angle_convex)) ((ConfigOptionFloat, curve_smoothing_angle_concave)) + ((ConfigOptionFloatOrPercent, default_acceleration)) ((ConfigOptionFloatOrPercent, default_speed)) ((ConfigOptionBool, ensure_vertical_shell_thickness)) ((ConfigOptionBool, enforce_full_fill_volume)) ((ConfigOptionFloatOrPercent, external_infill_margin)) + ((ConfigOptionFloatOrPercent, external_perimeter_acceleration)) ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width)) ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_spacing)) ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_change_odd_layers)) @@ -865,6 +874,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPercent, fill_top_flow_ratio)) ((ConfigOptionPercent, fill_smooth_distribution)) ((ConfigOptionFloatOrPercent, fill_smooth_width)) + ((ConfigOptionFloatOrPercent, gap_fill_acceleration)) ((ConfigOptionBool, gap_fill_enabled)) ((ConfigOptionFloatOrPercent, gap_fill_extension)) ((ConfigOptionPercent, gap_fill_flow_match_perimeter)) @@ -880,6 +890,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, hole_to_polyhole)) ((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold)) ((ConfigOptionBool, hole_to_polyhole_twisted)) + ((ConfigOptionFloatOrPercent, infill_acceleration)) ((ConfigOptionInt, infill_extruder)) ((ConfigOptionFloatOrPercent, infill_extrusion_width)) ((ConfigOptionFloatOrPercent, infill_extrusion_spacing)) @@ -895,8 +906,11 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, infill_dense)) ((ConfigOptionEnum, infill_dense_algo)) ((ConfigOptionBool, infill_first)) + ((ConfigOptionFloatOrPercent, internal_bridge_acceleration)) + ((ConfigOptionFloatOrPercent, internal_bridge_speed)) // Ironing options ((ConfigOptionBool, ironing)) + ((ConfigOptionFloatOrPercent, ironing_acceleration)) ((ConfigOptionFloat, ironing_angle)) ((ConfigOptionEnum, ironing_type)) ((ConfigOptionPercent, ironing_flowrate)) @@ -909,6 +923,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, milling_speed)) ((ConfigOptionFloatOrPercent, min_width_top_surface)) // Detect bridging perimeters + ((ConfigOptionFloatOrPercent, overhangs_acceleration)) ((ConfigOptionFloatOrPercent, overhangs_max_slope)) ((ConfigOptionFloat, overhangs_bridge_threshold)) ((ConfigOptionInt, overhangs_bridge_upper_layers)) @@ -919,6 +934,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, overhangs_reverse)) ((ConfigOptionFloatOrPercent, overhangs_reverse_threshold)) ((ConfigOptionEnum, no_perimeter_unsupported_algo)) + ((ConfigOptionFloatOrPercent, perimeter_acceleration)) ((ConfigOptionBool, perimeter_round_corners)) ((ConfigOptionInt, perimeter_extruder)) ((ConfigOptionFloatOrPercent, perimeter_extrusion_width)) @@ -935,10 +951,14 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPercent, print_extrusion_multiplier)) ((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)) ((ConfigOptionEnum, solid_fill_pattern)) + ((ConfigOptionFloatOrPercent, solid_infill_acceleration)) ((ConfigOptionFloat, solid_infill_below_area)) ((ConfigOptionFloat, solid_infill_below_layer_area)) ((ConfigOptionFloatOrPercent, solid_infill_below_width)) @@ -955,6 +975,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPercent, thin_perimeters)) ((ConfigOptionPercent, thin_perimeters_all)) ((ConfigOptionBool, thin_walls)) + ((ConfigOptionFloatOrPercent, thin_walls_acceleration)) ((ConfigOptionFloatOrPercent, thin_walls_min_width)) ((ConfigOptionFloatOrPercent, thin_walls_overlap)) ((ConfigOptionFloatOrPercent, thin_walls_speed)) @@ -963,10 +984,12 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, top_infill_extrusion_spacing)) ((ConfigOptionInt, top_solid_layers)) ((ConfigOptionFloat, top_solid_min_thickness)) + ((ConfigOptionFloatOrPercent, top_solid_infill_acceleration)) ((ConfigOptionPercent, top_solid_infill_overlap)) ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) + ((ConfigOptionFloatOrPercent, travel_acceleration)) + ((ConfigOptionBool, travel_deceleration_use_target)) ((ConfigOptionBool, wipe_into_infill)) - ) PRINT_CONFIG_CLASS_DEFINE( @@ -1032,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)) @@ -1171,12 +1194,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) ((ConfigOptionPoints, bed_shape)) ((ConfigOptionInts, bed_temperature)) - ((ConfigOptionFloatOrPercent, bridge_acceleration)) - ((ConfigOptionFloatOrPercent, bridge_internal_acceleration)) ((ConfigOptionInts, bridge_fan_speed)) - ((ConfigOptionInts, bridge_internal_fan_speed)) ((ConfigOptionFloatOrPercent, bridge_precision)) - ((ConfigOptionFloatOrPercent, brim_acceleration)) ((ConfigOptionInts, chamber_temperature)) ((ConfigOptionBool, complete_objects)) ((ConfigOptionFloat, parallel_objects_step)) @@ -1185,13 +1204,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionEnum, complete_objects_sort)) ((ConfigOptionFloats, colorprint_heights)) //((ConfigOptionBools, cooling)) - ((ConfigOptionFloatOrPercent, default_acceleration)) ((ConfigOptionInts, disable_fan_first_layers)) ((ConfigOptionInts, default_fan_speed)) ((ConfigOptionEnum, draft_shield)) ((ConfigOptionFloat, duplicate_distance)) ((ConfigOptionBool, enforce_retract_first_layer)) - ((ConfigOptionFloatOrPercent, external_perimeter_acceleration)) ((ConfigOptionInts, external_perimeter_fan_speed)) ((ConfigOptionFloat, extruder_clearance_height)) ((ConfigOptionFloat, extruder_clearance_radius)) @@ -1204,18 +1221,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionStrings, filament_notes)) ((ConfigOptionPercents, filament_max_overlap)) ((ConfigOptionPercents, filament_shrink)) - ((ConfigOptionFloatOrPercent, first_layer_acceleration)) ((ConfigOptionInts, first_layer_bed_temperature)) - ((ConfigOptionFloatOrPercent, first_layer_speed)) - ((ConfigOptionFloatOrPercent, first_layer_infill_speed)) - ((ConfigOptionFloat, first_layer_min_speed)) ((ConfigOptionInts, first_layer_temperature)) ((ConfigOptionInts, full_fan_speed_layer)) - ((ConfigOptionFloatOrPercent, gap_fill_acceleration)) ((ConfigOptionInts, gap_fill_fan_speed)) - ((ConfigOptionFloatOrPercent, infill_acceleration)) ((ConfigOptionInts, infill_fan_speed)) - ((ConfigOptionFloatOrPercent, ironing_acceleration)) + ((ConfigOptionInts, internal_bridge_fan_speed)) ((ConfigOptionFloat, lift_min)) ((ConfigOptionInts, max_fan_speed)) ((ConfigOptionFloatsOrPercents, max_layer_height)) @@ -1224,8 +1235,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloats, milling_diameter)) ((ConfigOptionStrings, milling_toolchange_end_gcode)) ((ConfigOptionStrings, milling_toolchange_start_gcode)) - //((ConfigOptionPoints, milling_offset)) - //((ConfigOptionFloats, milling_z_offset)) //((ConfigOptionInts, min_fan_speed)) // now fan_printer_min_speed ((ConfigOptionFloatsOrPercents, min_layer_height)) ((ConfigOptionFloats, min_print_speed)) @@ -1235,11 +1244,10 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionBool, only_retract_when_crossing_perimeters)) ((ConfigOptionBool, ooze_prevention)) ((ConfigOptionString, output_filename_format)) - ((ConfigOptionFloatOrPercent, overhangs_acceleration)) ((ConfigOptionInts, overhangs_fan_speed)) - ((ConfigOptionFloatOrPercent, perimeter_acceleration)) ((ConfigOptionInts, perimeter_fan_speed)) ((ConfigOptionStrings, post_process)) + ((ConfigOptionPoint, priming_position)) ((ConfigOptionString, print_custom_variables)) ((ConfigOptionString, printer_custom_variables)) ((ConfigOptionString, printer_model)) @@ -1259,7 +1267,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInt, skirts)) ((ConfigOptionFloats, slowdown_below_layer_time)) ((ConfigOptionBool, spiral_vase)) - ((ConfigOptionFloatOrPercent, solid_infill_acceleration)) ((ConfigOptionInts, solid_infill_fan_speed)) ((ConfigOptionInt, standby_temperature_delta)) ((ConfigOptionFloatOrPercent, support_material_acceleration)) @@ -1267,7 +1274,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloatOrPercent, support_material_interface_acceleration)) ((ConfigOptionInts, support_material_interface_fan_speed)) ((ConfigOptionInts, temperature)) - ((ConfigOptionFloatOrPercent, thin_walls_acceleration)) ((ConfigOptionInt, threads)) ((ConfigOptionPoints, thumbnails)) ((ConfigOptionString, thumbnails_color)) @@ -1281,9 +1287,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, time_start_gcode)) ((ConfigOptionFloat, time_toolchange)) ((ConfigOptionInts, top_fan_speed)) - ((ConfigOptionFloatOrPercent, top_solid_infill_acceleration)) - ((ConfigOptionFloatOrPercent, travel_acceleration)) - ((ConfigOptionBool, travel_deceleration_use_target)) ((ConfigOptionBools, wipe)) ((ConfigOptionBool, wipe_tower)) ((ConfigOptionFloatOrPercent, wipe_tower_brim_width)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8d06e30f780..20fc7fc317a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -150,16 +150,19 @@ namespace Slic3r { // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing // hollow objects - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { - const PrintRegion ®ion = this->printing_region(region_id); + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { + const PrintRegion ®ion = this->printing_region(region_id); if (!region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) continue; - + // use an antomic idx instead of the range, to avoid a thread being very late because it's on the difficult layers. + std::atomic_size_t next_layer_idx(0); BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size() - 1), - [this, ®ion, region_id](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + [this, ®ion, region_id, &next_layer_idx](const tbb::blocked_range& range) { + //TODO: find a better wya to just fire the threads. + //for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + for (size_t layer_idx = next_layer_idx++; layer_idx < m_layers.size() - 1; layer_idx = next_layer_idx++) { m_print->throw_if_canceled(); LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id); @@ -214,10 +217,14 @@ namespace Slic3r { } BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start"; + // use an antomic idx instead of the range, to avoid a thread being very late because it's on the difficult layers. + //TODO: sort the layers by difficulty (difficult first) (number of points, region, surfaces, .. ?) (and use parallel_for_each( list.begin(), list.end(), ApplyFoo() );) + std::atomic_size_t next_layer_idx(0); tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &atomic_count, nb_layers_update](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + [this, &atomic_count, nb_layers_update, &next_layer_idx](const tbb::blocked_range& range) { + //for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + for (size_t layer_idx = next_layer_idx++; layer_idx < m_layers.size(); layer_idx = next_layer_idx++) { std::chrono::time_point start_make_perimeter = std::chrono::system_clock::now(); m_print->throw_if_canceled(); m_layers[layer_idx]->make_perimeters(); @@ -275,6 +282,7 @@ namespace Slic3r { // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. + m_print->set_status( 0, L("Detect surfaces types"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->detect_surfaces_type(); m_print->throw_if_canceled(); @@ -282,11 +290,17 @@ namespace Slic3r { // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); - for (auto* layer : m_layers) - for (auto* region : layer->m_regions) { + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { + Layer *layer = m_layers[layer_idx]; + m_print->set_status(int(100 * layer_idx / m_layers.size()), + L("Prepare fill surfaces: layer %s / %s"), + {std::to_string(layer_idx), std::to_string(m_layers.size())}, + PrintBase::SlicingStatus::SECONDARY_STATE); + for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); m_print->throw_if_canceled(); } + } // solid_infill_below_area has just beeing applied at the end of prepare_fill_surfaces() apply_solid_infill_below_layer_area(); @@ -300,10 +314,12 @@ namespace Slic3r { // 3) Clip the internal surfaces by the grown top/bottom surfaces. // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. + m_print->set_status( 20, L("Process external surfaces"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->process_external_surfaces(); m_print->throw_if_canceled(); // Add solid fills to ensure the shell vertical thickness. + m_print->set_status( 40, L("Discover shells"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->discover_vertical_shells(); m_print->throw_if_canceled(); @@ -329,6 +345,7 @@ namespace Slic3r { m_print->throw_if_canceled(); //as there is some too thin solid surface, please deleted them and merge all of the surfacesthat are contigous. + m_print->set_status( 55, L("Clean surfaces"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->clean_surfaces(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -347,6 +364,7 @@ namespace Slic3r { //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. + m_print->set_status( 70, L("Clip surfaces"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->clip_fill_surfaces(); m_print->throw_if_canceled(); @@ -382,6 +400,7 @@ namespace Slic3r { m_print->throw_if_canceled(); // combine fill surfaces to honor the "infill every N layers" option + m_print->set_status( 85, L("Combine infill"), {}, PrintBase::SlicingStatus::SECONDARY_STATE); this->combine_infill(); m_print->throw_if_canceled(); @@ -444,7 +463,6 @@ namespace Slic3r { { // prerequisites this->prepare_infill(); - m_print->set_status(35, L("Infilling layers")); m_print->set_status(0, L("Infilling layer %s / %s"), { std::to_string(0), std::to_string(m_layers.size()) }, PrintBase::SlicingStatus::SECONDARY_STATE); if (this->set_started(posInfill)) { @@ -456,10 +474,14 @@ namespace Slic3r { const int nb_layers_update = std::max(1, (int)m_layers.size() / 20); BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; + // use an antomic idx instead of the range, to avoid a thread being very late because it's on the difficult layers. + std::atomic_size_t next_layer_idx(0); tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator, &atomic_count, nb_layers_update](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator, &atomic_count, nb_layers_update, &next_layer_idx] + (const tbb::blocked_range& range) { + //for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + for (size_t layer_idx = next_layer_idx++; layer_idx < m_layers.size(); layer_idx = next_layer_idx++) { std::chrono::time_point start_make_fill = std::chrono::system_clock::now(); m_print->throw_if_canceled(); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get()); @@ -684,7 +706,7 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() // Called by Print::apply(). // This method only accepts PrintObjectConfig and PrintRegionConfig option keys. -bool PrintObject::invalidate_state_by_config_options( + bool PrintObject::invalidate_state_by_config_options( const ConfigOptionResolver &old_config, const ConfigOptionResolver &new_config, const std::vector &opt_keys) { if (opt_keys.empty()) @@ -694,13 +716,13 @@ bool PrintObject::invalidate_state_by_config_options( bool invalidated = false; for (const t_config_option_key& opt_key : opt_keys) { if ( - opt_key == "gap_fill_enabled" - || opt_key == "gap_fill_extension" + opt_key == "gap_fill_extension" || opt_key == "gap_fill_last" || opt_key == "gap_fill_max_width" || opt_key == "gap_fill_min_area" || opt_key == "gap_fill_min_length" || opt_key == "gap_fill_min_width" + || opt_key == "min_width_top_surface" || opt_key == "only_one_perimeter_first_layer" || opt_key == "only_one_perimeter_top" || opt_key == "only_one_perimeter_top_other_algo" @@ -709,22 +731,21 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "overhangs_reverse" || opt_key == "overhangs_reverse_threshold" || opt_key == "overhangs_speed_enforce" + || opt_key == "perimeter_bonding" || opt_key == "perimeter_extrusion_change_odd_layers" || opt_key == "perimeter_extrusion_spacing" || opt_key == "perimeter_extrusion_width" || opt_key == "perimeter_reverse" - || opt_key == "infill_overlap" + || opt_key == "perimeter_round_corners" || opt_key == "thin_perimeters" || opt_key == "thin_perimeters_all" - || opt_key == "thin_walls" + || opt_key == "thin_walls_merge" || opt_key == "thin_walls_min_width" || opt_key == "thin_walls_overlap" || opt_key == "external_perimeters_first" || opt_key == "external_perimeters_hole" || opt_key == "external_perimeters_nothole" || opt_key == "external_perimeter_extrusion_change_odd_layers" - || opt_key == "external_perimeter_extrusion_spacing" - || opt_key == "external_perimeter_extrusion_width" || opt_key == "external_perimeters_vase" || opt_key == "perimeter_loop" || opt_key == "perimeter_loop_seam") { @@ -751,34 +772,49 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSlice); steps.emplace_back(posPerimeters); } else if ( - opt_key == "layer_height" - || opt_key == "first_layer_height" - || opt_key == "mmu_segmented_region_max_width" // || opt_key == "exact_last_layer_height" - || opt_key == "raft_contact_distance" - || opt_key == "raft_interface_layer_height" - || opt_key == "raft_layers" - || opt_key == "raft_layer_height" + opt_key == "bridge_type" || opt_key == "clip_multipart_objects" + || opt_key == "curve_smoothing_angle_concave" + || opt_key == "curve_smoothing_angle_convex" + || opt_key == "curve_smoothing_cutoff_dist" + || opt_key == "curve_smoothing_precision" + || opt_key == "dont_support_bridges" + || opt_key == "elephant_foot_min_width" //sla ? || opt_key == "first_layer_size_compensation" || opt_key == "first_layer_size_compensation_layers" - || opt_key == "elephant_foot_min_width" - || opt_key == "dont_support_bridges" + || opt_key == "first_layer_height" + || opt_key == "hole_size_compensation" + || opt_key == "hole_size_threshold" + || opt_key == "hole_to_polyhole" + || opt_key == "hole_to_polyhole_threshold" + || opt_key == "hole_to_polyhole_twisted" + || opt_key == "layer_height" + || opt_key == "min_bead_width" + || opt_key == "min_feature_size" + || opt_key == "mmu_segmented_region_max_width" + || opt_key == "model_precision" || opt_key == "overhangs_max_slope" || opt_key == "overhangs_bridge_threshold" || opt_key == "overhangs_bridge_upper_layers" + || opt_key == "raft_contact_distance" + || opt_key == "raft_interface_layer_height" + || opt_key == "raft_layers" + || opt_key == "raft_layer_height" + || opt_key == "perimeter_generator" || opt_key == "slice_closing_radius" || opt_key == "slicing_mode" || opt_key == "support_material_contact_distance_type" - || opt_key == "support_material_contact_distance_top" - || opt_key == "support_material_contact_distance_bottom" + || opt_key == "support_material_contact_distance" + || opt_key == "support_material_bottom_contact_distance" || opt_key == "support_material_interface_layer_height" || opt_key == "support_material_layer_height" - || opt_key == "xy_size_compensation" - || opt_key == "hole_size_compensation" - || opt_key == "hole_size_threshold" - || opt_key == "hole_to_polyhole" - || opt_key == "hole_to_polyhole_threshold") { + || opt_key == "wall_transition_length" + || opt_key == "wall_transition_filter_deviation" + || opt_key == "wall_transition_angle" + || opt_key == "wall_distribution_count" + || opt_key == "xy_inner_size_compensation" + || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { steps.emplace_back(posSupportMaterial); @@ -800,17 +836,15 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_enforce_layers" || opt_key == "support_material_extruder" || opt_key == "support_material_extrusion_width" - || opt_key == "support_material_bottom_contact_distance" || opt_key == "support_material_interface_layers" || opt_key == "support_material_bottom_interface_layers" || opt_key == "support_material_interface_angle" || opt_key == "support_material_interface_angle_increment" - || opt_key == "support_material_interface_pattern" || opt_key == "support_material_interface_contact_loops" || opt_key == "support_material_interface_extruder" + || opt_key == "support_material_interface_pattern" || opt_key == "support_material_interface_spacing" || opt_key == "support_material_pattern" - || opt_key == "support_material_interface_pattern" || opt_key == "support_material_style" || opt_key == "support_material_xy_spacing" || opt_key == "support_material_spacing" @@ -821,8 +855,7 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSupportMaterial); } else if (opt_key == "bottom_solid_layers") { steps.emplace_back(posPrepareInfill); - if (m_print->config().spiral_vase - || opt_key == "z_step") { + if (m_print->config().spiral_vase) { // Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again. // Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528. steps.emplace_back(posSlice); @@ -838,9 +871,10 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_every_layers" || opt_key == "infill_dense" || opt_key == "infill_dense_algo" - || opt_key == "infill_not_connected" || opt_key == "infill_only_where_needed" + || opt_key == "ironing" || opt_key == "ironing_type" + || opt_key == "over_bridge_flow_ratio" || opt_key == "solid_infill_below_area" || opt_key == "solid_infill_below_layer_area" || opt_key == "solid_infill_below_width" @@ -851,14 +885,15 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "top_solid_min_thickness") { steps.emplace_back(posPrepareInfill); } else if ( - opt_key == "top_fill_pattern" - || opt_key == "bottom_fill_pattern" + opt_key == "bottom_fill_pattern" || opt_key == "bridge_fill_pattern" - || opt_key == "solid_fill_pattern" + || opt_key == "bridge_overlap" + || opt_key == "bridge_overlap_min" || opt_key == "enforce_full_fill_volume" || opt_key == "fill_aligned_z" || opt_key == "fill_angle" || opt_key == "fill_angle_cross" + || opt_key == "fill_angle_follow_model" || opt_key == "fill_angle_increment" || opt_key == "fill_angle_template" || opt_key == "fill_top_flow_ratio" @@ -871,17 +906,20 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_connection_bridge" || opt_key == "infill_connection_solid" || opt_key == "infill_connection_top" - || opt_key == "seam_gap" - || opt_key == "seam_gap_external" + || opt_key == "ironing_angle" + || opt_key == "ironing_flowrate" + || opt_key == "ironing_spacing" + || opt_key == "solid_fill_pattern" + || opt_key == "top_fill_pattern" || opt_key == "top_infill_extrusion_spacing" || opt_key == "top_infill_extrusion_width" ) { steps.emplace_back(posInfill); - } else if (opt_key == "fill_pattern") { - steps.emplace_back(posInfill); + } else if (opt_key == "fill_pattern") { + steps.emplace_back(posInfill); - const auto *old_fill_pattern = old_config.option>(opt_key); - const auto *new_fill_pattern = new_config.option>(opt_key); - assert(old_fill_pattern && new_fill_pattern); + const auto *old_fill_pattern = old_config.option>(opt_key); + const auto *new_fill_pattern = new_config.option>(opt_key); + assert(old_fill_pattern && new_fill_pattern); // We need to recalculate infill surfaces when infill_only_where_needed is enabled, and we are switching from // the Lightning infill to another infill or vice versa. if (m_config.infill_only_where_needed && (new_fill_pattern->value == ipLightning || old_fill_pattern->value == ipLightning)) @@ -903,37 +941,35 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "bridged_infill_margin" || opt_key == "extra_perimeters" || opt_key == "extra_perimeters_odd_layers" + || opt_key == "extra_perimeters_overhangs" || opt_key == "external_infill_margin" || opt_key == "external_perimeter_overlap" || opt_key == "gap_fill_overlap" + || opt_key == "infill_overlap" || opt_key == "no_perimeter_unsupported_algo" - || opt_key == "filament_max_overlap" || opt_key == "perimeters" || opt_key == "perimeters_hole" || opt_key == "perimeter_overlap" || opt_key == "solid_infill_extrusion_change_odd_layers" || opt_key == "solid_infill_extrusion_spacing" - || opt_key == "solid_infill_extrusion_width") { + || opt_key == "solid_infill_extrusion_width" + || opt_key == "solid_infill_overlap" + || opt_key == "top_solid_infill_overlap") { steps.emplace_back(posPerimeters); steps.emplace_back(posPrepareInfill); - } else if (opt_key == "solid_infill_extrusion_change_odd_layers" - || opt_key == "solid_infill_extrusion_spacing" - || opt_key == "solid_infill_extrusion_width") { - // This value is used for calculating perimeter - infill overlap, thus perimeters need to be recalculated. - steps.emplace_back(posPerimeters); - steps.emplace_back(posPrepareInfill); } else if ( - opt_key == "external_perimeter_extrusion_width" - || opt_key == "perimeter_extruder" - || opt_key == "fuzzy_skin" - || opt_key == "fuzzy_skin_thickness" - || opt_key == "fuzzy_skin_point_dist" - || opt_key == "overhangs" - || opt_key == "thin_walls" - || opt_key == "thick_bridges") { + opt_key == "external_perimeter_extrusion_width" + || opt_key == "external_perimeter_extrusion_spacing" + || opt_key == "perimeter_extruder" + || opt_key == "fuzzy_skin" + || opt_key == "fuzzy_skin_thickness" + || opt_key == "fuzzy_skin_point_dist" + || opt_key == "thin_walls") { steps.emplace_back(posPerimeters); steps.emplace_back(posSupportMaterial); } else if (opt_key == "bridge_flow_ratio" + || opt_key == "extrusion_spacing" + || opt_key == "extrusion_width" || opt_key == "first_layer_extrusion_spacing" || opt_key == "first_layer_extrusion_width") { //if (m_config.support_material_contact_distance > 0.) { @@ -943,28 +979,51 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posInfill); steps.emplace_back(posSupportMaterial); //} - } else if ( - opt_key == "perimeter_generator" - || opt_key == "wall_transition_length" - || opt_key == "wall_transition_filter_deviation" - || opt_key == "wall_transition_angle" - || opt_key == "wall_distribution_count" - || opt_key == "min_feature_size" - || opt_key == "min_bead_width") { - steps.emplace_back(posSlice); } else if ( opt_key == "avoid_crossing_top" + || opt_key == "bridge_acceleration" || opt_key == "bridge_speed" - || opt_key == "bridge_speed_internal" + || opt_key == "brim_acceleration" + || opt_key == "brim_speed" || opt_key == "external_perimeter_speed" - || opt_key == "external_perimeters_vase" + || opt_key == "default_acceleration" + || opt_key == "default_speed" + || opt_key == "external_perimeter_acceleration" + || opt_key == "external_perimeter_cut_corners" + || opt_key == "first_layer_acceleration" + || opt_key == "first_layer_acceleration_over_raft" + || opt_key == "first_layer_flow_ratio" + || opt_key == "first_layer_infill_speed" + || opt_key == "first_layer_min_speed" + || opt_key == "first_layer_speed" + || opt_key == "first_layer_speed_over_raft" + || opt_key == "gap_fill_acceleration" + || opt_key == "gap_fill_flow_match_perimeter" || opt_key == "gap_fill_speed" + || opt_key == "infill_acceleration" || opt_key == "infill_speed" + || opt_key == "internal_bridge_acceleration" + || opt_key == "internal_bridge_speed" + || opt_key == "ironing_acceleration" + || opt_key == "ironing_speed" + || opt_key == "milling_after_z" + || opt_key == "milling_extra_size" + || opt_key == "milling_post_process" + || opt_key == "milling_speed" + || opt_key == "object_gcode" + || opt_key == "overhangs_acceleration" || opt_key == "overhangs_speed" + || opt_key == "perimeter_acceleration" || opt_key == "perimeter_speed" + || opt_key == "print_extrusion_multiplier" + || opt_key == "print_first_layer_temperature" + || opt_key == "print_retract_length" + || opt_key == "print_retract_lift" + || opt_key == "print_temperature" + || opt_key == "region_gcode" || opt_key == "seam_position" - || opt_key == "seam_preferred_direction" - || opt_key == "seam_preferred_direction_jitter" + //|| opt_key == "seam_preferred_direction" + //|| opt_key == "seam_preferred_direction_jitter" || opt_key == "seam_angle_cost" || opt_key == "seam_notch_all" || opt_key == "seam_notch_angle" @@ -972,17 +1031,25 @@ bool PrintObject::invalidate_state_by_config_options( || 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" + || opt_key == "solid_infill_acceleration" || opt_key == "solid_infill_speed" || opt_key == "support_material_interface_speed" || opt_key == "support_material_speed" + || opt_key == "thin_walls_acceleration" || opt_key == "thin_walls_speed" - || opt_key == "top_solid_infill_speed") { + || opt_key == "top_solid_infill_acceleration" + || opt_key == "top_solid_infill_speed" + || opt_key == "travel_acceleration" + || opt_key == "travel_deceleration_use_target") { invalidated |= m_print->invalidate_step(psGCodeExport); } else if ( - opt_key == "wipe_into_infill" + opt_key == "infill_first" + || opt_key == "wipe_into_infill" || opt_key == "wipe_into_objects") { invalidated |= m_print->invalidate_step(psWipeTower); invalidated |= m_print->invalidate_step(psGCodeExport); @@ -993,7 +1060,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "brim_ears_max_angle" || opt_key == "brim_ears_pattern" || opt_key == "brim_per_object" - || opt_key == "brim_separation") { + || opt_key == "brim_separation" + || opt_key == "brim_type") { invalidated |= m_print->invalidate_step(psSkirtBrim); // Brim is printed below supports, support invalidates brim and skirt. steps.emplace_back(posSupportMaterial); @@ -1273,7 +1341,7 @@ bool PrintObject::invalidate_state_by_config_options( //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); @@ -2594,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/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 81241b45238..49bccb9d634 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -38,6 +38,8 @@ set(SLIC3R_GUI_SOURCES GUI/CalibrationTempDialog.hpp GUI/CalibrationRetractionDialog.cpp GUI/CalibrationRetractionDialog.hpp + GUI/CalibrationPressureAdvDialog.cpp + GUI/CalibrationPressureAdvDialog.hpp GUI/ConfigSnapshotDialog.cpp GUI/ConfigSnapshotDialog.hpp GUI/CreateMMUTiledCanvas.cpp 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 new file mode 100644 index 00000000000..09c0831f81c --- /dev/null +++ b/src/slic3r/GUI/CalibrationPressureAdvDialog.cpp @@ -0,0 +1,823 @@ +#include "CalibrationPressureAdvDialog.hpp" +#include "I18N.hpp" +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/CustomGCode.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/Utils.hpp" +#include "GLCanvas3D.hpp" +#include "GUI.hpp" +#include "GUI_ObjectList.hpp" +#include "Plater.hpp" +#include "Tab.hpp" +#include +#include +#include +#include "wxExtensions.hpp" +#include "Jobs/ArrangeJob.hpp" +#include + +#pragma optimize("", off) +#if ENABLE_SCROLLABLE +static wxSize get_screen_size(wxWindow* window) +{ + const auto idx = wxDisplay::GetFromWindow(window); + wxDisplay display(idx != wxNOT_FOUND ? idx : 0u); + return display.GetClientArea().GetSize(); +} +#endif // ENABLE_SCROLLABLE + +namespace Slic3r { +namespace GUI { + +void CalibrationPressureAdvDialog::create_buttons(wxStdDialogButtonSizer* buttons){ + + const DynamicPrintConfig* printer_config = this->gui_app->get_tab(Preset::TYPE_PRINTER)->get_config(); + GCodeFlavor flavor = printer_config->option>("gcode_flavor")->value; //there a better way to only load the flavor ? + + wxString choices_first_layerPA[] = { + "0.025", + "0.030", + "0.035", + "0.040", + "0.045", + "0.050" + }; + firstPa = new wxComboBox(this, wxID_ANY, wxString{ "0.040" }, wxDefaultPosition, wxDefaultSize, 6, choices_first_layerPA); + firstPa->SetToolTip(_L("Select the first layer PA value to be used for the first layer only.")); + firstPa->SetSelection(3);// starting at 0! + + + wxString choices_start_PA[] = { + "0.0", + "0.010", + "0.020", + "0.030", + "0.040", + "0.050" + }; + startPa = new wxComboBox(this, wxID_ANY, wxString{ "0.0" }, wxDefaultPosition, wxDefaultSize, 6, choices_start_PA); + startPa->SetToolTip(_L("Select the starting PA value to be used.")); + startPa->SetSelection(0); + + wxString choices_end_PA[] = { + "0.10", + "0.20", + "0.30", + "0.40", + "0.50", + "0.60", + "0.70", + "0.80", + "0.90", + "1.00" + }; + endPa = new wxComboBox(this, wxID_ANY, wxString{ "0.10" }, wxDefaultPosition, wxDefaultSize, 10, choices_end_PA); + endPa->SetToolTip(_L("Select the ending PA value to be used.")); + endPa->SetSelection(0); + + wxString choices_increment_PA[] = { + "0.0010",///1000 hits + "0.0025", + "0.0035", + "0.005", //200 hits + "0.006", + "0.007", + "0.01",//100 hits + "0.1"//10 hits + }; + paIncrement = new wxComboBox(this, wxID_ANY, wxString{ "0.0025" }, wxDefaultPosition, wxDefaultSize, 8, choices_increment_PA); + paIncrement->SetToolTip(_L("Select the PA increment amount.")); + paIncrement->SetSelection(1); + + wxString choices_extrusion_role[] = { + "InternalInfill", + "BridgeInfill", + "ExternalPerimeter", + "GapFill", + "InternalBridgeInfill", + "Ironing", + "OverhangPerimeter", + "Perimeter", + "SolidInfill", + "SupportMaterial", + "SupportMaterialInterface", + "ThinWall", + "TopSolidInfill", + "FirstLayer", + // "Verify"//if this selected, disable/hide other buttons? + // 'verify' this choice will require the user to manually add in the PA numbers with the GUI from their realworld tests. + // the code will then load a 90_bend for each ER role, and give each bend seperate ER speed/width/ect values + // when printed and user added in the PA numbers correctly. it should make it easy to spot what ER roles need adjusting. + //TODO: once the main pressure advance feature is added, this can pull that values and insert here to save the manual adding in the numbers. + // supermerill: i don't understand, so I deactivated the feature for now. + }; + erPa = new wxComboBox(this, wxID_ANY, wxString{ "InternalInfill" }, wxDefaultPosition, wxDefaultSize, 14, choices_extrusion_role); + erPa->SetToolTip(_L("Select the extrusion role you want to generate a calibration for")); + erPa->SetSelection(0); + + + wxString number_of_runs[] = {"1","2","3","4","5"}; + nbRuns = new wxComboBox(this, wxID_ANY, wxString{ "1" }, wxDefaultPosition, wxDefaultSize, 5, number_of_runs); + nbRuns->SetToolTip(_L("Select the number of tests to generate, max 2 is reccomended due to bed size limits")); + nbRuns->SetSelection(0); + + enableST = new wxCheckBox(this, wxID_ANY, _L(""), wxDefaultPosition, wxDefaultSize ); + enableST->SetToolTip(_L("generate smooth time values")); + enableST->SetValue(false); + + // TODO : add another row of boxes for the 2nd/3rd ect of tests to create, user adjust parameters of new row for the 2nd/3rd test + // this will allow multi plate PA tests to be run + + + std::string prefix = (gcfMarlinFirmware == flavor || gcfMarlinLegacy == flavor) ? " LA " : ((gcfKlipper == flavor || gcfRepRap == flavor) ? " PA " : "unsupported firmware type"); + + if (prefix != "unsupported firmware type"){ + wxBoxSizer* vertical =new wxBoxSizer(wxVERTICAL); + wxBoxSizer* hsizer_common =new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hsizer_pa =new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* hsizer_speed =new wxBoxSizer(wxHORIZONTAL); + vertical->Add(hsizer_common); + vertical->Add(hsizer_pa); + vertical->Add(hsizer_speed); + + hsizer_common->Add(new wxStaticText(this, wxID_ANY, _L("Number of tests: "))); + hsizer_common->Add(nbRuns); + + hsizer_pa->Add(new wxStaticText(this, wxID_ANY, _L("First Layers" + prefix + "value: "))); + hsizer_pa->Add(firstPa); + hsizer_pa->AddSpacer(15); + hsizer_pa->Add(new wxStaticText(this, wxID_ANY, _L("Starting" + prefix + "value: "))); + hsizer_pa->Add(startPa); + hsizer_pa->AddSpacer(15); + hsizer_pa->Add(new wxStaticText(this, wxID_ANY, _L("Ending" + prefix + "value: "))); + hsizer_pa->Add(endPa); + hsizer_pa->AddSpacer(15); + hsizer_pa->Add(new wxStaticText(this, wxID_ANY, _L(prefix + "increments: "))); + hsizer_pa->Add(paIncrement); + + hsizer_speed->Add(new wxStaticText(this, wxID_ANY, _L("Extrusion role: "))); + hsizer_speed->Add(erPa); + if (gcfKlipper == flavor) { + hsizer_speed->AddSpacer(15); + hsizer_speed->Add(new wxStaticText(this, wxID_ANY, _L("Smooth time: "))); + hsizer_speed->Add(enableST); + } + hsizer_speed->AddSpacer(25); + + wxButton* bt = new wxButton(this, wxID_FILE1, _L("Generate")); + bt->Bind(wxEVT_BUTTON, &CalibrationPressureAdvDialog::create_geometry, this); + + vertical->Add(bt); + + buttons->Add(vertical); + } else { + buttons->Add(new wxStaticText(this, wxID_ANY, _L(prefix))); + } +} + +void CalibrationPressureAdvDialog::create_geometry(wxCommandEvent& event_args) { + /* + firstPa + startPa + endPa + paIncrement + erPa + enableST + */ + double first_pa, start_pa, end_pa, pa_increment = 0.01; + bool smooth_time = enableST->IsChecked(); + size_t nb_runs = nbRuns->GetSelection(); + nb_runs=nb_runs+1; + first_pa = firstPa->GetValue().ToDouble(&first_pa); + + if (!firstPa->GetValue().ToDouble(&first_pa)) { + first_pa = 0.025; + } + start_pa = startPa->GetValue().ToDouble(&start_pa); + if (!startPa->GetValue().ToDouble(&start_pa)) { + start_pa = 0.0; + } + end_pa = endPa->GetValue().ToDouble(&end_pa); + if (!endPa->GetValue().ToDouble(&end_pa)) { + end_pa = 1.0; + } + pa_increment = paIncrement->GetValue().ToDouble(&pa_increment); + if (!paIncrement->GetValue().ToDouble(&pa_increment)) { + pa_increment = 0.05; + } + + std::string extrusion_role = erPa->GetValue().ToStdString(); + std::string choice_extrusion_role[] = { + "InternalInfill", + "BridgeInfill", + "ExternalPerimeter", + "GapFill", + "InternalBridgeInfill", + "Ironing", + "OverhangPerimeter", + "Perimeter", + "SolidInfill", + "SupportMaterial", + "SupportMaterialInterface", + "ThinWall", + "TopSolidInfill", + "FirstLayer"//i've got added them all right? + }; + + std::unordered_map er_width_ToOptionKey = { + {"InternalInfill", "infill_extrusion_width"}, + //{"BridgeInfill", "placeholder"},//special calc required + {"ExternalPerimeter", "external_perimeter_extrusion_width"}, + //{"GapFill", "placeholder"},//special calc required + //{"InternalBridgeInfill", "placeholder"},//special calc required, TODO:find out where/how this is calculated + {"Ironing", "top_infill_extrusion_width"}, + {"OverhangPerimeter", "overhangs_width"}, + {"Perimeter", "perimeter_extrusion_width"}, + {"SolidInfill", "solid_infill_extrusion_width"}, + {"SupportMaterial", "support_material_extrusion_width"}, + {"SupportMaterialInterface", "support_material_extrusion_width"}, + {"ThinWall", "external_perimeter_extrusion_width"}, + {"TopSolidInfill", "top_infill_extrusion_width"}, + {"FirstLayer", "first_layer_extrusion_width"} + + }; + + std::unordered_map er_accel_ToOptionKey = { + {"InternalInfill", "infill_acceleration"}, + {"BridgeInfill", "bridge_acceleration"}, + {"ExternalPerimeter", "external_perimeter_acceleration"}, + {"GapFill", "gap_fill_acceleration"}, + {"InternalBridgeInfill", "bridge_internal_acceleration"}, + {"Ironing", "ironing_acceleration"}, + {"OverhangPerimeter", "overhangs_acceleration"}, + {"Perimeter", "perimeter_acceleration"}, + {"SolidInfill", "solid_infill_acceleration"}, + {"SupportMaterial", "support_material_acceleration"}, + {"SupportMaterialInterface", "support_material_interface_acceleration"}, + {"ThinWall", "top_solid_infill_acceleration"}, + {"TopSolidInfill", "top_solid_infill_acceleration"}, + {"FirstLayer", "first_layer_acceleration"} + }; + + std::unordered_map er_spacing_ToOptionKey = { + {"InternalInfill", "infill_extrusion_spacing"}, + //{"BridgeInfill", "placeholder"}, + {"ExternalPerimeter", "external_perimeter_extrusion_spacing"}, + //{"GapFill", "placeholder"},//special calc required for commented ones + //{"InternalBridgeInfill", "placeholder"}, + //{"Ironing", "ironing_spacing"}, TOFIX? TYPE: coFloat + {"Ironing", "top_infill_extrusion_spacing"}, + {"OverhangPerimeter", "external_perimeter_extrusion_spacing"}, + {"Perimeter", "perimeter_extrusion_spacing"}, + {"SolidInfill", "solid_infill_extrusion_spacing"}, + {"SupportMaterial", "external_perimeter_extrusion_spacing"}, //TOFIX? TYPE: coFloat + {"SupportMaterialInterface", "external_perimeter_extrusion_spacing"}, //TOFIX? TYPE: coFloat + {"ThinWall", "external_perimeter_extrusion_spacing"}, + {"TopSolidInfill", "top_infill_extrusion_spacing"}, + {"FirstLayer", "first_layer_extrusion_spacing"} + }; + + std::unordered_map er_speed_ToOptionKey = { + {"InternalInfill", "infill_speed"}, + {"BridgeInfill", "bridge_speed"}, + {"ExternalPerimeter", "external_perimeter_speed"}, + {"GapFill", "gap_fill_speed"}, + {"InternalBridgeInfill", "bridge_speed_internal"}, + {"Ironing", "ironing_speed"}, + {"OverhangPerimeter", "overhangs_speed"}, + {"Perimeter", "perimeter_speed"}, + {"SolidInfill", "solid_infill_speed"}, + {"SupportMaterial", "support_material_speed"}, + {"SupportMaterialInterface", "support_material_interface_speed"}, + {"ThinWall", "thin_walls_speed"}, + {"TopSolidInfill", "top_solid_infill_speed"}, + {"FirstLayer", "first_layer_speed"} + }; + +/* +struct ExtrusionSettings {// think a struct is better instead of all the maps ? + std::string extrusionWidth; + std::string acceleration; + std::string speed; +}; + + std::unordered_map extrusionRoleToOptionKey = { + {"InternalInfill", {"infill_extrusion_width", "infill_acceleration", "placeholder"}}, + //{"BridgeInfill", {"placeholder", "bridge_acceleration", "placeholder"}},//special calc required + {"ExternalPerimeter", {"external_perimeter_extrusion_width", "external_perimeter_acceleration"}}, + //{"GapFill", {"placeholder", "gap_fill_acceleration"}},//special calc required + //{"InternalBridgeInfill", {"placeholder", "bridge_internal_acceleration"}},//special calc required + {"Ironing", {"top_infill_extrusion_width", "ironing_acceleration"}}, + {"OverhangPerimeter", {"overhangs_width", "overhangs_acceleration"}}, + {"Perimeter", {"perimeter_extrusion_width", "perimeter_acceleration"}}, + {"SolidInfill", {"solid_infill_extrusion_width", "solid_infill_acceleration"}}, + {"SupportMaterial", {"support_material_extrusion_width", "support_material_acceleration"}}, + {"SupportMaterialInterface", {"support_material_extrusion_width", "support_material_interface_acceleration"}}, + {"ThinWall", {"external_perimeter_extrusion_width", "thin_walls_acceleration"}}, + {"TopSolidInfill", {"top_infill_extrusion_width", "top_solid_infill_acceleration"}} + };*/ + + int countincrements = 0; + int sizeofarray = static_cast((end_pa - start_pa) / pa_increment) + 2;//'+2' needed for odd/even numbers + std::vector pa_values(sizeofarray); + std::vector c_pa_values_c(sizeofarray); + + double incremented_pa_value = start_pa; + while (incremented_pa_value <= end_pa + pa_increment / 2) {//this makes a number to be used to load x number of 90 bend models for the PA test. + if (incremented_pa_value <= end_pa) { + double rounded_Pa = std::round(incremented_pa_value * 1000000.0) / 1000000.0; + pa_values[countincrements] = rounded_Pa;//store PA numbers in array to be used later. + c_pa_values_c[countincrements] = rounded_Pa; + countincrements++; + incremented_pa_value += pa_increment; + } + else { + pa_values[countincrements] = end_pa; + countincrements++;//failsafe if werid input numbers are provided that can't add the "ending pa" number to the array. + break; } + + }// is there a limit of how many models SS can load ? might be good to set a failsafe just so it won't load 10k+ models... + + bool has_to_arrange = false; + Plater* plat = this->main_frame->plater(); + Model& model = plat->model(); + if (!plat->new_project(L("Pressure calibration"))) + return; + + bool autocenter = gui_app->app_config->get("autocenter") == "1"; + if (autocenter) { + //disable auto-center for this calibration. + gui_app->app_config->set("autocenter", "0"); + } + + std::vector items; + for (size_t i = 0; i < nb_runs; i++){ + items.emplace_back((boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "base_plate.3mf").string()); + } + std::vector objs_idx = plat->load_files(items, true, false, false, false); + assert(objs_idx.size() == nb_runs); + const DynamicPrintConfig* print_config = this->gui_app->get_tab(Preset::TYPE_FFF_PRINT)->get_config(); + const DynamicPrintConfig* filament_config = this->gui_app->get_tab(Preset::TYPE_FFF_FILAMENT)->get_config(); + const DynamicPrintConfig* printer_config = this->gui_app->get_tab(Preset::TYPE_PRINTER)->get_config(); + + // --- 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->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; + + double er_width = print_config->get_abs_value("solid_infill_extrusion_width", nozzle_diameter); + double er_accel = print_config->get_abs_value("solid_infill_acceleration", nozzle_diameter); + double er_speed = print_config->get_abs_value("solid_infill_speed", nozzle_diameter); + double er_spacing = print_config->get_abs_value("external_perimeter_extrusion_spacing",1.0); + + double default_er_width = print_config->get_abs_value("extrusion_width", nozzle_diameter); + double default_er_speed = print_config->get_abs_value("default_speed", nozzle_diameter); + double default_er_accel = print_config->get_abs_value("default_acceleration", nozzle_diameter); + double default_er_spacing = print_config->get_abs_value("extrusion_spacing", nozzle_diameter); + double spacing_ratio = print_config->get_abs_value("perimeter_overlap",1.0); + double spacing_ratio_external = print_config->get_abs_value("external_perimeter_overlap",1.0); + double filament_max_overlap = filament_config->get_computed_value("filament_max_overlap",0);//maybe check for extruderID ? + + if (extrusion_role == "Verify") { + countincrements = 13; + er_width = default_er_width; + er_spacing = default_er_spacing; + er_width = er_width * 100 / nozzle_diameter; + er_width = std::round(er_width * 100.0) / 100.0; // Change number to percentage and round + } + else{ + for (int i = 0; i < sizeof(choice_extrusion_role) / sizeof(choice_extrusion_role[0]); i++) { + + if (er_width_ToOptionKey.find(extrusion_role) != er_width_ToOptionKey.end()) { + + er_width = print_config->get_abs_value(er_width_ToOptionKey[extrusion_role].c_str(), nozzle_diameter);//look at maps at match speed/width ect to the selecter ER role + er_speed = print_config->get_abs_value(er_speed_ToOptionKey[extrusion_role].c_str(), nozzle_diameter);//need to load this here?? + er_accel = print_config->get_abs_value(er_accel_ToOptionKey[extrusion_role].c_str(), nozzle_diameter);//need to load this here?? + er_spacing = print_config->get_abs_value(er_spacing_ToOptionKey[extrusion_role].c_str(), nozzle_diameter); + + //potential BUG if any of the values are 0 everything else would fail, need to pull the default value too and assign that? + if(er_width == 0){er_width =default_er_width; } + if(er_speed == 0){er_speed =default_er_speed; } + if(er_accel == 0){er_accel =default_er_accel; } + if(er_spacing == 0){er_spacing = default_er_spacing; } + + er_width = er_width * 100 / nozzle_diameter; + er_width = std::round(er_width * 100.0) / 100.0; + } else { + er_width = print_config->get_abs_value("solid_infill_extrusion_width", nozzle_diameter); //used for gapfill_width/bridges selection. TODO: add the bits for this here since gapfill/bridges need special calculations + er_width = er_width * 100 / nozzle_diameter; + er_width = std::round(er_width * 100.0) / 100.0; // Change number to percentage and round + + } + + } + } + + + //-- magical scaling is done here :) + //the 90_bend models need to be scaled correctly so there is no 'gapfill' since gapfill will effect results. + double xyzScale = nozzle_diameter / 0.4; + double er_width_to_scale = magical_scaling(nozzle_diameter,er_width,filament_max_overlap,spacing_ratio,spacing_ratio_external,base_layer_height,er_spacing); + //-- magical scaling + std::vector < std::vector> pressure_tower; + + std::string nozzle_diameter_str = std::to_string(nozzle_diameter); + nozzle_diameter_str.erase(nozzle_diameter_str.find_last_not_of('0') + 2, std::string::npos); + + + if (nozzle_diameter_str.back() == '.') {//if nozzle_diameter_str broke fix it by adding '0' to end, prob not needed? + nozzle_diameter_str += '0'; + } + + /*size_t decimal_pos = nozzle_diameter_str.find('.'); + // maybe adjust for this ? + // some users might have 0.0x nozzle size. if that's the case then they should just need to create the file and it should load. ie; 90_bend_0.450.3mf + if (decimal_pos != std::string::npos) { + size_t non_zero_pos = nozzle_diameter_str.find_first_not_of('0', decimal_pos + 2); + nozzle_diameter_str.erase(non_zero_pos, std::string::npos); + }*/ + + std::string bend_90_nozzle_size_3mf = "90_bend_" + nozzle_diameter_str + ".3mf"; + + for (size_t id_item = 0; id_item < nb_runs; id_item++) { + + pressure_tower.emplace_back(); + + double initial_model_height = 0.2; + double initial_90_bend_x = 41.20;//fusion=41.200 mm + double initial_90_bend_y = 20.93;//fusion=20.930 mm + double initial_number_x = 2.06;//fusion=2.063 mm + double initial_number_y = 4.12;//fusion=4.125 mm + double initial_border_x = 1.6;//fusion= 1.6mm + double initial_point_xy = 0.69;//fusion = 0.687 mm + + double z_scaled_model_height = initial_model_height * (first_layer_height / initial_model_height); + double xy_scaled_90_bend_x = initial_90_bend_x * er_width_to_scale; + double xy_scaled_90_bend_y = initial_90_bend_y * er_width_to_scale; + double xy_scaled_x = initial_border_x * er_width_to_scale; + double xy_scaled_number_x = initial_number_x * xyzScale * er_width_to_scale; + double xy_scaled_number_y = initial_number_y * xyzScale * er_width_to_scale; + double xy_scaled_point_xy = initial_point_xy * xyzScale * er_width_to_scale; + + + double thickness_offset = nozzle_diameter * er_width_to_scale * 2; + double z_scale_90_bend = xyzScale * 1.8 / initial_model_height; + double z_scale_factor = 0.0; + double new_z_world_coords = first_layer_height / 2.0 -base_layer_height; + + if(base_layer_height <= first_layer_height){//normal conditions firstlayer is greater than base + z_scale_factor = first_layer_height / initial_model_height; + }else{ + z_scale_factor = first_layer_height + first_layer_height; + } + // BUG: output error if first layer height is lower than base layer height + // this can cause the numbers to not "show up" on the preview because the z scale is calculated wrong. + // ie; first_layer_height=0.1 and base_layer_height =0.20 + //BUG: if first/base layer height are both .02 numbers don't show up when sliced. doesn't happen with windows, it did for linux ? + + + + std::vector bend_90_positions; + std::vector number_positions; + + if (extrusion_role == "Verify") { + + int nb_bends = 0; + for (const std::string& role : choice_extrusion_role) {//dynamic add and scale each 90bend model per extrusion role. + + if (er_width_ToOptionKey.find(role) != er_width_ToOptionKey.end()) { + + er_width = std::round((print_config->get_abs_value(er_width_ToOptionKey[role].c_str(), nozzle_diameter) * 100 / nozzle_diameter) * 100.0) / 100.0; + er_spacing = print_config->get_abs_value(er_spacing_ToOptionKey[role].c_str(), nozzle_diameter); + er_width_to_scale = magical_scaling(nozzle_diameter, er_width, filament_max_overlap, spacing_ratio, spacing_ratio_external, base_layer_height, er_spacing); + thickness_offset = nozzle_diameter * er_width_to_scale * 2; + + add_part(model.objects[objs_idx[id_item]], + (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "scaled_with_nozzle_size" / bend_90_nozzle_size_3mf).string(), + Vec3d{ -0.8, (initial_90_bend_y/2) * nb_bends , xyzScale - base_layer_height }, Vec3d{ er_width_to_scale, er_width_to_scale, z_scale_90_bend }); + pressure_tower.back().push_back(model.objects[objs_idx[id_item]]); + + Eigen::Vector3d modelPosition(-0.8, (initial_90_bend_y/2) * nb_bends, xyzScale - base_layer_height ); + bend_90_positions.push_back(modelPosition); + nb_bends++; + } + else{ + er_width = std::round((default_er_width * 100 / nozzle_diameter) * 100.0) / 100.0; + er_spacing = default_er_spacing; + er_width_to_scale = magical_scaling(nozzle_diameter, er_width, filament_max_overlap, spacing_ratio, spacing_ratio_external, base_layer_height, er_spacing); + thickness_offset = nozzle_diameter * er_width_to_scale * 2; + + add_part(model.objects[objs_idx[id_item]], + (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "scaled_with_nozzle_size" / bend_90_nozzle_size_3mf).string(), + Vec3d{ -0.8, (initial_90_bend_y/2) * nb_bends , xyzScale - base_layer_height }, Vec3d{ er_width_to_scale, er_width_to_scale, z_scale_90_bend }); + pressure_tower.back().push_back(model.objects[objs_idx[id_item]]); + + Eigen::Vector3d modelPosition(-0.8, (initial_90_bend_y/2) * nb_bends, xyzScale - base_layer_height ); + bend_90_positions.push_back(modelPosition); + nb_bends++; + + } + + } + } + else{//not verify + for (int nb_bends = 0; nb_bends < countincrements; nb_bends++){ + //const double magical_transformation_y_pos = 10.47; + add_part(model.objects[objs_idx[id_item]], + (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "scaled_with_nozzle_size" / bend_90_nozzle_size_3mf).string(), + Vec3d{ -0.8, double(nb_bends) * (thickness_offset*2) *2 , xyzScale - base_layer_height }, Vec3d{ er_width_to_scale, er_width_to_scale, z_scale_90_bend }); + pressure_tower.back().push_back(model.objects[objs_idx[id_item]]); + + Eigen::Vector3d modelPosition(-0.8, (double(nb_bends) * (thickness_offset*2) *2) , xyzScale - base_layer_height ); + bend_90_positions.push_back(modelPosition); + } + } + + for (int nb_bends = 0; nb_bends < countincrements;nb_bends++){ + + if(nb_bends == 1 && extrusion_role != "Verify") {//only load once. this onyl determines when the borders get loaded, keeping at top of list makes it easier to scroll down to. it can't be '0' since it needs the numbers positions! + + const double extra_size_y = xy_scaled_90_bend_y / 4; + const double extra_size_x = xy_scaled_number_x; + + const double magical_transformation_x_pos = 20.6;//what is this, and how is this calculated ? >:( + const double magical_transformation_y_pos = 10.47;//load a model without moving its pos to find see what it is.the number doesn't seem to change regardless of layer heights/nozzle size + Eigen::Vector3d bend_pos_first = bend_90_positions[0]; + Eigen::Vector3d bend_pos_mid = bend_90_positions[countincrements/2]; + Eigen::Vector3d bend_pos_last = bend_90_positions[countincrements-1]; + + Eigen::Vector3d number_pos_first = number_positions[0]; + Eigen::Vector3d number_pos_mid = number_positions[3]; + Eigen::Vector3d number_pos_last = number_positions[6]; + double numbers_total_width = (number_pos_last.x() + (xy_scaled_number_x / 2)) - (number_pos_first.x() - (xy_scaled_number_x / 2)); + + double scaled_r_border_x_percentage = ((numbers_total_width + extra_size_x) / initial_border_x) * 100; + double scaled_r_border_x_mm = (scaled_r_border_x_percentage / 100) * initial_border_x; + double scaled_tb_border_x = scaled_r_border_x_mm + xy_scaled_90_bend_x; + double scaled_tb_border_x_percentage = ((scaled_tb_border_x /* + extra_size_x*/) / initial_border_x) * 100; + + + double total_height = (bend_pos_last.y() + (xy_scaled_90_bend_y / 2)) - (bend_pos_first.y() - (xy_scaled_90_bend_y / 2)); + double scaled_border_y_percentage = ((total_height + extra_size_y) / initial_90_bend_y) * 100; + double border_scaled_y = (initial_border_x*(xy_scaled_x * 1.5)) / initial_90_bend_y;//need to fix for larger nozzle sizes. + + + double right_border_pos_x = number_pos_mid.x(); + double top_border_x_pos = ((number_pos_last.x() + (xy_scaled_number_x / 2)) + (bend_pos_first.x() - (xy_scaled_90_bend_x / 2))) / 2; + double left_border_pos_x = bend_pos_first.x() - (xy_scaled_90_bend_x / 2); + + //---------- + add_part(model.objects[objs_idx[id_item]], + (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "pa_border.3mf").string(), + Vec3d{ left_border_pos_x + magical_transformation_x_pos, bend_pos_mid.y(), new_z_world_coords }, //need to fix to adjust for nozzle_diameter since it breaks bottom_solid_layers + /*scale*/Vec3d{ xy_scaled_x * 1.5, scaled_border_y_percentage*0.01, z_scale_factor }); // Left border + //---------- + add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "pa_border.3mf").string(), + Vec3d{ right_border_pos_x + magical_transformation_x_pos , bend_pos_mid.y(), new_z_world_coords }, + /*scale*/Vec3d{ scaled_r_border_x_percentage*0.01 , scaled_border_y_percentage*0.01 , z_scale_factor });// right border + + bool enable_top_bottom = true; + if(enable_top_bottom == true){//remove later + //---------- + add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "pa_border.3mf").string(), + Vec3d{ top_border_x_pos + magical_transformation_x_pos , bend_pos_first.y() - (xy_scaled_90_bend_y /1.8), new_z_world_coords }, //need to fix to adjust for nozzle_diameter since it breaks bottom_solid_layers + /*scale*/Vec3d{ scaled_tb_border_x_percentage*0.01, border_scaled_y, z_scale_factor });//bottom border + //---------- + add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "pa_border.3mf").string(), + Vec3d{ top_border_x_pos + magical_transformation_x_pos , bend_pos_last.y() + (xy_scaled_90_bend_y /1.8) , new_z_world_coords }, //need to fix to adjust for nozzle_diameter since it breaks bottom_solid_layers + /*scale*/Vec3d{ scaled_tb_border_x_percentage*0.01, border_scaled_y, z_scale_factor });//top border + } + + // position in printer coords are half of scaled size! + // scale model in percentage from original models xy values! + //---------- + } + //} + + if (extrusion_role != "Verify") {// possible to load the words for each ER role? + + if (nb_bends % 2 == 1) { // Skip generating every second number + continue; + } + + Eigen::Vector3d bend_90_pos = bend_90_positions[nb_bends]; + const double magical_transformation_y_pos = 10.47; + const double magical_transformation_num_x_pos = 1.03; + const double magical_transformation_num_y_pos = 2.06;// -2.03 + const double magical_transformation_z_pos = 0.12;//0.1 is the transformation value, but set slightly higher so numbers would be "inside" right border this might be dependant on z_scale_factor + + double bend_90_y = bend_90_pos.y() + magical_transformation_y_pos + (xy_scaled_90_bend_y/2); + double bend_90_x = bend_90_pos.x() + magical_transformation_num_x_pos; + double xpos_initial = bend_90_x + (xy_scaled_90_bend_x/2) - xy_scaled_number_x + nozzle_diameter; + double ypos_inital = bend_90_y /*+ (xy_scaled_number_y/2)*/; + double ypos_point = bend_90_y - (xy_scaled_number_y/2) - nozzle_diameter; + + double xpos = xpos_initial; + double ypos = ypos_inital; + + std::string pa_values_string = std::to_string(pa_values[nb_bends]); + std::string threemf =".3mf"; + + for (int j = 0; j < 7; ++j) {//not sure how the code will respond with a positive array list? ie ; 100.2 this moves decimal point thus breaking the code from loading model since "..3mf" not a real file + + std::string numered3mfpath = pa_values_string[j] + threemf; + + if (pa_values_string[j] == '.') { + + add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / "point.3mf").string(), + Vec3d{ xpos + xy_scaled_number_x + nozzle_diameter , ypos_point, z_scaled_model_height - magical_transformation_z_pos }, Vec3d{ xyzScale * er_width_to_scale, xyzScale+(xyzScale/2), z_scale_factor }); + + Eigen::Vector3d modelPosition(xpos + xy_scaled_number_x + nozzle_diameter + magical_transformation_num_x_pos, ypos_point, z_scaled_model_height - magical_transformation_z_pos ); + number_positions.push_back(modelPosition); + xpos = xpos + xy_scaled_point_xy + (nozzle_diameter * 2 ); + } + else if (std::isdigit(pa_values_string[j])) { + + add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_pressure" / numered3mfpath).string(), + Vec3d{ xpos + xy_scaled_number_x + nozzle_diameter, ypos, z_scaled_model_height - magical_transformation_z_pos }, Vec3d{ xyzScale * er_width_to_scale, xyzScale * er_width_to_scale, z_scale_factor }); + + Eigen::Vector3d modelPosition(xpos + xy_scaled_number_x + nozzle_diameter + magical_transformation_num_x_pos, ypos, z_scaled_model_height - magical_transformation_z_pos ); + number_positions.push_back(modelPosition); + xpos = xpos + xy_scaled_number_x + nozzle_diameter; + } + } + } + } + } + + + /// --- translate --- + //bool autocenter = gui_app->app_config->get("autocenter") == "1"; + has_to_arrange = true; + /*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; + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 5 * xyzScale - 5 }); + }*/ + + std::string set_advance_prefix =""; + if (gcfKlipper == flavor) { + if(smooth_time == false){ + set_advance_prefix = "SET_PRESSURE_ADVANCE ADVANCE="; + } + else{ + set_advance_prefix = "SET_PRESSURE_ADVANCE SMOOTH_TIME="; + } + } + else if (gcfMarlinFirmware == flavor) { + set_advance_prefix = "M900 K"; + } + else if(gcfRepRap == flavor){ + set_advance_prefix = "M572 S"; + } + + /// --- main config, modify object config when possible --- + DynamicPrintConfig new_print_config = *print_config; + DynamicPrintConfig new_printer_config = *printer_config; + new_print_config.set_key_value("complete_objects", new ConfigOptionBool(false)); //true is required for multi tests on single plate. + new_print_config.set_key_value("gap_fill_enabled", new ConfigOptionBool(true)); //should be false?, enabled for testing + new_print_config.set_key_value("top_solid_layers", new ConfigOptionInt(0)); + new_print_config.set_key_value("only_one_perimeter_top", new ConfigOptionBool(false)); + new_print_config.set_key_value("bottom_solid_layers", new ConfigOptionInt(1)); + new_print_config.set_key_value("fill_density", new ConfigOptionPercent(0)); + new_print_config.set_key_value("min_width_top_surface", new ConfigOptionFloatOrPercent(0.0,false)); + new_print_config.set_key_value("bottom_fill_pattern", new ConfigOptionEnum(ipMonotonicWGapFill)); + new_print_config.set_key_value("seam_position", new ConfigOptionEnum(spRear));//BUG: should be fixed in 2.7 merge/SS 2.5.59.7, when this is changed the "perimeters & shell" doesn't turn red indicating a change. + new_print_config.set_key_value("avoid_crossing_perimeters", new ConfigOptionBool(false)); + new_print_config.set_key_value("perimeter_overlap", new ConfigOptionPercent(100)); + new_print_config.set_key_value("external_perimeter_overlap", new ConfigOptionPercent(100)); + new_printer_config.set_key_value("before_layer_gcode", new ConfigOptionString(std::string("{if layer_num == 0} ") + set_advance_prefix + std::to_string(first_pa) + " {endif}")); + + for (size_t i = 0; i < nb_runs; i++) { + /* + gcfRepRap, + gcfSprinter, + gcfRepetier, + gcfTeacup, + gcfMakerWare, + gcfMarlinLegacy, + gcfMarlinFirmware, + gcfLerdge, + gcfKlipper, + gcfSailfish, + gcfMach3, + gcfMachinekit, + gcfSmoothie, + gcfNoExtrusion*/ + + size_t num_part = 0; + const int extra_vol = 1; + for (ModelObject* part : pressure_tower[i]) {//loop though each part/volume and assign the modifers + + std::string er_role =""; + if (extrusion_role == "Verify") { + er_role = choice_extrusion_role[num_part]; + if (er_width_ToOptionKey.find(er_role) != er_width_ToOptionKey.end()) { + + er_width = std::round((print_config->get_abs_value(er_width_ToOptionKey[er_role].c_str(), nozzle_diameter) * 100 / nozzle_diameter) * 100.0) / 100.0; + er_speed = print_config->get_abs_value(er_speed_ToOptionKey[er_role].c_str(), nozzle_diameter); + er_accel = print_config->get_abs_value(er_accel_ToOptionKey[er_role].c_str(), nozzle_diameter); + } + else{ + er_width = std::round((default_er_width * 100 / nozzle_diameter) * 100.0) / 100.0; + er_speed = default_er_speed; + er_accel = default_er_accel; + } + } + + + er_width = (er_width == 0) ? std::round((default_er_width * 100 / nozzle_diameter) * 100.0) / 100.0 : er_width; + er_speed = (er_speed == 0) ? default_er_speed : er_speed; + er_accel = (er_accel == 0) ? default_er_accel : er_accel; + + /// --- custom config --- // this is for forcing each model to have x print modifiers + + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(er_width, true)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(er_width, true));//TODO: check widths and ect breaks if any values are in mm/percentage + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("perimeter_speed", new ConfigOptionFloatOrPercent(er_speed, false)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("external_perimeter_speed", new ConfigOptionFloatOrPercent(er_speed, false)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("gap_fill_speed", new ConfigOptionFloatOrPercent(er_speed, false)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("perimeter_acceleration", new ConfigOptionFloatOrPercent(er_accel, false)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("external_perimeter_acceleration", new ConfigOptionFloatOrPercent(er_accel, false)); + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("gap_fill_acceleration", new ConfigOptionFloatOrPercent(er_accel, false)); + if (extrusion_role == "Verify") { + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("region_gcode", new ConfigOptionString(set_advance_prefix + " ; " + er_role ));//user manual type in values + } + else{//add '\n' in? answer: you can, not mandatory as it's verified. + model.objects[objs_idx[i]]->volumes[num_part + extra_vol]->config.set_key_value("region_gcode", new ConfigOptionString(set_advance_prefix + std::to_string(pa_values[num_part]) + " ; " + extrusion_role )); + } + num_part++; + } + } + + //update plater + this->gui_app->get_tab(Preset::TYPE_FFF_PRINT)->load_config(new_print_config); + plat->on_config_change(new_print_config); + this->gui_app->get_tab(Preset::TYPE_PRINTER)->load_config(new_printer_config); + plat->on_config_change(new_printer_config); + plat->changed_objects(objs_idx); + this->gui_app->get_tab(Preset::TYPE_FFF_PRINT)->update_dirty(); + this->gui_app->get_tab(Preset::TYPE_PRINTER)->update_dirty(); + plat->is_preview_shown(); + //update everything, easier to code. + ObjectList* obj = this->gui_app->obj_list(); + obj->update_after_undo_redo(); + + + // arrange if needed, after new settings, to take them into account + if (has_to_arrange) { + //update print config (done at reslice but we need it here) + if (plat->printer_technology() == ptFFF) + plat->fff_print().apply(plat->model(), *plat->config()); + std::shared_ptr fake_statusbar = std::make_shared(); + ArrangeJob arranger(std::dynamic_pointer_cast(fake_statusbar), plat); + arranger.prepare_all(); + arranger.process(); + arranger.finalize(); + } + + + if (extrusion_role != "Verify") {//don't auto slice so user can manual add PA values + //plat->reslice(); //forces a slice of plater. + } + + if (autocenter) { + //re-enable auto-center after this calibration. + gui_app->app_config->set("autocenter", "1"); + } +} + +double CalibrationPressureAdvDialog::magical_scaling(double nozzle_diameter, double er_width, double filament_max_overlap, double spacing_ratio, double spacing_ratio_external, double base_layer_height, double er_spacing ){ + + double xyzScale = nozzle_diameter / 0.4; + double er_width_decimal = er_width * nozzle_diameter / 100.0;//models are generated to be default width of x4 lines for the walls ie; 0.4mm nozzle is 1.6mm thick walls + double er_width_to_scale =1.0; + double overlap_ratio = 1; + if (filament_max_overlap) {overlap_ratio = filament_max_overlap;} + + spacing_ratio = std::min(overlap_ratio * 0.5f, spacing_ratio_external / 2.0); + double new_scale_spacing = er_width_decimal-base_layer_height*float(1. -0.25 *PI)* spacing_ratio; + double spacing_value = std::round((new_scale_spacing / nozzle_diameter) * 100); //spacing_value = Round((Spacing / Max Nozzle Diameter) * 100) + er_spacing = (std::round(spacing_value * 10000) / 10000) *0.01; + + + if (xyzScale > 4 ) { + er_width_to_scale = 1.0; + } + else{ + er_width_to_scale = er_spacing -(nozzle_diameter/2*0.01);//need to scale slightly under to help with models being correct TODO: test more configurations of nozzle sizes/layer heights + //if use has the 'wrong' min layer height for a nozzle size, the model will get filled with "gapfill" not a normal extrusion, need to test more for what variables 'break' it + } + + return er_width_to_scale; +} + +} // namespace GUI +} // namespace Slic3r +#pragma optimize("", on) \ No newline at end of file diff --git a/src/slic3r/GUI/CalibrationPressureAdvDialog.hpp b/src/slic3r/GUI/CalibrationPressureAdvDialog.hpp new file mode 100644 index 00000000000..4521e07daba --- /dev/null +++ b/src/slic3r/GUI/CalibrationPressureAdvDialog.hpp @@ -0,0 +1,37 @@ +#ifndef slic3r_GUI_CalibrationPressureAdvDialog_hpp_ +#define slic3r_GUI_CalibrationPressureAdvDialog_hpp_ + +#include "CalibrationAbstractDialog.hpp" +//pressure advance PressureAdv +namespace Slic3r { +namespace GUI { + +class CalibrationPressureAdvDialog : public CalibrationAbstractDialog +{ + +public: + CalibrationPressureAdvDialog(GUI_App* app, MainFrame* mainframe) : CalibrationAbstractDialog(app, mainframe, "Pressure calibration") + { create(boost::filesystem::path("calibration") / "filament_pressure", "filament_pressure.html", wxSize(1600, 600)); Centre(wxBOTH);} + virtual ~CalibrationPressureAdvDialog(){ } + +protected: + void create_buttons(wxStdDialogButtonSizer* sizer) override; + void create_geometry(wxCommandEvent& event_args); + double magical_scaling(double, double, double, double, double, double, double ); + + //i've set choice boxes for now just to save me typing numbers in when i want to test it :) + wxComboBox* firstPa; //first layer PA -user manual entry + wxComboBox* startPa; //starting PA value -user manual entry + //wxTextCtrl* firstPa; //edit to suit for manual data entry, + wxComboBox* endPa; //ending PA value -user manual entry + wxComboBox* paIncrement;//increment PA by this value -user manual entry~~ or have drop down box ? + wxComboBox* erPa; //extrusion role Pressure/Linear Advance -user choice select + wxComboBox* nbRuns; + wxCheckBox* enableST; // checkbox for "smooth_time" - klipper only feature? + +}; + +} // namespace GUI +} // namespace Slic3r + +#endif 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 cd9ee58b3f5..8b0cbe11f67 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -428,12 +428,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("infill_first", (has_solid_infill || have_infill)); - for (auto el : {"fill_angle_cross","fill_angle_increment", "fill_angle_template", "fill_angle_cross", "bridge_angle", "infill_extrusion_width", + for (auto el : {"fill_angle_cross","fill_angle_follow_model","fill_angle_increment", "fill_angle_template", "bridge_angle", "infill_extrusion_width", "infill_extrusion_spacing", "infill_extrusion_change_odd_layers", "infill_speed" }) toggle_field(el, have_infill || has_solid_infill); 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); @@ -579,7 +584,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("support_material_acceleration", have_default_acceleration && (have_support_material || have_brim || have_skirt)); toggle_field("support_material_interface_acceleration", have_default_acceleration && have_support_material && have_support_interface); toggle_field("brim_acceleration", have_default_acceleration && (have_brim || have_skirt)); - for (auto el : { "bridge_acceleration", "bridge_internal_acceleration", "overhangs_acceleration", "gap_fill_acceleration", "travel_acceleration", "travel_deceleration_use_target", "first_layer_acceleration" }) + for (auto el : { "bridge_acceleration", "internal_bridge_acceleration", "overhangs_acceleration", "gap_fill_acceleration", "travel_acceleration", "travel_deceleration_use_target", "first_layer_acceleration" }) toggle_field(el, have_default_acceleration); // for default speed, it needs at least a dependent field with a % 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 3af8dd5bea2..902f4798aaa 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -66,6 +66,7 @@ #include "CalibrationOverBridgeDialog.hpp" #include "CalibrationTempDialog.hpp" #include "CalibrationRetractionDialog.hpp" +#include "CalibrationPressureAdvDialog.hpp" #include "ConfigSnapshotDialog.hpp" #include "CreateMMUTiledCanvas.hpp" #include "FreeCADDialog.hpp" @@ -1981,6 +1982,10 @@ void GUI_App::calibration_retraction_dialog() { change_calibration_dialog(nullptr, new CalibrationRetractionDialog(this, mainframe)); } +void GUI_App::calibration_pressureadv_dialog() +{ + change_calibration_dialog(nullptr, new CalibrationPressureAdvDialog(this, mainframe)); +} void GUI_App::freecad_script_dialog() { change_calibration_dialog(nullptr, new FreeCADDialog(this, mainframe)); @@ -3130,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 @@ -3138,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_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 3dc7f0c9081..25654af8e90 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -253,6 +253,7 @@ class GUI_App : public wxApp void over_bridge_dialog(); void calibration_cube_dialog(); void calibration_retraction_dialog(); + void calibration_pressureadv_dialog(); void freecad_script_dialog(); void tiled_canvas_dialog(); //void support_tuning(); //have to do multiple, in a submenu diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 096e0bee1db..1c944dc7785 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -52,7 +52,7 @@ static bool is_improper_category(const Slic3r::OptionCategory& category, const i static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF = { { OptionCategory::perimeter , { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, - { OptionCategory::infill , { "fill_density", "fill_pattern" } }, + { OptionCategory::infill , { "fill_density", "fill_pattern", "fill_angle" } }, { OptionCategory::support , { "support_material", "support_material_auto", "support_material_threshold", "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", "support_material_spacing" } }, 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..1053aee5500 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 0307adeed63..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()); } } } @@ -1960,6 +1960,8 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { wxGetApp().filament_temperature_dialog(); }); append_menu_item(m_calibration_menu, wxID_ANY, _(L("Extruder retraction calibration")), _(L("Create a test print to help you to set your retraction length.")), [this](wxCommandEvent&) { wxGetApp().calibration_retraction_dialog(); }); + append_menu_item(m_calibration_menu, wxID_ANY, _(L("Pressure calibration")), _(L("Create a model for tuning Pressure Linear advance.")), + [this](wxCommandEvent&) { wxGetApp().calibration_pressureadv_dialog(); }); m_calibration_menu->AppendSeparator(); append_menu_item(m_calibration_menu, wxID_ANY, _(L("Bridge flow calibration")), _(L("Create a test print to help you to set your bridge flow ratio.")), [this](wxCommandEvent&) { wxGetApp().bridge_tuning_dialog(); }); @@ -2179,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 305e7b642a5..abf1b19c4a3 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -108,7 +108,7 @@ std::string PresetHints::cooling_description(const Preset &preset_fil, const Pre const int support_fan_speed = preset_fil.config.opt_int("support_material_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("support_material_fan_speed"); const int supp_inter_fan_speed = preset_fil.config.opt_int("support_material_interface_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("support_material_interface_fan_speed"); const int bridge_fan_speed = preset_fil.config.opt_int("bridge_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("bridge_fan_speed"); - const int bridge_internal_fan_speed = preset_fil.config.opt_int("bridge_internal_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("bridge_internal_fan_speed"); + const int internal_bridge_fan_speed = preset_fil.config.opt_int("internal_bridge_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("internal_bridge_fan_speed"); const int overhangs_fan_speed = preset_fil.config.opt_int("overhangs_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("overhangs_fan_speed"); const int gap_fill_fan_speed = preset_fil.config.opt_int("gap_fill_fan_speed", 0) == 1 ? 0 : preset_fil.config.get_int("gap_fill_fan_speed"); const int disable_fan_first_layers = preset_fil.config.opt_int("disable_fan_first_layers", 0); @@ -133,7 +133,7 @@ std::string PresetHints::cooling_description(const Preset &preset_fil, const Pre format_simple_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Solid surfaces"), solid_fan_speed); format_simple_fan_speed(out, min_fan_speed, default_fan_speed, _L("Top surfaces"), top_fan_speed); format_double_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Supports"), support_fan_speed, _L("Support interfaces"), supp_inter_fan_speed); - format_double_fan_speed(out, min_fan_speed, default_fan_speed, _L("Bridges"), bridge_fan_speed, _L("Internal bridges"), bridge_internal_fan_speed); + format_double_fan_speed(out, min_fan_speed, default_fan_speed, _L("Bridges"), bridge_fan_speed, _L("Internal bridges"), internal_bridge_fan_speed); format_simple_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Perimeter overhangs"), overhangs_fan_speed); format_simple_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Gap fills"), gap_fill_fan_speed); @@ -160,7 +160,7 @@ std::string PresetHints::cooling_description(const Preset &preset_fil, const Pre surface_list += ","; surface_list += _L("Bridges"); } - if (bridge_internal_fan_speed > 0) { + if (internal_bridge_fan_speed > 0) { surface_list += ","; surface_list += _L("Internal bridges"); } @@ -243,7 +243,7 @@ std::string PresetHints::cooling_description(const Preset &preset_fil, const Pre out += "\n\n" + _L("! 1 for the External perimeters fan speed is Deprecated, please set it to 0 to stop the fan!"); if (preset_fil.config.opt_int("bridge_fan_speed", 0) == 1) out += "\n\n" + _L("! 1 for the Bridge fan speed is Deprecated, please set it to 0 to stop the fan!"); - if (preset_fil.config.opt_int("bridge_internal_fan_speed", 0) == 1) + if (preset_fil.config.opt_int("internal_bridge_fan_speed", 0) == 1) out += "\n\n" + _L("! 1 for the Infill bridge fan speed is Deprecated, please set it to 0 to stop the fan!"); return out.ToStdString(); @@ -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;