mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 07:27:41 -06:00
Merge remote-tracking branch 'origin/main' into dev/gizmo
This commit is contained in:
commit
cf5c19fa24
19 changed files with 328 additions and 170 deletions
|
@ -1787,10 +1787,10 @@ msgid "An SD card needs to be inserted before printing via LAN."
|
|||
msgstr "Před tiskem přes LAN je třeba vložit SD kartu."
|
||||
|
||||
msgid "Sending gcode file over LAN"
|
||||
msgstr "Odesílání souboru gcode přes LAN"
|
||||
msgstr "Odesílání souboru gkód přes LAN"
|
||||
|
||||
msgid "Sending gcode file to sdcard"
|
||||
msgstr "Odesílání souboru gcode na sdcard"
|
||||
msgstr "Odesílání souboru gkód na sd kartu"
|
||||
|
||||
#, c-format, boost-format
|
||||
msgid "Successfully sent. Close current page in %s s"
|
||||
|
@ -2181,7 +2181,7 @@ msgid ""
|
|||
"(Currently supporting automatic supply of consumables with the same brand, "
|
||||
"material type, and color)"
|
||||
msgstr ""
|
||||
"Pokud v AMS existují dva identické filameny, bude povolena záloha AMS "
|
||||
"Pokud v AMS existují dva identické filamenty, bude povolena záloha AMS "
|
||||
"filamentu. \n"
|
||||
"(Aktuálně podporuje automatické doplňování spotřebního materiálu stejné "
|
||||
"značky, typu materiálu a barvy)"
|
||||
|
@ -2333,7 +2333,7 @@ msgid ""
|
|||
"Error message: %1%.\n"
|
||||
"Source file %2%."
|
||||
msgstr ""
|
||||
"Soubor gcode se nepodařilo uložit.\n"
|
||||
"Soubor gkód se nepodařilo uložit.\n"
|
||||
"Chybová zpráva: %1%.\n"
|
||||
"Zdrojový soubor %2%."
|
||||
|
||||
|
@ -2524,7 +2524,7 @@ msgstr ""
|
|||
"typ časosběru je tradiční."
|
||||
|
||||
msgid " But machines with I3 structure will not generate timelapse videos."
|
||||
msgstr " Ale stroje s I3 strukturou nevytvoří timelapse videa."
|
||||
msgstr " Ale stroje s I3 strukturou nevytvářejí časosběrná videa."
|
||||
|
||||
msgid ""
|
||||
"Change these settings automatically? \n"
|
||||
|
@ -2645,7 +2645,7 @@ msgid "Calibrating the micro lida"
|
|||
msgstr "Kalibrace mikro lida"
|
||||
|
||||
msgid "Calibrating extrusion flow"
|
||||
msgstr "Kalibrace extruze průtpku"
|
||||
msgstr "Kalibrace extruze průtoku"
|
||||
|
||||
msgid "Paused due to nozzle temperature malfunction"
|
||||
msgstr "Pozastaveno kvůli poruše teploty trysky"
|
||||
|
@ -3449,7 +3449,7 @@ msgid "Export current sliced file"
|
|||
msgstr "Exportovat aktuální Slicovaný soubor"
|
||||
|
||||
msgid "Export all plate sliced file"
|
||||
msgstr "Exportovat všechny soubor slicované na pdložce"
|
||||
msgstr "Exportovat všechny soubor slicované na podložce"
|
||||
|
||||
msgid "Export G-code"
|
||||
msgstr "Exportovat G-kód"
|
||||
|
@ -4132,7 +4132,7 @@ msgid " upload config prase failed\n"
|
|||
msgstr " nahrávání konfigurace se nepodařilo zpracovat\n"
|
||||
|
||||
msgid " No corresponding storage bucket\n"
|
||||
msgstr " Žádný odpovídající úložný bucket\n"
|
||||
msgstr " Žádný odpovídající úložný prostor\n"
|
||||
|
||||
msgid " can not be opened\n"
|
||||
msgstr " nelze otevřít\n"
|
||||
|
@ -4366,7 +4366,7 @@ msgid ""
|
|||
"The application cannot run normally because OpenGL version is lower than "
|
||||
"2.0.\n"
|
||||
msgstr ""
|
||||
"Aplikace nemůže běžet normálně, protože máte nižší verzi OpenGLnež 2.0.\n"
|
||||
"Aplikace nemůže běžet normálně, protože máte nižší verzi OpenGL než 2.0.\n"
|
||||
|
||||
msgid "Please upgrade your graphics card driver."
|
||||
msgstr "Prosím aktualizujte ovladač grafické karty."
|
||||
|
@ -4808,7 +4808,7 @@ msgid ""
|
|||
"The loaded file contains gcode only, Can not enter the Prepare page"
|
||||
msgstr ""
|
||||
"Režim pouze náhled:\n"
|
||||
"Načtený soubor obsahuje pouze gcode, nelze vstoupit na stránku Příprava"
|
||||
"Načtený soubor obsahuje pouze gkód, nelze vstoupit na stránku Příprava"
|
||||
|
||||
msgid "You can keep the modified presets to the new project or discard them"
|
||||
msgstr "Upravené předvolby si můžete ponechat pro nový projekt nebo je zahodit"
|
||||
|
@ -4914,7 +4914,7 @@ msgid "The following characters are not allowed by a FAT file system:"
|
|||
msgstr "Následující znaky nejsou v souborovém systému FAT povoleny:"
|
||||
|
||||
msgid "Save Sliced file as:"
|
||||
msgstr "Uložit Slicované soubor jako:"
|
||||
msgstr "Uložit Slicovaný soubor jako:"
|
||||
|
||||
#, c-format, boost-format
|
||||
msgid ""
|
||||
|
@ -4932,7 +4932,7 @@ msgstr ""
|
|||
"kladné části."
|
||||
|
||||
msgid "Is the printer ready? Is the print sheet in place, empty and clean?"
|
||||
msgstr "Je tiskarna připravená k tisku? Je podložka prázdná a čistá?"
|
||||
msgstr "Je tiskárna připravená k tisku? Je podložka prázdná a čistá?"
|
||||
|
||||
msgid "Upload and Print"
|
||||
msgstr "Nahrát a Tisknout"
|
||||
|
@ -6226,7 +6226,7 @@ msgid "Jerk limitation"
|
|||
msgstr "Omezení Jerk-Ryv"
|
||||
|
||||
msgid "Single extruder multimaterial setup"
|
||||
msgstr "Nastavení multimaterialu s jedním extruderem"
|
||||
msgstr "Nastavení multimateriálu s jedním extruderem"
|
||||
|
||||
msgid "Wipe tower"
|
||||
msgstr "Čistící věž"
|
||||
|
@ -6558,7 +6558,7 @@ msgid "Login"
|
|||
msgstr "Přihlášení"
|
||||
|
||||
msgid "The configuration package is changed in previous Config Guide"
|
||||
msgstr "Konfigurační balíček byl změněn v předchozím Config Guide"
|
||||
msgstr "Konfigurační balíček byl změněn v předchozím průvodci konfigurací"
|
||||
|
||||
msgid "Configuration package changed"
|
||||
msgstr "Konfigurační balíček změněn"
|
||||
|
@ -8221,7 +8221,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Materiál může mít objemovou změnu po přepnutí mezi roztaveným a krystalickým "
|
||||
"stavem. Toto nastavení proporcionálně změní veškerý vytlačovací tok tohoto "
|
||||
"filamentu v gcode. Doporučený rozsah hodnot je mezi 0,95 a 1,05. Možná "
|
||||
"filamentu v gkódu. Doporučený rozsah hodnot je mezi 0,95 a 1,05. Možná "
|
||||
"můžete tuto hodnotu vyladit, abyste získali pěkně rovný povrch, když dochází "
|
||||
"k mírnému přetečení nebo podtečení"
|
||||
|
||||
|
@ -8323,7 +8323,7 @@ msgid ""
|
|||
"Filament diameter is used to calculate extrusion in gcode, so it's important "
|
||||
"and should be accurate"
|
||||
msgstr ""
|
||||
"Průměr filamentu se používá k výpočtu vytlačování v gcode, takže je důležitý "
|
||||
"Průměr filamentu se používá k výpočtu vytlačování v gkódu, takže je důležitý "
|
||||
"a měl by být přesný"
|
||||
|
||||
msgid "Shrinkage"
|
||||
|
@ -8337,7 +8337,7 @@ msgid ""
|
|||
"Be sure to allow enough space between objects, as this compensation is done "
|
||||
"after the checks."
|
||||
msgstr ""
|
||||
"Zadejte procento smrštění, které filament získá po ochlazení (94% pokud "
|
||||
"Zadejte procento smrštění, které filament získá po ochlazení (94% i pokud "
|
||||
"naměříte 94mm místo 100mm). Část bude pro kompenzaci zmenšena v xy. Bere se "
|
||||
"v úvahu pouze filamentu použit pro obvod.\n"
|
||||
"Ujistěte se aby byl mezi objekty dostatek prostoru, protože tato kompenzace "
|
||||
|
@ -8431,7 +8431,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Doba, po kterou firmware tiskárny (nebo jednotka Multi Material 2.0) zavádí "
|
||||
"nový filament během jeho výměny (při provádění kódu T). Tento čas je přidán "
|
||||
"k celkové době tisku pomocí G-code odhadovače tiskového času."
|
||||
"k celkové době tisku pomocí G-kódu odhadovače tiskového času."
|
||||
|
||||
msgid "Ramming parameters"
|
||||
msgstr "Parametry rapidní extruze"
|
||||
|
@ -8450,7 +8450,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Doba, po kterou firmware tiskárny (nebo jednotka Multi Material 2.0) vysouvá "
|
||||
"filament během jeho výměny (při provádění kódu T). Tento čas je přidán k "
|
||||
"celkové době tisku pomocí G-code odhadovače tiskového času."
|
||||
"celkové době tisku pomocí G-kódu odhadovače tiskového času."
|
||||
|
||||
msgid "Enable ramming for multitool setups"
|
||||
msgstr "Povolení rapidní extruze tiskárny s více nástroji"
|
||||
|
@ -9037,7 +9037,7 @@ msgid "G-code flavor"
|
|||
msgstr "Druh G-kódu"
|
||||
|
||||
msgid "What kind of gcode the printer is compatible with"
|
||||
msgstr "S jakým typem gcode je tiskárna kompatibilní"
|
||||
msgstr "S jakým typem gkódu je tiskárna kompatibilní"
|
||||
|
||||
msgid "Klipper"
|
||||
msgstr "Klipper"
|
||||
|
@ -9181,7 +9181,7 @@ msgstr ""
|
|||
"použije výchozí metodu."
|
||||
|
||||
msgid "This gcode part is inserted at every layer change after lift z"
|
||||
msgstr "Tato část gcode je vložena při každé změně vrstvy po zvednutí z"
|
||||
msgstr "Tato část gkódu je vložena při každé změně vrstvy po zvednutí z"
|
||||
|
||||
msgid "Supports silent mode"
|
||||
msgstr "Podporuje tichý režim"
|
||||
|
@ -9198,7 +9198,7 @@ msgid ""
|
|||
"pause G-code in gcode viewer"
|
||||
msgstr ""
|
||||
"Tento G-kód bude použit jako kód pro pozastavený tisk. Uživatel může vložit "
|
||||
"pauzu G-kód do prohlížeče gcode"
|
||||
"pauzu G-kód do prohlížeče gkódu"
|
||||
|
||||
msgid "This G-code will be used as a custom code"
|
||||
msgstr "Tento G-kód bude použit jako vlastní kód"
|
||||
|
@ -9669,7 +9669,7 @@ msgid ""
|
|||
"resolution and more time to slice"
|
||||
msgstr ""
|
||||
"Cesta G-kódu se generuje po zjednodušení obrysu modelu, aby se předešlo "
|
||||
"příliš velkému počtu bodů a Linek gcode v souboru gcode. Menší hodnota "
|
||||
"příliš velkému počtu bodů a Linek gkódu v souboru gkód. Menší hodnota "
|
||||
"znamená vyšší rozlišení a více času na slicování"
|
||||
|
||||
msgid "Travel distance threshold"
|
||||
|
@ -9680,7 +9680,7 @@ msgid ""
|
|||
"threshold"
|
||||
msgstr ""
|
||||
"Spusťte retrakci pouze tehdy, když je dráha jízdy delší než tato hraniční "
|
||||
"hohnota"
|
||||
"hodnota"
|
||||
|
||||
msgid "Retract amount before wipe"
|
||||
msgstr "Délka retrakce před očištěním"
|
||||
|
@ -9925,7 +9925,7 @@ msgid ""
|
|||
"The printing speed in exported gcode will be slowed down, when the estimated "
|
||||
"layer time is shorter than this value, to get better cooling for these layers"
|
||||
msgstr ""
|
||||
"Rychlost tisku v exportovaném kódu gcode se zpomalí, když je odhadovaná doba "
|
||||
"Rychlost tisku v exportovaném kódu gkód se zpomalí, když je odhadovaná doba "
|
||||
"vrstvy kratší než tato hodnota, aby se dosáhlo lepšího chlazení pro tyto "
|
||||
"vrstvy"
|
||||
|
||||
|
@ -10089,7 +10089,7 @@ msgid ""
|
|||
"print bed, set this to -0.3 (or fix your endstop)."
|
||||
msgstr ""
|
||||
"Tato hodnota bude přidána (nebo odečtena) ze všech souřadnic Z ve výstupním "
|
||||
"G-code. Používá se ke kompenzování špatné pozice endstopu Z. Například pokud "
|
||||
"G-kódu. Používá se ke kompenzování špatné pozice endstopu Z. Například pokud "
|
||||
"endstop 0 skutečně ponechá trysku 0,3 mm daleko od tiskové podložky, "
|
||||
"nastavte hodnotu -0,3 (nebo dolaďte svůj koncový doraz)."
|
||||
|
||||
|
@ -10283,7 +10283,7 @@ msgstr ""
|
|||
"ušetří materiál a sníží poškození objektu.\n"
|
||||
"Pro stromovou podpěru, tenký a organický styl bude agresivněji slučovat "
|
||||
"větve a ušetří mnoho materiálu (výchozí organický), zatímco hybridní styl "
|
||||
"vytvoří podobnou strukturu jako běžná podpěra pod velkými plochými převiy."
|
||||
"vytvoří podobnou strukturu jako běžná podpěra pod velkými plochými převisy."
|
||||
|
||||
msgid "Snug"
|
||||
msgstr "Přiléhavý"
|
||||
|
@ -10390,7 +10390,7 @@ msgstr ""
|
|||
"automaticky vypočítány"
|
||||
|
||||
msgid "Tree support brim width"
|
||||
msgstr "Šířka Limce podpěr stromů"
|
||||
msgstr "Šířka Límce podpěr stromů"
|
||||
|
||||
msgid "Distance from tree branch to the outermost brim line"
|
||||
msgstr "Vzdálenost od větve stromu k nejvzdálenější linii Límce"
|
||||
|
@ -10406,7 +10406,7 @@ msgid "Tree support branch diameter"
|
|||
msgstr "Průměr větve podpěr stromů"
|
||||
|
||||
msgid "This setting determines the initial diameter of support nodes."
|
||||
msgstr "Toto nastavení určuje počáteční průměr uzlů poděry."
|
||||
msgstr "Toto nastavení určuje počáteční průměr uzlů podpěr."
|
||||
|
||||
#. TRN PrintSettings: #lmFIXME
|
||||
msgid "Branch Diameter Angle"
|
||||
|
@ -10501,7 +10501,7 @@ msgid ""
|
|||
"This gcode is inserted when change filament, including T command to trigger "
|
||||
"tool change"
|
||||
msgstr ""
|
||||
"Tento gcode se vloží při výměně filamentu, včetně příkazu T ke spuštění "
|
||||
"Tento gkód se vloží při výměně filamentu, včetně příkazu T ke spuštění "
|
||||
"výměny nástroje"
|
||||
|
||||
msgid ""
|
||||
|
@ -10957,7 +10957,7 @@ msgid "Update the configs values of 3mf to latest."
|
|||
msgstr "Aktualizujte konfigurační hodnoty 3mf na nejnovější."
|
||||
|
||||
msgid "Load default filaments"
|
||||
msgstr "Načíst výchozí filameny"
|
||||
msgstr "Načíst výchozí filamenty"
|
||||
|
||||
msgid "Load first filament as default for those not loaded"
|
||||
msgstr "Načíst první filament jako výchozí pro ty, které nebyly načteny"
|
||||
|
@ -11399,7 +11399,7 @@ msgid ""
|
|||
"3. If the max volumetric speed or print temperature is changed in the "
|
||||
"filament setting."
|
||||
msgstr ""
|
||||
"Nyní jsme přidali automatickou kalibraci pro různé filameny, která je plně "
|
||||
"Nyní jsme přidali automatickou kalibraci pro různé filamenty, která je plně "
|
||||
"automatizovaná a výsledek bude uložen do tiskárny pro budoucí použití. "
|
||||
"Kalibraci musíte provést pouze v následujících omezených případech:\n"
|
||||
"1. Pokud použijete nový filament jiné značky/modelu nebo je filament vlhký;\n"
|
||||
|
@ -11747,7 +11747,7 @@ msgid "Success to get history result"
|
|||
msgstr "Úspěšně načtený historický výsledek kalibrace dynamiky průtoku"
|
||||
|
||||
msgid "Refreshing the historical Flow Dynamics Calibration records"
|
||||
msgstr "Aktualizace historických záznamů kalibrace dynamiky průtpku probíhá"
|
||||
msgstr "Aktualizace historických záznamů kalibrace dynamiky průtoku probíhá"
|
||||
|
||||
msgid "Action"
|
||||
msgstr "Akce"
|
||||
|
@ -11768,7 +11768,7 @@ msgid "Service name"
|
|||
msgstr "Název služby"
|
||||
|
||||
msgid "OctoPrint version"
|
||||
msgstr "Service name"
|
||||
msgstr "Verze OctoPrintu"
|
||||
|
||||
msgid "Searching for devices"
|
||||
msgstr "Vyhledávání zařízení"
|
||||
|
|
|
@ -148,7 +148,7 @@ msgid "Bucket fill"
|
|||
msgstr "Flächenfüllung"
|
||||
|
||||
msgid "Height range"
|
||||
msgstr "Höhenreichweite"
|
||||
msgstr "Höhenbereich"
|
||||
|
||||
msgid "Ctrl + Shift + Enter"
|
||||
msgstr "Strg + Umschalt + Eingabetaste"
|
||||
|
@ -163,7 +163,7 @@ msgid "Triangle"
|
|||
msgstr "Dreieck"
|
||||
|
||||
msgid "Height Range"
|
||||
msgstr "Höhenreichweite"
|
||||
msgstr "Höhenbereich"
|
||||
|
||||
msgid "Vertical"
|
||||
msgstr "Vertikal"
|
||||
|
|
|
@ -65,7 +65,10 @@
|
|||
"50%"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"1"
|
||||
"2"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"4"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"220"
|
||||
|
|
|
@ -22,9 +22,8 @@ public:
|
|||
} else {
|
||||
m_E = 0;
|
||||
m_retracted = 0;
|
||||
m_restart_extra = 0;
|
||||
}
|
||||
|
||||
m_restart_extra = 0;
|
||||
m_absolute_E = 0;
|
||||
}
|
||||
|
||||
|
@ -34,7 +33,7 @@ public:
|
|||
double retract(double length, double restart_extra);
|
||||
double unretract();
|
||||
double E() const { return m_share_extruder ? m_share_E : m_E; }
|
||||
void reset_E() { m_E = 0.; m_share_E = 0.; }
|
||||
void reset_E() { reset(); }
|
||||
double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; }
|
||||
double e_per_mm3() const { return m_e_per_mm3; }
|
||||
// Used filament volume in mm^3.
|
||||
|
|
|
@ -1402,6 +1402,8 @@ namespace DoExport {
|
|||
//BBS
|
||||
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value);
|
||||
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value);
|
||||
//Orca
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change extrusion role G-code")), config.change_extrusion_role_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause G-code")), config.machine_pause_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) {
|
||||
|
@ -4755,6 +4757,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
|
||||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
//Orca: process custom gcode for extrusion role change
|
||||
if (path.role() != m_last_extrusion_role && !m_config.change_extrusion_role_gcode.value.empty()) {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(path.role())));
|
||||
config.set_key_value("last_extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(m_last_extrusion_role)));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z));
|
||||
gcode += this->placeholder_parser_process("change_extrusion_role_gcode",
|
||||
m_config.change_extrusion_role_gcode.value, m_writer.extruder()->id(), &config)
|
||||
+ "\n";
|
||||
}
|
||||
|
||||
// extrude arc or line
|
||||
if (m_enable_extrusion_role_markers) {
|
||||
if (path.role() != m_last_extrusion_role) {
|
||||
|
@ -5002,6 +5016,35 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
return gcode;
|
||||
}
|
||||
|
||||
//Orca: get string name of extrusion role. used for change_extruder_role_gcode
|
||||
std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & role)
|
||||
{
|
||||
switch (role) {
|
||||
case erPerimeter: return "Perimeter";
|
||||
case erExternalPerimeter: return "ExternalPerimeter";
|
||||
case erOverhangPerimeter: return "OverhangPerimeter";
|
||||
case erInternalInfill: return "InternalInfill";
|
||||
case erSolidInfill: return "SolidInfill";
|
||||
case erTopSolidInfill: return "TopSolidInfill";
|
||||
case erBottomSurface: return "BottomSurface";
|
||||
case erBridgeInfill:
|
||||
case erInternalBridgeInfill: return "BridgeInfill";
|
||||
case erGapFill: return "GapFill";
|
||||
case erIroning: return "Ironing";
|
||||
case erSkirt: return "Skirt";
|
||||
case erBrim: return "Brim";
|
||||
case erSupportMaterial: return "SupportMaterial";
|
||||
case erSupportMaterialInterface: return "SupportMaterialInterface";
|
||||
case erSupportTransition: return "SupportTransition";
|
||||
case erWipeTower: return "WipeTower";
|
||||
case erCustom:
|
||||
case erMixed:
|
||||
case erCount:
|
||||
case erNone:
|
||||
default: return "Mixed";
|
||||
}
|
||||
}
|
||||
|
||||
std::string encodeBase64(uint64_t value)
|
||||
{
|
||||
//Always use big endian mode
|
||||
|
|
|
@ -205,6 +205,7 @@ public:
|
|||
// inside the generated string and after the G-code export finishes.
|
||||
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
|
||||
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
||||
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &);
|
||||
|
||||
// For Perl bindings, to be used exclusively by unit tests.
|
||||
unsigned int layer_count() const { return m_layer_count; }
|
||||
|
@ -213,7 +214,7 @@ public:
|
|||
|
||||
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
|
||||
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
|
||||
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
|
||||
std::string set_extruder(unsigned int extruder_id, double print_z);
|
||||
bool is_BBL_Printer();
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "../Layer.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../EdgeGrid.hpp"
|
||||
|
@ -13,6 +17,8 @@
|
|||
#include <unordered_set>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
//#define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct TravelPoint
|
||||
|
@ -20,6 +26,8 @@ struct TravelPoint
|
|||
Point point;
|
||||
// Index of the polygon containing this point. A negative value indicates that the point is not on any border.
|
||||
int border_idx;
|
||||
// simplify_travel() doesn't remove this point.
|
||||
bool do_not_remove = false;
|
||||
};
|
||||
|
||||
struct Intersection
|
||||
|
@ -32,6 +40,8 @@ struct Intersection
|
|||
Point point;
|
||||
// Distance from the first point in the corresponding boundary
|
||||
float distance;
|
||||
// simplify_travel() doesn't remove this point.
|
||||
bool do_not_remove = false;
|
||||
};
|
||||
|
||||
struct ClosestLine
|
||||
|
@ -207,8 +217,8 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||
const ClosestLine &cl_start = start_lines[cl_indices.first];
|
||||
const ClosestLine &cl_end = end_lines[cl_indices.second];
|
||||
std::vector<Intersection> new_intersections;
|
||||
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
|
||||
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
|
||||
return new_intersections;
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +269,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||
if (cl_start_idx != std::numeric_limits<size_t>::max()) {
|
||||
// If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine.
|
||||
const ClosestLine &cl_start = start_lines[cl_start_idx];
|
||||
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)};
|
||||
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true};
|
||||
} else {
|
||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
||||
// vector of intersections. This allows in some cases when it is more than one around ClosestLine start point chose that one which
|
||||
|
@ -267,7 +277,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||
// use the first one, which is the closest one to the start point.
|
||||
size_t start_closest_lines_idx = find_closest_line_with_same_boundary_idx(start_lines, new_intersections, true);
|
||||
const ClosestLine &cl_start = (start_closest_lines_idx != std::numeric_limits<size_t>::max()) ? start_lines[start_closest_lines_idx] : start_lines.front();
|
||||
new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
|
||||
new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +286,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||
if (cl_end_idx != std::numeric_limits<size_t>::max()) {
|
||||
// If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine.
|
||||
const ClosestLine &cl_end = end_lines[cl_end_idx];
|
||||
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)};
|
||||
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true};
|
||||
} else {
|
||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
||||
// vector of intersections. This allows in some cases when it is more than one around ClosestLine end point chose that one which
|
||||
|
@ -284,7 +294,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||
// use the first one, which is the closest one to the end point.
|
||||
size_t end_closest_lines_idx = find_closest_line_with_same_boundary_idx(end_lines, new_intersections, false);
|
||||
const ClosestLine &cl_end = (end_closest_lines_idx != std::numeric_limits<size_t>::max()) ? end_lines[end_closest_lines_idx] : end_lines.front();
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
|
||||
}
|
||||
}
|
||||
return new_intersections;
|
||||
|
@ -350,8 +360,6 @@ static Polyline to_polyline(const std::vector<TravelPoint> &travel)
|
|||
return result;
|
||||
}
|
||||
|
||||
// #define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
|
||||
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
static void export_travel_to_svg(const Polygons &boundary,
|
||||
const Line &original_travel,
|
||||
|
@ -359,16 +367,17 @@ static void export_travel_to_svg(const Polygons &boundary,
|
|||
const std::vector<Intersection> &intersections,
|
||||
const std::string &path)
|
||||
{
|
||||
BoundingBox bbox = get_extents(boundary);
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(boundary);
|
||||
::Slic3r::SVG svg(path, bbox);
|
||||
svg.draw_outline(boundary, "green");
|
||||
svg.draw(original_travel, "blue");
|
||||
svg.draw(result_travel, "red");
|
||||
svg.draw(original_travel.a, "black");
|
||||
svg.draw(original_travel.b, "grey");
|
||||
svg.draw_outline(boundary, "green", stroke_width);
|
||||
svg.draw(original_travel, "blue", stroke_width);
|
||||
svg.draw(result_travel, "red", stroke_width);
|
||||
svg.draw(original_travel.a, "black", stroke_width);
|
||||
svg.draw(original_travel.b, "grey", stroke_width);
|
||||
|
||||
for (const Intersection &intersection : intersections)
|
||||
svg.draw(intersection.point, "lightseagreen");
|
||||
svg.draw(intersection.point, "lightseagreen", stroke_width);
|
||||
}
|
||||
|
||||
static void export_travel_to_svg(const Polygons &boundary,
|
||||
|
@ -433,21 +442,30 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
|
|||
|
||||
visitor.pt_current = ¤t_point;
|
||||
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||
if (travel[point_idx_2].point == current_point) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
continue;
|
||||
}
|
||||
if (!next.do_not_remove)
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||
// Workaround for some issue in MSVC 19.29.30037 32-bit compiler.
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove)
|
||||
break;
|
||||
#else
|
||||
if (travel[point_idx_2].do_not_remove)
|
||||
break;
|
||||
#endif
|
||||
if (travel[point_idx_2].point == current_point) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
continue;
|
||||
}
|
||||
|
||||
visitor.pt_next = &travel[point_idx_2].point;
|
||||
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
// Check if deleting point causes crossing a boundary
|
||||
if (!visitor.intersect) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
visitor.pt_next = &travel[point_idx_2].point;
|
||||
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
// Check if deleting point causes crossing a boundary
|
||||
if (!visitor.intersect) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simplified_path.emplace_back(next);
|
||||
}
|
||||
|
@ -473,7 +491,7 @@ static float get_perimeter_spacing(const Layer &layer)
|
|||
size_t regions_count = 0;
|
||||
float perimeter_spacing = 0.f;
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
if (layer_region != nullptr && !layer_region->slices.empty()) {
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
|
||||
++regions_count;
|
||||
}
|
||||
|
@ -494,7 +512,7 @@ static float get_perimeter_spacing_external(const Layer &layer)
|
|||
for (const PrintObject *object : layer.object()->print()->objects())
|
||||
if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l)
|
||||
for (const LayerRegion *layer_region : l->regions())
|
||||
if (layer_region != nullptr && !layer_region->slices.empty()) {
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
|
||||
++ regions_count;
|
||||
}
|
||||
|
@ -507,6 +525,25 @@ static float get_perimeter_spacing_external(const Layer &layer)
|
|||
return perimeter_spacing;
|
||||
}
|
||||
|
||||
// Returns average perimeter width calculated from all LayerRegion within the layer.
|
||||
static float get_external_perimeter_width(const Layer &layer)
|
||||
{
|
||||
size_t regions_count = 0;
|
||||
float perimeter_width = 0.f;
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_width += float(layer_region->flow(frExternalPerimeter).scaled_width());
|
||||
++regions_count;
|
||||
}
|
||||
|
||||
assert(perimeter_width >= 0.f);
|
||||
if (regions_count != 0)
|
||||
perimeter_width /= float(regions_count);
|
||||
else
|
||||
perimeter_width = get_default_perimeter_spacing(*layer.object());
|
||||
return perimeter_width;
|
||||
}
|
||||
|
||||
// Called by avoid_perimeters() and by simplify_travel_heuristics().
|
||||
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
|
@ -566,7 +603,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
|
|||
// Offset of the polygon's point using get_middle_point_offset is used to simplify the calculation of intersection between the
|
||||
// boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the
|
||||
// appended point will be inside the polygon and not on the polygon border.
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)});
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx), intersection_first.do_not_remove});
|
||||
|
||||
// Check if intersection line also exit the boundary polygon
|
||||
if (it_second_r != it_last_item) {
|
||||
|
@ -590,7 +627,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
|
|||
// Append the farthest intersection into the path
|
||||
left_idx = intersection_second.line_idx;
|
||||
right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1);
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx)});
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx), intersection_second.do_not_remove});
|
||||
// Skip intersections in between
|
||||
it_first = it_second;
|
||||
}
|
||||
|
@ -645,22 +682,22 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary
|
|||
// Check if anyone of ExPolygons contains whole travel.
|
||||
// called by need_wipe() and AvoidCrossingPerimeters::travel_to()
|
||||
// FIXME Lukas H.: Maybe similar approach could also be used for ExPolygon::contains()
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons,
|
||||
const std::vector<BoundingBox> &ex_polygons_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslice,
|
||||
static bool any_expolygon_contains(const ExPolygons &lslices_offset,
|
||||
const std::vector<BoundingBox> &lslices_offset_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslices_offset,
|
||||
const Line &travel)
|
||||
{
|
||||
assert(ex_polygons.size() == ex_polygons_bboxes.size());
|
||||
if(!grid_lslice.bbox().contains(travel.a) || !grid_lslice.bbox().contains(travel.b))
|
||||
assert(lslices_offset.size() == lslices_offset_bboxes.size());
|
||||
if(!grid_lslices_offset.bbox().contains(travel.a) || !grid_lslices_offset.bbox().contains(travel.b))
|
||||
return false;
|
||||
|
||||
FirstIntersectionVisitor visitor(grid_lslice);
|
||||
FirstIntersectionVisitor visitor(grid_lslices_offset);
|
||||
visitor.pt_current = &travel.a;
|
||||
visitor.pt_next = &travel.b;
|
||||
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
grid_lslices_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
if (!visitor.intersect) {
|
||||
for (const ExPolygon &ex_polygon : ex_polygons) {
|
||||
const BoundingBox &bbox = ex_polygons_bboxes[&ex_polygon - &ex_polygons.front()];
|
||||
for (const ExPolygon &ex_polygon : lslices_offset) {
|
||||
const BoundingBox &bbox = lslices_offset_bboxes[&ex_polygon - &lslices_offset.front()];
|
||||
if (bbox.contains(travel.a) && bbox.contains(travel.b) && ex_polygon.contains(travel.a))
|
||||
return true;
|
||||
}
|
||||
|
@ -670,18 +707,18 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons,
|
|||
|
||||
// Check if anyone of ExPolygons contains whole travel.
|
||||
// called by need_wipe()
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice, const Polyline &travel)
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice_offset, const Polyline &travel)
|
||||
{
|
||||
assert(ex_polygons.size() == ex_polygons_bboxes.size());
|
||||
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice](const Point &point) { return !grid_lslice.bbox().contains(point); }))
|
||||
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice_offset](const Point &point) { return !grid_lslice_offset.bbox().contains(point); }))
|
||||
return false;
|
||||
|
||||
FirstIntersectionVisitor visitor(grid_lslice);
|
||||
FirstIntersectionVisitor visitor(grid_lslice_offset);
|
||||
bool any_intersection = false;
|
||||
for (size_t line_idx = 1; line_idx < travel.size(); ++line_idx) {
|
||||
visitor.pt_current = &travel.points[line_idx - 1];
|
||||
visitor.pt_next = &travel.points[line_idx];
|
||||
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
grid_lslice_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
any_intersection = visitor.intersect;
|
||||
if (any_intersection) break;
|
||||
}
|
||||
|
@ -698,13 +735,13 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec
|
|||
}
|
||||
|
||||
static bool need_wipe(const GCode &gcodegen,
|
||||
const EdgeGrid::Grid &grid_lslice,
|
||||
const Line &original_travel,
|
||||
const Polyline &result_travel,
|
||||
const size_t intersection_count)
|
||||
const ExPolygons &lslices_offset,
|
||||
const std::vector<BoundingBox> &lslices_offset_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslices_offset,
|
||||
const Line &original_travel,
|
||||
const Polyline &result_travel,
|
||||
const size_t intersection_count)
|
||||
{
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool z_lift_enabled = gcodegen.config().z_hop.get_at(gcodegen.writer().extruder()->id()) > 0.;
|
||||
bool wipe_needed = false;
|
||||
|
||||
|
@ -714,16 +751,16 @@ static bool need_wipe(const GCode &gcodegen,
|
|||
// The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
|
||||
// If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
|
||||
if (z_lift_enabled) {
|
||||
if (any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, original_travel)) {
|
||||
if (any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, original_travel)) {
|
||||
// Check if original_travel and result_travel are not same.
|
||||
// If both are the same, then it is possible to skip testing of result_travel
|
||||
wipe_needed = !(result_travel.size() > 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) &&
|
||||
!any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
|
||||
!any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
|
||||
} else {
|
||||
wipe_needed = true;
|
||||
}
|
||||
} else {
|
||||
wipe_needed = !any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
|
||||
wipe_needed = !any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,55 +982,71 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
|
|||
// ExPolygons are handled one by one so returned ExPolygons could intersect.
|
||||
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
||||
{
|
||||
double min_contour_width = 2. * offset + SCALED_EPSILON;
|
||||
double search_radius = 2. * (offset + min_contour_width);
|
||||
ExPolygons ex_poly_result = ex_polygons;
|
||||
const std::vector<double> min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON};
|
||||
ExPolygons ex_poly_result = ex_polygons;
|
||||
resample_expolygons(ex_poly_result, offset / 2, scaled<double>(0.5));
|
||||
|
||||
for (ExPolygon &ex_poly : ex_poly_result) {
|
||||
BoundingBox bbox(get_extents(ex_poly));
|
||||
bbox.offset(SCALED_EPSILON);
|
||||
EdgeGrid::Grid grid;
|
||||
grid.set_bbox(bbox);
|
||||
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
||||
|
||||
std::vector<std::vector<float>> ex_poly_distances;
|
||||
precompute_expolygon_distances(ex_poly, ex_poly_distances);
|
||||
// Filter out expolygons smaller than 0.1mm^2
|
||||
if (Vec2d bbox_size = bbox.size().cast<double>(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f)))
|
||||
continue;
|
||||
|
||||
std::vector<std::vector<float>> offsets;
|
||||
offsets.reserve(ex_poly.holes.size() + 1);
|
||||
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
|
||||
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
|
||||
assert(poly.is_counter_clockwise() == (idx_contour == 0));
|
||||
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius);
|
||||
for (float &distance : distances) {
|
||||
if (distance < min_contour_width)
|
||||
distance = 0.f;
|
||||
else if (distance > min_contour_width + 2. * offset)
|
||||
distance = - float(offset);
|
||||
else
|
||||
distance = - (distance - float(min_contour_width)) / 2.f;
|
||||
}
|
||||
offsets.emplace_back(distances);
|
||||
}
|
||||
for (const double &min_contour_width : min_contour_width_values) {
|
||||
const size_t min_contour_width_idx = &min_contour_width - &min_contour_width_values.front();
|
||||
const double search_radius = 2. * (offset + min_contour_width);
|
||||
|
||||
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
|
||||
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
|
||||
if (offset_ex_poly.size() == 1) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
} else if (offset_ex_poly.size() > 1) {
|
||||
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
||||
// tiny artefacts polygons, so these artefacts are removed.
|
||||
double max_area = offset_ex_poly.front().area();
|
||||
size_t max_area_idx = 0;
|
||||
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
|
||||
double area = offset_ex_poly[poly_idx].area();
|
||||
if (max_area < area) {
|
||||
max_area = area;
|
||||
max_area_idx = poly_idx;
|
||||
EdgeGrid::Grid grid;
|
||||
grid.set_bbox(bbox);
|
||||
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
||||
|
||||
std::vector<std::vector<float>> ex_poly_distances;
|
||||
precompute_expolygon_distances(ex_poly, ex_poly_distances);
|
||||
|
||||
std::vector<std::vector<float>> offsets;
|
||||
offsets.reserve(ex_poly.holes.size() + 1);
|
||||
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
|
||||
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
|
||||
assert(poly.is_counter_clockwise() == (idx_contour == 0));
|
||||
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius);
|
||||
for (float &distance : distances) {
|
||||
if (distance < min_contour_width)
|
||||
distance = 0.f;
|
||||
else if (distance > min_contour_width + 2. * offset)
|
||||
distance = -float(offset);
|
||||
else
|
||||
distance = -(distance - float(min_contour_width)) / 2.f;
|
||||
}
|
||||
offsets.emplace_back(distances);
|
||||
}
|
||||
|
||||
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
|
||||
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
|
||||
if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
break;
|
||||
} else if ((min_contour_width_idx + 1) < min_contour_width_values.size()) {
|
||||
continue; // Try the next round with bigger min_contour_width.
|
||||
} else if (offset_ex_poly.size() == 1) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
break;
|
||||
} else if (offset_ex_poly.size() > 1) {
|
||||
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
||||
// tiny artefacts polygons, so these artefacts are removed.
|
||||
double max_area = offset_ex_poly.front().area();
|
||||
size_t max_area_idx = 0;
|
||||
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
|
||||
double area = offset_ex_poly[poly_idx].area();
|
||||
if (max_area < area) {
|
||||
max_area = area;
|
||||
max_area_idx = poly_idx;
|
||||
}
|
||||
}
|
||||
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
||||
break;
|
||||
}
|
||||
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
||||
}
|
||||
}
|
||||
return ex_poly_result;
|
||||
|
@ -1133,10 +1186,8 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
Vec2d startf = start.cast<double>();
|
||||
Vec2d endf = end .cast<double>();
|
||||
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr);
|
||||
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
|
||||
bool is_support_layer = dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr;
|
||||
if (!use_external && (is_support_layer || (!m_lslices_offset.empty() && !any_expolygon_contains(m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel)))) {
|
||||
// Initialize m_internal only when it is necessary.
|
||||
if (m_internal.boundaries.empty())
|
||||
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer())));
|
||||
|
@ -1186,7 +1237,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
} else if (max_detour_length_exceeded) {
|
||||
*could_be_wipe_disabled = false;
|
||||
} else
|
||||
*could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count);
|
||||
*could_be_wipe_disabled = !need_wipe(gcodegen, m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel, result_pl, travel_intersection_count);
|
||||
|
||||
return result_pl;
|
||||
}
|
||||
|
@ -1197,13 +1248,21 @@ void AvoidCrossingPerimeters::init_layer(const Layer &layer)
|
|||
{
|
||||
m_internal.clear();
|
||||
m_external.clear();
|
||||
m_lslices_offset.clear();
|
||||
m_lslices_offset_bboxes.clear();
|
||||
|
||||
float perimeter_offset = -get_external_perimeter_width(layer) / float(2.);
|
||||
m_lslices_offset = offset_ex(layer.lslices, perimeter_offset);
|
||||
|
||||
m_lslices_offset_bboxes.reserve(m_lslices_offset.size());
|
||||
for (const ExPolygon &ex_poly : m_lslices_offset)
|
||||
m_lslices_offset_bboxes.emplace_back(get_extents(ex_poly));
|
||||
|
||||
BoundingBox bbox_slice(get_extents(layer.lslices));
|
||||
bbox_slice.offset(SCALED_EPSILON);
|
||||
|
||||
m_grid_lslice.set_bbox(bbox_slice);
|
||||
//FIXME 1mm grid?
|
||||
m_grid_lslice.create(layer.lslices, coord_t(scale_(1.)));
|
||||
m_grid_lslices_offset.set_bbox(bbox_slice);
|
||||
m_grid_lslices_offset.create(m_lslices_offset, coord_t(scale_(1.)));
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2022 Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_AvoidCrossingPerimeters_hpp_
|
||||
#define slic3r_AvoidCrossingPerimeters_hpp_
|
||||
|
||||
|
@ -58,8 +62,11 @@ private:
|
|||
// we enable it by default for the first travel move in print
|
||||
bool m_disabled_once { true };
|
||||
|
||||
// Lslices offseted by half an external perimeter width. Used for detection if line or polyline is inside of any polygon.
|
||||
ExPolygons m_lslices_offset;
|
||||
std::vector<BoundingBox> m_lslices_offset_bboxes;
|
||||
// Used for detection of line or polyline is inside of any polygon.
|
||||
EdgeGrid::Grid m_grid_lslice;
|
||||
EdgeGrid::Grid m_grid_lslices_offset;
|
||||
// Store all needed data for travels inside object
|
||||
Boundary m_internal;
|
||||
// Store all needed data for travels outside object
|
||||
|
|
|
@ -320,7 +320,7 @@ std::string GCodeWriter::reset_e(bool force)
|
|||
return "";
|
||||
|
||||
if (m_extruder != nullptr) {
|
||||
if (m_extruder->E() == 0. && ! force)
|
||||
if (is_zero(m_extruder->E()) && ! force)
|
||||
return "";
|
||||
m_extruder->reset_E();
|
||||
}
|
||||
|
@ -709,7 +709,7 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
|
|||
length = 1;
|
||||
|
||||
std::string gcode;
|
||||
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
|
||||
if (double dE = m_extruder->retract(length, restart_extra); !is_zero(dE)) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
|
||||
}
|
||||
|
@ -737,7 +737,7 @@ std::string GCodeWriter::unretract()
|
|||
if (FLAVOR_IS(gcfMakerWare))
|
||||
gcode = "M101 ; extruder on\n";
|
||||
|
||||
if (double dE = m_extruder->unretract(); dE != 0) {
|
||||
if (double dE = m_extruder->unretract(); !is_zero(dE)) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
|
||||
gcode += this->reset_e();
|
||||
|
|
|
@ -819,7 +819,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
|||
"printer_technology",
|
||||
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
|
||||
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
|
||||
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode",
|
||||
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
|
||||
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
|
||||
"default_print_profile", "inherits",
|
||||
"silent_mode",
|
||||
|
|
|
@ -3970,6 +3970,15 @@ def = this->add("filament_loading_speed", coFloats);
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("change_extrusion_role_gcode", coString);
|
||||
def->label = L("Change extrusion role G-code");
|
||||
def->tooltip = L("This gcode is inserted when the extrusion role is changed");
|
||||
def->multiline = true;
|
||||
def->full_width = true;
|
||||
def->height = 5;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("top_surface_line_width", coFloatOrPercent);
|
||||
def->label = L("Top surface");
|
||||
def->category = L("Quality");
|
||||
|
|
|
@ -969,6 +969,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, single_extruder_multi_material_priming))
|
||||
((ConfigOptionBool, wipe_tower_no_sparse_layers))
|
||||
((ConfigOptionString, change_filament_gcode))
|
||||
((ConfigOptionString, change_extrusion_role_gcode))
|
||||
((ConfigOptionFloat, travel_speed))
|
||||
((ConfigOptionFloat, travel_speed_z))
|
||||
((ConfigOptionBool, silent_mode))
|
||||
|
|
|
@ -278,6 +278,12 @@ constexpr inline T sqr(T x)
|
|||
return x * x;
|
||||
}
|
||||
|
||||
template<typename Number> constexpr
|
||||
inline bool is_zero(Number value)
|
||||
{
|
||||
return std::fabs(double(value)) < 1e-6;
|
||||
}
|
||||
|
||||
template <typename T, typename Number>
|
||||
constexpr inline T lerp(const T& a, const T& b, Number t)
|
||||
{
|
||||
|
|
|
@ -2123,16 +2123,15 @@ void GUI_App::init_app_config()
|
|||
app_config = new AppConfig();
|
||||
//app_config = new AppConfig(is_editor() ? AppConfig::EAppMode::Editor : AppConfig::EAppMode::GCodeViewer);
|
||||
|
||||
m_config_corrupted = false;
|
||||
// load settings
|
||||
m_app_conf_exists = app_config->exists();
|
||||
if (m_app_conf_exists) {
|
||||
std::string error = app_config->load();
|
||||
if (!error.empty()) {
|
||||
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
||||
throw Slic3r::RuntimeError(
|
||||
_u8L("OrcaSlicer configuration file may be corrupted and is not abled to be parsed."
|
||||
"Please delete the file and try again.") +
|
||||
"\n\n" + app_config->config_path() + "\n\n" + error);
|
||||
// Orca: if the config file is corrupted, we will show a error dialog and create a default config file.
|
||||
m_config_corrupted = true;
|
||||
|
||||
}
|
||||
// Save orig_version here, so its empty if no app_config existed before this run.
|
||||
m_last_config_version = app_config->orig_version();//parse_semver_from_ini(app_config->config_path());
|
||||
|
@ -2749,6 +2748,7 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
if (m_post_initialized && app_config->dirty())
|
||||
app_config->save();
|
||||
|
||||
});
|
||||
|
||||
m_initialized = true;
|
||||
|
@ -2756,6 +2756,13 @@ bool GUI_App::on_init_inner()
|
|||
flush_logs();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "finished the gui app init";
|
||||
if (m_config_corrupted) {
|
||||
m_config_corrupted = false;
|
||||
show_error(nullptr,
|
||||
_u8L(
|
||||
"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\nOrcaSlicer has attempted to recreate the "
|
||||
"configuration file.\nPlease note, application settings will be lost, but printer profiles will not be affected."));
|
||||
}
|
||||
//BBS: delete splash screen
|
||||
delete scrn;
|
||||
return true;
|
||||
|
|
|
@ -658,6 +658,7 @@ private:
|
|||
bool m_datadir_redefined { false };
|
||||
std::string m_older_data_dir_path;
|
||||
boost::optional<Semver> m_last_config_version;
|
||||
bool m_config_corrupted { false };
|
||||
};
|
||||
|
||||
DECLARE_APP(GUI_App)
|
||||
|
|
|
@ -149,6 +149,17 @@ bool GLGizmoText::on_init()
|
|||
|
||||
reset_text_info();
|
||||
|
||||
m_desc["font"] = _L("Font");
|
||||
m_desc["size"] = _L("Size");
|
||||
m_desc["thickness"] = _L("Thickness");
|
||||
m_desc["text_gap"] = _L("Text Gap");
|
||||
m_desc["angle"] = _L("Angle");
|
||||
m_desc["embeded_depth"] = _L("Embeded\ndepth");
|
||||
m_desc["input_text"] = _L("Input text");
|
||||
|
||||
m_desc["surface"] = _L("Surface");
|
||||
m_desc["horizontal_text"] = _L("Horizontal text");
|
||||
|
||||
m_desc["rotate_text_caption"] = _L("Shift + Mouse move up or dowm");
|
||||
m_desc["rotate_text"] = _L("Rotate text");
|
||||
|
||||
|
@ -667,13 +678,13 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale);
|
||||
GizmoImguiBegin("Text", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
float space_size = m_imgui->get_style_scaling() * 8;
|
||||
float font_cap = m_imgui->calc_text_size(_L("Font")).x;
|
||||
float size_cap = m_imgui->calc_text_size(_L("Size")).x;
|
||||
float thickness_cap = m_imgui->calc_text_size(_L("Thickness")).x;
|
||||
float input_cap = m_imgui->calc_text_size(_L("Input text")).x;
|
||||
float depth_cap = m_imgui->calc_text_size(_L("Embeded")).x;
|
||||
float caption_size = std::max(std::max(font_cap, size_cap), std::max(depth_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x;
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const std::array<std::string, 7> cap_array = std::array<std::string, 7>{ "font", "size", "thickness", "text_gap", "angle", "embeded_depth", "input_text" };
|
||||
float caption_size = 0.0f;
|
||||
for (const auto &t : cap_array) {
|
||||
caption_size = std::max(caption_size, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
caption_size += space_size + ImGui::GetStyle().WindowPadding.x;
|
||||
|
||||
float input_text_size = m_imgui->scaled(10.0f);
|
||||
float button_size = ImGui::GetFrameHeight();
|
||||
|
@ -700,7 +711,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
||||
m_imgui->text(_L("Font"));
|
||||
m_imgui->text(m_desc["font"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
push_combo_style(currt_scale);
|
||||
|
@ -740,7 +751,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::PopStyleVar(2);
|
||||
pop_combo_style();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Size"));
|
||||
m_imgui->text(m_desc["size"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(input_size);
|
||||
if(ImGui::InputFloat("###font_size", &m_font_size, 0.0f, 0.0f, "%.2f"))
|
||||
|
@ -767,7 +778,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
ImGui::PopStyleVar(3);
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Thickness"));
|
||||
m_imgui->text(m_desc["thickness"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
float old_value = m_thickness;
|
||||
|
@ -782,7 +793,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
const float drag_left_width = caption_size + slider_width + space_size;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Text Gap"));
|
||||
m_imgui->text(m_desc["text_gap"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
if (m_imgui->bbl_slider_float_style("##text_gap", &m_text_gap, -100, 100, "%.2f", 1.0f, true))
|
||||
|
@ -793,7 +804,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Angle"));
|
||||
m_imgui->text(m_desc["angle"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
if (m_imgui->bbl_slider_float_style("##angle", &m_rotate_angle, 0, 360, "%.2f", 1.0f, true))
|
||||
|
@ -804,7 +815,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Embeded\ndepth"));
|
||||
m_imgui->text(m_desc["embeded_depth"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
old_value = m_embeded_depth;
|
||||
|
@ -815,7 +826,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Input text"));
|
||||
m_imgui->text(m_desc["input_text"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
|
||||
|
@ -833,12 +844,12 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
|||
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (m_imgui->bbl_checkbox(_L("Surface"), m_is_surface_text))
|
||||
if (m_imgui->bbl_checkbox(m_desc["surface"], m_is_surface_text))
|
||||
m_need_update_text = true;
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (m_imgui->bbl_checkbox(_L("Horizontal text"), m_keep_horizontal))
|
||||
if (m_imgui->bbl_checkbox(m_desc["horizontal_text"], m_keep_horizontal))
|
||||
m_need_update_text = true;
|
||||
|
||||
//ImGui::SameLine();
|
||||
|
|
|
@ -16,7 +16,7 @@ constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2
|
|||
|
||||
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
|
||||
static const float HORIZONTAL_SLIDER_WINDOW_HEIGHT = 64.0f;
|
||||
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 124.0f;
|
||||
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 160.0f;
|
||||
static const float GROOVE_WIDTH = 12.0f;
|
||||
static const ImVec2 ONE_LAYER_MARGIN = ImVec2(20.0f, 20.0f);
|
||||
static const ImVec2 ONE_LAYER_BUTTON_SIZE = ImVec2(28.0f, 28.0f);
|
||||
|
|
|
@ -534,7 +534,7 @@ void PartPlate::calc_vertex_for_icons(int index, PickingModel &model)
|
|||
ExPolygon poly;
|
||||
auto bed_ext = get_extents(m_shape);
|
||||
Vec2d p = bed_ext[2];
|
||||
if (m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
|
||||
if (m_plater && m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
|
||||
p[1] -= std::max(
|
||||
0.0, (bed_ext.size()(1) - 5 * PARTPLATE_ICON_SIZE - 4 * PARTPLATE_ICON_GAP_Y - PARTPLATE_ICON_GAP_TOP) / 2);
|
||||
|
||||
|
|
|
@ -3232,6 +3232,17 @@ void TabPrinter::build_fff()
|
|||
option.opt.height = gcode_field_height;//150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup, opt_key, value);
|
||||
};
|
||||
|
||||
option = optgroup->get_option("change_extrusion_role_gcode");
|
||||
option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = gcode_field_height;//150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue