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