diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml
index 93a7923fa2..9e3d0f1e05 100644
--- a/.github/workflows/build_all.yml
+++ b/.github/workflows/build_all.yml
@@ -43,11 +43,11 @@ jobs:
fail-fast: false
matrix:
include:
- - os: ubuntu-22.04
- - os: ubuntu-24.04
- - os: windows-latest
- - os: macos-13
- arch: x86_64
+ #- os: ubuntu-22.04
+ #- os: ubuntu-24.04
+ #- os: windows-latest
+ #- os: macos-13
+ # arch: x86_64
- os: macos-15
arch: arm64
uses: ./.github/workflows/build_check_cache.yml
diff --git a/.github/workflows/build_ubuntu.yml b/.github/workflows/build_ubuntu.yml
index d2587735e2..fff488956a 100644
--- a/.github/workflows/build_ubuntu.yml
+++ b/.github/workflows/build_ubuntu.yml
@@ -1,6 +1,6 @@
name: Build Linux
-on: [pull_request]
+#on: [pull_request]
jobs:
appimage-builder:
diff --git a/bbl/i18n/BambuStudio.pot b/bbl/i18n/BambuStudio.pot
index 3dfff3f2bb..98523879ff 100644
--- a/bbl/i18n/BambuStudio.pot
+++ b/bbl/i18n/BambuStudio.pot
@@ -14583,175 +14583,175 @@ msgstr ""
msgid "Learn more about purge mode"
msgstr ""
-
-#: resources/data/hints.ini: [hint:How to use keyboard shortcuts]
-msgid "How to use keyboard shortcuts\nDid you know that Bambu Studio offers a wide range of keyboard shortcuts and 3D scene operations."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Cut Tool]
-msgid "Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Fix Model]
-msgid "Fix Model\nDid you know that you can fix a corrupted 3D model to avoid a lot of slicing problems on the Windows system?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Timelapse]
-msgid "Timelapse\nDid you know that you can generate a timelapse video during each print?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Auto-Arrange]
-msgid "Auto-Arrange\nDid you know that you can auto-arrange all objects in your project?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Auto-Orient]
-msgid "Auto-Orient\nDid you know that you can rotate objects to an optimal orientation for printing by a simple click?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Lay on Face]
-msgid "Lay on Face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the F key."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Object List]
-msgid "Object List\nDid you know that you can view all objects/parts in a list and change settings for each object/part?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Simplify Model]
-msgid "Simplify Model\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Slicing Parameter Table]
-msgid "Slicing Parameter Table\nDid you know that you can view all objects/parts on a table and change settings for each object/part?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Split to Objects/Parts]
-msgid "Split to Objects/Parts\nDid you know that you can split a big object into small ones for easy colorizing or printing?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Subtract a Part]
-msgid "Subtract a Part\nDid you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Bambu Studio. Read more in the documentation."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:STEP]
-msgid "STEP\nDid you know that you can improve your print quality by slicing a STEP file instead of an STL?\nBambu Studio supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try!"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Z seam location]
-msgid "Z seam location\nDid you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Fine-tuning for flow rate]
-msgid "Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned for even better-looking prints? Depending on the material, you can improve the overall finish of the printed model by doing some fine-tuning."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Split your prints into plates]
-msgid "Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height]
-msgid "Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Support painting]
-msgid "Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Different types of supports]
-msgid "Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Printing Silk Filament]
-msgid "Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Brim for better adhesion]
-msgid "Brim for better adhesion\nDid you know that when printing models have a small contact interface with the printing surface, it's recommended to use a brim?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Set parameters for multiple objects]
-msgid "Set parameters for multiple objects\nDid you know that you can set slicing parameters for all selected objects at one time?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Stack objects]
-msgid "Stack objects\nDid you know that you can stack objects as a whole one?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Flush into support/objects/infill]
-msgid "Flush into support/objects/infill\nDid you know that you can save the wasted filament by flushing them into support/objects/infill during filament change?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Improve strength]
-msgid "Improve strength\nDid you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?"
-msgstr ""
-
-#: resources/data/hints.ini: [hint:When need to print with the printer door opened]
-msgid "When need to print with the printer door opened\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature. More info about this in the Wiki."
-msgstr ""
-
-#: resources/data/hints.ini: [hint:Avoid warping]
-msgid "Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Single-Material Only]
-msgid "Single-Material Only\nHelio currently simulates one material and one nozzle per job. Multi-material or multi-extruder G-code adds long pauses that break thermal continuity, so results wouldn’t be meaningful."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: One Plate per Job]
-msgid "One Plate per Job\nUpload G-code with a single build plate—multi-plate projects aren’t yet supported, so only the first plate would run."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: What is the Thermal Quality Index?]
-msgid "What is the Thermal Quality Index?\nThe Thermal Quality Index (scale –100 to +100) shows how hot or cold each region prints—green (≈ 0) is the “just right” zone for strong, warp-free parts. Keep most of the part green for best results."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Voxel-Level Accuracy]
-msgid "Voxel-Level Accuracy\nWe predict temperature in every voxel at every time-step, and for standard jobs the forecast is typically within ±5–10 °C. Pauses, custom firmware or odd cooling can widen that margin."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Fan & Airflow Model]
-msgid "Fan & Airflow Model\nA simplified fan-and-room model shows how cooling settings change part temps without slow CFD maths—great for day-to-day tuning. Chamber vortices aren’t yet simulated so runs stay fast."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: TQI Limits Explained]
-msgid "TQI Limits Explained\n-100 → too cold: tensile strength is ~50 % lower than parts printed at the ideal 0 (ASTM D638 dog-bone tests). +100 → too hot: layers stay molten and may sag or collapse. Keep regions near 0 for peak strength and accuracy."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: What Drives Runtime?]
-msgid "What Drives Runtime?\nExtra layers, dense infill, lots of tiny arcs (small mesh elements) or very slow printing speeds all extend simulation time because the solver must step through more seconds. Multi-core CPUs or CUDA GPUs speed things up."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Nozzle Temp Range]
-msgid "Nozzle Temp Range\nSupported set-points are 190 – 320 °C; anything outside is clamped to keep physics realistic."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Debugging Flowchart]
-msgid "Debugging Flowchart\nNot sure why a result looks off? Follow our step-by-step debugging flowchart to trace settings, G-code and material issues in minutes."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Why Cooling Varies]
-msgid "Why Cooling Varies\nOuter walls and bridges cool fastest while thick interiors stay warmer—geometry, airflow and tool-path all play a part, and the simulation visualises these differences."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Extrusion Temp Model]
-msgid "Extrusion Temp Model\nMaterial properties shape the melt curve, but printer geometry decides how much heat the filament actually gains, so the model is material-specific and printer-calibrated."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Nozzle setting of 190 °C vs 320 °C?]
-msgid "Nozzle setting of 190 °C vs 320 °C?\nA 100 °C nozzle change only nudges the thermal index because extrusion temp, flow rate and post-deposition cooling dominate the part’s heat history."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Bed Temperature influence]
-msgid "Bed Temperature influence\nOnly the first-layer bed temp feeds the model right now; later bed changes aren’t yet captured."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Actual Tool-Path]
-msgid "Actual Tool-Path\nYes—your exact G-code path, speeds and fan commands are simulated."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Mesh Resolution]
-msgid "Mesh Resolution\nThe voxel grid is finer than the G-code line spacing, capturing layer-by-layer detail without wasting compute."
-msgstr ""
-
-#: resources/data/helio_hints.ini: [hint: Shrinkage, Warping & Stress]
-msgid "Shrinkage, Warping & Stress\nBy controlling the thermal index you can remove the heat-driven causes of warp and stress."
-msgstr ""
+
+#: resources/data/hints.ini: [hint:How to use keyboard shortcuts]
+msgid "How to use keyboard shortcuts\nDid you know that Bambu Studio offers a wide range of keyboard shortcuts and 3D scene operations."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Cut Tool]
+msgid "Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Fix Model]
+msgid "Fix Model\nDid you know that you can fix a corrupted 3D model to avoid a lot of slicing problems on the Windows system?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Timelapse]
+msgid "Timelapse\nDid you know that you can generate a timelapse video during each print?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Auto-Arrange]
+msgid "Auto-Arrange\nDid you know that you can auto-arrange all objects in your project?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Auto-Orient]
+msgid "Auto-Orient\nDid you know that you can rotate objects to an optimal orientation for printing by a simple click?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Lay on Face]
+msgid "Lay on Face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the F key."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Object List]
+msgid "Object List\nDid you know that you can view all objects/parts in a list and change settings for each object/part?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Simplify Model]
+msgid "Simplify Model\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Slicing Parameter Table]
+msgid "Slicing Parameter Table\nDid you know that you can view all objects/parts on a table and change settings for each object/part?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Split to Objects/Parts]
+msgid "Split to Objects/Parts\nDid you know that you can split a big object into small ones for easy colorizing or printing?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Subtract a Part]
+msgid "Subtract a Part\nDid you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Bambu Studio. Read more in the documentation."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:STEP]
+msgid "STEP\nDid you know that you can improve your print quality by slicing a STEP file instead of an STL?\nBambu Studio supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try!"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Z seam location]
+msgid "Z seam location\nDid you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Fine-tuning for flow rate]
+msgid "Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned for even better-looking prints? Depending on the material, you can improve the overall finish of the printed model by doing some fine-tuning."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Split your prints into plates]
+msgid "Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height]
+msgid "Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Support painting]
+msgid "Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Different types of supports]
+msgid "Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Printing Silk Filament]
+msgid "Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Brim for better adhesion]
+msgid "Brim for better adhesion\nDid you know that when printing models have a small contact interface with the printing surface, it's recommended to use a brim?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Set parameters for multiple objects]
+msgid "Set parameters for multiple objects\nDid you know that you can set slicing parameters for all selected objects at one time?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Stack objects]
+msgid "Stack objects\nDid you know that you can stack objects as a whole one?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Flush into support/objects/infill]
+msgid "Flush into support/objects/infill\nDid you know that you can save the wasted filament by flushing them into support/objects/infill during filament change?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Improve strength]
+msgid "Improve strength\nDid you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?"
+msgstr ""
+
+#: resources/data/hints.ini: [hint:When need to print with the printer door opened]
+msgid "When need to print with the printer door opened\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature. More info about this in the Wiki."
+msgstr ""
+
+#: resources/data/hints.ini: [hint:Avoid warping]
+msgid "Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Single-Material Only]
+msgid "Single-Material Only\nHelio currently simulates one material and one nozzle per job. Multi-material or multi-extruder G-code adds long pauses that break thermal continuity, so results wouldn’t be meaningful."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: One Plate per Job]
+msgid "One Plate per Job\nUpload G-code with a single build plate—multi-plate projects aren’t yet supported, so only the first plate would run."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: What is the Thermal Quality Index?]
+msgid "What is the Thermal Quality Index?\nThe Thermal Quality Index (scale –100 to +100) shows how hot or cold each region prints—green (≈ 0) is the “just right” zone for strong, warp-free parts. Keep most of the part green for best results."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Voxel-Level Accuracy]
+msgid "Voxel-Level Accuracy\nWe predict temperature in every voxel at every time-step, and for standard jobs the forecast is typically within ±5–10 °C. Pauses, custom firmware or odd cooling can widen that margin."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Fan & Airflow Model]
+msgid "Fan & Airflow Model\nA simplified fan-and-room model shows how cooling settings change part temps without slow CFD maths—great for day-to-day tuning. Chamber vortices aren’t yet simulated so runs stay fast."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: TQI Limits Explained]
+msgid "TQI Limits Explained\n-100 → too cold: tensile strength is ~50 % lower than parts printed at the ideal 0 (ASTM D638 dog-bone tests). +100 → too hot: layers stay molten and may sag or collapse. Keep regions near 0 for peak strength and accuracy."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: What Drives Runtime?]
+msgid "What Drives Runtime?\nExtra layers, dense infill, lots of tiny arcs (small mesh elements) or very slow printing speeds all extend simulation time because the solver must step through more seconds. Multi-core CPUs or CUDA GPUs speed things up."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Nozzle Temp Range]
+msgid "Nozzle Temp Range\nSupported set-points are 190 – 320 °C; anything outside is clamped to keep physics realistic."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Debugging Flowchart]
+msgid "Debugging Flowchart\nNot sure why a result looks off? Follow our step-by-step debugging flowchart to trace settings, G-code and material issues in minutes."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Why Cooling Varies]
+msgid "Why Cooling Varies\nOuter walls and bridges cool fastest while thick interiors stay warmer—geometry, airflow and tool-path all play a part, and the simulation visualises these differences."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Extrusion Temp Model]
+msgid "Extrusion Temp Model\nMaterial properties shape the melt curve, but printer geometry decides how much heat the filament actually gains, so the model is material-specific and printer-calibrated."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Nozzle setting of 190 °C vs 320 °C?]
+msgid "Nozzle setting of 190 °C vs 320 °C?\nA 100 °C nozzle change only nudges the thermal index because extrusion temp, flow rate and post-deposition cooling dominate the part’s heat history."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Bed Temperature influence]
+msgid "Bed Temperature influence\nOnly the first-layer bed temp feeds the model right now; later bed changes aren’t yet captured."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Actual Tool-Path]
+msgid "Actual Tool-Path\nYes—your exact G-code path, speeds and fan commands are simulated."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Mesh Resolution]
+msgid "Mesh Resolution\nThe voxel grid is finer than the G-code line spacing, capturing layer-by-layer detail without wasting compute."
+msgstr ""
+
+#: resources/data/helio_hints.ini: [hint: Shrinkage, Warping & Stress]
+msgid "Shrinkage, Warping & Stress\nBy controlling the thermal index you can remove the heat-driven causes of warp and stress."
+msgstr ""
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index b6300663bb..e408f05b34 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -25,1805 +25,2097 @@
#include
#include
#include
-//BBS: add json support
+// BBS: add json support
#include "nlohmann/json.hpp"
using namespace nlohmann;
-//FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion)
-// This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy().
+// FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion)
+// This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy().
#include "PrintConfig.hpp"
-namespace Slic3r {
-
-//BBS: add json support
-//static const std::string CONFIG_VERSION_KEY = "version";
-//static const std::string CONFIG_NAME_KEY = "name";
-//static const std::string CONFIG_URL_KEY = "url";
-//static const std::string CONFIG_TYPE_KEY = "type";
-//static const std::string CONFIG_FROM_KEY = "from";
-//static const std::string CONFIG_INHERITS_KEY = "inherits";
-//static const std::string CONFIG_INSTANT_KEY = "instantiation";
-
-// Escape \n, \r and backslash
-std::string escape_string_cstyle(const std::string &str)
+namespace Slic3r
{
- // Allocate a buffer twice the input string length,
- // so the output will fit even if all input characters get escaped.
- std::vector out(str.size() * 2, 0);
- char *outptr = out.data();
- for (size_t i = 0; i < str.size(); ++ i) {
- char c = str[i];
- if (c == '\r') {
- (*outptr ++) = '\\';
- (*outptr ++) = 'r';
- } else if (c == '\n') {
- (*outptr ++) = '\\';
- (*outptr ++) = 'n';
- } else if (c == '\\') {
- (*outptr ++) = '\\';
- (*outptr ++) = '\\';
- } else
- (*outptr ++) = c;
- }
- return std::string(out.data(), outptr - out.data());
-}
-std::string escape_strings_cstyle(const std::vector &strs)
-{
- // 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;
- // 2) Fill in the buffer.
- std::vector out(outbuflen, 0);
- char *outptr = out.data();
- for (size_t j = 0; j < strs.size(); ++ j) {
- if (j > 0)
- // Separate the strings.
- (*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.
- bool should_quote = strs.size() == 1 && str.empty();
- for (size_t i = 0; i < str.size(); ++ i) {
+ // BBS: add json support
+ // static const std::string CONFIG_VERSION_KEY = "version";
+ // static const std::string CONFIG_NAME_KEY = "name";
+ // static const std::string CONFIG_URL_KEY = "url";
+ // static const std::string CONFIG_TYPE_KEY = "type";
+ // static const std::string CONFIG_FROM_KEY = "from";
+ // static const std::string CONFIG_INHERITS_KEY = "inherits";
+ // static const std::string CONFIG_INSTANT_KEY = "instantiation";
+
+ // Escape \n, \r and backslash
+ std::string escape_string_cstyle(const std::string &str)
+ {
+ // Allocate a buffer twice the input string length,
+ // so the output will fit even if all input characters get escaped.
+ std::vector out(str.size() * 2, 0);
+ char *outptr = out.data();
+ for (size_t i = 0; i < str.size(); ++i)
+ {
char c = str[i];
- if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
- should_quote = true;
- break;
+ if (c == '\r')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = 'r';
+ }
+ else if (c == '\n')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = 'n';
+ }
+ else if (c == '\\')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = '\\';
}
+ else
+ (*outptr++) = c;
}
- if (should_quote) {
- (*outptr ++) = '"';
- for (size_t i = 0; i < str.size(); ++ i) {
+ return std::string(out.data(), outptr - out.data());
+ }
+
+ std::string escape_strings_cstyle(const std::vector &strs)
+ {
+ // 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;
+ // 2) Fill in the buffer.
+ std::vector out(outbuflen, 0);
+ char *outptr = out.data();
+ for (size_t j = 0; j < strs.size(); ++j)
+ {
+ if (j > 0)
+ // Separate the strings.
+ (*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.
+ bool should_quote = strs.size() == 1 && str.empty();
+ for (size_t i = 0; i < str.size(); ++i)
+ {
char c = str[i];
- if (c == '\\' || c == '"') {
- (*outptr ++) = '\\';
- (*outptr ++) = c;
- } else if (c == '\r') {
- (*outptr ++) = '\\';
- (*outptr ++) = 'r';
- } else if (c == '\n') {
- (*outptr ++) = '\\';
- (*outptr ++) = 'n';
- } else
- (*outptr ++) = c;
- }
- (*outptr ++) = '"';
- } else {
- memcpy(outptr, str.data(), str.size());
- outptr += str.size();
+ if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n')
+ {
+ should_quote = true;
+ break;
+ }
+ }
+ if (should_quote)
+ {
+ (*outptr++) = '"';
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ char c = str[i];
+ if (c == '\\' || c == '"')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = c;
+ }
+ else if (c == '\r')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = 'r';
+ }
+ else if (c == '\n')
+ {
+ (*outptr++) = '\\';
+ (*outptr++) = 'n';
+ }
+ else
+ (*outptr++) = c;
+ }
+ (*outptr++) = '"';
+ }
+ else
+ {
+ memcpy(outptr, str.data(), str.size());
+ outptr += str.size();
+ }
}
+ return std::string(out.data(), outptr - out.data());
}
- return std::string(out.data(), outptr - out.data());
-}
-// Unescape \n, \r and backslash
-bool unescape_string_cstyle(const std::string &str, std::string &str_out)
-{
- std::vector out(str.size(), 0);
- char *outptr = out.data();
- for (size_t i = 0; i < str.size(); ++ i) {
- char c = str[i];
- if (c == '\\') {
- if (++ i == str.size())
- return false;
- c = str[i];
- if (c == 'r')
- (*outptr ++) = '\r';
- else if (c == 'n')
- (*outptr ++) = '\n';
+ // Unescape \n, \r and backslash
+ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
+ {
+ std::vector out(str.size(), 0);
+ char *outptr = out.data();
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ char c = str[i];
+ if (c == '\\')
+ {
+ if (++i == str.size())
+ return false;
+ c = str[i];
+ if (c == 'r')
+ (*outptr++) = '\r';
+ else if (c == 'n')
+ (*outptr++) = '\n';
+ else
+ (*outptr++) = c;
+ }
else
- (*outptr ++) = c;
- } else
- (*outptr ++) = c;
+ (*outptr++) = c;
+ }
+ str_out.assign(out.data(), outptr - out.data());
+ return true;
}
- str_out.assign(out.data(), outptr - out.data());
- return true;
-}
-bool unescape_strings_cstyle(const std::string &str, std::vector &out)
-{
- if (str.empty())
- return true;
+ bool unescape_strings_cstyle(const std::string &str, std::vector &out)
+ {
+ if (str.empty())
+ return true;
- size_t i = 0;
- for (;;) {
- // Skip white spaces.
- char c = str[i];
- while (c == ' ' || c == '\t') {
- if (++ i == str.size())
- return true;
- c = str[i];
- }
- // Start of a word.
- std::vector buf;
- buf.reserve(16);
- // Is it enclosed in quotes?
- c = str[i];
- if (c == '"') {
- // Complex case, string is enclosed in quotes.
- for (++ i; i < str.size(); ++ i) {
+ size_t i = 0;
+ for (;;)
+ {
+ // Skip white spaces.
+ char c = str[i];
+ while (c == ' ' || c == '\t')
+ {
+ if (++i == str.size())
+ return true;
c = str[i];
- if (c == '"') {
- // End of string.
- break;
+ }
+ // Start of a word.
+ std::vector buf;
+ buf.reserve(16);
+ // Is it enclosed in quotes?
+ c = str[i];
+ if (c == '"')
+ {
+ // Complex case, string is enclosed in quotes.
+ for (++i; i < str.size(); ++i)
+ {
+ c = str[i];
+ if (c == '"')
+ {
+ // End of string.
+ break;
+ }
+ if (c == '\\')
+ {
+ if (++i == str.size())
+ return false;
+ c = str[i];
+ if (c == 'r')
+ c = '\r';
+ else if (c == 'n')
+ c = '\n';
+ }
+ buf.push_back(c);
}
- if (c == '\\') {
- if (++ i == str.size())
- return false;
+ if (i == str.size())
+ return false;
+ ++i;
+ }
+ else
+ {
+ for (; i < str.size(); ++i)
+ {
c = str[i];
- if (c == 'r')
- c = '\r';
- else if (c == 'n')
- c = '\n';
+ if (c == ';')
+ break;
+ buf.push_back(c);
}
- buf.push_back(c);
}
+ // Store the string into the output vector.
+ out.push_back(std::string(buf.data(), buf.size()));
if (i == str.size())
- return false;
- ++ i;
- } else {
- for (; i < str.size(); ++ i) {
+ return true;
+ // Skip white spaces.
+ c = str[i];
+ while (c == ' ' || c == '\t')
+ {
+ if (++i == str.size())
+ // End of string. This is correct.
+ return true;
c = str[i];
- if (c == ';')
- break;
- buf.push_back(c);
}
- }
- // Store the string into the output vector.
- out.push_back(std::string(buf.data(), buf.size()));
- if (i == str.size())
- return true;
- // Skip white spaces.
- c = str[i];
- while (c == ' ' || c == '\t') {
- if (++ i == str.size())
- // End of string. This is correct.
+ if (c != ';')
+ return false;
+ if (++i == str.size())
+ {
+ // Emit one additional empty string.
+ out.push_back(std::string());
return true;
- c = str[i];
+ }
}
- if (c != ';')
- return false;
- if (++ i == str.size()) {
- // Emit one additional empty string.
- out.push_back(std::string());
- return true;
+ }
+
+ std::string escape_ampersand(const std::string &str)
+ {
+ // Allocate a buffer 2 times the input string length,
+ // so the output will fit even if all input characters get escaped.
+ std::vector out(str.size() * 6, 0);
+ char *outptr = out.data();
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ char c = str[i];
+ if (c == '&')
+ {
+ (*outptr++) = '&';
+ (*outptr++) = '&';
+ }
+ else
+ (*outptr++) = c;
}
+ return std::string(out.data(), outptr - out.data());
}
-}
-std::string escape_ampersand(const std::string& str)
-{
- // Allocate a buffer 2 times the input string length,
- // so the output will fit even if all input characters get escaped.
- std::vector out(str.size() * 6, 0);
- char* outptr = out.data();
- for (size_t i = 0; i < str.size(); ++i) {
- char c = str[i];
- if (c == '&') {
- (*outptr++) = '&';
- (*outptr++) = '&';
- } else
- (*outptr++) = c;
+ void ConfigOptionDeleter::operator()(ConfigOption *p)
+ {
+ delete p;
}
- return std::string(out.data(), outptr - out.data());
-}
-void ConfigOptionDeleter::operator()(ConfigOption* p) {
- delete p;
-}
+ std::vector ConfigOptionDef::cli_args(const std::string &key) const
+ {
+ std::vector args;
+ if (this->cli != ConfigOptionDef::nocli)
+ {
+ const std::string &cli = this->cli;
+ // FIXME What was that for? Check the "readline" documentation.
+ // Neither '=' nor '!' is used in any of the cli parameters currently defined by PrusaSlicer.
+ // std::string cli = this->cli.substr(0, this->cli.find("="));
+ // boost::trim_right_if(cli, boost::is_any_of("!"));
+ if (cli.empty())
+ {
+ // Convert an option key to CLI argument by replacing underscores with dashes.
+ std::string opt = key;
+ boost::replace_all(opt, "_", "-");
+ args.emplace_back(std::move(opt));
+ }
+ else
+ boost::split(args, cli, boost::is_any_of("|"));
+ }
+ return args;
+ }
-std::vector ConfigOptionDef::cli_args(const std::string &key) const
-{
- std::vector args;
- if (this->cli != ConfigOptionDef::nocli) {
- const std::string &cli = this->cli;
- //FIXME What was that for? Check the "readline" documentation.
- // Neither '=' nor '!' is used in any of the cli parameters currently defined by PrusaSlicer.
-// std::string cli = this->cli.substr(0, this->cli.find("="));
-// boost::trim_right_if(cli, boost::is_any_of("!"));
- if (cli.empty()) {
- // Convert an option key to CLI argument by replacing underscores with dashes.
- std::string opt = key;
- boost::replace_all(opt, "_", "-");
- args.emplace_back(std::move(opt));
- } else
- boost::split(args, cli, boost::is_any_of("|"));
+ ConfigOption *ConfigOptionDef::create_empty_option() const
+ {
+ if (this->nullable)
+ {
+ switch (this->type)
+ {
+ case coFloats:
+ return new ConfigOptionFloatsNullable();
+ case coInts:
+ return new ConfigOptionIntsNullable();
+ case coPercents:
+ return new ConfigOptionPercentsNullable();
+ case coFloatsOrPercents:
+ return new ConfigOptionFloatsOrPercentsNullable();
+ case coBools:
+ return new ConfigOptionBoolsNullable();
+ default:
+ throw ConfigurationError(std::string("Unknown option type for nullable option ") + this->label);
+ }
+ }
+ else
+ {
+ switch (this->type)
+ {
+ case coFloat:
+ return new ConfigOptionFloat();
+ case coFloats:
+ return new ConfigOptionFloats();
+ case coInt:
+ return new ConfigOptionInt();
+ case coInts:
+ return new ConfigOptionInts();
+ case coString:
+ return new ConfigOptionString();
+ case coStrings:
+ return new ConfigOptionStrings();
+ case coPercent:
+ return new ConfigOptionPercent();
+ case coPercents:
+ return new ConfigOptionPercents();
+ case coFloatOrPercent:
+ return new ConfigOptionFloatOrPercent();
+ case coFloatsOrPercents:
+ return new ConfigOptionFloatsOrPercents();
+ case coPoint:
+ return new ConfigOptionPoint();
+ case coPoints:
+ return new ConfigOptionPoints();
+ case coPoint3:
+ return new ConfigOptionPoint3();
+ // case coPoint3s: return new ConfigOptionPoint3s();
+ case coBool:
+ return new ConfigOptionBool();
+ case coBools:
+ return new ConfigOptionBools();
+ case coEnum:
+ return new ConfigOptionEnumGeneric(this->enum_keys_map);
+ // BBS
+ case coEnums:
+ return new ConfigOptionEnumsGeneric(this->enum_keys_map);
+ default:
+ throw ConfigurationError(std::string("Unknown option type for option ") + this->label);
+ }
+ }
}
- return args;
-}
-ConfigOption* ConfigOptionDef::create_empty_option() const
-{
- if (this->nullable) {
- switch (this->type) {
- case coFloats: return new ConfigOptionFloatsNullable();
- case coInts: return new ConfigOptionIntsNullable();
- case coPercents: return new ConfigOptionPercentsNullable();
- case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
- case coBools: return new ConfigOptionBoolsNullable();
- default: throw ConfigurationError(std::string("Unknown option type for nullable option ") + this->label);
- }
- } else {
- switch (this->type) {
- case coFloat: return new ConfigOptionFloat();
- case coFloats: return new ConfigOptionFloats();
- case coInt: return new ConfigOptionInt();
- case coInts: return new ConfigOptionInts();
- case coString: return new ConfigOptionString();
- case coStrings: return new ConfigOptionStrings();
- case coPercent: return new ConfigOptionPercent();
- case coPercents: return new ConfigOptionPercents();
- case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
- case coFloatsOrPercents: return new ConfigOptionFloatsOrPercents();
- case coPoint: return new ConfigOptionPoint();
- case coPoints: return new ConfigOptionPoints();
- case coPoint3: return new ConfigOptionPoint3();
- // case coPoint3s: return new ConfigOptionPoint3s();
- case coBool: return new ConfigOptionBool();
- case coBools: return new ConfigOptionBools();
- case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
- // BBS
- case coEnums: return new ConfigOptionEnumsGeneric(this->enum_keys_map);
- default: throw ConfigurationError(std::string("Unknown option type for option ") + this->label);
- }
- }
-}
+ ConfigOption *ConfigOptionDef::create_default_option() const
+ {
+ if (this->default_value)
+ {
+ ConfigOptionType type = this->default_value->type();
+ if (type == coEnum)
+ return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
-ConfigOption* ConfigOptionDef::create_default_option() const
-{
- if (this->default_value) {
- ConfigOptionType type = this->default_value->type();
- if (type == coEnum)
- return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
-
- if (type == coEnums) {
- auto dft = this->default_value->clone();
- if (dft->nullable()) {
- ConfigOptionEnumsGenericNullable *opt = dynamic_cast(this->default_value->clone());
- opt->keys_map = this->enum_keys_map;
- return opt;
- } else {
- ConfigOptionEnumsGeneric *opt = dynamic_cast(this->default_value->clone());
- opt->keys_map = this->enum_keys_map;
- return opt;
- }
- delete dft;
+ if (type == coEnums)
+ {
+ auto dft = this->default_value->clone();
+ if (dft->nullable())
+ {
+ ConfigOptionEnumsGenericNullable *opt = dynamic_cast(this->default_value->clone());
+ opt->keys_map = this->enum_keys_map;
+ return opt;
+ }
+ else
+ {
+ ConfigOptionEnumsGeneric *opt = dynamic_cast(this->default_value->clone());
+ opt->keys_map = this->enum_keys_map;
+ return opt;
+ }
+ delete dft;
+ }
+
+ return this->default_value->clone();
}
+ return this->create_empty_option();
+ }
- return this->default_value->clone();
+ // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
+ ConfigOptionDef *ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
+ {
+ static size_t serialization_key_ordinal_last = 0;
+ ConfigOptionDef *opt = &this->options[opt_key];
+ opt->opt_key = opt_key;
+ opt->type = type;
+ opt->serialization_key_ordinal = ++serialization_key_ordinal_last;
+ this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt;
+ return opt;
}
- return this->create_empty_option();
-}
-// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
-ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
-{
- static size_t serialization_key_ordinal_last = 0;
- ConfigOptionDef *opt = &this->options[opt_key];
- opt->opt_key = opt_key;
- opt->type = type;
- opt->serialization_key_ordinal = ++ serialization_key_ordinal_last;
- this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt;
- return opt;
-}
+ ConfigOptionDef *ConfigDef::add_nullable(const t_config_option_key &opt_key, ConfigOptionType type)
+ {
+ ConfigOptionDef *def = this->add(opt_key, type);
+ def->nullable = true;
+ return def;
+ }
-ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, ConfigOptionType type)
-{
- ConfigOptionDef *def = this->add(opt_key, type);
- def->nullable = true;
- return def;
-}
+ std::ostream &ConfigDef::print_cli_help(std::ostream &out, bool show_defaults, std::function filter) const
+ {
+ // prepare a function for wrapping text
+ auto wrap = [](std::string text, size_t line_length) -> std::string
+ {
+ std::istringstream words(text);
+ std::ostringstream wrapped;
+ std::string word;
-std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const
-{
- // prepare a function for wrapping text
- auto wrap = [](std::string text, size_t line_length) -> std::string {
- std::istringstream words(text);
- std::ostringstream wrapped;
- std::string word;
-
- if (words >> word) {
- wrapped << word;
- size_t space_left = line_length - word.length();
- while (words >> word) {
- if (space_left < word.length() + 1) {
- wrapped << '\n' << word;
- space_left = line_length - word.length();
- } else {
- wrapped << ' ' << word;
- space_left -= word.length() + 1;
+ if (words >> word)
+ {
+ wrapped << word;
+ size_t space_left = line_length - word.length();
+ while (words >> word)
+ {
+ if (space_left < word.length() + 1)
+ {
+ wrapped << '\n'
+ << word;
+ space_left = line_length - word.length();
+ }
+ else
+ {
+ wrapped << ' ' << word;
+ space_left -= word.length() + 1;
+ }
}
}
+ return wrapped.str();
+ };
+
+ // get the unique categories
+ std::set categories;
+ for (const auto &opt : this->options)
+ {
+ const ConfigOptionDef &def = opt.second;
+ if (filter(def))
+ categories.insert(def.category);
}
- return wrapped.str();
- };
- // get the unique categories
- std::set categories;
- for (const auto& opt : this->options) {
- const ConfigOptionDef& def = opt.second;
- if (filter(def))
- categories.insert(def.category);
- }
+ for (auto category : categories)
+ {
+ if (category != "")
+ {
+ out << category << ":" << std::endl;
+ }
+ else if (categories.size() > 1)
+ {
+ out << "Misc options:" << std::endl;
+ }
- for (auto category : categories) {
- if (category != "") {
- out << category << ":" << std::endl;
- } else if (categories.size() > 1) {
- out << "Misc options:" << std::endl;
- }
+ for (const auto &opt : this->options)
+ {
+ const ConfigOptionDef &def = opt.second;
+ if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
+ continue;
- for (const auto& opt : this->options) {
- const ConfigOptionDef& def = opt.second;
- if (def.category != category || def.cli == ConfigOptionDef::nocli || !filter(def))
- continue;
+ // get all possible variations: --foo, --foobar, -f...
+ std::vector cli_args = def.cli_args(opt.first);
+ if (cli_args.empty())
+ continue;
- // get all possible variations: --foo, --foobar, -f...
- std::vector cli_args = def.cli_args(opt.first);
- if (cli_args.empty())
- continue;
-
- for (auto& arg : cli_args) {
- arg.insert(0, (arg.size() == 1) ? "-" : "--");
- //BBS: refine the print help format
- if (!def.cli_params.empty())
- arg += " " + def.cli_params;
- /*if ( def.type == coInt || def.type == coInts) {
- arg += " int_value";
- } else if (def.type == coFloat || def.type == coFloatOrPercent || def.type == coFloats) {
- arg += " float_value";
- } else if (def.type == coPoint) {
- arg += " X,Y";
- } else if (def.type == coPoint3) {
- arg += " X,Y,Z";
- } else if (def.type == coString || def.type == coStrings) {
- arg += " filename_lists";
- }*/
- }
+ for (auto &arg : cli_args)
+ {
+ arg.insert(0, (arg.size() == 1) ? "-" : "--");
+ // BBS: refine the print help format
+ if (!def.cli_params.empty())
+ arg += " " + def.cli_params;
+ /*if ( def.type == coInt || def.type == coInts) {
+ arg += " int_value";
+ } else if (def.type == coFloat || def.type == coFloatOrPercent || def.type == coFloats) {
+ arg += " float_value";
+ } else if (def.type == coPoint) {
+ arg += " X,Y";
+ } else if (def.type == coPoint3) {
+ arg += " X,Y,Z";
+ } else if (def.type == coString || def.type == coStrings) {
+ arg += " filename_lists";
+ }*/
+ }
- // left: command line options
- const std::string cli = boost::algorithm::join(cli_args, ", ");
- out << " " << std::left << std::setw(20) << cli;
-
- // right: option description
- std::string descr = def.tooltip;
- bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
- if (show_defaults_this && def.default_value && def.type != coBool
- && (def.type != coString || !def.default_value->serialize().empty())) {
- descr += " (";
- if (!def.sidetext.empty()) {
- descr += def.sidetext + ", ";
- } else if (!def.enum_values.empty()) {
- descr += boost::algorithm::join(def.enum_values, ", ") + "; ";
+ // left: command line options
+ const std::string cli = boost::algorithm::join(cli_args, ", ");
+ out << " " << std::left << std::setw(20) << cli;
+
+ // right: option description
+ std::string descr = def.tooltip;
+ bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
+ if (show_defaults_this && def.default_value && def.type != coBool && (def.type != coString || !def.default_value->serialize().empty()))
+ {
+ descr += " (";
+ if (!def.sidetext.empty())
+ {
+ descr += def.sidetext + ", ";
+ }
+ else if (!def.enum_values.empty())
+ {
+ descr += boost::algorithm::join(def.enum_values, ", ") + "; ";
+ }
+ descr += "default: " + def.default_value->serialize() + ")";
}
- descr += "default: " + def.default_value->serialize() + ")";
- }
- // wrap lines of description
- descr = wrap(descr, 80);
- std::vector lines;
- boost::split(lines, descr, boost::is_any_of("\n"));
+ // wrap lines of description
+ descr = wrap(descr, 80);
+ std::vector lines;
+ boost::split(lines, descr, boost::is_any_of("\n"));
- // if command line options are too long, print description in new line
- for (size_t i = 0; i < lines.size(); ++i) {
- if (i == 0 && cli.size() > 19)
- out << std::endl;
- if (i > 0 || cli.size() > 19)
- out << std::string(21, ' ');
- out << lines[i] << std::endl;
+ // if command line options are too long, print description in new line
+ for (size_t i = 0; i < lines.size(); ++i)
+ {
+ if (i == 0 && cli.size() > 19)
+ out << std::endl;
+ if (i > 0 || cli.size() > 19)
+ out << std::string(21, ' ');
+ out << lines[i] << std::endl;
+ }
}
}
+ return out;
}
- return out;
-}
-void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent)
-{
- // loop through options and apply them
- for (const t_config_option_key &opt_key : keys) {
- // Create a new option with default value for the key.
- // If the key is not in the parameter definition, or this ConfigBase is a static type and it does not support the parameter,
- // an exception is thrown if not ignore_nonexistent.
- ConfigOption *my_opt = this->option(opt_key, true);
- if (my_opt == nullptr) {
- // opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
- // This is only possible if other is of DynamicConfig type.
- if (auto n = opt_key.find('#'); n != std::string::npos) {
- auto opt_key2 = opt_key.substr(0, n);
- auto my_opt2 = dynamic_cast(this->option(opt_key2));
- auto other_opt = other.option(opt_key2);
- if (my_opt2 == nullptr && other_opt) {
- my_opt2 = dynamic_cast(this->option(opt_key2, true));
- if (my_opt2->empty()) {
- my_opt2->resize(1, other_opt);
+ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent)
+ {
+ // loop through options and apply them
+ for (const t_config_option_key &opt_key : keys)
+ {
+ // Create a new option with default value for the key.
+ // If the key is not in the parameter definition, or this ConfigBase is a static type and it does not support the parameter,
+ // an exception is thrown if not ignore_nonexistent.
+ ConfigOption *my_opt = this->option(opt_key, true);
+ if (my_opt == nullptr)
+ {
+ // opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
+ // This is only possible if other is of DynamicConfig type.
+ if (auto n = opt_key.find('#'); n != std::string::npos)
+ {
+ auto opt_key2 = opt_key.substr(0, n);
+ auto my_opt2 = dynamic_cast(this->option(opt_key2));
+ auto other_opt = other.option(opt_key2);
+ if (my_opt2 == nullptr && other_opt)
+ {
+ my_opt2 = dynamic_cast(this->option(opt_key2, true));
+ if (my_opt2->empty())
+ {
+ my_opt2->resize(1, other_opt);
+ }
+ }
+ if (my_opt2)
+ {
+ int index = std::atoi(opt_key.c_str() + n + 1);
+ if (other_opt)
+ my_opt2->set_at(other_opt, index, index);
+ continue;
}
- }
- if (my_opt2) {
- int index = std::atoi(opt_key.c_str() + n + 1);
- if (other_opt)
- my_opt2->set_at(other_opt, index, index);
- continue;
}
+ if (ignore_nonexistent)
+ continue;
+ throw UnknownOptionException(opt_key);
}
- if (ignore_nonexistent)
- continue;
- throw UnknownOptionException(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (other_opt == nullptr)
+ {
+ // The key was not found in the source config, therefore it will not be initialized!
+ // printf("Not found, therefore not initialized: %s\n", opt_key.c_str());
+ }
+ else
+ my_opt->set(other_opt);
}
- const ConfigOption *other_opt = other.option(opt_key);
- if (other_opt == nullptr) {
- // The key was not found in the source config, therefore it will not be initialized!
-// printf("Not found, therefore not initialized: %s\n", opt_key.c_str());
- } else
- my_opt->set(other_opt);
}
-}
-// Are the two configs equal? Ignoring options not present in both configs.
-//BBS: add skipped keys logic
-bool ConfigBase::equals(const ConfigBase &other, const std::set* skipped_keys) const
-{
- for (const t_config_option_key &opt_key : this->keys()) {
- if (skipped_keys && (skipped_keys->count(opt_key) != 0))
- continue;
- const ConfigOption *this_opt = this->option(opt_key);
- const ConfigOption *other_opt = other.option(opt_key);
- if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
- return false;
+ // Are the two configs equal? Ignoring options not present in both configs.
+ // BBS: add skipped keys logic
+ bool ConfigBase::equals(const ConfigBase &other, const std::set *skipped_keys) const
+ {
+ for (const t_config_option_key &opt_key : this->keys())
+ {
+ if (skipped_keys && (skipped_keys->count(opt_key) != 0))
+ continue;
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
+ return false;
+ }
+ return true;
}
- return true;
-}
-// Returns options differing in the two configs, ignoring options not present in both configs.
-t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
-{
- t_config_option_keys diff;
- for (const t_config_option_key &opt_key : this->keys()) {
- const ConfigOption *this_opt = this->option(opt_key);
- const ConfigOption *other_opt = other.option(opt_key);
- if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
- diff.emplace_back(opt_key);
+ // Returns options differing in the two configs, ignoring options not present in both configs.
+ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
+ {
+ t_config_option_keys diff;
+ for (const t_config_option_key &opt_key : this->keys())
+ {
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
+ diff.emplace_back(opt_key);
+ }
+ return diff;
}
- return diff;
-}
-// Returns options being equal in the two configs, ignoring options not present in both configs.
-t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
-{
- t_config_option_keys equal;
- for (const t_config_option_key &opt_key : this->keys()) {
- const ConfigOption *this_opt = this->option(opt_key);
- const ConfigOption *other_opt = other.option(opt_key);
- if (this_opt != nullptr && other_opt != nullptr && *this_opt == *other_opt)
- equal.emplace_back(opt_key);
+ // Returns options being equal in the two configs, ignoring options not present in both configs.
+ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
+ {
+ t_config_option_keys equal;
+ for (const t_config_option_key &opt_key : this->keys())
+ {
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt == *other_opt)
+ equal.emplace_back(opt_key);
+ }
+ return equal;
}
- return equal;
-}
-
-std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
-{
- const ConfigOption* opt = this->option(opt_key);
- assert(opt != nullptr);
- return opt->serialize();
-}
-void ConfigBase::set(const std::string &opt_key, int value, bool create)
-{
- ConfigOption *opt = this->option_throw(opt_key, create);
- switch (opt->type()) {
- case coInt: static_cast(opt)->value = value; break;
- case coFloat: static_cast(opt)->value = value; break;
- case coFloatOrPercent: static_cast(opt)->value = value; static_cast(opt)->percent = false; break;
- case coString: static_cast(opt)->value = std::to_string(value); break;
- default: throw BadOptionTypeException("Configbase::set() - conversion from int not possible");
+ std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
+ {
+ const ConfigOption *opt = this->option(opt_key);
+ assert(opt != nullptr);
+ return opt->serialize();
}
-}
-void ConfigBase::set(const std::string &opt_key, double value, bool create)
-{
- ConfigOption *opt = this->option_throw(opt_key, create);
- switch (opt->type()) {
- case coFloat: static_cast(opt)->value = value; break;
- case coFloatOrPercent: static_cast(opt)->value = value; static_cast(opt)->percent = false; break;
- case coString: static_cast(opt)->value = float_to_string_decimal_point(value); break;
- default: throw BadOptionTypeException("Configbase::set() - conversion from float not possible");
+ void ConfigBase::set(const std::string &opt_key, int value, bool create)
+ {
+ ConfigOption *opt = this->option_throw(opt_key, create);
+ switch (opt->type())
+ {
+ case coInt:
+ static_cast(opt)->value = value;
+ break;
+ case coFloat:
+ static_cast(opt)->value = value;
+ break;
+ case coFloatOrPercent:
+ static_cast(opt)->value = value;
+ static_cast(opt)->percent = false;
+ break;
+ case coString:
+ static_cast(opt)->value = std::to_string(value);
+ break;
+ default:
+ throw BadOptionTypeException("Configbase::set() - conversion from int not possible");
+ }
}
-}
-bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append)
-{
- t_config_option_key opt_key = opt_key_src;
- std::string value = value_src;
- // Both opt_key and value may be modified by handle_legacy().
- // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
- this->handle_legacy(opt_key, value);
- if (opt_key.empty()) {
- // Ignore the option.
- //BBS: record these options, keep only one repeated opt_key
- auto iter = std::find(substitutions_ctxt.unrecogized_keys.begin(), substitutions_ctxt.unrecogized_keys.end(), opt_key_src);
- if (iter == substitutions_ctxt.unrecogized_keys.end())
- substitutions_ctxt.unrecogized_keys.push_back(opt_key_src);
- return true;
+ void ConfigBase::set(const std::string &opt_key, double value, bool create)
+ {
+ ConfigOption *opt = this->option_throw(opt_key, create);
+ switch (opt->type())
+ {
+ case coFloat:
+ static_cast(opt)->value = value;
+ break;
+ case coFloatOrPercent:
+ static_cast(opt)->value = value;
+ static_cast(opt)->percent = false;
+ break;
+ case coString:
+ static_cast(opt)->value = float_to_string_decimal_point(value);
+ break;
+ default:
+ throw BadOptionTypeException("Configbase::set() - conversion from float not possible");
+ }
}
- return this->set_deserialize_raw(opt_key, value, substitutions_ctxt, append);
-}
-
-void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append)
-{
- if (! this->set_deserialize_nothrow(opt_key_src, value_src, substitutions_ctxt, append))
- throw BadOptionValueException(format("Invalid value provided for parameter %1%: %2%", opt_key_src, value_src));
-}
-
-void ConfigBase::set_deserialize(std::initializer_list items, ConfigSubstitutionContext& substitutions_ctxt)
-{
- for (const SetDeserializeItem &item : items)
- this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append);
-}
-bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append)
-{
- t_config_option_key opt_key = opt_key_src;
- // Try to deserialize the option by its name.
- const ConfigDef *def = this->def();
- if (def == nullptr)
- throw NoDefinitionException(opt_key);
- const ConfigOptionDef *optdef = def->get(opt_key);
- if (optdef == nullptr) {
- // If we didn't find an option, look for any other option having this as an alias.
- for (const auto &opt : def->options) {
- for (const t_config_option_key &opt_key2 : opt.second.aliases) {
- if (opt_key2 == opt_key) {
- opt_key = opt.first;
- optdef = &opt.second;
- break;
- }
- }
- if (optdef != nullptr)
- break;
+ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext &substitutions_ctxt, bool append)
+ {
+ t_config_option_key opt_key = opt_key_src;
+ std::string value = value_src;
+ // Both opt_key and value may be modified by handle_legacy().
+ // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
+ this->handle_legacy(opt_key, value);
+ if (opt_key.empty())
+ {
+ // Ignore the option.
+ // BBS: record these options, keep only one repeated opt_key
+ auto iter = std::find(substitutions_ctxt.unrecogized_keys.begin(), substitutions_ctxt.unrecogized_keys.end(), opt_key_src);
+ if (iter == substitutions_ctxt.unrecogized_keys.end())
+ substitutions_ctxt.unrecogized_keys.push_back(opt_key_src);
+ return true;
}
- if (optdef == nullptr)
- throw UnknownOptionException(opt_key);
+ return this->set_deserialize_raw(opt_key, value, substitutions_ctxt, append);
}
- if (! optdef->shortcut.empty()) {
- // Aliasing for example "solid_layers" to "top_shell_layers" and "bottom_shell_layers".
- for (const t_config_option_key &shortcut : optdef->shortcut)
- // Recursive call.
- if (! this->set_deserialize_raw(shortcut, value, substitutions_ctxt, append))
- return false;
- return true;
+ void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext &substitutions_ctxt, bool append)
+ {
+ if (!this->set_deserialize_nothrow(opt_key_src, value_src, substitutions_ctxt, append))
+ throw BadOptionValueException(format("Invalid value provided for parameter %1%: %2%", opt_key_src, value_src));
}
- ConfigOption *opt = this->option(opt_key, true);
- assert(opt != nullptr);
- bool success = false;
- bool substituted = false;
- if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) {
- //FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release.
- bool nullable = opt->nullable();
- ConfigHelpers::DeserializationSubstitution default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToFalse;
- 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;
- if (values.size() == 1 && values.front() == 1)
- default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
- }
- auto result = nullable ?
- static_cast(opt)->deserialize_with_substitutions(value, append, default_value) :
- static_cast(opt)->deserialize_with_substitutions(value, append, default_value);
- success = result != ConfigHelpers::DeserializationResult::Failed;
- substituted = result == ConfigHelpers::DeserializationResult::Substituted;
- } else {
- //bool test = (opt_key == "filament_end_gcode");
- success = opt->deserialize(value, append);
- if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable &&
- // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value.
- // That means, we expect enum values being added in the future and possibly booleans being converted to enums.
- (optdef->type == coEnum || optdef->type == coEnums || optdef->type == coBool) /*&& ConfigHelpers::looks_like_enum_value(value)*/) {
- // Deserialize failed, try to substitute with a default value.
- //assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent);
- if (optdef->type == coBool)
- static_cast(opt)->value = ConfigHelpers::enum_looks_like_true_value(value);
- else
- // Just use the default of the option.
- opt->set(optdef->default_value.get());
- success = true;
- substituted = true;
- }
- }
-
- if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable ||
- substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) {
- // Log the substitution.
- ConfigSubstitution config_substitution;
- config_substitution.opt_def = optdef;
- config_substitution.old_value = value;
- config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
- substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
+ void ConfigBase::set_deserialize(std::initializer_list items, ConfigSubstitutionContext &substitutions_ctxt)
+ {
+ for (const SetDeserializeItem &item : items)
+ this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append);
}
- return success;
-}
-double ConfigBase::get_abs_value_at(const t_config_option_key &opt_key, size_t index) const
-{
- const ConfigOption *raw_opt = this->option(opt_key);
- assert(raw_opt != nullptr);
- if (raw_opt->type() == coFloats) {
- return static_cast(raw_opt)->get_at(index);
- }
- if (raw_opt->type() == coFloatsOrPercents) {
+ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext &substitutions_ctxt, bool append)
+ {
+ t_config_option_key opt_key = opt_key_src;
+ // Try to deserialize the option by its name.
const ConfigDef *def = this->def();
- if (def == nullptr) throw NoDefinitionException(opt_key);
- const ConfigOptionDef *opt_def = def->get(opt_key);
- assert(opt_def != nullptr);
+ if (def == nullptr)
+ throw NoDefinitionException(opt_key);
+ const ConfigOptionDef *optdef = def->get(opt_key);
+ if (optdef == nullptr)
+ {
+ // If we didn't find an option, look for any other option having this as an alias.
+ for (const auto &opt : def->options)
+ {
+ for (const t_config_option_key &opt_key2 : opt.second.aliases)
+ {
+ if (opt_key2 == opt_key)
+ {
+ opt_key = opt.first;
+ optdef = &opt.second;
+ break;
+ }
+ }
+ if (optdef != nullptr)
+ break;
+ }
+ if (optdef == nullptr)
+ throw UnknownOptionException(opt_key);
+ }
- if (opt_def->ratio_over.empty()) {
- return 0;
- } else {
- const ConfigOption *ratio_opt = this->option(opt_def->ratio_over);
- assert(ratio_opt->type() == coFloats);
- const ConfigOptionFloats *ratio_values = static_cast(ratio_opt);
- return static_cast(raw_opt)->get_at(index).get_abs_value(ratio_values->get_at(index));
+ if (!optdef->shortcut.empty())
+ {
+ // Aliasing for example "solid_layers" to "top_shell_layers" and "bottom_shell_layers".
+ for (const t_config_option_key &shortcut : optdef->shortcut)
+ // Recursive call.
+ if (!this->set_deserialize_raw(shortcut, value, substitutions_ctxt, append))
+ return false;
+ return true;
}
- }
- throw ConfigurationError("ConfigBase::get_abs_value_at(): Not a valid option type for get_abs_value_at()");
-}
+ ConfigOption *opt = this->option(opt_key, true);
+ assert(opt != nullptr);
+ bool success = false;
+ bool substituted = false;
+ if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable)
+ {
+ // FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release.
+ bool nullable = opt->nullable();
+ ConfigHelpers::DeserializationSubstitution default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToFalse;
+ 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;
+ if (values.size() == 1 && values.front() == 1)
+ default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
+ }
+ auto result = nullable ? static_cast(opt)->deserialize_with_substitutions(value, append, default_value) : static_cast(opt)->deserialize_with_substitutions(value, append, default_value);
+ success = result != ConfigHelpers::DeserializationResult::Failed;
+ substituted = result == ConfigHelpers::DeserializationResult::Substituted;
+ }
+ else
+ {
+ // bool test = (opt_key == "filament_end_gcode");
+ success = opt->deserialize(value, append);
+ if (!success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable &&
+ // Only allow substitutions of an enum value by another enum value or a boolean value with an enum value.
+ // That means, we expect enum values being added in the future and possibly booleans being converted to enums.
+ (optdef->type == coEnum || optdef->type == coEnums || optdef->type == coBool) /*&& ConfigHelpers::looks_like_enum_value(value)*/)
+ {
+ // Deserialize failed, try to substitute with a default value.
+ // assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent);
+ if (optdef->type == coBool)
+ static_cast(opt)->value = ConfigHelpers::enum_looks_like_true_value(value);
+ else
+ // Just use the default of the option.
+ opt->set(optdef->default_value.get());
+ success = true;
+ substituted = true;
+ }
+ }
-// Return an absolute value of a possibly relative config variable.
-// For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height.
-double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
-{
- // Get stored option value.
- const ConfigOption *raw_opt = this->option(opt_key);
- assert(raw_opt != nullptr);
- if (raw_opt->type() == coFloat)
- return static_cast(raw_opt)->value;
- if (raw_opt->type() == coFloatOrPercent) {
- // Get option definition.
- const ConfigDef *def = this->def();
- if (def == nullptr)
- throw NoDefinitionException(opt_key);
- const ConfigOptionDef *opt_def = def->get(opt_key);
- assert(opt_def != nullptr);
- // Compute absolute value over the absolute value of the base option.
- //FIXME there are some ratio_over chains, which end with empty ratio_with.
- // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
- return opt_def->ratio_over.empty() ? 0. :
- static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
+ if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable ||
+ substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent))
+ {
+ // Log the substitution.
+ ConfigSubstitution config_substitution;
+ config_substitution.opt_def = optdef;
+ config_substitution.old_value = value;
+ config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
+ substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
+ }
+ return success;
}
- std::string err_info = "ConfigBase::get_abs_value(): Not a valid option type for get_abs_value(), parameter : " + opt_key;
- throw ConfigurationError(err_info);
-}
-// Return an absolute value of a possibly relative config variable.
-// For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value.
-double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const
-{
- // Get stored option value.
- const ConfigOption *raw_opt = this->option(opt_key);
- assert(raw_opt != nullptr);
- if (raw_opt->type() != coFloatOrPercent)
- throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
- // Compute absolute value.
- return static_cast(raw_opt)->get_abs_value(ratio_over);
-}
+ double ConfigBase::get_abs_value_at(const t_config_option_key &opt_key, size_t index) const
+ {
+ const ConfigOption *raw_opt = this->option(opt_key);
+ assert(raw_opt != nullptr);
+ if (raw_opt->type() == coFloats)
+ {
+ return static_cast(raw_opt)->get_at(index);
+ }
+ if (raw_opt->type() == coFloatsOrPercents)
+ {
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException(opt_key);
+ const ConfigOptionDef *opt_def = def->get(opt_key);
+ assert(opt_def != nullptr);
-void ConfigBase::setenv_() const
-{
- t_config_option_keys opt_keys = this->keys();
- for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
- // prepend the SLIC3R_ prefix
- std::ostringstream ss;
- ss << "SLIC3R_";
- ss << *it;
- std::string envname = ss.str();
-
- // capitalize environment variable name
- for (size_t i = 0; i < envname.size(); ++i)
- envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
-
- boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1);
+ if (opt_def->ratio_over.empty())
+ {
+ return 0;
+ }
+ else
+ {
+ const ConfigOption *ratio_opt = this->option(opt_def->ratio_over);
+ assert(ratio_opt->type() == coFloats);
+ const ConfigOptionFloats *ratio_values = static_cast(ratio_opt);
+ return static_cast(raw_opt)->get_at(index).get_abs_value(ratio_values->get_at(index));
+ }
+ }
+
+ throw ConfigurationError("ConfigBase::get_abs_value_at(): Not a valid option type for get_abs_value_at()");
}
-}
-//BBS
-ConfigSubstitutions ConfigBase::load_string_map(std::map& key_values, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- CNumericLocalesSetter locales_setter;
-
- ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
- std::map::iterator it;
- for (it = key_values.begin(); it != key_values.end(); it++) {
- try {
- t_config_option_key opt_key = it->first;
- this->set_deserialize(opt_key, it->second, substitutions_ctxt);
- }
- catch (UnknownOptionException& /* e */) {
- // ignore
+ // Return an absolute value of a possibly relative config variable.
+ // For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height.
+ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
+ {
+ // Get stored option value.
+ const ConfigOption *raw_opt = this->option(opt_key);
+ assert(raw_opt != nullptr);
+ if (raw_opt->type() == coFloat)
+ return static_cast(raw_opt)->value;
+ if (raw_opt->type() == coFloatOrPercent)
+ {
+ // Get option definition.
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException(opt_key);
+ const ConfigOptionDef *opt_def = def->get(opt_key);
+ assert(opt_def != nullptr);
+ // Compute absolute value over the absolute value of the base option.
+ // FIXME there are some ratio_over chains, which end with empty ratio_with.
+ // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
+ return opt_def->ratio_over.empty() ? 0. : static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
}
+ std::string err_info = "ConfigBase::get_abs_value(): Not a valid option type for get_abs_value(), parameter : " + opt_key;
+ throw ConfigurationError(err_info);
}
- return std::move(substitutions_ctxt.substitutions);
-}
-//BBS: add json support
-ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- std::map key_values;
- if (is_gcode_file(file))
- return this->load_from_gcode_file(file, compatibility_rule);
- else if (is_json_file(file)) {
- std::string reason;
- return this->load_from_json(file, compatibility_rule, key_values, reason);
- }
- else {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "unsupported format for config file" << file;
- return ConfigSubstitutions();
- //return this->load_from_ini(file, compatibility_rule);
+ // Return an absolute value of a possibly relative config variable.
+ // For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value.
+ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const
+ {
+ // Get stored option value.
+ const ConfigOption *raw_opt = this->option(opt_key);
+ assert(raw_opt != nullptr);
+ if (raw_opt->type() != coFloatOrPercent)
+ throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
+ // Compute absolute value.
+ return static_cast(raw_opt)->get_abs_value(ratio_over);
}
-}
-//BBS: add json support
-ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule, std::map& key_values, std::string& reason)
-{
- int ret = 0;
- ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+ void ConfigBase::setenv_() const
+ {
+ t_config_option_keys opt_keys = this->keys();
+ for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it)
+ {
+ // prepend the SLIC3R_ prefix
+ std::ostringstream ss;
+ ss << "SLIC3R_";
+ ss << *it;
+ std::string envname = ss.str();
- ret = load_from_json(file, substitutions_ctxt, true, key_values, reason);
- return std::move(substitutions_ctxt.substitutions);
-}
+ // capitalize environment variable name
+ for (size_t i = 0; i < envname.size(); ++i)
+ envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i] - ('a' - 'A') : envname[i];
-int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map& key_values, std::string& reason)
-{
- if (!boost::filesystem::exists(file)) {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("%1% file not exist.") % file;
- return -1;
+ boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1);
+ }
}
- json j;
- std::list different_settings_append;
- std::string new_support_style;
- std::string is_infill_first;
- std::string get_wall_sequence;
- bool is_project_settings = false;
-
- CNumericLocalesSetter locales_setter;
-
- std::function parse_str_arr = [&parse_str_arr](const json::const_iterator& it, const char single_sep,const char array_sep,const bool escape_string_style,std::string& value_str)->bool {
- // must have consistent type name
- std::string consistent_type;
- for (auto iter = it.value().begin(); iter != it.value().end(); ++iter) {
- if (consistent_type.empty())
- consistent_type = iter.value().type_name();
- else {
- if (consistent_type != iter.value().type_name())
- return false;
+
+ // BBS
+ ConfigSubstitutions ConfigBase::load_string_map(std::map &key_values, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ {
+ CNumericLocalesSetter locales_setter;
+
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+ std::map::iterator it;
+ for (it = key_values.begin(); it != key_values.end(); it++)
+ {
+ try
+ {
+ t_config_option_key opt_key = it->first;
+ this->set_deserialize(opt_key, it->second, substitutions_ctxt);
+ }
+ catch (UnknownOptionException & /* e */)
+ {
+ // ignore
}
}
+ return std::move(substitutions_ctxt.substitutions);
+ }
+
+ // BBS: add json support
+ ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ {
+ std::map key_values;
+ if (is_gcode_file(file))
+ return this->load_from_gcode_file(file, compatibility_rule);
+ else if (is_json_file(file))
+ {
+ std::string reason;
+ return this->load_from_json(file, compatibility_rule, key_values, reason);
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "unsupported format for config file" << file;
+ return ConfigSubstitutions();
+ // return this->load_from_ini(file, compatibility_rule);
+ }
+ }
+
+ // BBS: add json support
+ ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule, std::map &key_values, std::string &reason)
+ {
+ int ret = 0;
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+
+ ret = load_from_json(file, substitutions_ctxt, true, key_values, reason);
+ return std::move(substitutions_ctxt.substitutions);
+ }
+
+ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext &substitution_context, bool load_inherits_to_config, std::map &key_values, std::string &reason)
+ {
+ if (!boost::filesystem::exists(file))
+ {
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("%1% file not exist.") % file;
+ return -1;
+ }
+ json j;
+ std::list different_settings_append;
+ std::string new_support_style;
+ std::string is_outer_second;
+ std::string is_infill_first;
+ std::string get_wall_sequence;
+ bool is_project_settings = false;
+
+ CNumericLocalesSetter locales_setter;
- bool first = true;
- for (auto iter = it.value().begin(); iter != it.value().end(); iter++) {
- if (iter.value().is_array()) {
- if (!first)
- value_str += array_sep;
+ std::function parse_str_arr = [&parse_str_arr](const json::const_iterator &it, const char single_sep, const char array_sep, const bool escape_string_style, std::string &value_str) -> bool
+ {
+ // must have consistent type name
+ std::string consistent_type;
+ for (auto iter = it.value().begin(); iter != it.value().end(); ++iter)
+ {
+ if (consistent_type.empty())
+ consistent_type = iter.value().type_name();
else
- first = false;
- bool success = parse_str_arr(iter, single_sep, array_sep,escape_string_style, value_str);
- if (!success)
- return false;
+ {
+ if (consistent_type != iter.value().type_name())
+ return false;
+ }
}
- else if (iter.value().is_string()) {
- if (!first)
- value_str += single_sep;
+
+ bool first = true;
+ for (auto iter = it.value().begin(); iter != it.value().end(); iter++)
+ {
+ if (iter.value().is_array())
+ {
+ if (!first)
+ value_str += array_sep;
+ else
+ first = false;
+ bool success = parse_str_arr(iter, single_sep, array_sep, escape_string_style, value_str);
+ if (!success)
+ return false;
+ }
+ else if (iter.value().is_string())
+ {
+ if (!first)
+ value_str += single_sep;
+ else
+ first = false;
+ if (!escape_string_style)
+ value_str += iter.value();
+ else
+ {
+ value_str += "\"";
+ value_str += escape_string_cstyle(iter.value());
+ value_str += "\"";
+ }
+ }
else
- first = false;
- if (!escape_string_style)
- value_str += iter.value();
- else {
- value_str += "\"";
- value_str += escape_string_cstyle(iter.value());
- value_str += "\"";
+ {
+ // should not happen
+ return false;
}
}
- else {
- //should not happen
- return false;
- }
- }
- return true;
+ return true;
};
- try {
- boost::nowide::ifstream ifs(file);
- ifs >> j;
- ifs.close();
+ try
+ {
+ boost::nowide::ifstream ifs(file);
+ ifs >> j;
+ ifs.close();
- const ConfigDef* config_def = this->def();
- if (config_def == nullptr) {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no config defs!";
- return -1;
- }
- //parse the json elements
- for (auto it = j.begin(); it != j.end(); it++) {
- if (boost::iequals(it.key(),BBL_JSON_KEY_VERSION)) {
- key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
- //skip it
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
- key_values.emplace(BBL_JSON_KEY_NAME, it.value());
- if (it.value() == "project_settings")
- is_project_settings = true;
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_URL)) {
- key_values.emplace(BBL_JSON_KEY_URL, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_TYPE)) {
- key_values.emplace(BBL_JSON_KEY_TYPE, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_SETTING_ID)) {
- key_values.emplace(BBL_JSON_KEY_SETTING_ID, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID)) {
- key_values.emplace(BBL_JSON_KEY_FILAMENT_ID, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_FROM)) {
- key_values.emplace(BBL_JSON_KEY_FROM, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) {
- key_values.emplace(BBL_JSON_KEY_DESCRIPTION, it.value());
- }
- else if (boost::iequals(it.key(), BBL_JSON_KEY_INSTANTIATION)) {
- key_values.emplace(BBL_JSON_KEY_INSTANTIATION, it.value());
- }
- else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INHERITS)) {
- key_values.emplace(BBL_JSON_KEY_INHERITS, it.value());
- }
- else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INCLUDES)) {
- key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump());
+ const ConfigDef *config_def = this->def();
+ if (config_def == nullptr)
+ {
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": no config defs!";
+ return -1;
}
- else {
- t_config_option_key opt_key = it.key();
- std::string value_str;
-
- if (it.value().is_string()) {
- //bool test1 = (it.key() == std::string("end_gcode"));
- this->set_deserialize(opt_key, it.value(), substitution_context);
- //some logic for special values
- if (opt_key == "support_type") {
- //std::string new_value = dynamic_cast(this->option(opt_key))->value;
- if (it.value() == "hybrid(auto)") {
- different_settings_append.push_back(opt_key);
- different_settings_append.push_back("support_style");
- new_support_style = "tree_hybrid";
- }
- } else if (opt_key == "wall_infill_order") {
- //BBS: check wall_infill order to decide if it be different and append to diff_setting_append
- if (it.value() == "outer wall/inner wall/infill" || it.value() == "infill/outer wall/inner wall" || it.value() == "inner-outer-inner wall/infill") {
- get_wall_sequence = "wall_seq_diff_to_system";
+ // parse the json elements
+ for (auto it = j.begin(); it != j.end(); it++)
+ {
+ if (boost::iequals(it.key(), BBL_JSON_KEY_VERSION))
+ {
+ key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM))
+ {
+ // skip it
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME))
+ {
+ key_values.emplace(BBL_JSON_KEY_NAME, it.value());
+ if (it.value() == "project_settings")
+ is_project_settings = true;
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_URL))
+ {
+ key_values.emplace(BBL_JSON_KEY_URL, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_TYPE))
+ {
+ key_values.emplace(BBL_JSON_KEY_TYPE, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_SETTING_ID))
+ {
+ key_values.emplace(BBL_JSON_KEY_SETTING_ID, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID))
+ {
+ key_values.emplace(BBL_JSON_KEY_FILAMENT_ID, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_FROM))
+ {
+ key_values.emplace(BBL_JSON_KEY_FROM, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_DESCRIPTION))
+ {
+ key_values.emplace(BBL_JSON_KEY_DESCRIPTION, it.value());
+ }
+ else if (boost::iequals(it.key(), BBL_JSON_KEY_INSTANTIATION))
+ {
+ key_values.emplace(BBL_JSON_KEY_INSTANTIATION, it.value());
+ }
+ else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INHERITS))
+ {
+ key_values.emplace(BBL_JSON_KEY_INHERITS, it.value());
+ }
+ else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INCLUDES))
+ {
+ key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump());
+ }
+ else
+ {
+ t_config_option_key opt_key = it.key();
+ std::string value_str;
+
+ if (it.value().is_string())
+ {
+ // bool test1 = (it.key() == std::string("end_gcode"));
+ this->set_deserialize(opt_key, it.value(), substitution_context);
+ // some logic for special values
+ if (opt_key == "support_type")
+ {
+ // std::string new_value = dynamic_cast(this->option(opt_key))->value;
+ if (it.value() == "hybrid(auto)")
+ {
+ different_settings_append.push_back(opt_key);
+ different_settings_append.push_back("support_style");
+ new_support_style = "tree_hybrid";
+ }
}
+ else if (opt_key == "wall_infill_order")
+ {
+ // BBS: check wall_infill order to decide if it be different and append to diff_setting_append
+ if (it.value() == "outer wall/inner wall/infill" || it.value() == "infill/outer wall/inner wall" || it.value() == "inner-outer-inner wall/infill" || it.value() == "infill/outer wall/inner/inner wall" || it.value() == "inner-outer-inner/inner wall/infill")
+ {
+ get_wall_sequence = "wall_seq_diff_to_system";
+ }
+
+ if (it.value() == "infill/outer wall/inner wall" || it.value() == "infill/inner wall/outer wall")
+ {
+ different_settings_append.push_back("is_infill_first");
+ is_infill_first = "true";
- if (it.value() == "infill/outer wall/inner wall" || it.value() == "infill/inner wall/outer wall") {
- different_settings_append.push_back("is_infill_first");
- is_infill_first = "true";
+ // different_settings_append.push_back("is_outer_second");
+ // is_outer_second = "true";
+ }
}
}
- }
- else if (it.value().is_array()) {
- t_config_option_key opt_key_src = opt_key;
- this->handle_legacy(opt_key, value_str);
- if (opt_key.empty()) {
- //BBS: record these options
- substitution_context.unrecogized_keys.push_back(opt_key_src);
- continue;
- }
- bool valid = true, first = true;
- const ConfigOptionDef* optdef = config_def->get(opt_key);
- if (optdef == nullptr) {
- // If we didn't find an option, look for any other option having this as an alias.
- for (const auto& opt : config_def->options) {
- for (const t_config_option_key& opt_key2 : opt.second.aliases) {
- if (opt_key2 == opt_key) {
- opt_key = opt.first;
- optdef = &opt.second;
- break;
+ else if (it.value().is_array())
+ {
+ t_config_option_key opt_key_src = opt_key;
+ this->handle_legacy(opt_key, value_str);
+ if (opt_key.empty())
+ {
+ // BBS: record these options
+ substitution_context.unrecogized_keys.push_back(opt_key_src);
+ continue;
+ }
+ bool valid = true, first = true;
+ const ConfigOptionDef *optdef = config_def->get(opt_key);
+ if (optdef == nullptr)
+ {
+ // If we didn't find an option, look for any other option having this as an alias.
+ for (const auto &opt : config_def->options)
+ {
+ for (const t_config_option_key &opt_key2 : opt.second.aliases)
+ {
+ if (opt_key2 == opt_key)
+ {
+ opt_key = opt.first;
+ optdef = &opt.second;
+ break;
+ }
}
+ if (optdef != nullptr)
+ break;
}
- if (optdef != nullptr)
+ }
+
+ char single_sep = ',';
+ char array_sep = '#'; // currenty not used
+ bool escape_string_type = false;
+ if (optdef)
+ {
+ switch (optdef->type)
+ {
+ case coStrings:
+ escape_string_type = true;
+ single_sep = ';';
+ break;
+ case coPointsGroups:
+ single_sep = '#';
break;
+ default:
+ break;
+ }
}
- }
- char single_sep = ',';
- char array_sep = '#'; // currenty not used
- bool escape_string_type = false;
- if (optdef) {
- switch (optdef->type)
+ // BBS: we only support 2 depth array
+ valid = parse_str_arr(it, single_sep, array_sep, escape_string_type, value_str);
+ if (!valid)
{
- case coStrings:
- escape_string_type = true;
- single_sep = ';';
- break;
- case coPointsGroups:
- single_sep = '#';
- break;
- default:
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file << " error, invalid json array for " << it.key();
break;
}
+ if (valid)
+ this->set_deserialize(opt_key, value_str, substitution_context);
}
-
- // BBS: we only support 2 depth array
- valid = parse_str_arr(it, single_sep, array_sep,escape_string_type, value_str);
- if (!valid) {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file << " error, invalid json array for " << it.key();
- break;
+ else
+ {
+ // should not happen
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file << " error, invalid json type for " << it.key();
}
- if (valid)
- this->set_deserialize(opt_key, value_str, substitution_context);
}
- else {
- //should not happen
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<* opt = this->option>("support_style", true);
- opt->value = smsTreeHybrid;
}
+ if (!different_settings_append.empty())
+ {
+ if (!new_support_style.empty())
+ {
+ ConfigOptionEnum *opt = this->option>("support_style", true);
+ opt->value = smsTreeHybrid;
+ }
- if (!is_infill_first.empty()) {
- ConfigOptionBool *opt = this->option("is_infill_first", true);
- opt->value = true;
- }
+ if (!is_outer_second.empty())
+ {
+ ConfigOptionBool *opt = this->option("is_outer_second", true);
+ opt->value = true;
+ }
- if (is_project_settings) {
- std::vector& different_settings = this->option("different_settings_to_system", true)->values;
- size_t size = different_settings.size();
- if (size == 0) {
- size = this->option("filament_settings_id")->values.size() + 2;
- different_settings.resize(size);
+ if (!is_infill_first.empty())
+ {
+ ConfigOptionBool *opt = this->option("is_infill_first", true);
+ opt->value = true;
}
- std::vector is_first(size, false);
- std::vector> original_diffs(size);
- for (int index = 0; index < size; index++)
+ if (is_project_settings)
{
- if (different_settings[index].empty()) {
- is_first[index] = true;
+ std::vector &different_settings = this->option("different_settings_to_system", true)->values;
+ size_t size = different_settings.size();
+ if (size == 0)
+ {
+ size = this->option("filament_settings_id")->values.size() + 2;
+ different_settings.resize(size);
}
- else {
- // remove unneeded key
- if (get_wall_sequence.empty()) {
- std::string wall_sqe_string = "wall_sequence";
- int pos=different_settings[index].find(wall_sqe_string);
-
- if (pos != different_settings[index].npos) {
- int erase_len = wall_sqe_string.size();
- if (pos + erase_len < different_settings[index].size() && different_settings[index][pos + erase_len] == ';')
- erase_len++;
- different_settings[index].erase(pos, erase_len);
- }
-
- }
- if (different_settings[index].empty()) {
+ std::vector is_first(size, false);
+ std::vector> original_diffs(size);
+ for (int index = 0; index < size; index++)
+ {
+ if (different_settings[index].empty())
+ {
is_first[index] = true;
- continue;
}
+ else
+ {
+ // remove unneeded key
+ if (get_wall_sequence.empty())
+ {
+ std::string wall_sqe_string = "wall_sequence";
+ int pos = different_settings[index].find(wall_sqe_string);
+
+ if (pos != different_settings[index].npos)
+ {
+ int erase_len = wall_sqe_string.size();
+ if (pos + erase_len < different_settings[index].size() && different_settings[index][pos + erase_len] == ';')
+ erase_len++;
+ different_settings[index].erase(pos, erase_len);
+ }
+ }
- Slic3r::unescape_strings_cstyle(different_settings[index], original_diffs[index]);
+ if (different_settings[index].empty())
+ {
+ is_first[index] = true;
+ continue;
+ }
+
+ Slic3r::unescape_strings_cstyle(different_settings[index], original_diffs[index]);
+ }
}
- }
- for (auto diff_key : different_settings_append)
- {
- //get the index in the group
- int index = 0;
- bool need_insert = true;
- if (diff_key == "support_type")
- index = 0;
- else if (diff_key == "support_style")
- index = 0;
- else if (diff_key == "is_infill_first")
- index = 0;
-
- //check whether exist firstly
- if (!original_diffs[index].empty()) {
- for (int j = 0; j < original_diffs[index].size(); j++) {
- if (original_diffs[index][j] == diff_key) {
- need_insert = false;
- break;
+ for (auto diff_key : different_settings_append)
+ {
+ // get the index in the group
+ int index = 0;
+ bool need_insert = true;
+ if (diff_key == "support_type")
+ index = 0;
+ else if (diff_key == "support_style")
+ index = 0;
+ else if (diff_key == "is_outer_second")
+ index = 0;
+ else if (diff_key == "is_infill_first")
+ index = 0;
+
+ // check whether exist firstly
+ if (!original_diffs[index].empty())
+ {
+ for (int j = 0; j < original_diffs[index].size(); j++)
+ {
+ if (original_diffs[index][j] == diff_key)
+ {
+ need_insert = false;
+ break;
+ }
}
}
- }
- if (!need_insert)
- continue;
+ if (!need_insert)
+ continue;
- //insert this key
- if (!is_first[index])
- different_settings[index] += ";";
- else
- is_first[index] = false;
- different_settings[index] += diff_key;
+ // insert this key
+ if (!is_first[index])
+ different_settings[index] += ";";
+ else
+ is_first[index] = false;
+ different_settings[index] += diff_key;
+ }
}
}
+ return 0;
}
- return 0;
- }
- catch (const std::ifstream::failure &err) {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<load(tree, compatibility_rule);
+ }
+ catch (const ConfigurationError &e)
+ {
+ throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what()));
+ }
}
- return -1;
-}
-ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- try {
+ ConfigSubstitutions ConfigBase::load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ {
boost::property_tree::ptree tree;
- boost::nowide::ifstream ifs(file);
- boost::property_tree::read_ini(ifs, tree);
+ std::istringstream iss(data);
+ boost::property_tree::read_ini(iss, tree);
return this->load(tree, compatibility_rule);
- } catch (const ConfigurationError &e) {
- throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what()));
}
-}
-
-ConfigSubstitutions ConfigBase::load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- boost::property_tree::ptree tree;
- std::istringstream iss(data);
- boost::property_tree::read_ini(iss, tree);
- return this->load(tree, compatibility_rule);
-}
-
-// Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF.
-// Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment).
-ConfigSubstitutions ConfigBase::load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- // Convert the "data" string into INI format by removing the semi-colons at the start of a line.
- // Also the "; generated by PrusaSlicer ..." comment line will be removed.
- size_t j = 0;
- for (size_t i = 0; i < data.size();)
- if (i == 0 || data[i] == '\n') {
- // Start of a line.
- if (data[i] == '\n') {
- // Consume LF, don't keep empty lines.
- if (j > 0 && data[j - 1] != '\n')
- data[j ++] = data[i];
- ++ i;
- }
- // Skip all leading spaces;
- for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++ i) ;
- // Skip the semicolon (comment indicator).
- if (i < data.size() && data[i] == ';')
- ++ i;
- // Skip all leading spaces after semicolon.
- for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++ i) ;
- if (strncmp(data.data() + i, "generated by ", 13) == 0) {
- // Skip the "; generated by ..." line.
- for (; i < data.size() && data[i] != '\n'; ++ i);
- }
- } else if (data[i] == '\r' && i + 1 < data.size() && data[i + 1] == '\n') {
- // Skip CR.
- ++ i;
- } else {
- // Consume the rest of the data.
- data[j ++] = data[i ++];
- }
- data.erase(data.begin() + j, data.end());
-
- return this->load_from_ini_string(data, compatibility_rule);
-}
-ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
- for (const boost::property_tree::ptree::value_type &v : tree) {
- try {
- t_config_option_key opt_key = v.first;
- this->set_deserialize(opt_key, v.second.get_value(), substitutions_ctxt);
- } catch (UnknownOptionException & /* e */) {
- // ignore
- }
- }
- return std::move(substitutions_ctxt.substitutions);
-}
-
-// BBS
-static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
-static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; }
-static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); }
-static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); }
-
-static const char* skip_word(const char* c) {
- for (; !is_end_of_word(*c); ++c)
- ; // silence -Wempty-body
- return c;
-}
+ // Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF.
+ // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment).
+ ConfigSubstitutions ConfigBase::load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ {
+ // Convert the "data" string into INI format by removing the semi-colons at the start of a line.
+ // Also the "; generated by PrusaSlicer ..." comment line will be removed.
+ size_t j = 0;
+ for (size_t i = 0; i < data.size();)
+ if (i == 0 || data[i] == '\n')
+ {
+ // Start of a line.
+ if (data[i] == '\n')
+ {
+ // Consume LF, don't keep empty lines.
+ if (j > 0 && data[j - 1] != '\n')
+ data[j++] = data[i];
+ ++i;
+ }
+ // Skip all leading spaces;
+ for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++i)
+ ;
+ // Skip the semicolon (comment indicator).
+ if (i < data.size() && data[i] == ';')
+ ++i;
+ // Skip all leading spaces after semicolon.
+ for (; i < data.size() && (data[i] == ' ' || data[i] == '\t'); ++i)
+ ;
+ if (strncmp(data.data() + i, "generated by ", 13) == 0)
+ {
+ // Skip the "; generated by ..." line.
+ for (; i < data.size() && data[i] != '\n'; ++i)
+ ;
+ }
+ }
+ else if (data[i] == '\r' && i + 1 < data.size() && data[i + 1] == '\n')
+ {
+ // Skip CR.
+ ++i;
+ }
+ else
+ {
+ // Consume the rest of the data.
+ data[j++] = data[i++];
+ }
+ data.erase(data.begin() + j, data.end());
-static const char* skip_whitespaces(const char* c) {
- for (; is_whitespace(*c); ++c)
- ; // silence -Wempty-body
- return c;
-}
+ return this->load_from_ini_string(data, compatibility_rule);
+ }
-// Load the config keys from the given string.
-size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
-{
- if (str == nullptr)
- return 0;
-
- // BBS. Remove line numbers.
- std::regex match_pattern("\nN[0-9]* *");
- std::string replace_pattern = "\n";
- char* result = (char*)calloc(strlen(str) + 1, 1);
- std::regex_replace(result, str, str + strlen(str), match_pattern, replace_pattern);
-
- // Walk line by line in reverse until a non-configuration key appears.
- const char *data_start = result;
- data_start = skip_whitespaces(data_start);
- if (std::toupper(*data_start) == 'N')
- data_start = skip_word(data_start);
-
- // boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
- const char *end = data_start + strlen(data_start);
- size_t num_key_value_pairs = 0;
- for (;;) {
- // Extract next line.
- for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
- if (end == data_start)
- break;
- const char *start = end ++;
- for (; start > data_start && *start != '\r' && *start != '\n'; --start);
- if (start == data_start)
- break;
- // Extracted a line from start to end. Extract the key = value pair.
- if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ')
- break;
- const char *key = start + 2;
- if (!((*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')))
- // A key must start with a letter.
- break;
- const char *sep = key;
- for (; sep != end && *sep != '='; ++ sep) ;
- if (sep == end || sep[-1] != ' ' || sep[1] != ' ')
- break;
- const char *value = sep + 2;
- if (value > end)
- break;
- const char *key_end = sep - 1;
- if (key_end - key < 3)
- break;
- // The key may contain letters, digits and underscores.
- for (const char *c = key; c != key_end; ++ c)
- if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
- key = nullptr;
- break;
+ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule)
+ {
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+ for (const boost::property_tree::ptree::value_type &v : tree)
+ {
+ try
+ {
+ t_config_option_key opt_key = v.first;
+ this->set_deserialize(opt_key, v.second.get_value(), substitutions_ctxt);
+ }
+ catch (UnknownOptionException & /* e */)
+ {
+ // ignore
}
- if (key == nullptr)
- break;
- try {
- config.set_deserialize(std::string(key, key_end), std::string(value, end), substitutions);
- ++num_key_value_pairs;
- }
- catch (UnknownOptionException & /* e */) {
- // ignore
}
- end = start;
+ return std::move(substitutions_ctxt.substitutions);
}
// BBS
- free(result);
- return num_key_value_pairs;
-}
+ static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
+ static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; }
+ static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); }
+ static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); }
-// Reading a config from G-code back to front for performance reasons: We don't want to scan
-// hundreds of MB file for a short config block, which we expect to find at the end of the G-code.
-class ReverseLineReader
-{
-public:
- using pos_type = boost::nowide::ifstream::pos_type;
+ static const char *skip_word(const char *c)
+ {
+ for (; !is_end_of_word(*c); ++c)
+ ; // silence -Wempty-body
+ return c;
+ }
- // Stop at file_start
- ReverseLineReader(boost::nowide::ifstream &ifs, pos_type file_start) : m_ifs(ifs), m_file_start(file_start)
+ static const char *skip_whitespaces(const char *c)
{
- m_ifs.seekg(0, m_ifs.end);
- m_file_pos = m_ifs.tellg();
- m_block.assign(m_block_size, 0);
+ for (; is_whitespace(*c); ++c)
+ ; // silence -Wempty-body
+ return c;
}
- bool getline(std::string &out) {
- out.clear();
- for (;;) {
- if (m_block_len == 0) {
- // Read the next block.
- m_block_len = size_t(std::min(m_block_size, m_file_pos - m_file_start));
- if (m_block_len == 0)
- return false;
- m_file_pos -= m_block_len;
- m_ifs.seekg(m_file_pos, m_ifs.beg);
- if (! m_ifs.read(m_block.data(), m_block_len))
- return false;
- }
+ // Load the config keys from the given string.
+ size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions)
+ {
+ if (str == nullptr)
+ return 0;
- assert(m_block_len > 0);
- // Non-empty buffer. Find another LF.
- int i = int(m_block_len) - 1;
- for (; i >= 0; -- i)
- if (m_block[i] == '\n')
+ // BBS. Remove line numbers.
+ std::regex match_pattern("\nN[0-9]* *");
+ std::string replace_pattern = "\n";
+ char *result = (char *)calloc(strlen(str) + 1, 1);
+ std::regex_replace(result, str, str + strlen(str), match_pattern, replace_pattern);
+
+ // Walk line by line in reverse until a non-configuration key appears.
+ const char *data_start = result;
+ data_start = skip_whitespaces(data_start);
+ if (std::toupper(*data_start) == 'N')
+ data_start = skip_word(data_start);
+
+ // boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
+ const char *end = data_start + strlen(data_start);
+ size_t num_key_value_pairs = 0;
+ for (;;)
+ {
+ // Extract next line.
+ for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end)
+ ;
+ if (end == data_start)
+ break;
+ const char *start = end++;
+ for (; start > data_start && *start != '\r' && *start != '\n'; --start)
+ ;
+ if (start == data_start)
+ break;
+ // Extracted a line from start to end. Extract the key = value pair.
+ if (end - (++start) < 10 || start[0] != ';' || start[1] != ' ')
+ break;
+ const char *key = start + 2;
+ if (!((*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')))
+ // A key must start with a letter.
+ break;
+ const char *sep = key;
+ for (; sep != end && *sep != '='; ++sep)
+ ;
+ if (sep == end || sep[-1] != ' ' || sep[1] != ' ')
+ break;
+ const char *value = sep + 2;
+ if (value > end)
+ break;
+ const char *key_end = sep - 1;
+ if (key_end - key < 3)
+ break;
+ // The key may contain letters, digits and underscores.
+ for (const char *c = key; c != key_end; ++c)
+ if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_'))
+ {
+ key = nullptr;
break;
- // i is position of LF or -1 if not found.
- if (i == -1) {
- // LF not found. Just make a backup of the buffer and continue.
- out.insert(out.begin(), m_block.begin(), m_block.begin() + m_block_len);
- m_block_len = 0;
- } else {
- assert(i >= 0);
- // Copy new line to the output. It may be empty.
- out.insert(out.begin(), m_block.begin() + i + 1, m_block.begin() + m_block_len);
- // Block length without the newline.
- m_block_len = i;
- // Remove CRLF from the end of the block.
- if (m_block_len > 0 && m_block[m_block_len - 1] == '\r')
- -- m_block_len;
- return true;
+ }
+ if (key == nullptr)
+ break;
+ try
+ {
+ config.set_deserialize(std::string(key, key_end), std::string(value, end), substitutions);
+ ++num_key_value_pairs;
+ }
+ catch (UnknownOptionException & /* e */)
+ {
+ // ignore
}
+ end = start;
}
- assert(false);
- return false;
+
+ // BBS
+ free(result);
+ return num_key_value_pairs;
}
-private:
- boost::nowide::ifstream &m_ifs;
- std::vector m_block;
- size_t m_block_size = 65536;
- size_t m_block_len = 0;
- pos_type m_file_start;
- pos_type m_file_pos = 0;
-};
-
-// Load the config keys from the tail of a G-code file.
-ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
-{
- // Read a 64k block from the end of the G-code.
- boost::nowide::ifstream ifs(file);
- BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before parse_file %1%") % file.c_str();
- // Look for Slic3r or BambuStudio header.
- // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user.
- //BBS
- bool has_delimiters = true;
+ // Reading a config from G-code back to front for performance reasons: We don't want to scan
+ // hundreds of MB file for a short config block, which we expect to find at the end of the G-code.
+ class ReverseLineReader
{
- //BBS
- std::string bambuslicer_gcode_header = "; ";
- bambuslicer_gcode_header += SLIC3R_APP_NAME;
+ public:
+ using pos_type = boost::nowide::ifstream::pos_type;
- std::string header;
- bool header_found = false;
- while (std::getline(ifs, header)) {
- // BBS
- const char* line_c = skip_whitespaces(header.c_str());
- if (std::toupper(*line_c) == 'N')
- line_c = skip_word(line_c);
- line_c = skip_whitespaces(line_c);
- // BBS
- if (strncmp(bambuslicer_gcode_header.c_str(), line_c, strlen(bambuslicer_gcode_header.c_str())) == 0) {
- header_found = true;
- break;
- }
+ // Stop at file_start
+ ReverseLineReader(boost::nowide::ifstream &ifs, pos_type file_start) : m_ifs(ifs), m_file_start(file_start)
+ {
+ m_ifs.seekg(0, m_ifs.end);
+ m_file_pos = m_ifs.tellg();
+ m_block.assign(m_block_size, 0);
}
- if (!header_found) {
- std::string error_message = "Not a gcode file generated by ";
- error_message += SLIC3R_APP_FULL_NAME;
- error_message += ".";
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << error_message;
- throw Slic3r::RuntimeError(error_message.c_str());
+
+ bool getline(std::string &out)
+ {
+ out.clear();
+ for (;;)
+ {
+ if (m_block_len == 0)
+ {
+ // Read the next block.
+ m_block_len = size_t(std::min(m_block_size, m_file_pos - m_file_start));
+ if (m_block_len == 0)
+ return false;
+ m_file_pos -= m_block_len;
+ m_ifs.seekg(m_file_pos, m_ifs.beg);
+ if (!m_ifs.read(m_block.data(), m_block_len))
+ return false;
+ }
+
+ assert(m_block_len > 0);
+ // Non-empty buffer. Find another LF.
+ int i = int(m_block_len) - 1;
+ for (; i >= 0; --i)
+ if (m_block[i] == '\n')
+ break;
+ // i is position of LF or -1 if not found.
+ if (i == -1)
+ {
+ // LF not found. Just make a backup of the buffer and continue.
+ out.insert(out.begin(), m_block.begin(), m_block.begin() + m_block_len);
+ m_block_len = 0;
+ }
+ else
+ {
+ assert(i >= 0);
+ // Copy new line to the output. It may be empty.
+ out.insert(out.begin(), m_block.begin() + i + 1, m_block.begin() + m_block_len);
+ // Block length without the newline.
+ m_block_len = i;
+ // Remove CRLF from the end of the block.
+ if (m_block_len > 0 && m_block[m_block_len - 1] == '\r')
+ --m_block_len;
+ return true;
+ }
+ }
+ assert(false);
+ return false;
}
- }
- ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
- size_t key_value_pairs = 0;
+ private:
+ boost::nowide::ifstream &m_ifs;
+ std::vector m_block;
+ size_t m_block_size = 65536;
+ size_t m_block_len = 0;
+ pos_type m_file_start;
+ pos_type m_file_pos = 0;
+ };
- if (has_delimiters)
+ // Load the config keys from the tail of a G-code file.
+ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
{
- //BBS
- // PrusaSlicer starting with 2.4.0-alpha0 delimits the config section stored into G-code with
- // ; CONFIG_BLOCK_START
- // ...
- // ; CONFIG_BLOCK_END
- bool begin_found = false;
- bool end_found = false;
- std::string line;
- while (std::getline(ifs, line)) {
- if ( boost::starts_with(line, "; CONFIG_BLOCK_START")) {
- begin_found = true;
- break;
+ // Read a 64k block from the end of the G-code.
+ boost::nowide::ifstream ifs(file);
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before parse_file %1%") % file.c_str();
+ // Look for Slic3r or BambuStudio header.
+ // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user.
+ // BBS
+ bool has_delimiters = true;
+ {
+ // BBS
+ std::string bambuslicer_gcode_header = "; ";
+ bambuslicer_gcode_header += SLIC3R_APP_NAME;
+
+ std::string header;
+ bool header_found = false;
+ while (std::getline(ifs, header))
+ {
+ // BBS
+ const char *line_c = skip_whitespaces(header.c_str());
+ if (std::toupper(*line_c) == 'N')
+ line_c = skip_word(line_c);
+ line_c = skip_whitespaces(line_c);
+ // BBS
+ if (strncmp(bambuslicer_gcode_header.c_str(), line_c, strlen(bambuslicer_gcode_header.c_str())) == 0)
+ {
+ header_found = true;
+ break;
+ }
+ }
+ if (!header_found)
+ {
+ std::string error_message = "Not a gcode file generated by ";
+ error_message += SLIC3R_APP_FULL_NAME;
+ error_message += ".";
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << error_message;
+ throw Slic3r::RuntimeError(error_message.c_str());
}
}
- if (!begin_found) {
- //BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block closing tag \"; CONFIG_BLOCK_START\" not found when reading %1%", file);
- throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_START\" not found"));
- }
- std::string key, value;
- while (std::getline(ifs, line)) {
- if (boost::starts_with(line, "; CONFIG_BLOCK_END")) {
- end_found = true;
- break;
+
+ ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
+ size_t key_value_pairs = 0;
+
+ if (has_delimiters)
+ {
+ // BBS
+ // PrusaSlicer starting with 2.4.0-alpha0 delimits the config section stored into G-code with
+ // ; CONFIG_BLOCK_START
+ // ...
+ // ; CONFIG_BLOCK_END
+ bool begin_found = false;
+ bool end_found = false;
+ std::string line;
+ while (std::getline(ifs, line))
+ {
+ if (boost::starts_with(line, "; CONFIG_BLOCK_START"))
+ {
+ begin_found = true;
+ break;
+ }
}
- // line should be a valid key = value pair.
- auto pos = line.find('=');
- if (pos != std::string::npos && pos > 1 && line.front() == ';') {
- key = line.substr(1, pos - 1);
- value = line.substr(pos + 1);
- boost::trim(key);
- boost::trim(value);
- try {
- this->set_deserialize(key, value, substitutions_ctxt);
- ++ key_value_pairs;
- } catch (UnknownOptionException & /* e */) {
- // ignore
+ if (!begin_found)
+ {
+ // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block closing tag \"; CONFIG_BLOCK_START\" not found when reading %1%", file);
+ throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_START\" not found"));
+ }
+ std::string key, value;
+ while (std::getline(ifs, line))
+ {
+ if (boost::starts_with(line, "; CONFIG_BLOCK_END"))
+ {
+ end_found = true;
+ break;
}
+ // line should be a valid key = value pair.
+ auto pos = line.find('=');
+ if (pos != std::string::npos && pos > 1 && line.front() == ';')
+ {
+ key = line.substr(1, pos - 1);
+ value = line.substr(pos + 1);
+ boost::trim(key);
+ boost::trim(value);
+ try
+ {
+ this->set_deserialize(key, value, substitutions_ctxt);
+ ++key_value_pairs;
+ }
+ catch (UnknownOptionException & /* e */)
+ {
+ // ignore
+ }
+ }
+ }
+ if (!end_found)
+ {
+ // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block opening tag \"; CONFIG_BLOCK_END\" not found when reading %1%", file);
+ throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_END\" not found"));
}
}
- if (!end_found) {
- //BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block opening tag \"; CONFIG_BLOCK_END\" not found when reading %1%", file);
- throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_END\" not found"));
+ else
+ {
+ auto header_end_pos = ifs.tellg();
+ // Slicer older than 2.4.0-alpha0 do not emit any delimiter.
+ // Try a heuristics reading the G-code from back.
+ ifs.seekg(0, ifs.end);
+ auto file_length = ifs.tellg();
+ auto data_length = std::min(65535, file_length - header_end_pos);
+ ifs.seekg(file_length - data_length, ifs.beg);
+ std::vector data(size_t(data_length) + 1, 0);
+ ifs.read(data.data(), data_length);
+ ifs.close();
+ key_value_pairs = load_from_gcode_string_legacy(*this, data.data(), substitutions_ctxt);
}
- }
- else
- {
- auto header_end_pos = ifs.tellg();
- // Slicer older than 2.4.0-alpha0 do not emit any delimiter.
- // Try a heuristics reading the G-code from back.
- ifs.seekg(0, ifs.end);
- auto file_length = ifs.tellg();
- auto data_length = std::min(65535, file_length - header_end_pos);
- ifs.seekg(file_length - data_length, ifs.beg);
- std::vector data(size_t(data_length) + 1, 0);
- ifs.read(data.data(), data_length);
- ifs.close();
- key_value_pairs = load_from_gcode_string_legacy(*this, data.data(), substitutions_ctxt);
- }
- if (key_value_pairs < 80) {
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs);
- throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
- }
+ if (key_value_pairs < 80)
+ {
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs);
+ throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
+ }
- BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finished to parse_file %1%") % file.c_str();
- return std::move(substitutions_ctxt.substitutions);
-}
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finished to parse_file %1%") % file.c_str();
+ return std::move(substitutions_ctxt.substitutions);
+ }
-//BBS: add json support
-void ConfigBase::save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const
-{
- json j;
- //record the headers
- j[BBL_JSON_KEY_VERSION] = version;
- j[BBL_JSON_KEY_NAME] = name;
- j[BBL_JSON_KEY_FROM] = from;
-
- //record all the key-values
- for (const std::string &opt_key : this->keys())
+ // BBS: add json support
+ void ConfigBase::save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const
{
- if (from.compare("project") == 0) {
- if (opt_key.compare("print_host") == 0
- || opt_key.compare("print_host_webui") == 0
- || opt_key.compare("printhost_apikey") == 0
- || opt_key.compare("printhost_cafile") == 0
- || opt_key.compare("printhost_user") == 0
- || opt_key.compare("printhost_password") == 0
- || opt_key.compare("printhost_port") == 0
- ) {
- continue;
+ json j;
+ // record the headers
+ j[BBL_JSON_KEY_VERSION] = version;
+ j[BBL_JSON_KEY_NAME] = name;
+ j[BBL_JSON_KEY_FROM] = from;
+
+ // record all the key-values
+ for (const std::string &opt_key : this->keys())
+ {
+ if (from.compare("project") == 0)
+ {
+ if (opt_key.compare("print_host") == 0 || opt_key.compare("print_host_webui") == 0 || opt_key.compare("printhost_apikey") == 0 || opt_key.compare("printhost_cafile") == 0 || opt_key.compare("printhost_user") == 0 || opt_key.compare("printhost_password") == 0 || opt_key.compare("printhost_port") == 0)
+ {
+ continue;
+ }
+ }
+ const ConfigOption *opt = this->option(opt_key);
+ if (opt->is_scalar())
+ {
+ if (opt->type() == coString)
+ // keep \n, \r, \t
+ j[opt_key] = (dynamic_cast(opt))->value;
+ else
+ j[opt_key] = opt->serialize();
}
- }
- const ConfigOption* opt = this->option(opt_key);
- if ( opt->is_scalar() ) {
- if (opt->type() == coString)
- //keep \n, \r, \t
- j[opt_key] = (dynamic_cast(opt))->value;
else
- j[opt_key] = opt->serialize();
- }
- else {
- const ConfigOptionVectorBase* vec = static_cast(opt);
- //if (!vec->empty())
- std::vector string_values = vec->vserialize();
-
- /*for (int i = 0; i < string_values.size(); i++)
{
- std::string string_value = escape_string_cstyle(string_values[i]);
- j[opt_key][i] = string_value;
- }*/
+ const ConfigOptionVectorBase *vec = static_cast(opt);
+ // if (!vec->empty())
+ std::vector string_values = vec->vserialize();
- json j_array(string_values);
- j[opt_key] = j_array;
+ /*for (int i = 0; i < string_values.size(); i++)
+ {
+ std::string string_value = escape_string_cstyle(string_values[i]);
+ j[opt_key][i] = string_value;
+ }*/
+
+ json j_array(string_values);
+ j[opt_key] = j_array;
+ }
}
- }
- boost::nowide::ofstream c;
- c.open(file, std::ios::out | std::ios::trunc);
- c << std::setw(4) << j << std::endl;
- c.close();
+ boost::nowide::ofstream c;
+ c.open(file, std::ios::out | std::ios::trunc);
+ c << std::setw(4) << j << std::endl;
+ c.close();
- BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", saved config to %1%\n") % PathSanitizer::sanitize(file);
-}
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", saved config to %1%\n") % PathSanitizer::sanitize(file);
+ }
-void ConfigBase::save(const std::string &file) const
-{
- boost::nowide::ofstream c;
- c.open(file, std::ios::out | std::ios::trunc);
- c << "# " << Slic3r::header_slic3r_generated() << std::endl;
- for (const std::string &opt_key : this->keys())
- c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
- c.close();
-
- BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", saved config to %1%\n")%file;
-}
+ void ConfigBase::save(const std::string &file) const
+ {
+ boost::nowide::ofstream c;
+ c.open(file, std::ios::out | std::ios::trunc);
+ c << "# " << Slic3r::header_slic3r_generated() << std::endl;
+ for (const std::string &opt_key : this->keys())
+ c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
+ c.close();
+
+ BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", saved config to %1%\n") % file;
+ }
-// Set all the nullable values to nils.
-void ConfigBase::null_nullables()
-{
- for (const std::string &opt_key : this->keys()) {
- ConfigOption *opt = this->optptr(opt_key, false);
- assert(opt != nullptr);
- if (opt->nullable())
- opt->deserialize("nil", ForwardCompatibilitySubstitutionRule::Disable);
+ // Set all the nullable values to nils.
+ void ConfigBase::null_nullables()
+ {
+ for (const std::string &opt_key : this->keys())
+ {
+ ConfigOption *opt = this->optptr(opt_key, false);
+ assert(opt != nullptr);
+ if (opt->nullable())
+ opt->deserialize("nil", ForwardCompatibilitySubstitutionRule::Disable);
+ }
}
-}
-DynamicConfig::DynamicConfig(const ConfigBase& rhs, const t_config_option_keys& keys)
-{
- for (const t_config_option_key& opt_key : keys)
- this->options[opt_key] = std::unique_ptr(rhs.option(opt_key)->clone());
-}
+ DynamicConfig::DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys)
+ {
+ for (const t_config_option_key &opt_key : keys)
+ this->options[opt_key] = std::unique_ptr(rhs.option(opt_key)->clone());
+ }
-bool DynamicConfig::operator==(const DynamicConfig &rhs) const
-{
- auto it1 = this->options.begin();
- auto it1_end = this->options.end();
- auto it2 = rhs.options.begin();
- auto it2_end = rhs.options.end();
- for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2)
- if (it1->first != it2->first || *it1->second != *it2->second)
- // key or value differ
- return false;
- return it1 == it1_end && it2 == it2_end;
-}
+ bool DynamicConfig::operator==(const DynamicConfig &rhs) const
+ {
+ auto it1 = this->options.begin();
+ auto it1_end = this->options.end();
+ auto it2 = rhs.options.begin();
+ auto it2_end = rhs.options.end();
+ for (; it1 != it1_end && it2 != it2_end; ++it1, ++it2)
+ if (it1->first != it2->first || *it1->second != *it2->second)
+ // key or value differ
+ return false;
+ return it1 == it1_end && it2 == it2_end;
+ }
-// Remove options with all nil values, those are optional and it does not help to hold them.
-size_t DynamicConfig::remove_nil_options()
-{
- size_t cnt_removed = 0;
- for (auto it = options.begin(); it != options.end();)
- if (it->second->is_nil()) {
- it = options.erase(it);
- ++ cnt_removed;
- } else
- ++ it;
- return cnt_removed;
-}
+ // Remove options with all nil values, those are optional and it does not help to hold them.
+ size_t DynamicConfig::remove_nil_options()
+ {
+ size_t cnt_removed = 0;
+ for (auto it = options.begin(); it != options.end();)
+ if (it->second->is_nil())
+ {
+ it = options.erase(it);
+ ++cnt_removed;
+ }
+ else
+ ++it;
+ return cnt_removed;
+ }
-ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
-{
- auto it = options.find(opt_key);
- if (it != options.end())
- // Option was found.
- return it->second.get();
- if (! create)
- // Option was not found and a new option shall not be created.
- return nullptr;
- // Try to create a new ConfigOption.
- const ConfigDef *def = this->def();
- if (def == nullptr)
- throw NoDefinitionException(opt_key);
- const ConfigOptionDef *optdef = def->get(opt_key);
- if (optdef == nullptr)
-// throw ConfigurationError(std::string("Invalid option name: ") + opt_key);
- // Let the parent decide what to do if the opt_key is not defined by this->def().
- return nullptr;
- ConfigOption *opt = optdef->create_default_option();
- this->options.emplace_hint(it, opt_key, std::unique_ptr(opt));
- return opt;
-}
+ ConfigOption *DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
+ {
+ auto it = options.find(opt_key);
+ if (it != options.end())
+ // Option was found.
+ return it->second.get();
+ if (!create)
+ // Option was not found and a new option shall not be created.
+ return nullptr;
+ // Try to create a new ConfigOption.
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException(opt_key);
+ const ConfigOptionDef *optdef = def->get(opt_key);
+ if (optdef == nullptr)
+ // throw ConfigurationError(std::string("Invalid option name: ") + opt_key);
+ // Let the parent decide what to do if the opt_key is not defined by this->def().
+ return nullptr;
+ ConfigOption *opt = optdef->create_default_option();
+ this->options.emplace_hint(it, opt_key, std::unique_ptr(opt));
+ return opt;
+ }
-const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) const
-{
- auto it = options.find(opt_key);
- return (it == options.end()) ? nullptr : it->second.get();
-}
+ const ConfigOption *DynamicConfig::optptr(const t_config_option_key &opt_key) const
+ {
+ auto it = options.find(opt_key);
+ return (it == options.end()) ? nullptr : it->second.get();
+ }
-bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys)
-{
- // cache the CLI option => opt_key mapping
- std::map opts;
- for (const auto &oit : this->def()->options)
- for (const std::string &t : oit.second.cli_args(oit.first))
- opts[t] = oit.first;
-
- bool parse_options = true;
- for (int i = 1; i < argc; ++ i) {
- std::string token = argv[i];
- // Store non-option arguments in the provided vector.
- if (! parse_options || ! boost::starts_with(token, "-")) {
- extra->push_back(token);
- continue;
- }
+ bool DynamicConfig::read_cli(int argc, const char *const argv[], t_config_option_keys *extra, t_config_option_keys *keys)
+ {
+ // cache the CLI option => opt_key mapping
+ std::map opts;
+ for (const auto &oit : this->def()->options)
+ for (const std::string &t : oit.second.cli_args(oit.first))
+ opts[t] = oit.first;
+
+ bool parse_options = true;
+ for (int i = 1; i < argc; ++i)
+ {
+ std::string token = argv[i];
+ // Store non-option arguments in the provided vector.
+ if (!parse_options || !boost::starts_with(token, "-"))
+ {
+ extra->push_back(token);
+ continue;
+ }
#ifdef __APPLE__
- if (boost::starts_with(token, "-psn_"))
- // OSX launcher may add a "process serial number", for example "-psn_0_989382" to the command line.
- // While it is supposed to be dropped since OSX 10.9, we will rather ignore it.
- continue;
+ if (boost::starts_with(token, "-psn_"))
+ // OSX launcher may add a "process serial number", for example "-psn_0_989382" to the command line.
+ // While it is supposed to be dropped since OSX 10.9, we will rather ignore it.
+ continue;
#endif /* __APPLE__ */
- // Stop parsing tokens as options when -- is supplied.
- if (token == "--") {
- parse_options = false;
- continue;
- }
- // Remove leading dashes (one or two).
- token.erase(token.begin(), token.begin() + (boost::starts_with(token, "--") ? 2 : 1));
- // Read value when supplied in the --key=value form.
- std::string value;
- {
- size_t equals_pos = token.find("=");
- if (equals_pos != std::string::npos) {
- value = token.substr(equals_pos+1);
- token.erase(equals_pos);
+ // Stop parsing tokens as options when -- is supplied.
+ if (token == "--")
+ {
+ parse_options = false;
+ continue;
}
- }
- // Look for the cli -> option mapping.
- auto it = opts.find(token);
- bool no = false;
- if (it == opts.end()) {
- //BBS: don't use 'no-' for boolean options
- boost::nowide::cerr << "Invalid option --" << token.c_str() << std::endl;
- return false;
- /* Remove the "no-" prefix used to negate boolean options.
- std::string yes_token;
- if (boost::starts_with(token, "no-")) {
- yes_token = token.substr(3);
- it = opts.find(yes_token);
- no = true;
- }
- if (it == opts.end()) {
+ // Remove leading dashes (one or two).
+ token.erase(token.begin(), token.begin() + (boost::starts_with(token, "--") ? 2 : 1));
+ // Read value when supplied in the --key=value form.
+ std::string value;
+ {
+ size_t equals_pos = token.find("=");
+ if (equals_pos != std::string::npos)
+ {
+ value = token.substr(equals_pos + 1);
+ token.erase(equals_pos);
+ }
+ }
+ // Look for the cli -> option mapping.
+ auto it = opts.find(token);
+ bool no = false;
+ if (it == opts.end())
+ {
+ // BBS: don't use 'no-' for boolean options
boost::nowide::cerr << "Invalid option --" << token.c_str() << std::endl;
return false;
+ /* Remove the "no-" prefix used to negate boolean options.
+ std::string yes_token;
+ if (boost::starts_with(token, "no-")) {
+ yes_token = token.substr(3);
+ it = opts.find(yes_token);
+ no = true;
+ }
+ if (it == opts.end()) {
+ boost::nowide::cerr << "Invalid option --" << token.c_str() << std::endl;
+ return false;
+ }
+ if (no)
+ token = yes_token;*/
}
- if (no)
- token = yes_token;*/
- }
- const t_config_option_key &opt_key = it->second;
- const ConfigOptionDef &optdef = this->def()->options.at(opt_key);
+ const t_config_option_key &opt_key = it->second;
+ const ConfigOptionDef &optdef = this->def()->options.at(opt_key);
- // If the option type expects a value and it was not already provided,
- // look for it in the next token.
- if (value.empty() && optdef.type != coBool && optdef.type != coBools) {
- if (i == argc-1) {
- boost::nowide::cerr << "Need values for option --" << token.c_str() << std::endl;
- return false;
+ // If the option type expects a value and it was not already provided,
+ // look for it in the next token.
+ if (value.empty() && optdef.type != coBool && optdef.type != coBools)
+ {
+ if (i == argc - 1)
+ {
+ boost::nowide::cerr << "Need values for option --" << token.c_str() << std::endl;
+ return false;
+ }
+ value = argv[++i];
}
- value = argv[++ i];
- }
- /*if (no) {
- assert(optdef.type == coBool || optdef.type == coBools);
- if (! value.empty()) {
- boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl;
- return false;
- }
- }*/
+ /*if (no) {
+ assert(optdef.type == coBool || optdef.type == coBools);
+ if (! value.empty()) {
+ boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl;
+ return false;
+ }
+ }*/
- // Store the option value.
- const bool existing = this->has(opt_key);
- if (keys != nullptr && ! existing) {
- // Save the order of detected keys.
- keys->push_back(opt_key);
- }
- ConfigOption *opt_base = this->option(opt_key, true);
- ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast(opt_base) : nullptr;
- if (opt_vector) {
- if (! existing)
- // remove the default values
- opt_vector->clear();
- // 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);
- 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
- // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
- // unescaped by the calling shell.
- opt_vector->deserialize(value, true);
- } else if (opt_base->type() == coBool) {
- if (value.empty())
- static_cast(opt_base)->value = !no;
+ // Store the option value.
+ const bool existing = this->has(opt_key);
+ if (keys != nullptr && !existing)
+ {
+ // Save the order of detected keys.
+ keys->push_back(opt_key);
+ }
+ ConfigOption *opt_base = this->option(opt_key, true);
+ ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast(opt_base) : nullptr;
+ if (opt_vector)
+ {
+ if (!existing)
+ // remove the default values
+ opt_vector->clear();
+ // 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);
+ 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
+ // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
+ // unescaped by the calling shell.
+ opt_vector->deserialize(value, true);
+ }
+ else if (opt_base->type() == coBool)
+ {
+ if (value.empty())
+ static_cast(opt_base)->value = !no;
+ else
+ opt_base->deserialize(value);
+ }
+ else if (opt_base->type() == coString)
+ {
+ // Do not unescape single string values, the unescaping is left to the calling shell.
+ static_cast(opt_base)->value = value;
+ }
else
- opt_base->deserialize(value);
- } else if (opt_base->type() == coString) {
- // Do not unescape single string values, the unescaping is left to the calling shell.
- static_cast(opt_base)->value = value;
- } else {
- // Just bail out if the configuration value is not understood.
- ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
- // Any scalar value of a type different from Bool and String.
- if (! this->set_deserialize_nothrow(opt_key, value, context, false)) {
- boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
- return false;
- }
+ {
+ // Just bail out if the configuration value is not understood.
+ ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
+ // Any scalar value of a type different from Bool and String.
+ if (!this->set_deserialize_nothrow(opt_key, value, context, false))
+ {
+ boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
+ return false;
+ }
+ }
}
+ return true;
}
- return true;
-}
-t_config_option_keys DynamicConfig::keys() const
-{
- t_config_option_keys keys;
- keys.reserve(this->options.size());
- for (const auto &opt : this->options)
- keys.emplace_back(opt.first);
- return keys;
-}
+ t_config_option_keys DynamicConfig::keys() const
+ {
+ t_config_option_keys keys;
+ keys.reserve(this->options.size());
+ for (const auto &opt : this->options)
+ keys.emplace_back(opt.first);
+ return keys;
+ }
-void StaticConfig::set_defaults()
-{
- // use defaults from definition
- auto *defs = this->def();
- if (defs != nullptr) {
- for (const std::string &key : this->keys()) {
- const ConfigOptionDef *def = defs->get(key);
- ConfigOption *opt = this->option(key);
- if (def != nullptr && opt != nullptr && def->default_value)
- opt->set(def->default_value.get());
+ void StaticConfig::set_defaults()
+ {
+ // use defaults from definition
+ auto *defs = this->def();
+ if (defs != nullptr)
+ {
+ for (const std::string &key : this->keys())
+ {
+ const ConfigOptionDef *def = defs->get(key);
+ ConfigOption *opt = this->option(key);
+ if (def != nullptr && opt != nullptr && def->default_value)
+ opt->set(def->default_value.get());
+ }
}
}
-}
-t_config_option_keys StaticConfig::keys() const
-{
- t_config_option_keys keys;
- assert(this->def() != nullptr);
- for (const auto &opt_def : this->def()->options)
- if (this->option(opt_def.first) != nullptr)
- keys.push_back(opt_def.first);
- return keys;
-}
+ t_config_option_keys StaticConfig::keys() const
+ {
+ t_config_option_keys keys;
+ assert(this->def() != nullptr);
+ for (const auto &opt_def : this->def()->options)
+ if (this->option(opt_def.first) != nullptr)
+ keys.push_back(opt_def.first);
+ return keys;
+ }
-// Iterate over the pairs of options with equal keys, call the fn.
-// Returns true on early exit by fn().
-//BBS: add skipped key logic
-template
-static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn, const std::set* skipped_keys = nullptr)
-{
- std::map>::const_iterator i = lhs.cbegin();
- std::map>::const_iterator j = rhs.cbegin();
- while (i != lhs.cend() && j != rhs.cend())
- if (i->first < j->first)
- ++ i;
- else if (i->first > j->first)
- ++ j;
- else {
- assert(i->first == j->first);
- if (skipped_keys && (skipped_keys->count(i->first) != 0))
- {
- //do nothing
- }
- else if (fn(i->first, i->second.get(), j->second.get()))
- // Early exit by fn.
- return true;
- ++ i;
- ++ j;
- }
- // Finished to the end.
- return false;
-}
+ // Iterate over the pairs of options with equal keys, call the fn.
+ // Returns true on early exit by fn().
+ // BBS: add skipped key logic
+ template
+ static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn, const std::set *skipped_keys = nullptr)
+ {
+ std::map>::const_iterator i = lhs.cbegin();
+ std::map>::const_iterator j = rhs.cbegin();
+ while (i != lhs.cend() && j != rhs.cend())
+ if (i->first < j->first)
+ ++i;
+ else if (i->first > j->first)
+ ++j;
+ else
+ {
+ assert(i->first == j->first);
+ if (skipped_keys && (skipped_keys->count(i->first) != 0))
+ {
+ // do nothing
+ }
+ else if (fn(i->first, i->second.get(), j->second.get()))
+ // Early exit by fn.
+ return true;
+ ++i;
+ ++j;
+ }
+ // Finished to the end.
+ return false;
+ }
-// Are the two configs equal? Ignoring options not present in both configs.
-//BBS: add skipped keys logic
-bool DynamicConfig::equals(const DynamicConfig &other, const std::set* skipped_keys) const
-{
- return ! dynamic_config_iterate(*this, other,
- [](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; },
- skipped_keys);
-}
+ // Are the two configs equal? Ignoring options not present in both configs.
+ // BBS: add skipped keys logic
+ bool DynamicConfig::equals(const DynamicConfig &other, const std::set *skipped_keys) const
+ {
+ return !dynamic_config_iterate(*this, other, [](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r)
+ { return *l != *r; }, skipped_keys);
+ }
-// Returns options differing in the two configs, ignoring options not present in both configs.
-t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
-{
- t_config_option_keys diff;
- dynamic_config_iterate(*this, other,
- [&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
- if (*l != *r)
- diff.emplace_back(key);
- // Continue iterating.
- return false;
- });
- return diff;
-}
+ // Returns options differing in the two configs, ignoring options not present in both configs.
+ t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
+ {
+ t_config_option_keys diff;
+ dynamic_config_iterate(*this, other,
+ [&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r)
+ {
+ if (*l != *r)
+ diff.emplace_back(key);
+ // Continue iterating.
+ return false;
+ });
+ return diff;
+ }
-// Returns options being equal in the two configs, ignoring options not present in both configs.
-t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
-{
- t_config_option_keys equal;
- dynamic_config_iterate(*this, other,
- [&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
- if (*l == *r)
- equal.emplace_back(key);
- // Continue iterating.
- return false;
- });
- return equal;
-}
+ // Returns options being equal in the two configs, ignoring options not present in both configs.
+ t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
+ {
+ t_config_option_keys equal;
+ dynamic_config_iterate(*this, other,
+ [&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r)
+ {
+ if (*l == *r)
+ equal.emplace_back(key);
+ // Continue iterating.
+ return false;
+ });
+ return equal;
+ }
-double& DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx)
-{
- if (ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key))) {
- return opt_floats->get_at(idx);
- } else {
- ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key));
- assert(opt_floats_nullable != nullptr);
- return opt_floats_nullable->get_at(idx);
+ double &DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx)
+ {
+ if (ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key)))
+ {
+ return opt_floats->get_at(idx);
+ }
+ else
+ {
+ ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key));
+ assert(opt_floats_nullable != nullptr);
+ return opt_floats_nullable->get_at(idx);
+ }
}
-}
-const double& DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx) const
-{
- if (const ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key))) {
- return opt_floats->get_at(idx);
- } else if (const ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key))) {
- return opt_floats_nullable->get_at(idx);
- } else {
- assert(false);
- return 0;
+ const double &DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx) const
+ {
+ if (const ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key)))
+ {
+ return opt_floats->get_at(idx);
+ }
+ else if (const ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key)))
+ {
+ return opt_floats_nullable->get_at(idx);
+ }
+ else
+ {
+ assert(false);
+ return 0;
+ }
}
-}
-bool DynamicConfig::opt_bool(const t_config_option_key &opt_key, unsigned int idx) const {
- if (const ConfigOptionBools *opts = dynamic_cast(this->option(opt_key))) {
- return opts->get_at(idx) != 0;
- }
- else {
- const ConfigOptionBoolsNullable *opt_s = dynamic_cast(this->option(opt_key));
- assert(opt_s != nullptr);
- return opt_s->get_at(idx) != 0;
+ bool DynamicConfig::opt_bool(const t_config_option_key &opt_key, unsigned int idx) const
+ {
+ if (const ConfigOptionBools *opts = dynamic_cast(this->option(opt_key)))
+ {
+ return opts->get_at(idx) != 0;
+ }
+ else
+ {
+ const ConfigOptionBoolsNullable *opt_s = dynamic_cast(this->option(opt_key));
+ assert(opt_s != nullptr);
+ return opt_s->get_at(idx) != 0;
+ }
}
-}
}
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 3355feb36b..4d669eb954 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -39,18 +39,18 @@
// Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332.
// We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
-#if ! defined(TBB_VERSION_MAJOR)
- #include
+#if !defined(TBB_VERSION_MAJOR)
+#include
#endif
-#if ! defined(TBB_VERSION_MAJOR)
- static_assert(false, "TBB_VERSION_MAJOR not defined");
+#if !defined(TBB_VERSION_MAJOR)
+static_assert(false, "TBB_VERSION_MAJOR not defined");
#endif
#if TBB_VERSION_MAJOR >= 2021
- #include
- using slic3r_tbb_filtermode = tbb::filter_mode;
+#include
+using slic3r_tbb_filtermode = tbb::filter_mode;
#else
- #include
- using slic3r_tbb_filtermode = tbb::filter;
+#include
+using slic3r_tbb_filtermode = tbb::filter;
#endif
#include
@@ -70,7 +70,8 @@ using namespace std::literals::string_view_literals;
#include
#include
-namespace Slic3r {
+namespace Slic3r
+{
//! macro used to mark string used at localization,
//! return same string
@@ -78,160 +79,186 @@ namespace Slic3r {
#define _(s) Slic3r::I18N::translate(s)
#define _L(s) Slic3r::I18N::translate(s)
-static const float g_min_purge_volume = 100.f;
-static const float g_purge_volume_one_time = 135.f;
-static const int g_max_flush_count = 4;
-static const size_t g_max_label_object = 64;
-static const double smooth_speed_step = 10;
-static const double not_split_length = scale_(1.0);
-static const double max_step_length = scale_(1.0); // cut path if the path too long
-static const double min_step_length = scale_(0.4); // cut step
-
-Vec2d travel_point_1;
-Vec2d travel_point_2;
-Vec2d travel_point_3;
-static std::vector get_path_of_change_filament(const Print& print)
-{
- // give safe value in case there is no start_end_points in config
- std::vector out_points;
- out_points.emplace_back(Vec2d(54, 0));
- out_points.emplace_back(Vec2d(54, 0));
- out_points.emplace_back(Vec2d(54, 245));
-
- // get the start_end_points from config (20, -3) (54, 245)
- Pointfs points = print.config().start_end_points.values;
- if (points.size() != 2)
- return out_points;
+ static const float g_min_purge_volume = 100.f;
+ static const float g_purge_volume_one_time = 135.f;
+ static const int g_max_flush_count = 4;
+ static const size_t g_max_label_object = 64;
+ static const double smooth_speed_step = 10;
+ static const double not_split_length = scale_(1.0);
+ static const double max_step_length = scale_(1.0); // cut path if the path too long
+ static const double min_step_length = scale_(0.4); // cut step
+
+ Vec2d travel_point_1;
+ Vec2d travel_point_2;
+ Vec2d travel_point_3;
+ static std::vector get_path_of_change_filament(const Print &print)
+ {
+ // give safe value in case there is no start_end_points in config
+ std::vector out_points;
+ out_points.emplace_back(Vec2d(54, 0));
+ out_points.emplace_back(Vec2d(54, 0));
+ out_points.emplace_back(Vec2d(54, 245));
- Vec2d start_point = points[0];
- Vec2d end_point = points[1];
+ // get the start_end_points from config (20, -3) (54, 245)
+ Pointfs points = print.config().start_end_points.values;
+ if (points.size() != 2)
+ return out_points;
- // the cutter area size(18, 28)
- Pointfs excluse_area = print.config().bed_exclude_area.values;
- if (excluse_area.size() != 4)
- return out_points;
+ Vec2d start_point = points[0];
+ Vec2d end_point = points[1];
+
+ // the cutter area size(18, 28)
+ Pointfs excluse_area = print.config().bed_exclude_area.values;
+ if (excluse_area.size() != 4)
+ return out_points;
+
+ double cutter_area_x = excluse_area[2].x() + 2;
+ double cutter_area_y = excluse_area[2].y() + 2;
+
+ double start_x_position = start_point.x();
+ double end_x_position = end_point.x();
+ double end_y_position = end_point.y();
- double cutter_area_x = excluse_area[2].x() + 2;
- double cutter_area_y = excluse_area[2].y() + 2;
-
- double start_x_position = start_point.x();
- double end_x_position = end_point.x();
- double end_y_position = end_point.y();
-
- bool can_travel_form_left = true;
-
- // step 1: get the x-range intervals of all objects
- std::vector> object_intervals;
- for (PrintObject *print_object : print.objects()) {
- const PrintInstances &print_instances = print_object->instances();
- BoundingBoxf3 bounding_box = print_instances[0].model_instance->get_object()->bounding_box();
-
- if (bounding_box.min.x() < start_x_position && bounding_box.min.y() < cutter_area_y)
- can_travel_form_left = false;
-
- std::pair object_scope = std::make_pair(bounding_box.min.x() - 2, bounding_box.max.x() + 2);
- if (object_intervals.empty())
- object_intervals.push_back(object_scope);
- else {
- std::vector> new_object_intervals;
- bool intervals_intersect = false;
- std::pair new_merged_scope;
- for (auto object_interval : object_intervals) {
- if (object_interval.second >= object_scope.first && object_interval.first <= object_scope.second) {
- if (intervals_intersect) {
- new_merged_scope = std::make_pair(std::min(object_interval.first, new_merged_scope.first), std::max(object_interval.second, new_merged_scope.second));
- } else { // it is the first intersection
- new_merged_scope = std::make_pair(std::min(object_interval.first, object_scope.first), std::max(object_interval.second, object_scope.second));
+ bool can_travel_form_left = true;
+
+ // step 1: get the x-range intervals of all objects
+ std::vector> object_intervals;
+ for (PrintObject *print_object : print.objects())
+ {
+ const PrintInstances &print_instances = print_object->instances();
+ BoundingBoxf3 bounding_box = print_instances[0].model_instance->get_object()->bounding_box();
+
+ if (bounding_box.min.x() < start_x_position && bounding_box.min.y() < cutter_area_y)
+ can_travel_form_left = false;
+
+ std::pair object_scope = std::make_pair(bounding_box.min.x() - 2, bounding_box.max.x() + 2);
+ if (object_intervals.empty())
+ object_intervals.push_back(object_scope);
+ else
+ {
+ std::vector> new_object_intervals;
+ bool intervals_intersect = false;
+ std::pair new_merged_scope;
+ for (auto object_interval : object_intervals)
+ {
+ if (object_interval.second >= object_scope.first && object_interval.first <= object_scope.second)
+ {
+ if (intervals_intersect)
+ {
+ new_merged_scope = std::make_pair(std::min(object_interval.first, new_merged_scope.first), std::max(object_interval.second, new_merged_scope.second));
+ }
+ else
+ { // it is the first intersection
+ new_merged_scope = std::make_pair(std::min(object_interval.first, object_scope.first), std::max(object_interval.second, object_scope.second));
+ }
+ intervals_intersect = true;
}
- intervals_intersect = true;
- } else {
- new_object_intervals.push_back(object_interval);
+ else
+ {
+ new_object_intervals.push_back(object_interval);
+ }
+ }
+
+ if (intervals_intersect)
+ {
+ new_object_intervals.push_back(new_merged_scope);
+ object_intervals = new_object_intervals;
}
+ else
+ object_intervals.push_back(object_scope);
}
+ }
- if (intervals_intersect) {
- new_object_intervals.push_back(new_merged_scope);
- object_intervals = new_object_intervals;
- } else
- object_intervals.push_back(object_scope);
+ // step 2: get the available x-range
+ std::sort(object_intervals.begin(), object_intervals.end(),
+ [](const std::pair &left, const std::pair &right)
+ {
+ return left.first < right.first;
+ });
+ std::vector> available_intervals;
+ double start_position = 0;
+ for (auto object_interval : object_intervals)
+ {
+ if (object_interval.first > start_position)
+ available_intervals.push_back(std::make_pair(start_position, object_interval.first));
+ start_position = object_interval.second;
}
- }
+ available_intervals.push_back(std::make_pair(start_position, 255));
- // step 2: get the available x-range
- std::sort(object_intervals.begin(), object_intervals.end(),
- [](const std::pair &left, const std::pair &right) {
- return left.first < right.first;
- });
- std::vector> available_intervals;
- double start_position = 0;
- for (auto object_interval : object_intervals) {
- if (object_interval.first > start_position)
- available_intervals.push_back(std::make_pair(start_position, object_interval.first));
- start_position = object_interval.second;
- }
- available_intervals.push_back(std::make_pair(start_position, 255));
-
- // step 3: get the nearest path
- double new_path = 255;
- for (auto available_interval : available_intervals) {
- if (available_interval.first > end_x_position) {
- double distance = available_interval.first - end_x_position;
- new_path = abs(end_x_position - new_path) < distance ? new_path : available_interval.first;
- break;
- } else {
- if (available_interval.second >= end_x_position) {
- new_path = end_x_position;
+ // step 3: get the nearest path
+ double new_path = 255;
+ for (auto available_interval : available_intervals)
+ {
+ if (available_interval.first > end_x_position)
+ {
+ double distance = available_interval.first - end_x_position;
+ new_path = abs(end_x_position - new_path) < distance ? new_path : available_interval.first;
break;
- } else if (!can_travel_form_left && available_interval.second < start_x_position) {
- continue;
- } else {
- new_path = available_interval.second;
+ }
+ else
+ {
+ if (available_interval.second >= end_x_position)
+ {
+ new_path = end_x_position;
+ break;
+ }
+ else if (!can_travel_form_left && available_interval.second < start_x_position)
+ {
+ continue;
+ }
+ else
+ {
+ new_path = available_interval.second;
+ }
}
}
- }
- // step 4: generate path points (new_path == start_x_position means not need to change path)
- Vec2d out_point_1;
- Vec2d out_point_2;
- Vec2d out_point_3;
- if (new_path < start_x_position) {
- out_point_1 = Vec2d(start_x_position, cutter_area_y);
- out_point_2 = Vec2d(new_path, cutter_area_y);
- out_point_3 = Vec2d(new_path, end_y_position);
- } else {
- out_point_1 = Vec2d(new_path, 0);
- out_point_2 = Vec2d(new_path, 0);
- out_point_3 = Vec2d(new_path, end_y_position);
- }
+ // step 4: generate path points (new_path == start_x_position means not need to change path)
+ Vec2d out_point_1;
+ Vec2d out_point_2;
+ Vec2d out_point_3;
+ if (new_path < start_x_position)
+ {
+ out_point_1 = Vec2d(start_x_position, cutter_area_y);
+ out_point_2 = Vec2d(new_path, cutter_area_y);
+ out_point_3 = Vec2d(new_path, end_y_position);
+ }
+ else
+ {
+ out_point_1 = Vec2d(new_path, 0);
+ out_point_2 = Vec2d(new_path, 0);
+ out_point_3 = Vec2d(new_path, end_y_position);
+ }
- out_points.clear();
- out_points.emplace_back(out_point_1);
- out_points.emplace_back(out_point_2);
- out_points.emplace_back(out_point_3);
+ out_points.clear();
+ out_points.emplace_back(out_point_1);
+ out_points.emplace_back(out_point_2);
+ out_points.emplace_back(out_point_3);
- return out_points;
-}
+ return out_points;
+ }
-// Only add a newline in case the current G-code does not end with a newline.
- static inline void check_add_eol(std::string& gcode)
+ // Only add a newline in case the current G-code does not end with a newline.
+ static inline void check_add_eol(std::string &gcode)
{
if (!gcode.empty() && gcode.back() != '\n')
gcode += '\n';
}
-
// Return true if tch_prefix is found in custom_gcode
- static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder)
+ static bool custom_gcode_changes_tool(const std::string &custom_gcode, const std::string &tch_prefix, unsigned next_extruder)
{
bool ok = false;
size_t from_pos = 0;
size_t pos = 0;
- while ((pos = custom_gcode.find(tch_prefix, from_pos)) != std::string::npos) {
+ while ((pos = custom_gcode.find(tch_prefix, from_pos)) != std::string::npos)
+ {
if (pos + 1 == custom_gcode.size())
break;
from_pos = pos + 1;
// only whitespace is allowed before the command
- while (--pos < custom_gcode.size() && custom_gcode[pos] != '\n') {
+ while (--pos < custom_gcode.size() && custom_gcode[pos] != '\n')
+ {
if (!std::isspace(custom_gcode[pos]))
goto NEXT;
}
@@ -247,12 +274,13 @@ static std::vector get_path_of_change_filament(const Print& print)
return ok;
}
- std::string OozePrevention::pre_toolchange(GCode& gcodegen)
+ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
{
std::string gcode;
// move to the nearest standby point
- if (!this->standby_points.empty()) {
+ if (!this->standby_points.empty())
+ {
// get current position in print coordinates
Vec3d writer_pos = gcodegen.writer().get_position();
Point pos = Point::new_scale(writer_pos(0), writer_pos(1));
@@ -265,49 +293,50 @@ static std::vector get_path_of_change_filament(const Print& print)
triggered by the caller) nor reduce_crossing_wall and also because the coordinates
of the destination point must not be transformed by origin nor current extruder offset. */
gcode += gcodegen.writer().travel_to_xy(unscale(standby_point),
- "move to standby position");
+ "move to standby position");
}
- if (gcodegen.config().standby_temperature_delta.value != 0) {
+ if (gcodegen.config().standby_temperature_delta.value != 0)
+ {
// we assume that heating is always slower than cooling, so no need to block
- gcode += gcodegen.writer().set_temperature
- (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().filament()->id());
+ gcode += gcodegen.writer().set_temperature(this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().filament()->id());
}
return gcode;
}
- std::string OozePrevention::post_toolchange(GCode& gcodegen)
+ std::string OozePrevention::post_toolchange(GCode &gcodegen)
{
- return (gcodegen.config().standby_temperature_delta.value != 0) ?
- gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().filament()->id()) :
- std::string();
+ return (gcodegen.config().standby_temperature_delta.value != 0) ? gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().filament()->id()) : std::string();
}
int
- OozePrevention::_get_temp(GCode& gcodegen)
+ OozePrevention::_get_temp(GCode &gcodegen)
{
return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0)
- ? gcodegen.config().nozzle_temperature_initial_layer.get_at(gcodegen.writer().filament()->id())
- : gcodegen.config().nozzle_temperature.get_at(gcodegen.writer().filament()->id());
+ ? gcodegen.config().nozzle_temperature_initial_layer.get_at(gcodegen.writer().filament()->id())
+ : gcodegen.config().nozzle_temperature.get_at(gcodegen.writer().filament()->id());
}
std::string transform_gcode(const std::string &gcode, Vec2f pos, const Vec2f &translation, float angle)
{
- Vec2f extruder_offset(0, 0);
+ Vec2f extruder_offset(0, 0);
std::istringstream gcode_str(gcode);
- std::string gcode_out;
- std::string line;
- Vec2f transformed_pos = pos;
- Vec2f old_pos(-1000.1f, -1000.1f);
+ std::string gcode_out;
+ std::string line;
+ Vec2f transformed_pos = pos;
+ Vec2f old_pos(-1000.1f, -1000.1f);
- while (gcode_str) {
+ while (gcode_str)
+ {
std::getline(gcode_str, line); // we read the gcode line by line
- if (line.find("G1 ") == 0) {
+ if (line.find("G1 ") == 0)
+ {
bool never_skip = false;
- auto it = line.find(WipeTower::never_skip_tag());
- if (it != std::string::npos) {
+ auto it = line.find(WipeTower::never_skip_tag());
+ if (it != std::string::npos)
+ {
// remove the tag and remember we saw it
never_skip = true;
line.erase(it, it + WipeTower::never_skip_tag().size());
@@ -316,7 +345,8 @@ static std::vector get_path_of_change_filament(const Print& print)
std::istringstream line_str(line);
line_str >> std::noskipws; // don't skip whitespace
char ch = 0;
- while (line_str >> ch) {
+ while (line_str >> ch)
+ {
if (ch == 'X' || ch == 'Y')
line_str >> (ch == 'X' ? pos.x() : pos.y());
else
@@ -325,12 +355,15 @@ static std::vector get_path_of_change_filament(const Print& print)
transformed_pos = Eigen::Rotation2Df(angle) * pos + translation;
- if (transformed_pos != old_pos || never_skip) {
+ if (transformed_pos != old_pos || never_skip)
+ {
line = line_out.str();
std::ostringstream oss;
oss << std::fixed << std::setprecision(3) << "G1 ";
- if (transformed_pos.x() != old_pos.x() || never_skip) oss << " X" << transformed_pos.x() - extruder_offset.x();
- if (transformed_pos.y() != old_pos.y() || never_skip) oss << " Y" << transformed_pos.y() - extruder_offset.y();
+ if (transformed_pos.x() != old_pos.x() || never_skip)
+ oss << " X" << transformed_pos.x() - extruder_offset.x();
+ if (transformed_pos.y() != old_pos.y() || never_skip)
+ oss << " Y" << transformed_pos.y() - extruder_offset.y();
oss << " ";
line.replace(line.find("G1 "), 3, oss.str());
old_pos = transformed_pos;
@@ -349,32 +382,37 @@ static std::vector get_path_of_change_filament(const Print& print)
float a = 0.f, b = 0.f;
a = wt_max.x() + offset;
b = wt_min.x() - offset;
- if (a > left && a < right) return a;
- if (b > left && b < right) return b;
+ if (a > left && a < right)
+ return a;
+ if (b > left && b < right)
+ return b;
return default_value;
}
- std::string Wipe::wipe(GCode& gcodegen, bool toolchange, bool is_last)
+ std::string Wipe::wipe(GCode &gcodegen, bool toolchange, bool is_last)
{
std::string gcode;
/* Reduce feedrate a bit; travel speed is often too high to move on existing material.
Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */
- //OrcaSlicer
- double cur_speed = gcodegen.writer().get_current_speed();
- double wipe_speed = gcodegen.config().role_base_wipe_speed && cur_speed > EPSILON ? cur_speed / 60 :
- gcodegen.writer().config.travel_speed.get_at(get_config_idx_for_filament(gcodegen.writer().config, gcodegen.writer().filament()->id())) * gcodegen.config().wipe_speed.value / 100;
+ // OrcaSlicer
+ double cur_speed = gcodegen.writer().get_current_speed();
+ double wipe_speed = gcodegen.config().role_base_wipe_speed && cur_speed > EPSILON ? cur_speed / 60 : gcodegen.writer().config.travel_speed.get_at(get_config_idx_for_filament(gcodegen.writer().config, gcodegen.writer().filament()->id())) * gcodegen.config().wipe_speed.value / 100;
- if (toolchange) { wipe_speed = gcodegen.m_print->config().prime_tower_max_speed.value; }
+ if (toolchange)
+ {
+ wipe_speed = gcodegen.m_print->config().prime_tower_max_speed.value;
+ }
// get the retraction length
double length = toolchange
- ? gcodegen.writer().filament()->retract_length_toolchange()
- : gcodegen.writer().filament()->retraction_length();
+ ? gcodegen.writer().filament()->retract_length_toolchange()
+ : gcodegen.writer().filament()->retraction_length();
// Shorten the retraction length by the amount already retracted before wipe.
length *= (1. - gcodegen.writer().filament()->retract_before_wipe());
- if (length >= 0) {
+ if (length >= 0)
+ {
/* Calculate how long we need to travel in order to consume the required
amount of retraction. In other words, how far do we move in XY at wipe_speed
for the time needed to consume retraction_length at retraction_speed? */
@@ -387,38 +425,39 @@ static std::vector get_path_of_change_filament(const Print& print)
wipe_path.append(gcodegen.last_pos());
wipe_path.append(
this->path.points.begin() + 1,
- this->path.points.end()
- );
+ this->path.points.end());
wipe_path.clip_end(wipe_path.length() - wipe_dist);
// subdivide the retraction in segments
- if (!wipe_path.empty()) {
+ if (!wipe_path.empty())
+ {
// BBS. Handle short path case.
- if (wipe_path.length() < wipe_dist) {
+ if (wipe_path.length() < wipe_dist)
+ {
wipe_dist = wipe_path.length();
- //BBS: avoid to divide 0
+ // BBS: avoid to divide 0
wipe_dist = wipe_dist < EPSILON ? EPSILON : wipe_dist;
}
// add tag for processor
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n";
- //BBS: don't need to enable cooling makers when this is the last wipe. Because no more cooling layer will clean this "_WIPE"
+ // BBS: don't need to enable cooling makers when this is the last wipe. Because no more cooling layer will clean this "_WIPE"
gcode += gcodegen.writer().set_speed(wipe_speed * 60, "", (gcodegen.enable_cooling_markers() && !is_last) ? ";_WIPE" : "");
- for (const Line& line : wipe_path.lines()) {
+ for (const Line &line : wipe_path.lines())
+ {
double segment_length = line.length();
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
due to rounding (TODO: test and/or better math for this) */
double dE = length * (segment_length / wipe_dist) * 0.95;
- //BBS: fix this FIXME
- //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle.
- // Is it here for the cooling markers? Or should it be outside of the cycle?
- //gcode += gcodegen.writer().set_speed(wipe_speed * 60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : "");
+ // BBS: fix this FIXME
+ // FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle.
+ // Is it here for the cooling markers? Or should it be outside of the cycle?
+ // gcode += gcodegen.writer().set_speed(wipe_speed * 60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : "");
gcode += gcodegen.writer().extrude_to_xy(
gcodegen.point_to_gcode(line.b),
-dE,
- "wipe and retract"
- );
+ "wipe and retract");
}
// add tag for processor
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n";
@@ -432,17 +471,19 @@ static std::vector get_path_of_change_filament(const Print& print)
return gcode;
}
- static inline Point wipe_tower_point_to_object_point(GCode& gcodegen, const Vec2f& wipe_tower_pt)
+ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt)
{
return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
}
// set volumetric speed of outer wall ,ignore per obejct & region ,just use default setting
- static float get_outer_wall_volumetric_speed(const FullPrintConfig& config, const Print& print, int filament_id, int extruder_id) {
+ static float get_outer_wall_volumetric_speed(const FullPrintConfig &config, const Print &print, int filament_id, int extruder_id)
+ {
float outer_wall_volumetric_speed = 0;
float filament_max_volumetric_speed = config.filament_max_volumetric_speed.get_at(filament_id);
float outer_wall_line_width = print.default_region_config().outer_wall_line_width.value;
- if (outer_wall_line_width == 0.0) {
+ if (outer_wall_line_width == 0.0)
+ {
float default_line_width = print.default_object_config().line_width.value;
outer_wall_line_width = default_line_width == 0.0 ? config.filament_diameter.get_at(filament_id) : default_line_width;
}
@@ -454,7 +495,6 @@ static std::vector get_path_of_change_filament(const Print& print)
return outer_wall_volumetric_speed;
}
-
// Parse the custom G-code, try to find mcode_set_temp_dont_wait and mcode_set_temp_and_wait or optionally G10 with temperature inside the custom G-code.
// Returns true if one of the temp commands are found, and try to parse the target temperature value into temp_out.
static bool custom_gcode_sets_temperature(const std::string &gcode, const int mcode_set_temp_dont_wait, const int mcode_set_temp_and_wait, const bool include_g10, int &temp_out)
@@ -465,38 +505,49 @@ static std::vector get_path_of_change_filament(const Print& print)
const char *ptr = gcode.data();
bool temp_set_by_gcode = false;
- while (*ptr != 0) {
+ while (*ptr != 0)
+ {
// Skip whitespaces.
- for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
+ for (; *ptr == ' ' || *ptr == '\t'; ++ptr)
+ ;
if (*ptr == 'M' || // Line starts with 'M'. It is a machine command.
- (*ptr == 'G' && include_g10)) { // Only check for G10 if requested
+ (*ptr == 'G' && include_g10))
+ { // Only check for G10 if requested
bool is_gcode = *ptr == 'G';
- ++ ptr;
+ ++ptr;
// Parse the M or G code value.
char *endptr = nullptr;
int mgcode = int(strtol(ptr, &endptr, 10));
if (endptr != nullptr && endptr != ptr &&
- is_gcode ?
- // G10 found
- mgcode == 10 :
- // M104/M109 or M140/M190 found.
- (mgcode == mcode_set_temp_dont_wait || mgcode == mcode_set_temp_and_wait)) {
+ is_gcode
+ ?
+ // G10 found
+ mgcode == 10
+ :
+ // M104/M109 or M140/M190 found.
+ (mgcode == mcode_set_temp_dont_wait || mgcode == mcode_set_temp_and_wait))
+ {
ptr = endptr;
- if (! is_gcode)
+ if (!is_gcode)
// Let the caller know that the custom M-code sets the temperature.
temp_set_by_gcode = true;
// Now try to parse the temperature value.
// While not at the end of the line:
- while (strchr(";\r\n\0", *ptr) == nullptr) {
+ while (strchr(";\r\n\0", *ptr) == nullptr)
+ {
// Skip whitespaces.
- for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
- if (*ptr == 'S') {
+ for (; *ptr == ' ' || *ptr == '\t'; ++ptr)
+ ;
+ if (*ptr == 'S')
+ {
// Skip whitespaces.
- for (++ ptr; *ptr == ' ' || *ptr == '\t'; ++ ptr);
+ for (++ptr; *ptr == ' ' || *ptr == '\t'; ++ptr)
+ ;
// Parse an int.
endptr = nullptr;
long temp_parsed = strtol(ptr, &endptr, 10);
- if (endptr > ptr) {
+ if (endptr > ptr)
+ {
ptr = endptr;
temp_out = temp_parsed;
// Let the caller know that the custom G-code sets the temperature
@@ -504,17 +555,22 @@ static std::vector get_path_of_change_filament(const Print& print)
// can be used for other reasons
temp_set_by_gcode = true;
}
- } else {
+ }
+ else
+ {
// Skip this word.
- for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ ptr);
+ for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ptr)
+ ;
}
}
}
}
// Skip the rest of the line.
- for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
+ for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ptr)
+ ;
// Skip the end of line indicators.
- for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
+ for (; *ptr == '\r' || *ptr == '\n'; ++ptr)
+ ;
}
return temp_set_by_gcode;
}
@@ -523,41 +579,48 @@ static std::vector get_path_of_change_filament(const Print& print)
// start_pos refers to the last position before the wipe_tower.
// end_pos refers to the wipe tower's start_pos.
// using the print coordinate system
- Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const BoundingBox& printer_bbx) const
+ Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const BoundingBox &printer_bbx) const
{
- Polyline res;
- coord_t alpha = scaled(2.f); // offset distance
+ Polyline res;
+ coord_t alpha = scaled(2.f); // offset distance
BoundingBox avoid_polygon_inner = avoid_polygon;
avoid_polygon_inner.offset(alpha);
coord_t width = avoid_polygon_inner.max[0] - avoid_polygon_inner.min[0];
Polygon bed_polygon = printer_bbx.polygon();
- Vec2f v(1, 0); // the first print direction of end_pos.
- if (abs(end_pos[0] - avoid_polygon_inner.min[0]) < width / 2) v = -v; // judge whether the wipe tower's infill goes to the left or right.
+ Vec2f v(1, 0); // the first print direction of end_pos.
+ if (abs(end_pos[0] - avoid_polygon_inner.min[0]) < width / 2)
+ v = -v; // judge whether the wipe tower's infill goes to the left or right.
// Judge whether the avoid_polygon_inner is outside the printer_bbx.
// If so, do nothing and just go directly to the end_pos.
bool is_bbx_in_bed = true;
- Points avoid_points = avoid_polygon_inner.polygon().points;
- for (auto &wipe_tower_bbx_p : avoid_points) {
- if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1) {
+ Points avoid_points = avoid_polygon_inner.polygon().points;
+ for (auto &wipe_tower_bbx_p : avoid_points)
+ {
+ if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1)
+ {
is_bbx_in_bed = false;
break;
}
}
- if (!is_bbx_in_bed) {
+ if (!is_bbx_in_bed)
+ {
res.points.push_back(end_pos);
return res;
}
// Ray-Line Segment Intersection
- auto ray_intersetion_line = [](const Vec2d &a, const Vec2d &v1, const Vec2d &b, const Vec2d &c) -> std::pair {
- const Vec2d v2 = c - b;
- double denom = cross2(v1, v2);
- if (fabs(denom) < EPSILON) return {false, Point(0, 0)};
- const Vec2d v12 = (a - b);
- double nume_a = cross2(v2, v12);
- double nume_b = cross2(v1, v12);
- double t1 = nume_a / denom;
- double t2 = nume_b / denom;
- if (t1 >= 0 && t2 >= 0 && t2 <= 1.) {
+ auto ray_intersetion_line = [](const Vec2d &a, const Vec2d &v1, const Vec2d &b, const Vec2d &c) -> std::pair
+ {
+ const Vec2d v2 = c - b;
+ double denom = cross2(v1, v2);
+ if (fabs(denom) < EPSILON)
+ return {false, Point(0, 0)};
+ const Vec2d v12 = (a - b);
+ double nume_a = cross2(v2, v12);
+ double nume_b = cross2(v1, v12);
+ double t1 = nume_a / denom;
+ double t2 = nume_b / denom;
+ if (t1 >= 0 && t2 >= 0 && t2 <= 1.)
+ {
// Get the intersection point.
Vec2d res = a + t1 * v1;
return std::pair(true, scaled(res));
@@ -566,21 +629,23 @@ static std::vector get_path_of_change_filament(const Print& print)
};
struct Inter_info
{
- int inter_idx0 = -1;
+ int inter_idx0 = -1;
Point inter_p;
};
- auto calc_path_len = [](Points &points, Inter_info &beg_info, Inter_info &end_info, bool is_add) -> std::pair, double> {
- int beg = is_add ? (beg_info.inter_idx0 + 1) % points.size() : beg_info.inter_idx0;
- int end = is_add ? end_info.inter_idx0 : (end_info.inter_idx0 + 1) % points.size();
- int i = beg;
- double len = 0;
+ auto calc_path_len = [](Points &points, Inter_info &beg_info, Inter_info &end_info, bool is_add) -> std::pair, double>
+ {
+ int beg = is_add ? (beg_info.inter_idx0 + 1) % points.size() : beg_info.inter_idx0;
+ int end = is_add ? end_info.inter_idx0 : (end_info.inter_idx0 + 1) % points.size();
+ int i = beg;
+ double len = 0;
std::vector path;
path.push_back(beg_info.inter_p);
len += (unscale(beg_info.inter_p) - unscale(points[beg])).squaredNorm();
- while (i != end) {
- int ni = is_add ? (i + 1) % points.size() : (i - 1 + points.size()) % points.size();
- auto a = unscale(points[i]);
- auto b = unscale(points[ni]);
+ while (i != end)
+ {
+ int ni = is_add ? (i + 1) % points.size() : (i - 1 + points.size()) % points.size();
+ auto a = unscale(points[i]);
+ auto b = unscale(points[ni]);
len += (a - b).squaredNorm();
path.push_back(points[i]);
i = ni;
@@ -594,46 +659,56 @@ static std::vector get_path_of_change_filament(const Print& print)
// store in inter_info.
// represent this intersection by 'p'.
Inter_info inter_info;
- for (size_t i = 0; i < avoid_points.size(); i++) {
- const auto &a = avoid_points[i];
- const auto &b = avoid_points[(i + 1) % avoid_points.size()];
+ for (size_t i = 0; i < avoid_points.size(); i++)
+ {
+ const auto &a = avoid_points[i];
+ const auto &b = avoid_points[(i + 1) % avoid_points.size()];
auto [is_inter, inter_p] = ray_intersetion_line(unscale(end_pos), v.cast(), unscale(a), unscale(b));
- if (is_inter) {
+ if (is_inter)
+ {
inter_info.inter_idx0 = i;
- inter_info.inter_p = inter_p;
+ inter_info.inter_p = inter_p;
break;
}
}
- if (inter_info.inter_idx0 == -1) {
+ if (inter_info.inter_idx0 == -1)
+ {
res.points.push_back(end_pos);
return res;
}
// calculate the other intersection of start_to_p with the avoid_polygon.
// represent this intersection by 'p_'.
Inter_info inter_info2;
- Linef start_to_p(unscale(start_pos), unscale(inter_info.inter_p));
- for (size_t i = 0; i < avoid_points.size(); i++) {
- if (i == inter_info.inter_idx0) continue;
+ Linef start_to_p(unscale(start_pos), unscale(inter_info.inter_p));
+ for (size_t i = 0; i < avoid_points.size(); i++)
+ {
+ if (i == inter_info.inter_idx0)
+ continue;
Vec2d a = unscale(avoid_points[i]);
Vec2d b = unscale(avoid_points[(i + 1) % avoid_points.size()]);
Linef tower_edge(a, b);
Vec2d inter;
- if (line_alg::intersection(start_to_p, tower_edge, &inter)) {
- inter_info2.inter_p = scaled(inter);
+ if (line_alg::intersection(start_to_p, tower_edge, &inter))
+ {
+ inter_info2.inter_p = scaled(inter);
inter_info2.inter_idx0 = i;
break;
}
}
// if p_ does not exist, go directly to p.
// else p travels along the shorter path on the wipe_tower_offset_polygon to p_
- if (inter_info2.inter_idx0 == -1) {
+ if (inter_info2.inter_idx0 == -1)
+ {
res.points.push_back(inter_info.inter_p);
- } else {
+ }
+ else
+ {
std::vector path;
auto [path1, len1] = calc_path_len(avoid_points, inter_info2, inter_info, true);
auto [path2, len2] = calc_path_len(avoid_points, inter_info2, inter_info, false);
- path = len1 < len2 ? path1 : path2;
- for (size_t i = 0; i < path.size(); i++) {
+ path = len1 < len2 ? path1 : path2;
+ for (size_t i = 0; i < path.size(); i++)
+ {
res.points.push_back(path[i]);
}
}
@@ -641,7 +716,7 @@ static std::vector get_path_of_change_filament(const Print& print)
return res;
}
- std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const
+ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_filament_id, double z) const
{
gcodegen.reset_last_acceleration();
if (new_filament_id != -1 && new_filament_id != tcr.new_tool)
@@ -656,7 +731,8 @@ static std::vector get_path_of_change_filament(const Print& print)
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
- auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f {
+ auto transform_wt_pt = [&alpha, this](const Vec2f &pt) -> Vec2f
+ {
Vec2f out = Eigen::Rotation2Df(alpha) * pt;
out += m_wipe_tower_pos + m_rib_offset;
return out;
@@ -667,13 +743,14 @@ static std::vector get_path_of_change_filament(const Print& print)
Vec2f tool_change_start_pos = start_pos;
if (tcr.is_tool_change)
tool_change_start_pos = tcr.tool_change_start_pos;
- if (! tcr.priming) {
+ if (!tcr.priming)
+ {
start_pos = transform_wt_pt(start_pos);
end_pos = transform_wt_pt(end_pos);
tool_change_start_pos = transform_wt_pt(tool_change_start_pos);
}
- Vec2f wipe_tower_offset = (tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos) + m_rib_offset;
+ Vec2f wipe_tower_offset = (tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos) + m_rib_offset;
float wipe_tower_rotation = tcr.priming ? 0.f : alpha;
std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation);
@@ -684,7 +761,8 @@ static std::vector get_path_of_change_filament(const Print& print)
// BBS: toolchange gcode will move to start_pos,
// so only perform movement when printing sparse partition to support upper layer.
// start_pos is the position in plate coordinate.
- if (! tcr.priming && tcr.is_finish_first) {
+ if (!tcr.priming && tcr.is_finish_first)
+ {
// Move over the wipe tower.
gcode += gcodegen.retract();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
@@ -695,28 +773,30 @@ static std::vector get_path_of_change_filament(const Print& print)
gcode += gcodegen.unretract();
}
-
double current_z = gcodegen.writer().get_position().z();
if (z == -1.) // in case no specific z was provided, print at current_z pos
z = current_z;
- if (! is_approx(z, current_z)) {
+ if (!is_approx(z, current_z))
+ {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer.");
gcode += gcodegen.writer().unretract();
}
-
// Process the end filament gcode.
- bool add_change_filament_624 = false;
+ bool add_change_filament_624 = false;
std::string end_filament_gcode_str;
- if (gcodegen.writer().filament() != nullptr) {
+ if (gcodegen.writer().filament() != nullptr)
+ {
// Process the custom filament_end_gcode in case of single_extruder_multi_material.
- unsigned int old_filament_id = gcodegen.writer().filament()->id();
- const std::string& filament_end_gcode = gcodegen.config().filament_end_gcode.get_at(old_filament_id);
- if (gcodegen.writer().filament() != nullptr && !filament_end_gcode.empty()) {
+ unsigned int old_filament_id = gcodegen.writer().filament()->id();
+ const std::string &filament_end_gcode = gcodegen.config().filament_end_gcode.get_at(old_filament_id);
+ if (gcodegen.writer().filament() != nullptr && !filament_end_gcode.empty())
+ {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
- if (!gcodegen.m_filament_instances_code.empty()) {
+ if (!gcodegen.m_filament_instances_code.empty())
+ {
end_filament_gcode_str += ("M624 " + gcodegen.m_filament_instances_code + "\n");
gcodegen.m_filament_instances_code = "";
add_change_filament_624 = true;
@@ -726,7 +806,7 @@ static std::vector get_path_of_change_filament(const Print& print)
}
}
- //BBS: increase toolchange count
+ // BBS: increase toolchange count
gcodegen.m_toolchange_count++;
std::string toolchange_gcode_str;
@@ -740,7 +820,7 @@ static std::vector get_path_of_change_filament(const Print& print)
std::string toolchange_retract_str = gcodegen.retract(tcr.is_tool_change && !is_nozzle_change, false, auto_lift_type, true);
check_add_eol(toolchange_retract_str);
- //BBS: if needed, write the gcode_label_objects_end then priming tower, if the retract, didn't did it.
+ // BBS: if needed, write the gcode_label_objects_end then priming tower, if the retract, didn't did it.
std::string object_end_label_temp;
gcodegen.m_writer.add_object_end_labels(object_end_label_temp);
@@ -749,21 +829,23 @@ static std::vector get_path_of_change_filament(const Print& print)
std::string change_filament_gcode = gcodegen.config().change_filament_gcode.value;
bool is_used_travel_avoid_perimeter = gcodegen.m_config.prime_tower_skip_points.value;
- if (is_nozzle_change && !tcr.nozzle_change_result.is_extruder_change) is_used_travel_avoid_perimeter = false;
+ if (is_nozzle_change && !tcr.nozzle_change_result.is_extruder_change)
+ is_used_travel_avoid_perimeter = false;
// add nozzle change gcode into change filament gcode
std::string nozzle_change_gcode_trans;
- if (is_nozzle_change) {
+ if (is_nozzle_change)
+ {
// move to start_pos before nozzle change
std::string start_pos_str;
start_pos_str = gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(tcr.nozzle_change_result.start_pos) + plate_origin_2d), erMixed,
- "Move to nozzle change start pos");
+ "Move to nozzle change start pos");
check_add_eol(start_pos_str);
nozzle_change_gcode_trans += start_pos_str;
nozzle_change_gcode_trans += gcodegen.unretract();
nozzle_change_gcode_trans += transform_gcode(tcr.nozzle_change_result.gcode, tcr.nozzle_change_result.start_pos, wipe_tower_offset, wipe_tower_rotation);
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(tcr.nozzle_change_result.end_pos) + plate_origin_2d));
gcodegen.m_wipe.reset_path();
- for (const Vec2f& wipe_pt : tcr.nozzle_change_result.wipe_path)
+ for (const Vec2f &wipe_pt : tcr.nozzle_change_result.wipe_path)
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt) + plate_origin_2d));
nozzle_change_gcode_trans += gcodegen.retract(tcr.is_tool_change, false, auto_lift_type, true);
end_filament_gcode_str = nozzle_change_gcode_trans + end_filament_gcode_str;
@@ -772,8 +854,9 @@ static std::vector get_path_of_change_filament(const Print& print)
end_filament_gcode_str = toolchange_retract_str + object_end_label_temp + end_filament_gcode_str;
std::string wipe_next_start_point_str;
- bool need_travel_after_change_filament_gcode = false; // travel need be after the filament changed to get the correct "m_curr_extruder_id"
- if (! change_filament_gcode.empty()) {
+ bool need_travel_after_change_filament_gcode = false; // travel need be after the filament changed to get the correct "m_curr_extruder_id"
+ if (!change_filament_gcode.empty())
+ {
DynamicConfig config;
int old_filament_id = gcodegen.writer().filament() ? (int)gcodegen.writer().filament()->id() : -1;
int old_extruder_id = gcodegen.writer().filament() ? (int)gcodegen.writer().filament()->extruder_id() : -1;
@@ -783,11 +866,11 @@ static std::vector get_path_of_change_filament(const Print& print)
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
config.set_key_value("toolchange_z", new ConfigOptionFloat(z));
-// config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
+ // config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
// BBS
{
- GCodeWriter& gcode_writer = gcodegen.m_writer;
- FullPrintConfig& full_config = gcodegen.m_config;
+ GCodeWriter &gcode_writer = gcodegen.m_writer;
+ FullPrintConfig &full_config = gcodegen.m_config;
// set volumetric speed of outer wall ,ignore per obejct,just use default setting
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(full_config, *gcodegen.m_print, new_filament_id, gcodegen.get_extruder_id(new_filament_id));
@@ -798,7 +881,7 @@ static std::vector get_path_of_change_filament(const Print& print)
float old_retract_length_toolchange = (old_filament_id != -1) ? full_config.retract_length_toolchange.get_at(old_filament_id) : 0;
float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_filament_id);
float old_filament_retract_length_nc = full_config.filament_retract_length_nc.get_at(old_filament_id);
- int old_filament_temp = (old_filament_id != -1) ? (gcodegen.on_first_layer()? full_config.nozzle_temperature_initial_layer.get_at(old_filament_id) : full_config.nozzle_temperature.get_at(old_filament_id)) : 210;
+ int old_filament_temp = (old_filament_id != -1) ? (gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(old_filament_id) : full_config.nozzle_temperature.get_at(old_filament_id)) : 210;
int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_filament_id) : full_config.nozzle_temperature.get_at(new_filament_id);
float new_extruder_retracted_length = gcodegen.m_writer.get_extruder_retracted_length(new_filament_id);
Vec3d nozzle_pos = gcode_writer.get_position();
@@ -811,9 +894,9 @@ static std::vector get_path_of_change_filament(const Print& print)
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
int new_filament_e_feedrate = (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(new_filament_id) / filament_area);
new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate;
- float wipe_avoid_pos_x = 0.f;
+ float wipe_avoid_pos_x = 0.f;
{
- //set wipe_avoid_pos_x
+ // set wipe_avoid_pos_x
Vec2f box_min = transform_wt_pt(m_wipe_tower_bbx.min.cast());
Vec2f box_max = transform_wt_pt(m_wipe_tower_bbx.max.cast());
wipe_avoid_pos_x = get_wipe_avoid_pos_x(box_min, box_max, 3.f);
@@ -822,8 +905,8 @@ static std::vector get_path_of_change_filament(const Print& print)
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
config.set_key_value("relative_e_axis", new ConfigOptionBool(full_config.use_relative_e_distances));
config.set_key_value("toolchange_count", new ConfigOptionInt((int)gcodegen.m_toolchange_count));
- //BBS: fan speed is useless placeholer now, but we don't remove it to avoid
- //slicing error in old change_filament_gcode in old 3MF
+ // BBS: fan speed is useless placeholer now, but we don't remove it to avoid
+ // slicing error in old change_filament_gcode in old 3MF
config.set_key_value("fan_speed", new ConfigOptionInt((int)0));
config.set_key_value("old_retract_length", new ConfigOptionFloat(old_retract_length));
config.set_key_value("new_retract_length", new ConfigOptionFloat(new_retract_length));
@@ -849,11 +932,13 @@ static std::vector get_path_of_change_filament(const Print& print)
auto flush_v_speed = m_print_config->filament_flush_volumetric_speed.values;
auto flush_temps = m_print_config->filament_flush_temp.values;
- for (size_t idx = 0; idx < flush_v_speed.size(); ++idx) {
+ for (size_t idx = 0; idx < flush_v_speed.size(); ++idx)
+ {
if (flush_v_speed[idx] == 0)
flush_v_speed[idx] = m_print_config->filament_max_volumetric_speed.get_at(idx);
}
- for (size_t idx = 0; idx < flush_temps.size(); ++idx) {
+ for (size_t idx = 0; idx < flush_temps.size(); ++idx)
+ {
if (flush_temps[idx] == 0)
flush_temps[idx] = m_print_config->nozzle_temperature_range_high.get_at(idx);
}
@@ -873,8 +958,10 @@ static std::vector get_path_of_change_filament(const Print& print)
else
stop_pos = Vec2f(stop_pos.x() + 2.f, stop_pos.y());
BoundingBoxf printer_bbx = unscaled(get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon()));
- if (stop_pos.x() < printer_bbx.min[0]) stop_pos.x() = printer_bbx.min[0];
- if (stop_pos.x() > printer_bbx.max[0]) stop_pos.x() = printer_bbx.max[0];
+ if (stop_pos.x() < printer_bbx.min[0])
+ stop_pos.x() = printer_bbx.min[0];
+ if (stop_pos.x() > printer_bbx.max[0])
+ stop_pos.x() = printer_bbx.max[0];
}
config.set_key_value("wipe_tower_center_pos_x", new ConfigOptionFloat(stop_pos.x()));
@@ -887,14 +974,16 @@ static std::vector get_path_of_change_filament(const Print& print)
flush_count += 1;
float flush_unit = purge_length / flush_count;
int flush_idx = 0;
- for (; flush_idx < flush_count; flush_idx++) {
- char key_value[64] = { 0 };
+ for (; flush_idx < flush_count; flush_idx++)
+ {
+ char key_value[64] = {0};
snprintf(key_value, sizeof(key_value), "flush_length_%d", flush_idx + 1);
config.set_key_value(key_value, new ConfigOptionFloat(flush_unit));
}
- for (; flush_idx < g_max_flush_count; flush_idx++) {
- char key_value[64] = { 0 };
+ for (; flush_idx < g_max_flush_count; flush_idx++)
+ {
+ char key_value[64] = {0};
snprintf(key_value, sizeof(key_value), "flush_length_%d", flush_idx + 1);
config.set_key_value(key_value, new ConfigOptionFloat(0.f));
}
@@ -903,15 +992,16 @@ static std::vector get_path_of_change_filament(const Print& print)
check_add_eol(toolchange_gcode_str);
- //BBS
+ // BBS
{
- //BBS: current position and fan_speed is unclear after interting change_filament_gcode
+ // BBS: current position and fan_speed is unclear after interting change_filament_gcode
check_add_eol(toolchange_gcode_str);
toolchange_gcode_str += ";_FORCE_RESUME_FAN_SPEED\n";
gcodegen.writer().set_current_position_clear(false);
- //BBS: check whether custom gcode changes the z position. Update if changed
+ // BBS: check whether custom gcode changes the z position. Update if changed
double temp_z_after_tool_change;
- if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_str, temp_z_after_tool_change)) {
+ if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_str, temp_z_after_tool_change))
+ {
Vec3d pos = gcodegen.writer().get_position();
pos(2) = temp_z_after_tool_change;
gcodegen.writer().set_position(pos);
@@ -925,11 +1015,13 @@ static std::vector get_path_of_change_filament(const Print& print)
toolchange_command = gcodegen.writer().toolchange(new_filament_id);
if (!custom_gcode_changes_tool(toolchange_gcode_str, gcodegen.writer().toolchange_prefix(), new_filament_id))
toolchange_gcode_str += toolchange_command;
- else {
+ else
+ {
// We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
}
- if (need_travel_after_change_filament_gcode) {
+ if (need_travel_after_change_filament_gcode)
+ {
// After a filament change, the travel path leading to the wipe tower:
// start_point inside the previous printed object,
// end_point at the tower’s start_pos or at the starting point of the tower’s detour path.
@@ -937,20 +1029,23 @@ static std::vector get_path_of_change_filament(const Print& print)
gcodegen.m_avoid_crossing_perimeters.disable_once();
// move to start_pos for wiping after toolchange
- if (!is_used_travel_avoid_perimeter) {
+ if (!is_used_travel_avoid_perimeter)
+ {
std::string start_pos_str = gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d), erMixed, "Move to start pos");
check_add_eol(start_pos_str);
wipe_next_start_point_str = start_pos_str;
- } else {
+ }
+ else
+ {
// BBS:change travel_path
Vec3f gcode_last_pos;
GCodeProcessor::get_last_position_from_gcode(toolchange_gcode_str, gcode_last_pos);
- Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
- Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast() + plate_origin_2d.cast());
- Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
+ Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
+ Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast() + plate_origin_2d.cast());
+ Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
BoundingBox avoid_bbx, printer_bbx;
{
- //set printer_bbx
+ // set printer_bbx
printer_bbx = get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon());
printer_bbx.min = (wipe_tower_point_to_object_point(gcodegen, unscaled(printer_bbx.min) + plate_origin_2d));
@@ -958,20 +1053,23 @@ static std::vector get_path_of_change_filament(const Print& print)
}
{
// set avoid_bbx
- avoid_bbx = scaled(m_wipe_tower_bbx);
+ avoid_bbx = scaled(m_wipe_tower_bbx);
Polygon avoid_points = avoid_bbx.polygon();
- for (auto &p : avoid_points.points) {
+ for (auto &p : avoid_points.points)
+ {
Vec2f pp = transform_wt_pt(unscale(p).cast());
- p = wipe_tower_point_to_object_point(gcodegen, pp + plate_origin_2d);
+ p = wipe_tower_point_to_object_point(gcodegen, pp + plate_origin_2d);
}
avoid_bbx = BoundingBox(avoid_points.points);
}
std::string travel_to_wipe_tower_gcode;
- Polyline travel_polyline = generate_path_to_wipe_tower(gcode_last_pos2d_object, start_wipe_pos, avoid_bbx, printer_bbx);
+ Polyline travel_polyline = generate_path_to_wipe_tower(gcode_last_pos2d_object, start_wipe_pos, avoid_bbx, printer_bbx);
- for (size_t i = 0; i < travel_polyline.points.size(); ++i) {
+ for (size_t i = 0; i < travel_polyline.points.size(); ++i)
+ {
const auto &p = travel_polyline.points[i];
- if (i == travel_polyline.points.size() - 1) {
+ if (i == travel_polyline.points.size() - 1)
+ {
wipe_next_start_point_str = gcodegen.travel_to(p, erMixed, "Move to start pos");
check_add_eol(wipe_next_start_point_str);
break;
@@ -997,13 +1095,15 @@ static std::vector get_path_of_change_filament(const Print& print)
// Process the start filament gcode.
std::string start_filament_gcode_str;
const std::string &filament_start_gcode = gcodegen.config().filament_start_gcode.get_at(new_filament_id);
- if (!filament_start_gcode.empty()) {
+ if (!filament_start_gcode.empty())
+ {
// Process the filament_start_gcode for the active filament only.
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_filament_id));
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
start_filament_gcode_str = gcodegen.placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
- if (add_change_filament_624) {
+ if (add_change_filament_624)
+ {
start_filament_gcode_str += "M625\n";
add_change_filament_624 = false;
}
@@ -1022,7 +1122,7 @@ static std::vector get_path_of_change_filament(const Print& print)
gcode += tcr_gcode;
check_add_eol(toolchange_gcode_str);
- //OrcaSlicer: set new PA for new filament. BBS: never use for Bambu Printer
+ // OrcaSlicer: set new PA for new filament. BBS: never use for Bambu Printer
if (!gcodegen.is_BBL_Printer() && gcodegen.config().enable_pressure_advance.get_at(new_filament_id))
gcode += gcodegen.writer().set_pressure_advance(gcodegen.config().pressure_advance.get_at(new_filament_id));
@@ -1031,16 +1131,18 @@ static std::vector get_path_of_change_filament(const Print& print)
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast());
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos + plate_origin_2d));
- if (!is_approx(z, current_z)) {
+ if (!is_approx(z, current_z))
+ {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer.");
gcode += gcodegen.writer().unretract();
}
- else {
+ else
+ {
// Prepare a future wipe.
gcodegen.m_wipe.reset_path();
- for (const Vec2f& wipe_pt : tcr.wipe_path)
+ for (const Vec2f &wipe_pt : tcr.wipe_path)
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt) + plate_origin_2d));
gcode += gcodegen.retract(false, false, auto_lift_type, true);
}
@@ -1053,7 +1155,7 @@ static std::vector get_path_of_change_filament(const Print& print)
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
- std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const
+ std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult &tcr, const Vec2f &translation, float angle) const
{
Vec2f extruder_offset;
if (m_single_extruder_multi_material)
@@ -1068,26 +1170,30 @@ static std::vector get_path_of_change_filament(const Print& print)
Vec2f transformed_pos = pos;
Vec2f old_pos(-1000.1f, -1000.1f);
- while (gcode_str) {
- std::getline(gcode_str, line); // we read the gcode line by line
+ while (gcode_str)
+ {
+ std::getline(gcode_str, line); // we read the gcode line by line
// All G1 commands should be translated and rotated. X and Y coords are
// only pushed to the output when they differ from last time.
// WT generator can override this by appending the never_skip_tag
- if (line.find("G1 ") == 0 || line.find("G2 ") == 0 || line.find("G3 ") == 0) {
+ if (line.find("G1 ") == 0 || line.find("G2 ") == 0 || line.find("G3 ") == 0)
+ {
std::string cur_gcode_start = line.find("G1 ") == 0 ? "G1 " : (line.find("G2 ") == 0 ? "G2 " : "G3 ");
bool never_skip = false;
auto it = line.find(WipeTower::never_skip_tag());
- if (it != std::string::npos) {
+ if (it != std::string::npos)
+ {
// remove the tag and remember we saw it
never_skip = true;
line.erase(it, it + WipeTower::never_skip_tag().size());
}
std::ostringstream line_out;
std::istringstream line_str(line);
- line_str >> std::noskipws; // don't skip whitespace
+ line_str >> std::noskipws; // don't skip whitespace
char ch = 0;
- while (line_str >> ch) {
+ while (line_str >> ch)
+ {
if (ch == 'X' || ch == 'Y')
line_str >> (ch == 'X' ? pos.x() : pos.y());
else
@@ -1096,7 +1202,8 @@ static std::vector get_path_of_change_filament(const Print& print)
transformed_pos = Eigen::Rotation2Df(angle) * pos + translation;
- if (transformed_pos != old_pos || never_skip) {
+ if (transformed_pos != old_pos || never_skip)
+ {
line = line_out.str();
std::ostringstream oss;
oss << std::fixed << std::setprecision(3) << cur_gcode_start;
@@ -1113,13 +1220,16 @@ static std::vector get_path_of_change_filament(const Print& print)
gcode_out += line + "\n";
// If this was a toolchange command, we should change current extruder offset
- if (line == "[change_filament_gcode]") {
+ if (line == "[change_filament_gcode]")
+ {
// BBS
- if (!m_single_extruder_multi_material) {
+ if (!m_single_extruder_multi_material)
+ {
extruder_offset = m_extruder_offsets[tcr.new_tool].cast();
// If the extruder offset changed, add an extra move so everything is continuous
- if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) {
+ if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast())
+ {
std::ostringstream oss;
oss << std::fixed << std::setprecision(3)
<< "G1 X" << transformed_pos.x() - extruder_offset.x()
@@ -1129,15 +1239,14 @@ static std::vector