Merge branch 'main' into ui-fixes-100

This commit is contained in:
yw4z
2026-09-11 02:19:58 +03:00
committed by GitHub
114 changed files with 14373 additions and 1845 deletions
+94 -11
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -2293,8 +2293,6 @@ msgstr ""
msgid "%s has been removed."
msgstr ""
msgid "Switching application language"
msgstr ""
msgid "Select the language"
msgstr ""
@@ -4571,6 +4569,9 @@ msgid ""
"No - Cancel enabling spiral mode"
msgstr ""
msgid "N/A"
msgstr ""
msgid "Printing"
msgstr ""
@@ -5019,9 +5020,6 @@ msgstr ""
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr ""
msgid "N/A"
msgstr ""
msgid "System agents"
msgstr ""
@@ -5951,6 +5949,12 @@ msgstr ""
msgid "Save current project as"
msgstr ""
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr ""
@@ -7696,6 +7700,12 @@ msgstr ""
msgid "Customized Preset"
msgstr ""
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr ""
@@ -8074,6 +8084,17 @@ msgstr ""
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr ""
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr ""
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr ""
@@ -8271,8 +8292,6 @@ msgstr ""
msgid "Language selection"
msgstr ""
msgid "Switching application language while some presets are modified."
msgstr ""
msgid "Asia-Pacific"
msgstr ""
@@ -9124,9 +9143,6 @@ msgstr ""
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr ""
msgid "Publish"
msgstr ""
msgid "Publish was canceled"
msgstr ""
@@ -9142,6 +9158,64 @@ msgstr ""
msgid "Jump to webpage"
msgstr ""
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, possible-c-format, possible-boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, possible-c-format, possible-boost-format
msgid "Save %s as"
msgstr ""
@@ -19337,6 +19411,15 @@ msgstr ""
msgid "Skipping objects."
msgstr ""
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr ""
+94 -11
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2025-03-15 10:55+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -2522,8 +2522,6 @@ msgstr "Hi ha una actualització disponible. Obriu el quadre de diàleg del paqu
msgid "%s has been removed."
msgstr "%s s'ha eliminat."
msgid "Switching application language"
msgstr "Canvi d'idioma de l'aplicació"
msgid "Select the language"
msgstr "Seleccioneu l'idioma"
@@ -4995,6 +4993,9 @@ msgstr ""
"Sí: canviar aquesta configuració i activar el mode d'espiral automàticament\n"
"No - Renunciar a utilitzar el mode espiral aquesta vegada"
msgid "N/A"
msgstr "N/D"
msgid "Printing"
msgstr "Imprimint"
@@ -5456,9 +5457,6 @@ msgstr "Patró no vàlid. Utilitzeu N, N#K, o una llista separada per comes amb
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Format no vàlid. Format vectorial esperat: \"%1%\""
msgid "N/A"
msgstr "N/D"
# AI Translated
msgid "System agents"
msgstr "Agents del sistema"
@@ -6421,6 +6419,12 @@ msgstr "Desa el projecte com a"
msgid "Save current project as"
msgstr "Desar el projecte actual com"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importar 3MF STL/STEP/SVG/OBJ/AMF"
@@ -8275,6 +8279,12 @@ msgstr "Confirmeu que els Codis-G d'aquests perfils són segurs per evitar danys
msgid "Customized Preset"
msgstr "Perfil personalitzat"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
# AI Translated
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Els noms dels components dins del fitxer STEP no tenen format UTF-8!"
@@ -8687,6 +8697,17 @@ msgstr "Desa el fitxer Laminat com a:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "El fitxer %s s'ha enviat a l'emmagatzematge de la impressora i es pot visualitzar a la impressora."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publicar"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "El tipus de broquet no està establert. Establiu el broquet i torneu-ho a provar."
@@ -8901,8 +8922,6 @@ msgstr "Voleu continuar?"
msgid "Language selection"
msgstr "Selecció d'idiomes"
msgid "Switching application language while some presets are modified."
msgstr "Canviant l'idioma de l'aplicació mentre es modifiquen alguns perfils."
msgid "Asia-Pacific"
msgstr "Àsia-Pacífic"
@@ -9880,9 +9899,6 @@ msgstr "Anar a la pàgina web de publicació de models"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Nota: La preparació pot trigar uns quants minuts. Si us plau, sigui pacient."
msgid "Publish"
msgstr "Publicar"
msgid "Publish was canceled"
msgstr "La publicació ha estat cancel·lada"
@@ -9898,6 +9914,64 @@ msgstr "Carregant dades"
msgid "Jump to webpage"
msgstr "Anar a la pàgina web"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Desar %s com a"
@@ -21387,6 +21461,15 @@ msgstr "Aquesta acció no es pot desfer. Continuar?"
msgid "Skipping objects."
msgstr "Ometent objectes."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Seleccioneu filament"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Jakub Hencl\n"
"Language-Team: \n"
@@ -2482,8 +2482,6 @@ msgstr "Je k dispozici aktualizace. Otevřete dialog balíčku předvoleb a prov
msgid "%s has been removed."
msgstr "%s bylo odstraněno."
msgid "Switching application language"
msgstr "Přepnutí jazyka aplikace"
msgid "Select the language"
msgstr "Zvolte jazyk"
@@ -4953,6 +4951,10 @@ msgstr ""
"Ano – změnit tato nastavení a automaticky povolit spirálový režim\n"
"Ne – tentokrát nepoužít spirálový režim"
# AI Translated
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Tisk"
@@ -5412,10 +5414,6 @@ msgstr "Neplatný vzor. Použijte N, N#K nebo seznam oddělený čárkami s voli
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Neplatný formát. Očekávaný vektorový formát: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "Systémoví agenti"
@@ -6382,6 +6380,12 @@ msgstr "Uložit projekt jako"
msgid "Save current project as"
msgstr "Uložit aktuální projekt jako"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importovat 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8237,6 +8241,12 @@ msgstr "Potvrďte prosím, že je G-code v těchto předvolbách bezpečný, aby
msgid "Customized Preset"
msgstr "Přizpůsobená předvolba"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Názvy komponent v souboru STEP nejsou ve formátu UTF-8!"
@@ -8647,6 +8657,17 @@ msgstr "Uložit rozřezaný soubor jako:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Soubor %s byl odeslán do úložiště tiskárny a lze jej zobrazit na tiskárně."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publikovat"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Typ trysky není nastaven. Nastavte prosím trysku a zkuste to znovu."
@@ -8859,8 +8880,6 @@ msgstr "Chcete pokračovat?"
msgid "Language selection"
msgstr "Výběr jazyka"
msgid "Switching application language while some presets are modified."
msgstr "Přepnutí jazyka aplikace, když jsou některé předvolby upraveny."
msgid "Asia-Pacific"
msgstr "Asie-Pacifik"
@@ -9828,9 +9847,6 @@ msgstr "Přejít na webovou stránku publikace modelu"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Poznámka: Příprava může trvat několik minut. Buďte prosím trpěliví."
msgid "Publish"
msgstr "Publikovat"
msgid "Publish was canceled"
msgstr "Publikování bylo zrušeno"
@@ -9846,6 +9862,64 @@ msgstr "Nahrávání dat"
msgid "Jump to webpage"
msgstr "Přejít na webovou stránku"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Uložit %s jako"
@@ -21331,6 +21405,15 @@ msgstr "Tuto akci nelze vrátit zpět. Pokračovat?"
msgid "Skipping objects."
msgstr "Přeskakování objektů."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
# AI Translated
msgid "Select Filament"
msgstr "Vybrat filament"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Heiko Liebscher <hliebschergmail.com>\n"
"Language-Team: \n"
@@ -2430,8 +2430,6 @@ msgstr "Es ist ein Update verfügbar. Öffnen Sie den Profilbündel-Dialog, um e
msgid "%s has been removed."
msgstr "%s wurde entfernt."
msgid "Switching application language"
msgstr "Wechsel der Sprache"
msgid "Select the language"
msgstr "Sprache wählen"
@@ -4859,6 +4857,9 @@ msgstr ""
"Ja - Diese Einstellungen ändern und den Spiralmodus automatisch aktivieren\n"
"Nein - Spiralmodus nicht aktivieren"
msgid "N/A"
msgstr "Nicht verfügbar"
msgid "Printing"
msgstr "Drucken"
@@ -5318,9 +5319,6 @@ msgstr "Ungültiges Muster. Verwenden Sie N, N#K oder eine durch Kommas getrennt
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Ungültiges Format. Erwartetes Vektorformat: \"%1%\""
msgid "N/A"
msgstr "Nicht verfügbar"
# AI Translated
msgid "System agents"
msgstr "Systemagenten"
@@ -6274,6 +6272,12 @@ msgstr "Projekt speichern als"
msgid "Save current project as"
msgstr "Aktuelles Projekt speichern als"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importiere 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8107,6 +8111,12 @@ msgstr "Bitte bestätigen Sie, dass die G-Codes innerhalb dieser Profile sicher
msgid "Customized Preset"
msgstr "Benutzerdefinierte Profile"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Der Name der Komponenten in der Step-Datei ist nicht im UTF-8-Format!"
@@ -8519,6 +8529,17 @@ msgstr "Geslicte Datei speichern unter:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Die Datei %s wurde an den Speicher des Druckers gesendet und kann auf dem Drucker angezeigt werden."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Veröffentlichen"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Die Düsenart ist nicht eingestellt. Bitte stellen Sie die Düse ein und versuchen Sie es erneut."
@@ -8731,8 +8752,6 @@ msgstr "Möchten Sie fortfahren?"
msgid "Language selection"
msgstr "Sprachauswahl"
msgid "Switching application language while some presets are modified."
msgstr "Umschalten der Anwendungssprache, während einige Profile geändert werden."
msgid "Asia-Pacific"
msgstr "Asien-Pazifik"
@@ -9662,9 +9681,6 @@ msgstr "Zur Modellveröffentlichungs-Webseite springen"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Hinweis: Die Vorbereitung kann einige Minuten dauern. Bitte haben Sie Geduld."
msgid "Publish"
msgstr "Veröffentlichen"
msgid "Publish was canceled"
msgstr "Veröffentlichung wurde abgebrochen"
@@ -9680,6 +9696,64 @@ msgstr "Daten werden hochgeladen"
msgid "Jump to webpage"
msgstr "Zu einer Website springen"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "%s speichern als"
@@ -20816,6 +20890,15 @@ msgstr "Diese Aktion kann nicht rückgängig gemacht werden. Fortsetzen?"
msgid "Skipping objects."
msgstr "Objekte werden übersprungen."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Filament auswählen"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-06-17 15:44-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: \n"
@@ -2289,8 +2289,6 @@ msgstr ""
msgid "%s has been removed."
msgstr ""
msgid "Switching application language"
msgstr ""
msgid "Select the language"
msgstr ""
@@ -4567,6 +4565,9 @@ msgid ""
"No - Cancel enabling spiral mode"
msgstr ""
msgid "N/A"
msgstr ""
msgid "Printing"
msgstr ""
@@ -5015,9 +5016,6 @@ msgstr ""
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr ""
msgid "N/A"
msgstr ""
msgid "System agents"
msgstr ""
@@ -5947,6 +5945,12 @@ msgstr ""
msgid "Save current project as"
msgstr ""
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr ""
@@ -7692,6 +7696,12 @@ msgstr ""
msgid "Customized Preset"
msgstr ""
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr ""
@@ -8070,6 +8080,17 @@ msgstr ""
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr ""
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr ""
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr ""
@@ -8267,8 +8288,6 @@ msgstr ""
msgid "Language selection"
msgstr ""
msgid "Switching application language while some presets are modified."
msgstr ""
msgid "Asia-Pacific"
msgstr ""
@@ -9120,9 +9139,6 @@ msgstr ""
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr ""
msgid "Publish"
msgstr ""
msgid "Publish was canceled"
msgstr ""
@@ -9138,6 +9154,64 @@ msgstr ""
msgid "Jump to webpage"
msgstr ""
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr ""
@@ -19333,6 +19407,15 @@ msgstr ""
msgid "Skipping objects."
msgstr ""
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr ""
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Ian A. Bassi <>\n"
"Language-Team: \n"
@@ -2356,8 +2356,6 @@ msgstr "Hay una actualización disponible. Abra el cuadro de diálogo del paquet
msgid "%s has been removed."
msgstr "Se ha eliminado %s."
msgid "Switching application language"
msgstr "Cambiando el idioma de la aplicación"
msgid "Select the language"
msgstr "Seleccionar el idioma"
@@ -4729,6 +4727,9 @@ msgstr ""
"Sí - Cambiar estos ajustes y activar el modo espiral automáticamente\n"
"No - Dejar de usar el modo espiral esta vez"
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Imprimiendo"
@@ -5182,9 +5183,6 @@ msgstr "Patrón inválido. Use N, N#K, o una lista separada por comas con #K opc
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Formato inválido. Formato de vector esperado: \"%1%\""
msgid "N/A"
msgstr "N/A"
msgid "System agents"
msgstr "Agentes del sistema"
@@ -6129,6 +6127,12 @@ msgstr "Guardar proyecto como"
msgid "Save current project as"
msgstr "Guardar el proyecto actual como"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importar 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -7910,6 +7914,12 @@ msgstr "¡Por favor, confirme que el G-Code dentro de los perfiles son seguros p
msgid "Customized Preset"
msgstr "Perfil Personalizado"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "¡El nombre de los componentes dentro del archivo de pasos no tiene formato UTF-8!"
@@ -8305,6 +8315,17 @@ msgstr "Guardar el archivo laminado como:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "El archivo %s ha sido mandado al almacenamiento de la impresora y puede ser visualizado en la impresora."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publicar"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "El tipo de boquilla no está establecido. Configure la boquilla e inténtelo de nuevo."
@@ -8506,8 +8527,6 @@ msgstr "¿Quieres continuar?"
msgid "Language selection"
msgstr "Selección de idiomas"
msgid "Switching application language while some presets are modified."
msgstr "Cambiando idioma de la aplicación mientras se modifican algunos perfiles."
msgid "Asia-Pacific"
msgstr "Asia-Pacífico"
@@ -9419,9 +9438,6 @@ msgstr "Ir a la página web de publicación de modelos"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Nota: La preparación puede llevar varios minutos. Por favor, sea paciente."
msgid "Publish"
msgstr "Publicar"
msgid "Publish was canceled"
msgstr "La publicación fue cancelada"
@@ -9437,6 +9453,64 @@ msgstr "Cargando datos"
msgid "Jump to webpage"
msgstr "Ir a la página web"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Guardar %s como"
@@ -20409,6 +20483,15 @@ msgstr "Esta acción no se puede deshacer. ¿Continuar?"
msgid "Skipping objects."
msgstr "Omitiendo objetos."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Seleccionar Filamento"
+94 -11
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-07-20 13:33+0200\n"
"Last-Translator: Manu Goiogana <mgoiogana@gmail.com>\n"
"Language-Team: \n"
@@ -2390,8 +2390,6 @@ msgstr "Eguneratze bat dago erabilgarri. Ireki aurrezarpen-paketeen elkarrizketa
msgid "%s has been removed."
msgstr "%s kendu da."
msgid "Switching application language"
msgstr "Aplikazioaren hizkuntza aldatzen"
msgid "Select the language"
msgstr "Hautatu hizkuntza"
@@ -4771,6 +4769,9 @@ msgstr ""
"Bai - Aldatu ezarpen hauek eta gaitu espiral/loreontzi modua automatikoki\n"
"Ez - Utzi bertan behera espiral modua gaitzea"
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Inprimatzen"
@@ -5231,9 +5232,6 @@ msgstr "Patroi baliogabea. Erabili N, N#K edo komaz bereizitako zerrenda bat, sa
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Formatuak ez du balio. Espero den formatu bektoriala: \"%1%\""
msgid "N/A"
msgstr "N/A"
msgid "System agents"
msgstr "Sistema-agenteak"
@@ -6175,6 +6173,12 @@ msgstr "Gorde proiektua honela"
msgid "Save current project as"
msgstr "Gorde uneko proiektua honela"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Inportatu 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -7988,6 +7992,12 @@ msgstr "Berretsi aurrezarpen hauetako G-codea segurua dela, makinari kalterik ez
msgid "Customized Preset"
msgstr "Aurrezarpen pertsonalizatua"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "STEP fitxategiko osagai-izena(k) ez dago/daude UTF-8 formatuan!"
@@ -8388,6 +8398,17 @@ msgstr "Gorde xerratutako fitxategia honela:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "%s fitxategia inprimagailuaren biltegiratze-eremura bidali da eta inprimagailuan ikus daiteke."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Argitaratu"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Pita mota ez dago ezarrita. Ezarri pita eta saiatu berriro."
@@ -8587,8 +8608,6 @@ msgstr "Jarraitu nahi duzu?"
msgid "Language selection"
msgstr "Hizkuntza-hautaketa"
msgid "Switching application language while some presets are modified."
msgstr "Aplikazioaren hizkuntza aldatzen ari da aurrezarpen batzuk aldatuta dauden bitartean."
msgid "Asia-Pacific"
msgstr "Asia-Pazifikoa"
@@ -9505,9 +9524,6 @@ msgstr "Joan modeloa argitaratzeko web-orrira"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Oharra: prestaketak minutu batzuk iraun ditzake. Izan pazientzia."
msgid "Publish"
msgstr "Argitaratu"
msgid "Publish was canceled"
msgstr "Argitaratzea bertan behera utzi da"
@@ -9523,6 +9539,64 @@ msgstr "Datuak igotzen"
msgid "Jump to webpage"
msgstr "Joan web-orrira"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Gorde %s honela"
@@ -20596,6 +20670,15 @@ msgstr "Ekintza hau ezin da desegin. Jarraitu?"
msgid "Skipping objects."
msgstr "Objektuak saltatzen."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Hautatu filamentua"
+94 -11
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: Guislain Cyril, Thomas Lété\n"
@@ -2414,8 +2414,6 @@ msgstr "Une mise à jour est disponible. Ouvrez la boîte de dialogue du paquet
msgid "%s has been removed."
msgstr "%s a été supprimé."
msgid "Switching application language"
msgstr "Changer la langue de l'application"
msgid "Select the language"
msgstr "Sélectionner la langue"
@@ -4809,6 +4807,9 @@ msgstr ""
"Oui - Modifiez ces paramètres et activez automatiquement le mode spirale/vase\n"
"Non - Annuler l'activation du mode spirale"
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Impression"
@@ -5268,9 +5269,6 @@ msgstr "Motif invalide. Utilisez N, N#K, ou une liste séparée par des virgules
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Format invalide. Format vectoriel attendu : \"%1%\""
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "Agents système"
@@ -6223,6 +6221,12 @@ msgstr "Enregistrer le projet sous"
msgid "Save current project as"
msgstr "Enregistrer le projet actuel sous"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importer des fichiers 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8044,6 +8048,12 @@ msgstr "Veuillez vous assurer que les G-codes de ces préréglages sont sûrs af
msgid "Customized Preset"
msgstr "Préréglage personnalisé"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Le nom des composants dans le fichier STEP n'est pas au format UTF-8 !"
@@ -8445,6 +8455,17 @@ msgstr "Enregistrer le fichier découpé sous :"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Le fichier %s a été envoyé vers l'espace de stockage de l'imprimante et peut être visualisé sur l'imprimante."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publier"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Le type de buse n'est pas défini. Veuillez définir la buse et réessayer."
@@ -8655,8 +8676,6 @@ msgstr "Voulez-vous continuer ?"
msgid "Language selection"
msgstr "Sélection de la langue"
msgid "Switching application language while some presets are modified."
msgstr "Changement de langue de l’application alors que certains préréglages sont modifiés."
msgid "Asia-Pacific"
msgstr "Asie-Pacifique"
@@ -9576,9 +9595,6 @@ msgstr "Accéder à la page internet de publication des modèles"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Remarque : La préparation peut prendre plusieurs minutes. Veuillez patienter."
msgid "Publish"
msgstr "Publier"
msgid "Publish was canceled"
msgstr "La publication a été annulée"
@@ -9594,6 +9610,64 @@ msgstr "Téléversement des données"
msgid "Jump to webpage"
msgstr "Ouvrir la page internet"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Enregistrer %s sous"
@@ -20708,6 +20782,15 @@ msgstr "Cette action ne peut pas être annulée. Continuer ?"
msgid "Skipping objects."
msgstr "Ignorer des objets."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Sélectionner le filament"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -2460,8 +2460,6 @@ msgstr "Frissítés érhető el. Nyisd meg a beállításcsomag párbeszédablak
msgid "%s has been removed."
msgstr "%s eltávolítva."
msgid "Switching application language"
msgstr "Alkalmazás nyelvének váltása"
msgid "Select the language"
msgstr "Válaszd ki a nyelvet"
@@ -4905,6 +4903,10 @@ msgstr ""
"Igen - Módosítsd ezeket a beállításokat, és automatikusan kapcsold be a spirál módot\n"
"Nem - Most ne kapcsold be a spirál módot"
# AI Translated
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Nyomtatás"
@@ -5364,10 +5366,6 @@ msgstr "Érvénytelen minta. Használj N, N#K formátumot, vagy vesszővel elvá
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Érvénytelen formátum. Elvárt vektor formátum: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "Rendszerügynökök"
@@ -6325,6 +6323,12 @@ msgstr "Projekt mentése másként"
msgid "Save current project as"
msgstr "Jelenlegi projekt mentése másként"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importálás 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8159,6 +8163,12 @@ msgstr "Kérlek, győződj meg arról, hogy a beállításokban található G-k
msgid "Customized Preset"
msgstr "Egyedi beállítás"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "A STEP fájlon belüli komponens neve nem UTF-8 formátumban van!"
@@ -8571,6 +8581,17 @@ msgstr "Szeletelt fájl mentése mint:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "A(z) %s fájlt elküldtük a nyomtató tárhelyére. A fájl a nyomtatón tekinthető meg."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Közzététel"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "A fúvókatípus nincs beállítva. Állítsd be a fúvókát, majd próbáld újra."
@@ -8783,8 +8804,6 @@ msgstr "Szeretnéd folytatni?"
msgid "Language selection"
msgstr "Nyelv kiválasztása"
msgid "Switching application language while some presets are modified."
msgstr "Alkalmazás nyelvének átváltása, miközben egyes beállítások módosultak."
msgid "Asia-Pacific"
msgstr "Ázsia-Csendes-óceáni térség"
@@ -9736,9 +9755,6 @@ msgstr "Ugrás a modell közzététele weboldalra"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Megjegyzés: Az előkészítés több percig is eltarthat. Kérlek, várj."
msgid "Publish"
msgstr "Közzététel"
msgid "Publish was canceled"
msgstr "A közzététel törlésre került"
@@ -9754,6 +9770,64 @@ msgstr "Adatok feltöltése"
msgid "Jump to webpage"
msgstr "Ugrás a weboldalra"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "%s mentése"
@@ -21110,6 +21184,15 @@ msgstr "Ez a művelet nem vonható vissza. Folytatod?"
msgid "Skipping objects."
msgstr "Objektumok kihagyása."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Filament kiválasztása"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -2466,8 +2466,6 @@ msgstr "È disponibile un aggiornamento. Apri la finestra di dialogo del bundle
msgid "%s has been removed."
msgstr "%s è stato rimosso."
msgid "Switching application language"
msgstr "Cambio lingua applicazione"
msgid "Select the language"
msgstr "Seleziona la lingua"
@@ -4907,6 +4905,9 @@ msgstr ""
"Sì - Modifica queste impostazioni ed abilita la modalità spirale automaticamente\n"
"No - Annulla l'attivazione della modalità a spirale"
msgid "N/A"
msgstr "N/D"
msgid "Printing"
msgstr "Stampa"
@@ -5366,9 +5367,6 @@ msgstr "Schema non valido. Utilizzare N, N#K o un elenco separato da virgole con
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Formato non valido. Formato vettoriale previsto: \"%1%\""
msgid "N/A"
msgstr "N/D"
# AI Translated
msgid "System agents"
msgstr "Agenti di sistema"
@@ -6327,6 +6325,12 @@ msgstr "Salva progetto con nome"
msgid "Save current project as"
msgstr "Salva progetto corrente con nome"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importa 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8163,6 +8167,12 @@ msgstr "Si prega di confermare che i G-code all'interno di questi profili sono s
msgid "Customized Preset"
msgstr "Profilo personalizzato"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Il nome dei componenti all'interno del file STEP non è in formato UTF-8!"
@@ -8570,6 +8580,17 @@ msgstr "Salva file elaborato con nome:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Il file %s è stato inviato alla memoria della stampante e può essere visualizzato da lì."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Pubblica"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Il tipo di ugello non è impostato. Impostare l'ugello e riprovare."
@@ -8784,8 +8805,6 @@ msgstr "Vuoi continuare?"
msgid "Language selection"
msgstr "Selezione lingua"
msgid "Switching application language while some presets are modified."
msgstr "Cambio lingua applicazione durante la modifica di alcuni profili."
msgid "Asia-Pacific"
msgstr "Asia-Pacifico"
@@ -9754,9 +9773,6 @@ msgstr "Vai alla pagina web di pubblicazione del modello"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Nota: la preparazione può richiedere alcuni minuti. Si prega di avere pazienza."
msgid "Publish"
msgstr "Pubblica"
msgid "Publish was canceled"
msgstr "La pubblicazione è stata annullata"
@@ -9772,6 +9788,64 @@ msgstr "Caricamento dati"
msgid "Jump to webpage"
msgstr "Vai alla pagina web"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Salva %s con nome"
@@ -21134,6 +21208,15 @@ msgstr "Questa azione non può essere annullata. Continuare?"
msgid "Skipping objects."
msgstr "Salto degli oggetti."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Seleziona filamento"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -2473,8 +2473,6 @@ msgstr "アップデートが利用可能です。プリセットバンドルの
msgid "%s has been removed."
msgstr "%sを削除しました。"
msgid "Switching application language"
msgstr "アプリケーション言語の切り替え"
msgid "Select the language"
msgstr "言語を選択"
@@ -4920,6 +4918,10 @@ msgstr ""
"はい - 変更して、スパイラルモードを有効にします\n"
"いいえ - 変更せず、スパイラルモードを有効しません"
# AI Translated
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "造形中"
@@ -5378,10 +5380,6 @@ msgstr "無効なパターンです。N、N#K、またはオプション#K付き
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "無効なフォーマット、%1%であるはずです。"
# AI Translated
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "システムエージェント"
@@ -6336,6 +6334,12 @@ msgstr "プロジェクトを名前を付けて保存"
msgid "Save current project as"
msgstr "プロジェクトを名前を付けて保存"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "3MF/STL/STEP/SVG/OBJ/AMFをインポート"
@@ -8177,6 +8181,12 @@ msgstr "これらのプリセット内のG-codeがマシンに損傷を与えな
msgid "Customized Preset"
msgstr "カスタマイズされたプリセット"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "ファイルのエンコーディング方式はUTF-8形式ではありません"
@@ -8589,6 +8599,17 @@ msgstr "名前を付けて保存:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "%sを送信しました、プリンターにて確認できます"
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "公開する"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "ノズルタイプが設定されていません。ノズルを設定して再試行してください。"
@@ -8802,8 +8823,6 @@ msgstr "続行しますか?"
msgid "Language selection"
msgstr "言語選択"
msgid "Switching application language while some presets are modified."
msgstr "アプリケーション言語を切り替える時に、プリセットの変更があります"
msgid "Asia-Pacific"
msgstr "アジア太平洋地域"
@@ -9776,9 +9795,6 @@ msgstr "モデル公開ページに移動"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "注意: 準備するには数分かかる場合があります、暫くお待ち下さい。"
msgid "Publish"
msgstr "公開する"
msgid "Publish was canceled"
msgstr "公開は取り消しました"
@@ -9794,6 +9810,64 @@ msgstr "データをアップロード中"
msgid "Jump to webpage"
msgstr "ウェブページに移動"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "%sを名前つけて保存"
@@ -21678,6 +21752,15 @@ msgstr "この操作は元に戻せません。続行しますか?"
msgid "Skipping objects."
msgstr "オブジェクトをスキップ中。"
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "フィラメントを選択"
+95 -12
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2025-06-02 17:12+0900\n"
"Last-Translator: crwusiz <crwusiz@gmail.com>\n"
"Language-Team: \n"
@@ -2481,8 +2481,6 @@ msgstr "사용 가능한 업데이트가 있습니다. 사전 설정 번들 대
msgid "%s has been removed."
msgstr "%s이(가) 제거되었습니다."
msgid "Switching application language"
msgstr "응용 프로그램 언어 전환"
msgid "Select the language"
msgstr "언어 선택"
@@ -4931,6 +4929,10 @@ msgstr ""
"예 - 이 설정을 변경하고 나선 모드를 자동으로 활성화합니다\n"
"아니오 - 이번에는 나선 모드 사용을 포기합니다"
# AI Translated
msgid "N/A"
msgstr "해당 없음"
msgid "Printing"
msgstr "출력 중"
@@ -5390,10 +5392,6 @@ msgstr "잘못된 패턴입니다. N, N#K 또는 항목당 선택적 #K가 있
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "잘못된 형식입니다. 필요한 벡터 형식: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "해당 없음"
# AI Translated
msgid "System agents"
msgstr "시스템 에이전트"
@@ -6350,6 +6348,12 @@ msgstr "프로젝트 다른 이름으로 저장"
msgid "Save current project as"
msgstr "현재 프로젝트 다른 이름으로 저장"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "3MF/STL/STEP/SVG/OBJ/AMF 가져오기"
@@ -8195,6 +8199,12 @@ msgstr "이러한 사전 설정 내의 Gcode가 기계 손상을 방지할 수
msgid "Customized Preset"
msgstr "사용자 정의 프리셋"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
# AI Translated
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "STEP 파일 내부의 구성 요소 이름이 UTF-8 형식이 아닙니다!"
@@ -8622,6 +8632,17 @@ msgstr "슬라이스 파일을 다음으로 저장:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "%s 파일이 프린터의 저장 공간으로 전송되었으며 프린터에서 볼 수 있습니다."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "게시"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "노즐 유형이 설정되지 않았습니다. 노즐을 설정하고 다시 시도하세요."
@@ -8837,8 +8858,6 @@ msgstr "계속하시겠습니까?"
msgid "Language selection"
msgstr "언어 선택"
msgid "Switching application language while some presets are modified."
msgstr "일부 사전 설정이 수정되는 동안 응용 프로그램 언어를 전환합니다."
msgid "Asia-Pacific"
msgstr "아시아 태평양"
@@ -9865,9 +9884,6 @@ msgstr "모델 게시 웹 페이지로 이동"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "참고: 준비하는 데 몇 분 정도 걸릴 수 있습니다. 조금만 기다려 주십시오."
msgid "Publish"
msgstr "게시"
msgid "Publish was canceled"
msgstr "게시가 취소되었습니다"
@@ -9884,6 +9900,64 @@ msgstr "데이터 업로드 중"
msgid "Jump to webpage"
msgstr "웹 페이지로 이동"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "%s을(를) 다음으로 저장"
@@ -21536,6 +21610,15 @@ msgstr "이 작업은 취소할 수 없습니다. 계속하시겠습니까?"
msgid "Skipping objects."
msgstr "물체 건너뛰기"
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "필라멘트 선택"
+2
View File
@@ -136,6 +136,7 @@ src/slic3r/GUI/BackgroundSlicingProcess.cpp
src/slic3r/GUI/BedShapeDialog.cpp
src/slic3r/GUI/BedShapeDialog.hpp
src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigValueFormatter.cpp
src/slic3r/GUI/DeviceManager.cpp
src/slic3r/GUI/DeviceErrorDialog.cpp
src/slic3r/GUI/ExtraRenderers.cpp
@@ -175,6 +176,7 @@ src/slic3r/GUI/ProgressStatusBar.cpp
src/slic3r/GUI/PlateSettingsDialog.cpp
src/slic3r/GUI/PrivacyUpdateDialog.cpp
src/slic3r/GUI/PublishDialog.cpp
src/slic3r/GUI/PublishSettingsDialog.cpp
src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/Search.cpp
src/slic3r/GUI/Selection.cpp
+94 -11
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-07-02 14:13+0300\n"
"Last-Translator: Gintaras Kučinskas <sharanchius@gmail.com>\n"
"Language-Team: \n"
@@ -2449,8 +2449,6 @@ msgstr "Yra prieinamas atnaujinimas. Atidarykite profilių paketo dialogo langą
msgid "%s has been removed."
msgstr "%s buvo pašalintas."
msgid "Switching application language"
msgstr "Perjungiama programos kalba"
msgid "Select the language"
msgstr "Pasirinkite kalbą"
@@ -4893,6 +4891,9 @@ msgstr ""
"Taip – pakeisti šiuos nustatymus ir automatiškai įjungti spiralinį režimą\n"
"Ne – nenaudoti spiralinio režimo"
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Spausdinimas"
@@ -5352,9 +5353,6 @@ msgstr "Neteisingas šablonas. Naudokite N, N#K arba kableliais atskirtą sąra
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Netinkamas formatas. Tinkamas vektorinis formatas: \"%1%\""
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "Sisteminiai agentai"
@@ -6312,6 +6310,12 @@ msgstr "Įrašyti projektą kaip"
msgid "Save current project as"
msgstr "Įrašyti dabartinį projektą kaip"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importuoti 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8153,6 +8157,12 @@ msgstr "Patvirtinkite, kad šiuose profiliuose esantis G-kodas yra saugus, kad i
msgid "Customized Preset"
msgstr "Pritaikytas profilis"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Komponentų pavadinimai STEP faile nėra UTF-8 formato!"
@@ -8564,6 +8574,17 @@ msgstr "Išsaugoti susluoksniuotą failą kaip:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Failas %s išsiųstas į spausdintuvo laikmeną ir gali būti peržiūrimas spausdintuve."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Talpinti"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Purkštuko tipas nenustatytas. Nustatykite purkštuką ir bandykite dar kartą."
@@ -8774,8 +8795,6 @@ msgstr "Ar norite tęsti?"
msgid "Language selection"
msgstr "Kalbos pasirinkimas"
msgid "Switching application language while some presets are modified."
msgstr "Keičiama programos kalba, kai yra pakeistų profilių."
msgid "Asia-Pacific"
msgstr "Azija-Ramusis vandenynas"
@@ -9693,9 +9712,6 @@ msgstr "Pereiti į modelio talpinimo interneto puslapį"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Pastaba: paruošimas gali užtrukti kelias minutes. Būkite kantrūs."
msgid "Publish"
msgstr "Talpinti"
msgid "Publish was canceled"
msgstr "Publikavimas buvo atšauktas"
@@ -9711,6 +9727,64 @@ msgstr "Įkeliami duomenys"
msgid "Jump to webpage"
msgstr "Pereiti į interneto puslapį"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Išsaugoti %s kaip"
@@ -20865,6 +20939,15 @@ msgstr "Šio veiksmo atšaukti nebus galima. Tęsti?"
msgid "Skipping objects."
msgstr "Praleidžiami objektai."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Pasirinkti giją"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -2679,8 +2679,6 @@ msgstr "Er is een update beschikbaar. Open het dialoogvenster voor de voorinstel
msgid "%s has been removed."
msgstr "%s is verwijderd."
msgid "Switching application language"
msgstr "De taal van de applicatie wordt aangepast"
msgid "Select the language"
msgstr "Kies de taal"
@@ -5325,6 +5323,9 @@ msgstr ""
"Ja - Pas de instellingen aan en zet de vaas modus automatisch aan\n"
"Nee - Pas de vaas modus deze keer niet toe"
msgid "N/A"
msgstr "N/B"
msgid "Printing"
msgstr "Printen"
@@ -5861,9 +5862,6 @@ msgstr "Ongeldig patroon. Gebruik N, N#K of een door komma's gescheiden lijst me
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Onjuist formaat. Het Vector formaat wordt verwacht: \"%1%\""
msgid "N/A"
msgstr "N/B"
# AI Translated
msgid "System agents"
msgstr "Systeemagenten"
@@ -6888,6 +6886,12 @@ msgstr "Bewaar project als"
msgid "Save current project as"
msgstr "Bewaar huidig project als"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "3MF/STL/STEP/SVG/OBJ/AMF importeren"
@@ -8904,6 +8908,12 @@ msgstr "Controleer of de G-codes in deze presets veilig zijn om schade aan de ma
msgid "Customized Preset"
msgstr "Aangepaste voorinstelling"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Naam van componenten in step-bestand is niet UTF-8-formaat!"
@@ -9339,6 +9349,17 @@ msgstr "Bewaar het geslicede bestand als:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Het bestand %s is naar de opslagruimte van de printer gestuurd en kan op de printer worden bekeken."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publiceren"
# AI Translated
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Het mondstuktype is niet ingesteld. Stel het mondstuk in en probeer het opnieuw."
@@ -9579,8 +9600,6 @@ msgstr "Wilt u doorgaan?"
msgid "Language selection"
msgstr "Taal selectie"
msgid "Switching application language while some presets are modified."
msgstr "De taal van de toepassing aanpaasen terwijl sommige voorinstellingen zijn aangepast."
msgid "Asia-Pacific"
msgstr "Azië-Pacific"
@@ -10626,9 +10645,6 @@ msgstr "Ga naar de website om het model te publiceren"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Notitie: het voorbereiden kan enkele minuten duren. Even geduld alstublieft."
msgid "Publish"
msgstr "Publiceren"
msgid "Publish was canceled"
msgstr "Het publiceren is geannuleerd"
@@ -10645,6 +10661,64 @@ msgstr "Gegevens uploaden"
msgid "Jump to webpage"
msgstr "Ga naar de website"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Bewaar %s als"
@@ -23245,6 +23319,15 @@ msgstr "Deze actie kan niet ongedaan worden gemaakt. Doorgaan?"
msgid "Skipping objects."
msgstr "Objecten worden overgeslagen."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Selecteer filament"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer 2.3.0-rc\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Krzysztof Morga <<tlumaczeniebs@gmail.com>>\n"
"Language-Team: \n"
@@ -2512,8 +2512,6 @@ msgstr "Dostępna jest aktualizacja. Otwórz okno pakietu profili, aby ją zains
msgid "%s has been removed."
msgstr "%s został usunięty."
msgid "Switching application language"
msgstr "Zmiana języka aplikacji"
msgid "Select the language"
msgstr "Wybierz język"
@@ -5013,6 +5011,10 @@ msgstr ""
"Tak - Zmień te ustawienia automatycznie i włącz tryb Wazy\n"
"Nie - Zrezygnuj tym razem z używania trybu Wazy"
# AI Translated
msgid "N/A"
msgstr "Nie dotyczy"
msgid "Printing"
msgstr "Drukowanie"
@@ -5480,10 +5482,6 @@ msgstr "Nieprawidłowy wzorzec. Użyj N, N#K lub listy rozdzielonej przecinkami
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Nieprawidłowy format. Oczekiwano formatu wektorowego: „%1%”"
# AI Translated
msgid "N/A"
msgstr "Nie dotyczy"
# AI Translated
msgid "System agents"
msgstr "Agenci systemowi"
@@ -6469,6 +6467,12 @@ msgstr "Zapisz projekt jako"
msgid "Save current project as"
msgstr "Zapisz bieżący projekt jako"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importuj 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8353,6 +8357,12 @@ msgstr "Proszę potwierdź, że G-code w tych profilach jest bezpieczny, aby zap
msgid "Customized Preset"
msgstr "Dostosowany profil"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Nazwa komponentów w pliku step nie jest w formacie UTF-8!"
@@ -8779,6 +8789,17 @@ msgstr "Zapisz plik po wykonaniu cięcia jako:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Plik %s został wysłany do pamięci drukarki i można go obejrzeć na urządzeniu."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Opublikuj"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Nie ustawiono typu dyszy Wprowadź ustawienia dyszy i spróbuj ponownie."
@@ -8993,8 +9014,6 @@ msgstr "Czy kontynuować?"
msgid "Language selection"
msgstr "Wybór języka"
msgid "Switching application language while some presets are modified."
msgstr "Zmiana języka aplikacji przy jednoczesnym istniejących zmodyfikowanych ustawieniach."
msgid "Asia-Pacific"
msgstr "Azja i Pacyfik"
@@ -10021,9 +10040,6 @@ msgstr "Przejdź do strony publikacji modelu"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Uwaga: Przygotowanie może zająć kilka minut. Proszę o cierpliwość."
msgid "Publish"
msgstr "Opublikuj"
msgid "Publish was canceled"
msgstr "Publikacja została anulowana"
@@ -10040,6 +10056,64 @@ msgstr "Przesyłanie danych"
msgid "Jump to webpage"
msgstr "Przejdź na stronę"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Zapisz %s jako"
@@ -21723,6 +21797,15 @@ msgstr "Tego działania nie będzie można cofnąć. Kontynuować?"
msgid "Skipping objects."
msgstr "Pomijanie obiektów."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Wybierz filament"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-07-26 11:14-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: Portuguese, Brazilian\n"
@@ -2356,8 +2356,6 @@ msgstr "Há uma atualização disponível. Abra a caixa de diálogo do pacote de
msgid "%s has been removed."
msgstr "%s foi removido."
msgid "Switching application language"
msgstr "Alternando o idioma do aplicativo"
msgid "Select the language"
msgstr "Selecione o idioma"
@@ -4743,6 +4741,9 @@ msgstr ""
"Sim - Alterar essas configurações e ativar o modo espiral/vaso automaticamente\n"
"Não - Cancelar ativação do modo espiral"
msgid "N/A"
msgstr "N/D"
msgid "Printing"
msgstr "Imprimindo"
@@ -5196,9 +5197,6 @@ msgstr "Padrão inválido. Use N, N#K, ou uma lista separa por vírgulas com #K
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Formato inválido. Formato de vetor esperado: \"%1%\""
msgid "N/A"
msgstr "N/D"
msgid "System agents"
msgstr "Agentes do sistema"
@@ -6145,6 +6143,12 @@ msgstr "Salvar projeto como"
msgid "Save current project as"
msgstr "Salvar o projeto atual como"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importar 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -7941,6 +7945,12 @@ msgstr "Por favor, confirme se o G-code dentro dessas predefinições é seguro
msgid "Customized Preset"
msgstr "Predefinição Personalizada"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Os nomes dos componentes dentro do arquivo STEP não estão no formato UTF-8!"
@@ -8345,6 +8355,17 @@ msgstr "Salvar arquivo fatiado como:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "O arquivo %s foi enviado para o espaço de armazenamento da impressora e pode ser visualizado na impressora."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publicar"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "O tipo de bico não está configurado. Configure o bico e tente novamente."
@@ -8549,8 +8570,6 @@ msgstr "Você deseja continuar?"
msgid "Language selection"
msgstr "Seleção de idioma"
msgid "Switching application language while some presets are modified."
msgstr "Alternando idioma do aplicativo enquanto algumas predefinições são modificadas."
msgid "Asia-Pacific"
msgstr "Ásia-Pacífico"
@@ -9478,9 +9497,6 @@ msgstr "Ir para a página web de publicação de modelos"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Nota: A preparação pode levar vários minutos. Por favor, seja paciente."
msgid "Publish"
msgstr "Publicar"
msgid "Publish was canceled"
msgstr "Publicação cancelada"
@@ -9496,6 +9512,64 @@ msgstr "Enviando dados"
msgid "Jump to webpage"
msgstr "Ir para a página web"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Salvar %s como"
@@ -20522,6 +20596,15 @@ msgstr "Esta ação não pode ser desfeita. Continuar?"
msgid "Skipping objects."
msgstr "Ignorando objetos."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Selecionar Filamento"
+96 -13
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer V2.5.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-02-25 13:38+0300\n"
"Last-Translator: Felix14_v2\n"
"Language-Team: Felix14_v2 (ДС/ТГ: @felix14_v2, почта: aleks111001@list.ru), Andylg <andylg@yandex.ru>\n"
@@ -2431,8 +2431,6 @@ msgstr "Доступно обновление. Проверьте меню па
msgid "%s has been removed."
msgstr "%s был удалён."
msgid "Switching application language"
msgstr "Изменение языка приложения"
msgid "Select the language"
msgstr "Выбор языка"
@@ -4891,6 +4889,11 @@ msgid ""
"No - Cancel enabling spiral mode"
msgstr "Использовать эти настройки и режим вазы?"
# Не знаю, как и почему, но это, похоже, исправляет "вопросики" вместо
# символов
msgid "N/A"
msgstr "–"
msgid "Printing"
msgstr "Печать"
@@ -5360,11 +5363,6 @@ msgstr "Недопустимый шаблон. Используйте N, N#K и
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Недопустимый формат. Ожидаемый векторный формат: \"%1%\""
# Не знаю, как и почему, но это, похоже, исправляет "вопросики" вместо
# символов
msgid "N/A"
msgstr "–"
msgid "System agents"
msgstr "Системные агенты"
@@ -6378,6 +6376,12 @@ msgstr "Сохранить проект как"
msgid "Save current project as"
msgstr "Сохранить текущий проект как"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Импорт 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8212,6 +8216,12 @@ msgstr "Во избежание повреждения принтера убед
msgid "Customized Preset"
msgstr "Пользовательский профиль"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Имена компонентов внутри файла STEP не в формате UTF-8."
@@ -8624,6 +8634,17 @@ msgstr "Сохранить нарезанный файл как:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Файл %s отправлен в память принтера и может быть просмотрен на нём."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Опубликовать"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Не указан тип сопла. Укажите его и попробуйте ещё раз."
@@ -8829,8 +8850,6 @@ msgstr "Хотите продолжить?"
msgid "Language selection"
msgstr "Выбор языка"
msgid "Switching application language while some presets are modified."
msgstr "Смена языка приложения при изменении некоторых профилей."
msgid "Asia-Pacific"
msgstr "Азиатско-Тихоокеанский"
@@ -9770,9 +9789,6 @@ msgstr "Перейти на веб-страницу публикации мод
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Примечание: подготовка может занять несколько минут. Пожалуйста, наберитесь терпения."
msgid "Publish"
msgstr "Опубликовать"
msgid "Publish was canceled"
msgstr "Публикация была отменена"
@@ -9788,6 +9804,64 @@ msgstr "Отправка данных"
msgid "Jump to webpage"
msgstr "Перейти на страницу"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Сохранить %s как"
@@ -21541,6 +21615,15 @@ msgstr "Это действие необратимо. Продолжить?"
msgid "Skipping objects."
msgstr "Исключение объектов."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Выбрать материал"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -2767,8 +2767,6 @@ msgstr "Det finns en uppdatering tillgänglig. Öppna dialogrutan för förinst
msgid "%s has been removed."
msgstr "%s har tagits bort."
msgid "Switching application language"
msgstr "Byt applikationsspråk"
msgid "Select the language"
msgstr "Välj språk"
@@ -5387,6 +5385,10 @@ msgstr ""
"JA -Ändra dessa inställningar och möjliggör Spiral läge automatiskt\n"
"NEJ -Avbryt Spiral läge denna gång"
# AI Translated
msgid "N/A"
msgstr "Ej tillämpligt"
msgid "Printing"
msgstr "Utskrift pågår"
@@ -5936,10 +5938,6 @@ msgstr "Ogiltigt mönster. Använd N, N#K eller en kommaseparerad lista med valf
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Ogiltligt format. Förväntat vector format: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "Ej tillämpligt"
# AI Translated
msgid "System agents"
msgstr "Systemagenter"
@@ -6972,6 +6970,12 @@ msgstr "Spara Projekt som"
msgid "Save current project as"
msgstr "Spara nuvarande projekt som"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Importera 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8996,6 +9000,12 @@ msgstr "Bekräfta att G-koderna i dessa inställningar är säkra för att förh
msgid "Customized Preset"
msgstr "Anpassad inställning"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Komponent namnet i STEP filen är inte UTF-8 format!"
@@ -9432,6 +9442,17 @@ msgstr "Spara beredningen som:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Filen %s har skickats till skrivarens lagringsutrymme och kan visas på skrivaren."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Publicera"
# AI Translated
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Nozzeltypen är inte angiven. Ange nozzeln och försök igen."
@@ -9671,8 +9692,6 @@ msgstr "Fortsätta?"
msgid "Language selection"
msgstr "Språkval"
msgid "Switching application language while some presets are modified."
msgstr "Byter språk medans inställningarna ändras."
msgid "Asia-Pacific"
msgstr "Asien-Stillahavsområdet"
@@ -10740,9 +10759,6 @@ msgstr "Växla till modell publicerings hemsidan"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Notera: Förberedelserna kan ta flera minuter. Vänligen vänta."
msgid "Publish"
msgstr "Publicera"
msgid "Publish was canceled"
msgstr "Publiceringen avbröts"
@@ -10759,6 +10775,64 @@ msgstr "Laddar upp data"
msgid "Jump to webpage"
msgstr "Växla till hemsidan"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Spara %s som"
@@ -23513,6 +23587,15 @@ msgstr "Åtgärden kan inte ångras. Vill du fortsätta?"
msgid "Skipping objects."
msgstr "Hoppar över objekt."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Välj filament"
+94 -11
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-06-19 13:40+0700\n"
"Last-Translator: Icezaza\n"
"Language-Team: Thai\n"
@@ -2456,8 +2456,6 @@ msgstr "มีอัปเดตพร้อมใช้งาน เปิด
msgid "%s has been removed."
msgstr "ลบ %s แล้ว"
msgid "Switching application language"
msgstr "การเปลี่ยนภาษาของแอปพลิเคชัน"
msgid "Select the language"
msgstr "เลือกภาษา"
@@ -4887,6 +4885,9 @@ msgstr ""
"ใช่ - เปลี่ยนการตั้งค่าเหล่านี้และเปิดใช้งานโหมดเกลียวโดยอัตโนมัติ\n"
"ไม่ - เลิกใช้โหมดเกลียวในครั้งนี้"
msgid "N/A"
msgstr "ไม่มี"
msgid "Printing"
msgstr "กำลังพิมพ์"
@@ -5346,9 +5347,6 @@ msgstr "รูปแบบไม่ถูกต้อง ใช้ N, N#K หร
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "รูปแบบไม่ถูกต้อง รูปแบบเวกเตอร์ที่ต้องการ: \"%1%\""
msgid "N/A"
msgstr "ไม่มี"
# AI Translated
msgid "System agents"
msgstr "เอเจนต์ระบบ"
@@ -6304,6 +6302,12 @@ msgstr "บันทึกโปรเจกต์เป็น"
msgid "Save current project as"
msgstr "บันทึกโครงการปัจจุบันเป็น"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "นำเข้า 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8118,6 +8122,12 @@ msgstr "โปรดยืนยันว่ารหัส G ภายในค
msgid "Customized Preset"
msgstr "ค่าที่ตั้งไว้ล่วงหน้าที่กำหนดเอง"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "ชื่อของส่วนประกอบภายในไฟล์ STEP ไม่ใช่รูปแบบ UTF-8!"
@@ -8525,6 +8535,17 @@ msgstr "บันทึกไฟล์ที่สไลซ์เป็น:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "ไฟล์ %s ถูกส่งไปยังพื้นที่เก็บข้อมูลของเครื่องพิมพ์แล้ว และสามารถดูได้บนเครื่องพิมพ์"
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "เผยแพร่"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "ไม่ได้ตั้งค่าประเภทหัวฉีด โปรดตั้งหัวฉีดแล้วลองอีกครั้ง"
@@ -8735,8 +8756,6 @@ msgstr "ต้องการดำเนินการต่อหรือไ
msgid "Language selection"
msgstr "การเลือกภาษา"
msgid "Switching application language while some presets are modified."
msgstr "การสลับภาษาของแอปพลิเคชันในขณะที่มีการแก้ไขค่าที่ตั้งไว้บางส่วน"
msgid "Asia-Pacific"
msgstr "เอเชียแปซิฟิก"
@@ -9667,9 +9686,6 @@ msgstr "ข้ามไปที่โมเดลเผยแพร่หน้
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "หมายเหตุ: การเตรียมการอาจใช้เวลาหลายนาที กรุณาอดทน."
msgid "Publish"
msgstr "เผยแพร่"
msgid "Publish was canceled"
msgstr "การเผยแพร่ถูกยกเลิก"
@@ -9685,6 +9701,64 @@ msgstr "กำลังอัพโหลดข้อมูล"
msgid "Jump to webpage"
msgstr "ข้ามไปที่หน้าเว็บ"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "บันทึก %s เป็น"
@@ -20891,6 +20965,15 @@ msgstr "การทำงานนี้ไม่สามารถย้อน
msgid "Skipping objects."
msgstr "ข้ามวัตถุ"
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "เลือกเส้นพลาสติก"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-08-21 23:18+0300\n"
"Last-Translator: GlauTech\n"
"Language-Team: \n"
@@ -2480,8 +2480,6 @@ msgstr "Kullanılabilir bir güncelleme var. Güncellemek için ön ayar paketi
msgid "%s has been removed."
msgstr "%s kaldırıldı."
msgid "Switching application language"
msgstr "Uygulama dilini değiştirme"
msgid "Select the language"
msgstr "Dili seçin"
@@ -4949,6 +4947,10 @@ msgstr ""
"Evet - Bu ayarları değiştirin ve spiral modunu otomatik olarak etkinleştirin\n"
"Hayır - Bu sefer spiral modunu kullanmaktan vazgeçin"
# AI Translated
msgid "N/A"
msgstr "Yok"
msgid "Printing"
msgstr "Baskı"
@@ -5408,10 +5410,6 @@ msgstr "Geçersiz kalıp. N, N#K veya giriş başına isteğe bağlı #K ile vir
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Geçersiz format. Beklenen vektör formatı: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "Yok"
# AI Translated
msgid "System agents"
msgstr "Sistem aracıları"
@@ -6372,6 +6370,12 @@ msgstr "Projeyi farklı kaydet"
msgid "Save current project as"
msgstr "Mevcut projeyi farklı kaydet"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "3MF/STL/STEP/SVG/OBJ/AMF'yi içe aktar"
@@ -8215,6 +8219,12 @@ msgstr "Lütfen bu ön ayarlar içindeki G-code'larının makineye herhangi bir
msgid "Customized Preset"
msgstr "Özel Ayar"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Step dosyasındaki bileşenlerin adı UTF-8 formatında değil!"
@@ -8624,6 +8634,17 @@ msgstr "Dilimlenmiş dosyayı şu şekilde kaydedin:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "%s dosyası yazıcının depolama alanına gönderildi ve yazıcıda görüntülenebiliyor."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Yayınla"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Nozul tipi ayarlanmamış. Lütfen nozulu ayarlayın ve tekrar deneyin."
@@ -8837,8 +8858,6 @@ msgstr "Devam etmek istiyor musun?"
msgid "Language selection"
msgstr "Dil seçimi"
msgid "Switching application language while some presets are modified."
msgstr "Bazı ön ayarlar değiştirilirken uygulama dilinin değiştirilmesi."
msgid "Asia-Pacific"
msgstr "Asya Pasifik"
@@ -9811,9 +9830,6 @@ msgstr "Model yayınlama web sayfasına git"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Not: Hazırlık birkaç dakika sürebilir. Lütfen sabırlı olun."
msgid "Publish"
msgstr "Yayınla"
msgid "Publish was canceled"
msgstr "Yayınlama iptal edildi"
@@ -9829,6 +9845,64 @@ msgstr "Veriler yükleniyor"
msgid "Jump to webpage"
msgstr "Web sayfasına atla"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "%s'yi farklı kaydet"
@@ -21318,6 +21392,15 @@ msgstr "Bu eylem geri alınamaz. Devam etmek?"
msgid "Skipping objects."
msgstr "Nesneleri atlama."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Filament Seçin"
+94 -11
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: orcaslicerua\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-07-17 16:25+0300\n"
"Last-Translator: Andrij Mizyk <andm1zyk@proton.me>\n"
"Language-Team: Ukrainian\n"
@@ -2424,8 +2424,6 @@ msgstr "Доступне оновлення. Відкрийте вікно на
msgid "%s has been removed."
msgstr "%s вилучено."
msgid "Switching application language"
msgstr "Зміна мови програми"
msgid "Select the language"
msgstr "Вибрати мову"
@@ -4887,6 +4885,9 @@ msgstr ""
"Так – змінити ці налаштування та автоматично включити режим спіральна ваза\n"
"Ні - цього разу відмовитися від використання режиму спіральна ваза"
msgid "N/A"
msgstr "Н/Д"
msgid "Printing"
msgstr "Друк"
@@ -5358,9 +5359,6 @@ msgstr "Некоректний шаблон. Використовуйте N, N#K
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Невірний формат. Очікуваний векторний формат: \"%1%\""
msgid "N/A"
msgstr "Н/Д"
# AI Translated
msgid "System agents"
msgstr "Системні агенти"
@@ -6341,6 +6339,12 @@ msgstr "Зберегти проєкт як"
msgid "Save current project as"
msgstr "Зберегти поточний проєкт як"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Імпорт 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8223,6 +8227,12 @@ msgstr "Будь ласка, підтвердьте, що G-коди в цих
msgid "Customized Preset"
msgstr "Пристосований пресет"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
# AI Translated
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Назви компонентів усередині файлу STEP не у форматі UTF-8!"
@@ -8635,6 +8645,17 @@ msgstr "Зберегти нарізаний файл як:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "Файл %s надіслано до памʼяті принтера та доступний для перегляду на принтері."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Публікувати"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Тип сопла не встановлений. Будь ласка, оберіть сопло та спробуйте ще раз."
@@ -8851,8 +8872,6 @@ msgstr "Ви хочете продовжувати?"
msgid "Language selection"
msgstr "Вибір мови"
msgid "Switching application language while some presets are modified."
msgstr "Зміна мови програми при зміні деяких профілів."
msgid "Asia-Pacific"
msgstr "Азіатсько-Тихоокеанський регіон"
@@ -9814,9 +9833,6 @@ msgstr "Перейти на веб-сторінку публікації мод
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Примітка. Підготовка може тривати кілька хвилин. Будь ласка, будьте терплячі."
msgid "Publish"
msgstr "Публікувати"
msgid "Publish was canceled"
msgstr "Публікація скасована"
@@ -9832,6 +9848,64 @@ msgstr "Відвантаження даних"
msgid "Jump to webpage"
msgstr "Перейти на вебсторінку"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Зберегти %s як"
@@ -21495,6 +21569,15 @@ msgstr "Дію не можливо завершити. Продовжити?"
msgid "Skipping objects."
msgstr "Пропускання обʼєктів."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "Вибрати філамент"
+95 -12
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2025-10-02 17:43+0700\n"
"Last-Translator: \n"
"Language-Team: hainguyen.ts13@gmail.com\n"
@@ -2568,8 +2568,6 @@ msgstr "Có bản cập nhật khả dụng. Hãy mở hộp thoại gói cài
msgid "%s has been removed."
msgstr "%s đã bị xóa."
msgid "Switching application language"
msgstr "Đang chuyển ngôn ngữ ứng dụng"
msgid "Select the language"
msgstr "Chọn ngôn ngữ"
@@ -5143,6 +5141,10 @@ msgstr ""
"Yes - Thay đổi các cài đặt này và bật chế độ spiral tự động\n"
"No - Từ bỏ dùng chế độ spiral lần này"
# AI Translated
msgid "N/A"
msgstr "N/A"
msgid "Printing"
msgstr "Đang in"
@@ -5661,10 +5663,6 @@ msgstr "Mẫu không hợp lệ. Dùng N, N#K, hoặc danh sách phân cách d
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "Định dạng không hợp lệ. Mong đợi định dạng vector: \"%1%\""
# AI Translated
msgid "N/A"
msgstr "N/A"
# AI Translated
msgid "System agents"
msgstr "Tác nhân hệ thống"
@@ -6689,6 +6687,12 @@ msgstr "Lưu dự án thành"
msgid "Save current project as"
msgstr "Lưu dự án hiện tại thành"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "Nhập 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8629,6 +8633,12 @@ msgstr "Vui lòng xác nhận G-code trong các preset này an toàn để ngăn
msgid "Customized Preset"
msgstr "Preset tùy chỉnh"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "Tên của các thành phần bên trong file STEP không phải định dạng UTF-8!"
@@ -9056,6 +9066,17 @@ msgstr "Lưu file đã slice dưới dạng:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "File %s đã được gửi đến không gian lưu trữ của máy in và có thể được xem trên máy in."
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "Xuất bản"
# AI Translated
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "Chưa đặt loại đầu phun. Vui lòng đặt đầu phun rồi thử lại."
@@ -9286,8 +9307,6 @@ msgstr "Bạn có muốn tiếp tục?"
msgid "Language selection"
msgstr "Chọn ngôn ngữ"
msgid "Switching application language while some presets are modified."
msgstr "Đang chuyển đổi ngôn ngữ ứng dụng trong khi một số preset đã được chỉnh sửa."
msgid "Asia-Pacific"
msgstr "Châu Á-Thái Bình Dương"
@@ -10326,9 +10345,6 @@ msgstr "Chuyển đến trang web xuất bản model"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "Lưu ý: Chuẩn bị có thể mất vài phút. Vui lòng kiên nhẫn."
msgid "Publish"
msgstr "Xuất bản"
msgid "Publish was canceled"
msgstr "Xuất bản đã bị hủy"
@@ -10345,6 +10361,64 @@ msgstr "Đang tải dữ liệu lên"
msgid "Jump to webpage"
msgstr "Chuyển đến trang web"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "Lưu %s dưới dạng"
@@ -22165,6 +22239,15 @@ msgstr "Thao tác này không thể hoàn tác. Tiếp tục?"
msgid "Skipping objects."
msgstr "Đang bỏ qua các vật thể."
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
# AI Translated
msgid "Select Filament"
msgstr "Chọn filament"
+94 -11
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Slic3rPE\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2026-06-11 12:37-0300\n"
"Last-Translator: Handle <mail@bysb.net>\n"
"Language-Team: \n"
@@ -2361,8 +2361,6 @@ msgstr "有更新可用。打开预设包对话框进行更新。"
msgid "%s has been removed."
msgstr "%s 已被移除。"
msgid "Switching application language"
msgstr "切换应用程序语言"
msgid "Select the language"
msgstr "选择语言"
@@ -4743,6 +4741,9 @@ msgstr ""
"是 - 自动调整这些设置并开启旋转模式\n"
"否 - 暂不使用旋转模式"
msgid "N/A"
msgstr "不适用"
msgid "Printing"
msgstr "打印中"
@@ -5202,9 +5203,6 @@ msgstr "无效的模式。请使用 N、N#K 或逗号分隔的列表(每个条
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "无效格式,应该是\"%1%\"这种数组格式"
msgid "N/A"
msgstr "不适用"
# AI Translated
msgid "System agents"
msgstr "系统代理"
@@ -6159,6 +6157,12 @@ msgstr "项目另存为"
msgid "Save current project as"
msgstr "项目另存为"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "导入 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -7949,6 +7953,12 @@ msgstr "请确认这些预设中的G-codes是否安全,以防止对机器造
msgid "Customized Preset"
msgstr "自定义的预设"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "STEP 文件中的部件名称不是 UTF-8 格式!"
@@ -8354,6 +8364,17 @@ msgstr "切片文件另存为:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "文件%s已经发送到打印机的存储空间,可以在打印机上浏览。"
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "发布"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "未设置喷嘴类型。请设置喷嘴并重试。"
@@ -8564,8 +8585,6 @@ msgstr "是否继续?"
msgid "Language selection"
msgstr "语言选择"
msgid "Switching application language while some presets are modified."
msgstr "在切换应用语言之前发现某些参数预设有更改。"
msgid "Asia-Pacific"
msgstr "亚太"
@@ -9489,9 +9508,6 @@ msgstr "跳转到发布页面"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "提示:发布前需要一些准备时间,请耐心等待。"
msgid "Publish"
msgstr "发布"
msgid "Publish was canceled"
msgstr "发布已取消"
@@ -9507,6 +9523,64 @@ msgstr "正在上传数据"
msgid "Jump to webpage"
msgstr "跳转到网页"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "另存%s为"
@@ -20639,6 +20713,15 @@ msgstr "此操作无法撤消。继续?"
msgid "Skipping objects."
msgstr "跳过对象。"
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "选择耗材"
+94 -11
View File
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-02 09:39-0300\n"
"POT-Creation-Date: 2026-09-03 10:27+0800\n"
"PO-Revision-Date: 2025-11-28 13:48-0600\n"
"Last-Translator: tntchn <15895303+tntchn@users.noreply.github.com>\n"
"Language-Team: \n"
@@ -2425,8 +2425,6 @@ msgstr "有可用的更新。請開啟預設組合對話框進行更新。"
msgid "%s has been removed."
msgstr "%s 已移除。"
msgid "Switching application language"
msgstr "切換應用程式語言"
msgid "Select the language"
msgstr "選擇語言"
@@ -4872,6 +4870,9 @@ msgstr ""
"是 - 自動調整這些設定並啟用花瓶模式\n"
"否 - 不使用花瓶模式"
msgid "N/A"
msgstr "不適用"
msgid "Printing"
msgstr "列印中"
@@ -5331,9 +5332,6 @@ msgstr "無效的格式。請使用 N、N#K 或逗號分隔的清單,每個項
msgid "Invalid format. Expected vector format: \"%1%\""
msgstr "無效格式,應該是「%1%」這種格式"
msgid "N/A"
msgstr "不適用"
# AI Translated
msgid "System agents"
msgstr "系統代理程式"
@@ -6289,6 +6287,12 @@ msgstr "另存專案為"
msgid "Save current project as"
msgstr "將目前專案另存為"
msgid "Publish 3MF"
msgstr ""
msgid "Export a 3MF file with the selected settings embedded"
msgstr ""
msgid "Import 3MF/STL/STEP/SVG/OBJ/AMF"
msgstr "匯入 3MF/STL/STEP/SVG/OBJ/AMF"
@@ -8110,6 +8114,12 @@ msgstr "請確認這些預設中的 G-code 是安全的,以防止對列印裝
msgid "Customized Preset"
msgstr "自訂預設"
msgid "Some published settings could not be applied:"
msgstr ""
msgid "Some filament slots were changed:"
msgstr ""
msgid "Component name(s) inside step file not in UTF-8 format!"
msgstr "STEP 檔案內部元件的名稱不是 UTF-8 格式!"
@@ -8520,6 +8530,17 @@ msgstr "切片檔案另存為:"
msgid "The file %s has been sent to the printer's storage space and can be viewed on the printer."
msgstr "檔案 %s 已經傳送到列印裝置的儲存空間,可以在列印裝置上瀏覽。"
msgid "Publish 3MF file as:"
msgstr ""
msgid ""
"Failed to export the published 3MF file.\n"
"Please check whether the folder exists online or if other programs have the file open."
msgstr ""
msgid "Publish"
msgstr "發布"
msgid "The nozzle type is not set. Please set the nozzle and try again."
msgstr "噴嘴類型尚未設定。請設定噴嘴後再試一次。"
@@ -8730,8 +8751,6 @@ msgstr "是否繼續?"
msgid "Language selection"
msgstr "語言選擇"
msgid "Switching application language while some presets are modified."
msgstr "在切換應用程式語言之前發現某些參數預設有更改。"
msgid "Asia-Pacific"
msgstr "亞太"
@@ -9662,9 +9681,6 @@ msgstr "發布頁面"
msgid "Note: The preparation may take several minutes. Please be patient."
msgstr "提示:發布前需要一些準備時間,請耐心等待。"
msgid "Publish"
msgstr "發布"
msgid "Publish was canceled"
msgstr "發布已取消"
@@ -9680,6 +9696,64 @@ msgstr "正在上傳資料"
msgid "Jump to webpage"
msgstr "跳至網頁"
msgid "Material"
msgstr ""
msgid "Mixed filament"
msgstr ""
msgid "Some mixed filaments rely on filaments that will not be published:"
msgstr ""
#, c-format, boost-format
msgid "Filament %d (mixed)"
msgstr ""
msgid "needs"
msgstr ""
msgid "not enabled"
msgstr ""
msgid "material not published"
msgstr ""
msgid "To publish a mixed filament, enable every filament it uses and choose Full Publish or check its Type requirement."
msgstr ""
msgid "Publish anyway"
msgstr ""
msgid "Publish 3MF..."
msgstr ""
msgid "Select which settings to be published in the 3MF file"
msgstr ""
msgid "Publish 3MF Wiki"
msgstr ""
msgid "Publish 3MF Video Guide"
msgstr ""
msgid "Mixed filament - published as a whole when \"Enable\" above is selected"
msgstr ""
msgid "Publish this mixed filament and enable + Full Publish its component filaments"
msgstr ""
msgid "Publish this filament slot in the 3MF file"
msgstr ""
msgid "Full Publish"
msgstr ""
msgid "Embed the entire filament of this slot in the 3MF file"
msgstr ""
msgid "Filter non-selected"
msgstr ""
#, c-format, boost-format
msgid "Save %s as"
msgstr "另存 %s 為"
@@ -20832,6 +20906,15 @@ msgstr "此操作無法撤消。繼續?"
msgid "Skipping objects."
msgstr "跳過物件。"
msgid "Material Ratio"
msgstr ""
msgid "Model Height"
msgstr ""
msgid "Ratio"
msgstr ""
msgid "Select Filament"
msgstr "選擇線材"
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.5,8 14.5,1.5 7.5,14.5 6.3,9.7 Z" style="fill:none;stroke:gray;stroke-linecap:round;stroke-linejoin:round"/></svg>

After

Width:  |  Height:  |  Size: 209 B

+8 -2
View File
@@ -415,7 +415,9 @@ img.ModelThumbnail {
border-width: 1px;
border-style: solid;
border-radius: 4px;
background-color: inherit;
border-color: var(--border-color);
background-color: var(--bg-color);
color: var(--fg-color-text);
position: absolute;
left: 50%;
top: 200px;
@@ -423,13 +425,17 @@ img.ModelThumbnail {
}
#NoticeBar {
background-color:#00f0d8;
background-color: var(--main-color);
height: 40px;
line-height: 40px;
color: #fff;
text-align: center;
}
#NoticeBar.notice-error {
background-color: var(--button-bg-alert);
}
#NoticeContent {
padding: 4mm 10mm;
}
+36 -1
View File
@@ -37,6 +37,34 @@ function HandleStudio( pVal )
{
HandleModelList(pVal['response']);
}
else if(strCmd=='check_new_printers_result')
{
let button = document.getElementById("CheckNewPrintersBtn");
if (button) {
button.style.pointerEvents = "auto";
button.style.opacity = "1";
}
let noticeBar = document.getElementById("NoticeBar");
let noticeText = document.getElementById("NoticeText");
let hasError = pVal.hasOwnProperty("error");
noticeBar.classList.toggle("notice-error", hasError);
if (hasError) {
noticeBar.textContent = "Error";
noticeText.textContent = pVal["error"];
} else if (pVal["vendors"] && pVal["vendors"].length > 0) {
noticeBar.textContent = "New printers found";
noticeText.textContent = "New printer vendors installed: " + pVal["vendors"].join(", ");
} else if (pVal["declined"]) {
noticeBar.textContent = "Information";
noticeText.textContent = "New printer vendors were found, but installation was cancelled.";
} else {
noticeBar.textContent = "Information";
noticeText.textContent = "No new printers found.";
}
ShowNotice(1);
}
}
function HandleModelList( pVal )
@@ -78,9 +106,13 @@ function HandleModelList( pVal )
}
//Update Nozzel Html Append
// ORCA: HandleModelList can now be called more than once per dialog (e.g. after a new vendor
// is installed via "check for new printers"). pModel always holds the full, current list, so
// clear each vendor's printer area before repopulating instead of appending on top of a
// previous render.
for( let key in ModelHtml )
{
$(".OneVendorBlock[vendor='"+key+"'] .PrinterArea").append( ModelHtml[key] );
$(".OneVendorBlock[vendor='"+key+"'] .PrinterArea").empty().append( ModelHtml[key] );
}
//Update Checkbox
@@ -343,6 +375,9 @@ function OnExit()
let nTotal=ModelSelect.length;
if( nTotal==0 ) {
let noticeBar = document.getElementById("NoticeBar");
noticeBar.classList.add("notice-error");
noticeBar.textContent = "Error";
ShowNotice(1);
return 0;
}
+16
View File
@@ -36,6 +36,22 @@ function ConfirmSelect()
}
}
function CheckForNewPrinters()
{
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="check_for_new_printers";
tSend['data']={};
var button = document.getElementById("CheckNewPrintersBtn");
if (button) {
button.style.pointerEvents = "none";
button.style.opacity = "0.6";
}
SendWXMessage( JSON.stringify(tSend) );
}
function CreateNewPrinter()
{
var tSend={};
+2 -1
View File
@@ -82,7 +82,8 @@
</div>
<div id="AcceptArea">
<div class="ButtonStyleRegular ButtonTypeChoice trans" id="CreateBtn" onclick="CreateNewPrinter()">Create</div>
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t39" id="AcceptBtn" onclick="ConfirmSelect()">Confirm</div>
<div class="ButtonStyleRegular ButtonTypeChoice" id="CheckNewPrintersBtn" onclick="CheckForNewPrinters()">Check for new printers</div>
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t39" id="AcceptBtn" onclick="ConfirmSelect()">Confirm</div>
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t38" id="PreBtn" onclick="CancelSelect()">Cancel</div>
</div>
+44 -1
View File
@@ -716,7 +716,8 @@ body
background-color: #E4E4E4;
border-radius: 8px;
width: 184px;
height: 184px;
height: 184px;
position: relative;
}
.FileItem img
@@ -728,6 +729,19 @@ body
object-fit: cover;
}
.FileImg .FileLogoBadge
{
position: absolute;
top: 8px;
left: 8px;
width: 26px;
height: 26px;
padding: 2px;
box-sizing: border-box;
background-color: rgba(255,255,255,0.85);
border-radius: 50%;
}
.FileName
{
white-space: nowrap;
@@ -736,6 +750,35 @@ body
word-break: break-all;
}
.FileNamePack
{
display: flex;
align-items: center;
min-width: 0;
overflow: hidden;
}
.FileNamePack .FileName
{
flex: 1 1 auto;
min-width: 0;
}
.FilePublishedBadge
{
flex: 0 0 auto;
margin-right: 4px;
padding: 0 4px;
height: 16px;
line-height: 16px;
font-size: 10px;
font-weight: 600;
color: #FFFFFF;
background-color: #009688;
border-radius: 2px;
white-space: nowrap;
}
.FileDate
{
color: #A8A8A8;
+6 -2
View File
@@ -305,15 +305,19 @@ function ShowRecentFileList( pList )
let sImg=OneFile["image"] || sImages[sPath];
let sTime=OneFile['time'];
let sName=OneFile['project_name'];
let sPublished=OneFile['published'] == '1';
sImages[sPath] = sImg;
//let index=sPath.lastIndexOf('\\')>0?sPath.lastIndexOf('\\'):sPath.lastIndexOf('\/');
//let sShortName=sPath.substring(index+1,sPath.length);
let sBadge=sPublished? '<span class="FilePublishedBadge">PUB</span>':'';
let sLogoBadge=sPublished? '<img class="FileLogoBadge" src="../../images/OrcaSlicer_gradient_circle.svg" alt="" />':'';
let TmpHtml='<div class="FileItem" fpath="'+sPath+'" >'+
'<a class="FileTip" title="'+sPath+'"></a>'+
'<div class="FileImg" ><img src="'+sImg+'" onerror="this.onerror=null;this.src=\'img/d.png\';" alt="No Image" /></div>'+
'<div class="FileName TextS1">'+sName+'</div>'+
'<div class="FileImg" ><img src="'+sImg+'" onerror="this.onerror=null;this.src=\'img/d.png\';" alt="No Image" />'+sLogoBadge+'</div>'+
'<div class="FileNamePack">'+sBadge+'<div class="FileName TextS1">'+sName+'</div></div>'+
'<div class="FileDate">'+sTime+'</div>'+
'</div>';
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/fstream.hpp>
#include "stackwalker.h"
#include "StackWalker.h"
#include <eh.h>
class CBaseException : public CStackWalker
+1 -1
View File
@@ -425,7 +425,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context)
else
c = *context;
STACKFRAME64 sf = {0};
STACKFRAME64 sf = {};
DWORD imageType;
//intel X86
+12 -1
View File
@@ -42,6 +42,9 @@ namespace Slic3r {
static const std::string VERSION_CHECK_URL = "https://check-version.orcaslicer.com/latest";
static const std::string PROFILE_UPDATE_URL = "https://check-version.orcaslicer.com/profile";
constexpr const char* CONFIG_ORCA_UPDATER_URL = "orca_updater_url";
static const std::string MODELS_STR = "models";
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
@@ -635,6 +638,11 @@ void AppConfig::set_defaults()
set_bool("use_printer_agents", false);
}
if (get("enable_ota").empty())
{
set_bool("enable_ota", false);
}
// Remove legacy window positions/sizes
erase("app", "main_frame_maximized");
erase("app", "main_frame_pos");
@@ -1815,7 +1823,10 @@ std::string AppConfig::version_check_url() const
std::string AppConfig::profile_update_url() const
{
return PROFILE_UPDATE_URL;
std::string orca_updater_url = get(CONFIG_ORCA_UPDATER_URL);
if (orca_updater_url.empty())
return PROFILE_UPDATE_URL;
return orca_updater_url;
}
bool AppConfig::exists()
+8
View File
@@ -372,6 +372,8 @@ set(lisbslic3r_sources
Preset.hpp
PrincipalComponents2D.cpp
PrincipalComponents2D.hpp
PublishSettings.cpp
PublishSettings.hpp
PrintApply.cpp
PrintBase.cpp
PrintBase.hpp
@@ -556,6 +558,12 @@ if (_opts)
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
endif()
if (IS_CLANG_CL)
# CGAL passes /fp:strict /fp:except-. clang-cl reports the second as overriding part of
# the first; the settings cc1 receives are the same ones MSVC produces from that pair.
target_compile_options(libslic3r_cgal PRIVATE -Wno-overriding-option)
endif ()
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} admesh libigl mcut boost_libs)
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
+5 -5
View File
@@ -968,10 +968,10 @@ EmbossStyles Emboss::get_font_list_by_register() {
}
// TODO: Fix global function
bool CALLBACK EnumFamCallBack(LPLOGFONT lplf,
LPNEWTEXTMETRIC lpntm,
DWORD FontType,
LPVOID aFontList)
int CALLBACK EnumFamCallBack(const LOGFONT *lplf,
const TEXTMETRIC *lpntm,
DWORD FontType,
LPARAM aFontList)
{
std::vector<std::wstring> *fontList =
(std::vector<std::wstring> *) (aFontList);
@@ -988,7 +988,7 @@ EmbossStyles Emboss::get_font_list_by_enumeration() {
HDC hDC = GetDC(NULL);
std::vector<std::wstring> font_names;
EnumFontFamilies(hDC, (LPCTSTR) NULL, (FONTENUMPROC) EnumFamCallBack,
EnumFontFamilies(hDC, (LPCTSTR) NULL, EnumFamCallBack,
(LPARAM) &font_names);
EmbossStyles font_list;
+36 -39
View File
@@ -11,7 +11,7 @@
#include "AABBTreeLines.hpp"
#include "ExtrusionEntity.hpp"
#include "FillBase.hpp"
#include "Fill.hpp"
#include "FillRectilinear.hpp"
#include "FillLightning.hpp"
#include "FillConcentricInternal.hpp"
@@ -1234,6 +1234,33 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
return surface_fills;
}
// Orca: Anchors and printed infill must share the same body origin. Keep the choice
// here so per-model surface centering and separated sparse infill cannot drift apart.
static BoundingBox infill_bounding_box(const Layer &layer, const SurfaceFill &fill, const ExPolygon &expoly, BoundingBox bbox)
{
const auto &params = fill.params;
const auto &config = layer.regions()[fill.region_id]->region().config();
const bool external = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
const bool per_model = external && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model &&
(params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral);
const bool separate = !external && params.separated_infills &&
(is_separable_infill_pattern(params.pattern) || !config.solid_infill_rotate_template.value.empty() ||
!config.sparse_infill_rotate_template.value.empty());
if (per_model || separate) {
double best_overlap = 0.;
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_bboxes.size(); ++i) {
const double overlap = area(intersection_ex(layer.lslices[i], expoly));
if (overlap > best_overlap) {
best_overlap = overlap;
const Point center = layer.lslices_separated_component_bboxes[i].center();
bbox = layer.object()->bounding_box();
bbox.translate(center.x(), center.y());
}
}
}
return bbox;
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill> &fills)
{
@@ -1353,19 +1380,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// Orca: Checking the filling of a centered surface by drawing for each model parts
bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral;
if (is_top_or_bottom) {
params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern
}
// Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly
// fall through to the default (whole-object) bounding box below.
bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill;
bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills &&
(
is_separable_infill_pattern(surface_fill.params.pattern) ||
params.config->solid_infill_rotate_template != "" ||
params.config->sparse_infill_rotate_template != "" );
if( surface_fill.params.pattern == ipLockedZag ) {
params.locked_zag = true;
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
@@ -1389,34 +1406,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.can_reverse = false;
for (ExPolygon& expoly : surface_fill.expolygons) {
// Orca: separate infill / per-model pattern centering.
//
// Center the pattern on each connected body of the object independently, so every piece
// is filled exactly as if it were sliced on its own: touching/overlapping parts merge
// into one body sharing a center, while separate parts and disconnected islands (even
// interleaved-but-not-touching ones, e.g. chain links) each get their own. The body each
// island belongs to, and its full bounding box, were resolved in 3D by PrintObject::
// infill() (lslices_separated_component_bboxes, aligned with this layer's lslices). We
// match this fill region to the island it overlaps most, then re-use the whole-object
// bounding box (origin-centered — identical extent to the default, so coverage and cost
// are unchanged) re-centered on that body.
if (is_per_model_center || is_separate_infill) {
double best_overlap = 0.;
BoundingBox best_component;
for (size_t r = 0; r < this->lslices.size() && r < this->lslices_separated_component_bboxes.size(); ++ r) {
const double overlap = area(intersection_ex(this->lslices[r], expoly));
if (overlap > best_overlap) {
best_overlap = overlap;
best_component = this->lslices_separated_component_bboxes[r];
}
}
if (best_component.defined) {
const Point c = best_component.center();
BoundingBox part_bbox = bbox; // origin-centered, whole-object extent (from above)
part_bbox.translate(c.x(), c.y()); // re-center on this body
f->set_bounding_box(part_bbox);
}
} // - End: separate infill / per-model pattern centering
// Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface.
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
if (params.symmetric_infill_y_axis) {
@@ -1583,8 +1574,14 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
params.multiline = surface_fill.params.multiline;
params.gyroid_optimized = surface_fill.params.gyroid_optimized;
params.smooth_factor = surface_fill.params.smooth_factor;
// Orca: Match make_fills() when choosing the origin of plane-path patterns.
// Without the sparse extrusion role, the filler uses each surface's bounds
// instead of the object's bounds, so bridge anchors shift away from printed infill.
params.extrusion_role = surface_fill.params.extrusion_role;
for (ExPolygon &expoly : surface_fill.expolygons) {
// Orca: Match the per-body origin of make_fills() before generating physical anchors.
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);
+6
View File
@@ -14,6 +14,12 @@ namespace Slic3r {
class ExtrusionEntityCollection;
class LayerRegion;
class PrintObject;
// Orca: Share the layer rotation calculation between infill generation and internal
// bridge angle selection so both interpret rotation templates in the same way.
double calculate_infill_rotation_angle(const PrintObject *object, size_t layer_id,
const double &fixed_infill_angle, const std::string &template_string);
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
class Filler
+2 -2
View File
@@ -1395,8 +1395,8 @@ void Filler::_fill_surface_single(
}
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
const auto hook_length = coordf_t(scale_(params.anchor_length));
const auto hook_length_max = coordf_t(scale_(params.anchor_length_max));
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
+183 -14
View File
@@ -642,6 +642,11 @@ bool bbs_is_valid_object_type(const std::string& type)
namespace Slic3r {
bool is_published_3mf_flag(const std::string &value)
{
return value == "1";
}
void PlateData::parse_filament_info(GCodeProcessorResult *result)
{
if (!result) return;
@@ -1178,6 +1183,20 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// add backup & restore logic
bool _load_model_from_file(std::string filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Import3mfProgressFn proFn = nullptr,
BBLProject* project = nullptr, int plate_id = 0);
// A minimal published 3MF carries no slicer tags (any tag would make old receivers show
// a baked-in, wrong "old version" popup on their geometry-only fallback), so it
// classifies as From_Other. It is still a fully structured OrcaSlicer file though:
// identified by its own metadata, it keeps BBS-grade geometry handling (no instance
// splitting, no transform baking, no renaming) in this build. Old receivers without the
// publish feature don't know the metadata and take their third-party geometry path.
// Reads the parse-time metadata: the model XML carries it before its resources, while
// m_model->model_info is only filled in after the whole XML has been parsed.
bool _is_published_3mf() const {
const auto it = this->model_info.metadata_items.find(ORCA_PUBLISHED_TAG);
return it != this->model_info.metadata_items.end() && is_published_3mf_flag(it->second);
}
bool _is_svg_shape_file(const std::string &filename) const;
bool _extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function<bool (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)>, bool restore = false);
bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
@@ -2002,7 +2021,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
lock.close();
if (!m_is_bbl_3mf) {
if (!m_is_bbl_3mf && !_is_published_3mf()) {
// if the 3mf was not produced by OrcaSlicer and there is more than one instance,
// split the object in as many objects as instances
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", found 3mf from other vendor, split as instance");
@@ -3566,7 +3585,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_index_paths.insert({ object.first.second, object.first.first});
}
if (!m_is_bbl_3mf) {
if (!m_is_bbl_3mf && !_is_published_3mf()) {
// if the 3mf was not produced by OrcaSlicer and there is only one object,
// set the object name to match the filename
if (m_model->objects.size() == 1)
@@ -5289,7 +5308,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
if (!m_is_bbl_3mf) {
if (!m_is_bbl_3mf && !_is_published_3mf()) {
// if the 3mf was not produced by OrcaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
@@ -5935,6 +5954,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool m_save_gcode { false }; // whether to save gcode for normal save
bool m_skip_model { false }; // skip model when exporting .gcode.3mf
bool m_skip_auxiliary { false }; // skip normal axuiliary files
bool m_minimal_published { false }; // published 3MF: omit the project config, the embedded preset files and the slicer tags
bool m_use_loaded_id { false }; // whether to use loaded id for identify_id
bool m_share_mesh { false }; // whether to share mesh between objects
std::string m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE;
@@ -6034,6 +6054,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary;
m_share_mesh = store_params.strategy & SaveStrategy::ShareMesh;
m_from_backup_save = store_params.strategy & SaveStrategy::Backup;
m_minimal_published = store_params.strategy & SaveStrategy::MinimalPublished;
m_use_loaded_id = store_params.strategy & SaveStrategy::UseLoadedId;
@@ -6443,7 +6464,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// Adds slic3r print config file ("Metadata/Slic3r_PE.config").
// This file contains the content of FullPrintConfig / SLAFullPrintConfig.
if (config != nullptr) {
// Omitted for minimal published 3MF: OrcaSlicer versions without the publish feature
// then fall back to importing the geometry only, and new versions read the published
// payload from the model metadata instead.
if (config != nullptr && !m_minimal_published) {
// BBS: change to json format
// if (!_add_print_config_file_to_archive(archive, *config)) {
if (!_add_project_config_file_to_archive(archive, *config, model)) { return false; }
@@ -6456,8 +6480,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (cb_cancel) return false;
}
// BBS: add project config
if (project_presets.size() > 0) {
// BBS: add project config (omitted for minimal published 3MF)
if (!m_minimal_published && project_presets.size() > 0) {
// BBS: add project embedded preset files
_add_project_embedded_presets_to_archive(archive, model, project_presets);
@@ -6929,10 +6953,31 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// Orca: PRIVACY: do not store creation & modification date in 3mf
metadata_item_map[BBL_CREATION_DATE_TAG] = "";
metadata_item_map[BBL_MODIFICATION_TAG] = "";
// Orca: Write the BambuStudio compatibility version string using SLIC3R_VERSION
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SLIC3R_VERSION).str();
// Orca: Write the BambuStudio compatibility version string using SLIC3R_VERSION.
// A minimal published 3MF writes no slicer tags at all: any tag would route old
// receivers onto a geometry-only fallback whose baked-in popup misreports the
// file ("old OrcaSlicer version" / "BambuStudio"), while tag-less files classify
// as From_Other and import the geometry silently.
if (m_minimal_published) {
// metadata_item_map is seeded from the input file's metadata_items above, so a
// project opened from a regular Orca/BBS 3MF still carries the slicer-identifying
// tags it came with. Erase every one of them - not just the two most common -
// so a published 3MF is fully tag-less: old receivers classify it as From_Other
// and import the geometry silently instead of showing a baked-in "old version"
// popup, and no version marker survives to seed a later re-save.
metadata_item_map.erase(BBL_APPLICATION_TAG);
metadata_item_map.erase(ORCASLICER_TAG);
metadata_item_map.erase(BBS_3MF_VERSION);
metadata_item_map.erase(BBS_3MF_VERSION1);
} else {
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SLIC3R_VERSION).str();
}
}
metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
// The Bambu 3MF version marker is part of the slicer identity: omit it for a minimal
// published file along with the tags erased above (skipping the overwrite alone would
// leave the value the source file seeded into metadata_item_map).
if (!m_minimal_published)
metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
if (!model.mk_name.empty()) {
metadata_item_map[BBL_MAKERLAB_TAG] = xml_escape(model.mk_name);
@@ -6955,7 +7000,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
BOOST_LOG_TRIVIAL(info) << "bbs_3mf: save key= " << item.first << ", value = " << item.second;
stream << " <" << METADATA_TAG << " name=\"" << item.first << "\">"
<< xml_escape(item.second) << "</" << METADATA_TAG << ">\n";
if (item.first == BBL_APPLICATION_TAG) {
if (item.first == BBL_APPLICATION_TAG && !m_minimal_published) {
// The OrcaSlicer tag is only written for files that carry the Application
// tag, which a minimal published 3MF erases (see the map assignment above):
// the explicit !m_minimal_published guard keeps the tag-less guarantee from
// depending on that erase happening to run first.
stream << " <" << METADATA_TAG << " name=\"" << ORCASLICER_TAG << "\">"
<< xml_escape(SoftFever_VERSION) << "</" << METADATA_TAG << ">\n";
}
@@ -8795,7 +8844,7 @@ public:
auto model = object.get_model();
auto o = m_temp_model.add_object(object);
int backup_id = model->get_object_backup_id(object);
push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, 1 });
push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, { 1 } });
}
void remove_object_mesh(ModelObject& object) {
@@ -8805,7 +8854,7 @@ public:
void backup_soon() {
boost::lock_guard lock(m_mutex);
m_other_changes_backup = true;
m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } });
m_cond.notify_all();
}
@@ -8823,7 +8872,7 @@ public:
m_ui_tasks.clear();
m_tasks.clear();
}
m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, removeAll });
m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, { removeAll } });
++m_task_seq;
if (model.is_need_backup()) {
m_other_changes = false;
@@ -9038,7 +9087,7 @@ public:
else
m_cond.wait(lock);
if (m_interval > 0 && boost::get_system_time() > m_next_backup) {
m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } });
m_next_backup += boost::posix_time::seconds(m_interval);
// Maybe wakeup from power sleep
if (m_next_backup < boost::get_system_time())
@@ -9134,6 +9183,126 @@ std::string bbs_3mf_get_thumbnail(const char *path)
return data;
}
namespace {
// Parses just the model-file <metadata> elements, mirroring the importer's
// _handle_start_metadata/_handle_end_metadata (attribute-order independent, entity-unescaped,
// whitespace tolerant). Stops the parser as soon as the published flag node is read so the
// geometry/resources that follow are skipped, which keeps the per-file cost small.
struct PublishedXmlProbe
{
XML_Parser parser{nullptr};
bool in_metadata{false};
bool found{false};
bool published{false};
std::string curr_name;
std::string curr_value;
static std::string attribute(const char** attrs, const char* key)
{
if (attrs == nullptr)
return std::string();
// expat hands the attrs as a NULL-terminated {name, value, ...} array.
for (unsigned int a = 0; attrs[a] != nullptr; a += 2)
if (::strcmp(attrs[a], key) == 0 && attrs[a + 1] != nullptr)
return attrs[a + 1];
return std::string();
}
static void XMLCALL start(void* user_data, const char* name, const char** attrs)
{
auto* self = static_cast<PublishedXmlProbe*>(user_data);
if (::strcmp(name, METADATA_TAG) == 0) {
self->in_metadata = true;
self->curr_name = attribute(attrs, NAME_ATTR);
self->curr_value.clear();
} else {
self->in_metadata = false;
}
}
static void XMLCALL characters(void* user_data, const XML_Char* s, int len)
{
auto* self = static_cast<PublishedXmlProbe*>(user_data);
if (self->in_metadata)
self->curr_value.append(s, len);
}
static void XMLCALL end(void* user_data, const char* name)
{
auto* self = static_cast<PublishedXmlProbe*>(user_data);
if (!self->in_metadata || ::strcmp(name, METADATA_TAG) != 0)
return;
self->in_metadata = false;
if (self->curr_name == ORCA_PUBLISHED_TAG) {
self->published = is_published_3mf_flag(xml_unescape(self->curr_value));
self->found = true;
if (self->parser != nullptr)
XML_StopParser(self->parser, false);
}
}
};
} // namespace
bool bbs_3mf_is_published(const std::string &path)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
struct close_lock
{
mz_zip_archive *archive;
void close()
{
if (archive) {
close_zip_reader(archive);
archive = nullptr;
}
}
~close_lock() { close(); }
} lock{&archive};
if (!open_zip_reader(&archive, path))
return false;
// Read just the model XML (the metadata node sits before the resources, so the probe below
// stops early) rather than by a raw substring match; no geometry parsing.
int index = mz_zip_reader_locate_file(&archive, MODEL_FILE.c_str(), nullptr, 0);
if (index < 0)
return false;
mz_zip_archive_file_stat stat;
if (!mz_zip_reader_file_stat(&archive, index, &stat))
return false;
std::string xml(stat.m_uncomp_size, '\0');
if (!mz_zip_reader_extract_to_mem(&archive, index, xml.data(), xml.size(), 0))
return false;
XML_Parser parser = XML_ParserCreate(nullptr);
if (parser == nullptr)
return false;
PublishedXmlProbe probe;
probe.parser = parser;
XML_SetUserData(parser, &probe);
XML_SetElementHandler(parser, PublishedXmlProbe::start, PublishedXmlProbe::end);
XML_SetCharacterDataHandler(parser, PublishedXmlProbe::characters);
// Never resolve external entities from a file we are only probing.
XML_SetExternalEntityRefHandler(parser, nullptr);
XML_SetEntityDeclHandler(parser, nullptr);
const XML_Status status = XML_Parse(parser, xml.data(), static_cast<int>(xml.size()), 1);
// XML_StopParser(parser, false) from the end handler makes XML_Parse return
// XML_STATUS_ERROR with XML_ERROR_ABORTED - treat that as success (we stopped on the flag).
const bool parse_ok = (status == XML_STATUS_OK) ||
(XML_GetErrorCode(parser) == XML_ERROR_ABORTED && probe.found);
XML_ParserFree(parser);
if (!parse_ok)
return false;
return probe.published;
}
bool load_gcode_3mf_from_stream(std::istream &data, DynamicPrintConfig *config, Model *model, PlateDataPtrs *plate_data_list, Semver *file_version)
{
CNumericLocalesSetter locales_setter;
+19
View File
@@ -159,12 +159,28 @@ enum class SaveStrategy
SkipAuxiliary = 1 << 9,
UseLoadedId = 1 << 10,
ShareMesh = 1 << 11,
// Keep this separate from SplitModel, which uses the 0x1000 bit as part of its
// production-extension value.
MinimalPublished = 1 << 13,
SplitModel = 0x1000 | ProductionExt,
Encrypted = SecureContentExt | SplitModel,
Backup = 0x10000 | WithGcode | Silence | SkipStatic | SplitModel,
};
// Model metadata keys of a "published" 3MF (see MinimalPublished): the flag marks a minimal,
// tag-less publish export, the others carry the author-selected settings payload. Namespaced
// with the "orca_published" prefix because metadata_items round-trips verbatim through other
// slicers, where a bare "published" key could collide.
inline constexpr const char *ORCA_PUBLISHED_TAG = "orca_published";
inline constexpr const char *ORCA_PUBLISHED_KEYS_TAG = "orca_published_keys";
inline constexpr const char *ORCA_PUBLISHED_MATERIAL_TAG = "orca_published_material_keys";
inline constexpr const char *ORCA_PUBLISHED_CONFIG_TAG = "orca_published_config";
// Published files are produced with "1". The importer and the GUI loader both gate on this
// exact value, so a "0"/"false"/unknown value is rejected consistently.
bool is_published_3mf_flag(const std::string &value);
inline SaveStrategy operator | (SaveStrategy lhs, SaveStrategy rhs)
{
using T = std::underlying_type_t <SaveStrategy>;
@@ -277,6 +293,9 @@ extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSub
extern std::string bbs_3mf_get_thumbnail(const char * path);
// Lightweight check: does this 3mf carry the "published" (orca_published == "1") marker? Only reads the 3D/3dmodel.model metadata node
extern bool bbs_3mf_is_published(const std::string &path);
extern bool load_gcode_3mf_from_stream(std::istream & data, DynamicPrintConfig* config, Model* model, PlateDataPtrs* plate_data_list,
Semver* file_version);
+1 -1
View File
@@ -910,7 +910,7 @@ namespace Slic3r
unsigned int iterations = (1 << all_extruders.size());
unsigned int final_state = iterations - 1;
std::vector<std::vector<float>>cache(iterations, std::vector<float>(all_extruders.size(), 0x7fffffff));
std::vector<std::vector<float>>cache(iterations, std::vector<float>(all_extruders.size(), std::numeric_limits<float>::max()));
std::vector<std::vector<int>>prev(iterations, std::vector<int>(all_extruders.size(), -1));
cache[1][0] = 0.;
for (unsigned int state = 0; state < iterations; ++state) {
+2 -2
View File
@@ -30,8 +30,8 @@ bool Line::intersection_infinite(const Line &other, Point* point) const
return false;
double t1 = cross2(v12, v2) / denom;
Vec2d result = (a1 + t1 * v1);
if (result.x() > std::numeric_limits<coord_t>::max() || result.x() < std::numeric_limits<coord_t>::lowest() ||
result.y() > std::numeric_limits<coord_t>::max() || result.y() < std::numeric_limits<coord_t>::lowest()) {
if (result.x() > double(std::numeric_limits<coord_t>::max()) || result.x() < double(std::numeric_limits<coord_t>::lowest()) ||
result.y() > double(std::numeric_limits<coord_t>::max()) || result.y() < double(std::numeric_limits<coord_t>::lowest())) {
// Intersection has at least one of the coordinates much bigger (or smaller) than coord_t maximum value (or minimum).
// So it can not be stored into the Point without integer overflows. That could mean that input lines are parallel or near parallel.
return false;
+46
View File
@@ -3598,6 +3598,15 @@ void FacetsAnnotation::shift_states_above(const ModelVolume &mv, EnforcerBlocker
this->set(selector);
}
void FacetsAnnotation::remap_states(const ModelVolume &mv, const EnforcerBlockerStateMap &state_map)
{
if (empty()) return;
TriangleSelector selector(mv.mesh());
selector.deserialize(m_data, false);
selector.remap_triangle_state(state_map);
this->set(selector);
}
void FacetsAnnotation::set_enforcer_block_type_limit(const ModelVolume &mv,
EnforcerBlockerType max_type,
EnforcerBlockerType to_delete_filament,
@@ -3862,6 +3871,43 @@ bool model_has_advanced_features(const Model &model)
return false;
}
void remap_model_filament_slots(Model &model, const std::map<int, int> &slot_relocations)
{
if (slot_relocations.empty())
return;
// Paint states and the object/volume "extruder" configs store one-based slot numbers
// (see Sidebar::on_action_add_filament's insertion remap for the same encoding).
std::map<int, int> one_based_slots;
for (const auto &[from, to] : slot_relocations)
one_based_slots.emplace(from + 1, to + 1);
EnforcerBlockerStateMap paint_state_map;
for (size_t state = 0; state < paint_state_map.size(); ++state)
paint_state_map[state] = EnforcerBlockerType(state);
for (const auto &[one_based_from, one_based_to] : one_based_slots) {
assert(one_based_from >= 0 && size_t(one_based_from) < paint_state_map.size());
assert(one_based_to > 0 && size_t(one_based_to) < paint_state_map.size());
paint_state_map[size_t(one_based_from)] = EnforcerBlockerType(one_based_to);
}
auto remap_extruder_config = [&one_based_slots](ModelConfig &config) -> bool {
const auto it = config.has("extruder") ? one_based_slots.find(config.extruder()) : one_based_slots.end();
if (it == one_based_slots.end())
return false;
config.set("extruder", it->second);
return true;
};
for (ModelObject *object : model.objects) {
remap_extruder_config(object->config);
for (ModelVolume *volume : object->volumes) {
remap_extruder_config(volume->config);
volume->mmu_segmentation_facets.remap_states(*volume, paint_state_map);
}
}
}
#ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)
+11
View File
@@ -745,6 +745,10 @@ public:
// Shift painted filament indices >= threshold by delta. Used when a physical filament is
// inserted ahead of existing slots (mixed-color slots are kept at the end of the list).
void shift_states_above(const ModelVolume &mv, EnforcerBlockerType threshold, int delta);
// Relabel painted filament indices according to state_map (old state value -> new state
// value; untouched states keep their identity). Used when published-3MF import relocates
// mixed-filament definitions onto new slot numbers.
void remap_states(const ModelVolume &mv, const EnforcerBlockerStateMap &state_map);
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
bool empty() const { return m_data.triangles_to_split.empty(); }
@@ -1790,6 +1794,13 @@ bool model_has_multi_part_objects(const Model &model);
// If the model has advanced features, then it cannot be processed in simple mode.
bool model_has_advanced_features(const Model &model);
// Remap the model's filament-slot references after a published-3MF import relocated
// mixed-filament definitions onto new slot numbers: object/volume "extruder" configs and
// multi-material color-painting states (paint state stores the one-based slot number).
// slot_relocations maps the author's zero-based slot number to its final zero-based slot;
// entries are applied simultaneously (no chained lookups), untouched slots keep everything.
void remap_model_filament_slots(Model &model, const std::map<int, int> &slot_relocations);
#ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model);
+69
View File
@@ -3062,6 +3062,75 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
this->get_selected_preset().save(nullptr);
}
// A detached standalone preset for the Full Publish receiver: create a user preset holding
// the full resolved filament config (no inheritance, no vendor/alias links), parentless.
// Note: universal printer compatibility is not enforced here - callers apply
// make_publish_universal() to the config before handing it over when they need it.
// Mirrors save_current_preset(detach=true)'s creation branch but does not force-select or
// diff against a parent; the caller decides whether to select it.
// The published entry's filament_id is forwarded so user bases keep their stable
// material grouping (get_filament_presets() groups user bases by filament_id).
// The copy is a project-embedded preset: it lives inside the loaded project only
// (serialized into the saved .3mf, restored by load_project_embedded_presets) and
// never touches the user's library directory; Preset::save() early-returns for
// embedded presets, so persistence is skipped here too.
// Returns the final (uniquified) name; on collision "<base>" -> "<base> (Published)" ->
// "<base> (Published 2)" ...
std::string PresetCollection::add_detached_preset(const std::string &name_base, DynamicPrintConfig config,
const std::string &filament_id)
{
if (name_base.empty())
return std::string();
Preset stored(m_type, name_base);
stored.config = std::move(config);
stored.filament_id = filament_id;
// Uniquify verbatim; only on collision append " (Published)" then " (Published 2)".
const std::string base_name = name_base;
std::string final_name = base_name;
auto exists = [this](const std::string &candidate) -> bool {
const auto it = this->find_preset_internal(candidate);
return it != m_presets.end() && it->name == candidate;
};
if (exists(final_name)) {
final_name = base_name + " (Published)";
for (int i = 2; exists(final_name); ++i)
final_name = base_name + " (Published " + std::to_string(i) + ")";
}
// Creation branch of save_current_preset(detach=true), without its selection side
// effects or project-embedded path.
lock();
const auto it = this->find_preset_internal(final_name);
if (m_presets.begin() + m_idx_selected >= it)
++m_idx_selected;
Preset &preset = *m_presets.insert(it, stored);
preset.name = final_name;
preset.vendor = nullptr;
preset.alias.clear();
preset.renamed_from.clear();
preset.m_excluded_from.clear();
preset.setting_id.clear();
preset.inherits().clear();
preset.version = Semver::parse(SoftFever_VERSION).value_or(Semver());
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
preset.bundle_id.clear();
preset.file = this->path_for_preset(preset);
preset.is_visible = true;
preset.is_project_embedded = true;
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = final_name;
else if (m_type == Preset::TYPE_FILAMENT)
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = final_name;
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = final_name;
unlock();
return final_name;
}
bool PresetCollection::delete_current_preset()
{
Preset &selected = this->get_selected_preset();
+16
View File
@@ -631,6 +631,22 @@ public:
// All presets are marked as not modified and the new preset is activated.
//BBS: add project embedded preset logic
void save_current_preset(const std::string &new_name, bool detach = false, bool save_to_project = false, Preset* _curr_preset = nullptr);
// Insert a standalone user preset holding the full resolved config (no inheritance,
// no vendor links): the libslic3r equivalent of "Detach from parent". Takes a
// resolved config, clears parent/vendor/alias metadata, stamps filament_settings_id.
// Unlike save_current_preset it does not force-select or diff against a parent.
// Used by the published-3MF Full Publish path. The optional filament_id seeds the
// preset's stable material grouping (get_filament_presets groups user bases by
// filament_id); the published entry's filament_id is forwarded so the copy keeps
// the author's grouping.
// The copy is a project-embedded preset ("Preset Inside Project"): it lives inside
// the loaded project only, is serialized into the saved .3mf via
// get_current_project_embedded_presets(), and is never written to the user's
// library directory.
// Returns the final (uniquified) name; on collision the suffix rule is:
// "<base>" -> "<base> (Published)" -> "<base> (Published 2)" ...
std::string add_detached_preset(const std::string &name_base, DynamicPrintConfig config,
const std::string &filament_id = std::string());
// Delete the current preset, activate the first visible preset.
// returns true if the preset was deleted successfully.
File diff suppressed because it is too large Load Diff
+29 -3
View File
@@ -4,9 +4,11 @@
#include "Preset.hpp"
#include "PresetCacheFormat.hpp"
#include "AppConfig.hpp"
#include "PublishSettings.hpp"
#include "enum_bitmask.hpp"
#include <memory>
#include <map>
#include <set>
#include <shared_mutex>
#include <unordered_map>
@@ -168,6 +170,30 @@ struct PresetBundleMetadata
}
};
// A "published" 3MF project: keeps the user's currently-selected presets and overlays only the
// author-selected published keys onto the edited presets.
struct PublishedConfig
{
bool published = false;
std::vector<std::string> published_keys;
// Per-slot published material keys, applied positionally (author slot N -> receiver slot N).
// Partial entries are gated by the author's optional type requirement and written onto the
// slot's stored preset in place; full entries instead detach (see PublishedMaterialEntry in
// PublishSettings.hpp).
std::vector<PublishedMaterialEntry> material_keys;
// Keys that could not be applied (missing on the user's machine or vector size mismatch),
// filled in by load_config_file_config for notification purposes.
std::vector<std::string> skipped_keys;
// Human-readable notices of the slot material replacements performed while loading a
// published project, for the load notification.
std::vector<std::string> material_replacements;
// Mixed-filament entries that had to be moved off their authored slot on load (a real,
// physical filament occupied it): maps the author's zero-based slot number to its final
// zero-based slot. Consumers (e.g. model extruder/color-painting remapping) use this to
// keep geometry references pointing at the relocated definitions.
std::map<int, int> mixed_slot_relocations;
};
// Bundle of Print + Filament + Printer presets.
class PresetBundle
{
@@ -464,8 +490,8 @@ public:
// Load configuration that comes from a model file containing configuration, such as 3MF et al.
// This method is called by the Plater.
void load_config_model(const std::string &name, DynamicPrintConfig config, Semver file_version = Semver())
{ this->load_config_file_config(name, true, std::move(config), file_version); }
void load_config_model(const std::string &name, DynamicPrintConfig config, Semver file_version = Semver(), PublishedConfig *published_config = nullptr)
{ this->load_config_file_config(name, true, std::move(config), file_version, false, published_config); }
// Load an external config file containing the print, filament and printer presets.
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
@@ -646,7 +672,7 @@ private:
// Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path.
// and the external config is just referenced, not stored into user profile directory.
// If it is not an external config, then the config will be stored into the user profile directory.
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version = Semver(), bool selected = false);
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version = Semver(), bool selected = false, PublishedConfig *published_config = nullptr);
/*ConfigSubstitutions load_config_file_config_bundle(
const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);*/
+1 -1
View File
@@ -5430,7 +5430,7 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->readonly = false;
def->nullable = true;
def->set_default_value(new ConfigOptionFloatsNullable { {0.0} });
def->set_default_value(new ConfigOptionFloatsNullable { 0.0 });
def = this->add("cooling_tube_retraction", coFloat);
def->label = L("Cooling tube position");
+192 -126
View File
@@ -21,9 +21,11 @@
#include "TriangleMeshSlicer.hpp"
#include "Utils.hpp"
#include "Fill/FillAdaptive.hpp"
#include "Fill/Fill.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp"
#include "format.hpp"
#include "AABBTreeIndirect.hpp"
#include "AABBTreeLines.hpp"
#include <cstddef>
@@ -672,6 +674,98 @@ void PrintObject::prepare_infill()
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
// centering. Two islands belong to the same body when their slices overlap on adjacent
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
// chain links) stay separate, matching "split to objects". Each layer island then records
// the full bounding box of its body, so its infill is centered on that body as if it were
// sliced alone. Compute this before bridges so anchors and extrusion share the same origin.
bool needs_separated_components = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
const PrintRegionConfig &rc = this->printing_region(i).config();
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
needs_separated_components = true;
break;
}
}
// Orca: Fast path: the feature only changes anything when the object is made of more than one
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
// body already shares the object center, i.e. the default, so skip the connectivity pass.
if (needs_separated_components) {
int parts = 0;
const ModelVolume *first_part = nullptr;
for (const ModelVolume *v : this->model_object()->volumes)
if (v->is_model_part()) { ++ parts; first_part = v; }
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
needs_separated_components = false;
}
for (Layer *layer : m_layers)
layer->lslices_separated_component_bboxes.clear();
if (needs_separated_components) {
const size_t nl = m_layers.size();
std::vector<size_t> offset(nl + 1, 0); // Orca: flat index of the first island of each layer
for (size_t i = 0; i < nl; ++ i)
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
const size_t nreg = offset[nl];
// Orca: Union-find over every (layer, island).
std::vector<size_t> parent(nreg);
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
auto find = [&parent](size_t x) {
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
};
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
// Orca: Index the smaller of two consecutive layers instead of scanning every
// pair of islands. The tree prunes distant boxes on fragmented models; exact
// polygon intersections still decide connectivity for the remaining candidates.
for (size_t i = 0; i + 1 < nl; ++ i) {
m_print->throw_if_canceled();
size_t layer_a = i, layer_b = i + 1;
if (m_layers[layer_a]->lslices.size() < m_layers[layer_b]->lslices.size())
std::swap(layer_a, layer_b);
const Layer *la = m_layers[layer_a], *lb = m_layers[layer_b];
if (lb->lslices.empty())
continue;
using IslandTree = AABBTreeIndirect::Tree<2, coord_t>;
std::vector<AABBTreeIndirect::BoundingBoxWrapper> bboxes;
bboxes.reserve(lb->lslices.size());
for (size_t b = 0; b < lb->lslices.size(); ++ b)
bboxes.emplace_back(b, lb->lslices_bboxes[b]);
IslandTree tree;
tree.build_modify_input(bboxes);
for (size_t a = 0; a < la->lslices.size(); ++ a) {
const IslandTree::BoundingBox query(la->lslices_bboxes[a].min, la->lslices_bboxes[a].max);
AABBTreeIndirect::traverse(tree,
[&query](const IslandTree::Node &node) { return node.bbox.intersects(query); },
[&](const IslandTree::Node &node) {
const size_t b = node.idx;
// Orca: Tree boxes include an epsilon, so retain the original box
// filter. Already-connected islands cannot change the partition
// and need no further polygon intersection.
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
find(offset[layer_a] + a) != find(offset[layer_b] + b) &&
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
unite(offset[layer_a] + a, offset[layer_b] + b);
return true;
});
}
}
// Orca: Full bounding box of each body, indexed by its union-find root.
std::vector<BoundingBox> body_bbox(nreg);
for (size_t i = 0; i < nl; ++ i)
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
// Orca: Store the body bbox for every island.
for (size_t i = 0; i < nl; ++ i) {
Layer *layer = m_layers[i];
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
for (size_t a = 0; a < layer->lslices.size(); ++ a)
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
}
}
// the following step needs to be done before combination because it may need
// to remove only half of the combined infill
this->bridge_over_infill();
@@ -706,71 +800,6 @@ void PrintObject::infill()
if (this->set_started(posInfill)) {
m_print->set_status(35, L("Generating infill toolpath"));
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
// centering. Two islands belong to the same body when their slices overlap on adjacent
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
// chain links) stay separate, matching "split to objects". Each layer island then records
// the full bounding box of its body, so its infill is centered on that body as if it were
// sliced alone. Done once here, before the parallel fill, and only when a region needs it.
bool needs_separated_components = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
const PrintRegionConfig &rc = this->printing_region(i).config();
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
needs_separated_components = true;
break;
}
}
// Fast path: the feature only changes anything when the object is made of more than one
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
// body already shares the object center, i.e. the default, so skip the connectivity pass.
if (needs_separated_components) {
int parts = 0;
const ModelVolume *first_part = nullptr;
for (const ModelVolume *v : this->model_object()->volumes)
if (v->is_model_part()) { ++ parts; first_part = v; }
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
needs_separated_components = false;
}
for (Layer *layer : m_layers)
layer->lslices_separated_component_bboxes.clear();
if (needs_separated_components) {
const size_t nl = m_layers.size();
std::vector<size_t> offset(nl + 1, 0); // flat index of the first island of each layer
for (size_t i = 0; i < nl; ++ i)
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
const size_t nreg = offset[nl];
// Union-find over every (layer, island).
std::vector<size_t> parent(nreg);
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
auto find = [&parent](size_t x) {
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
};
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
// Join islands that overlap between two consecutive layers.
for (size_t i = 0; i + 1 < nl; ++ i) {
const Layer *la = m_layers[i], *lb = m_layers[i + 1];
for (size_t a = 0; a < la->lslices.size(); ++ a)
for (size_t b = 0; b < lb->lslices.size(); ++ b)
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
unite(offset[i] + a, offset[i + 1] + b);
}
// Full bounding box of each body, indexed by its union-find root.
std::vector<BoundingBox> body_bbox(nreg);
for (size_t i = 0; i < nl; ++ i)
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
// Store the body bbox for every island.
for (size_t i = 0; i < nl; ++ i) {
Layer *layer = m_layers[i];
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
for (size_t a = 0; a < layer->lslices.size(); ++ a)
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
}
}
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
@@ -1401,8 +1430,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_anchor_max"
|| opt_key == "top_surface_line_width"
|| opt_key == "bottom_surface_density"
|| opt_key == "center_of_surface_pattern"
|| opt_key == "separated_infills"
|| opt_key == "initial_layer_line_width"
|| opt_key == "small_area_infill_flow_compensation"
|| opt_key == "lateral_lattice_angle_1"
@@ -1410,6 +1437,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_overhang_angle") {
steps.emplace_back(posInfill);
} else if (opt_key == "sparse_infill_pattern"
// Orca: Body centering now also determines bridge anchors during preparation.
// Invalidating preparation also invalidates infill, including top/bottom surfaces.
|| opt_key == "center_of_surface_pattern"
|| opt_key == "separated_infills"
|| opt_key == "sparse_infill_smooth_factor"
|| opt_key == "symmetric_infill_y_axis"
|| opt_key == "infill_shift_step"
@@ -1480,13 +1511,9 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posPerimeters);
steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bridge_flow" || opt_key == "internal_bridge_flow") {
if (m_config.support_top_z_distance > 0.) {
// Only invalidate due to bridging if bridging is enabled.
// If later "support_top_z_distance" is modified, the complete PrintObject is invalidated anyway.
steps.emplace_back(posPerimeters);
steps.emplace_back(posInfill);
steps.emplace_back(posSupportMaterial);
}
steps.emplace_back(posPerimeters);
steps.emplace_back(posInfill);
steps.emplace_back(posSupportMaterial);
} else if (
opt_key == "wall_generator"
|| opt_key == "wall_transition_length"
@@ -1604,7 +1631,9 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
bool PrintObject::invalidate_all_steps()
{
// First call the "invalidate" functions, which may cancel background processing.
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
const bool inherited_invalidated = Inherited::invalidate_all_steps();
const bool print_invalidated = m_print->invalidate_all_steps();
bool result = inherited_invalidated || print_invalidated;
// Then reset some of the depending values.
m_slicing_params.valid = false;
return result;
@@ -3009,21 +3038,12 @@ void PrintObject::bridge_over_infill()
return diff(layers_sparse_infill, not_sparse_infill);
};
// LAMBDA do determine optimal bridging angle
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double infill_direction) {
// Orca: Derive the fallback bridge direction from the supplied anchor geometry.
// Pattern-specific angle selection belongs at the call site, where the supporting
// layer and region are known; this helper must not override it with a base config angle.
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors) {
AABBTreeLines::LinesDistancer<Line> lines_tree(anchors);
// Orca: since 3D Honeycomb was "fixed" by forcing coordf_t layerHeight = scale_(1.0), this is no longer needed.
// CorssHatch also does not need fixed angle.
//
// Check it the infill that require a fixed infill angle.
//switch (dominant_pattern) {
//case ip3DHoneycomb:
//case ipCrossHatch:
// return (infill_direction + 45.0) * 2.0 * M_PI / 360.;
//default: break;
//}
std::map<double, int> counted_directions;
for (const Polygon &p : bridged_area) {
double acc_distance = 0;
@@ -3089,18 +3109,15 @@ void PrintObject::bridge_over_infill()
if (bridging_angle == 0) {
bridging_angle = 0.001;
}
switch (dominant_pattern) {
case ipHilbertCurve: bridging_angle += 0.25 * PI; break;
case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break;
default: break;
}
return bridging_angle;
};
// LAMBDA that will fill given polygons with lines, exapand the lines to the nearest anchor, and reconstruct polygons from the newly
// generated lines
auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle) {
// Orca: Extend scan sections to the nearest anchors and reconstruct the bridge area.
// scan_spacing controls boundary sampling independently of the extrusion spacing;
// anchoring overlap and smoothing thresholds still use the physical bridging flow.
auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle,
coord_t scan_spacing, bool restore_anchors = false) {
auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) {
for (Line &l : lines) {
double ax = double(l.a.x());
@@ -3127,12 +3144,12 @@ void PrintObject::bridge_over_infill()
BoundingBox bb_x = get_extents(bridged_area);
BoundingBox bb_y = get_extents(anchors);
const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + bridging_flow.scaled_spacing() - 1) / bridging_flow.scaled_spacing();
const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + scan_spacing - 1) / scan_spacing;
std::vector<Line> vertical_lines(n_vlines);
for (size_t i = 0; i < n_vlines; i++) {
// Orca: Make sure the line is placed in the middle of the extrusion
// coord_t x = bb_x.min.x() + i * bridging_flow.scaled_spacing();
coord_t x = bb_x.min.x() + (i + 0.5) * bridging_flow.scaled_spacing();
// Orca: Sample the center of each reconstructed strip. Its edges lie
// half a scan step away, even when the sampling is finer than extrusion.
coord_t x = bb_x.min.x() + (i + 0.5) * scan_spacing;
coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing();
coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing();
vertical_lines[i].a = Point{x, y_min};
@@ -3155,7 +3172,11 @@ void PrintObject::bridge_over_infill()
auto anchors_intersections = anchors_and_walls_tree.intersections_with_line<true>(vertical_lines[i]);
for (Line &section : polygon_sections[i]) {
auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a,
// Orca: A repaired boundary may already overlap its anchor by one flow width.
// Include that overlap in the search so restoring rounded corners does not
// extend every already anchored section into the next sparse infill cell.
const coord_t overlap = restore_anchors ? bridging_flow.scaled_width() + SCALED_EPSILON : 0;
auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a + Point{0, overlap},
[](const Point &a, const std::pair<Point, size_t> &b) {
return a.y() > b.first.y();
});
@@ -3164,7 +3185,7 @@ void PrintObject::bridge_over_infill()
section.a.y() -= bridging_flow.scaled_width() * (0.5 + 0.5);
}
auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b,
auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b - Point{0, overlap},
[](const Point &a, const std::pair<Point, size_t> &b) {
return a.y() < b.first.y();
});
@@ -3194,7 +3215,9 @@ void PrintObject::bridge_over_infill()
});
}
// reconstruct polygon from polygon sections
// Orca: Reconstruct the polygon from scan sections. At discontinuities and
// strip starts/ends, use half the scan step for the X offsets; using half an
// extrusion spacing would overlap the finer strips and distort curved anchors.
struct TracedPoly
{
Points lows;
@@ -3220,8 +3243,8 @@ void PrintObject::bridge_over_infill()
36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
traced_poly.lows.push_back(candidate->a);
} else {
traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
traced_poly.lows.push_back(candidate->a - Point{bridging_flow.scaled_spacing() / 2, 0});
traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
traced_poly.lows.push_back(candidate->a - Point{scan_spacing / 2, 0});
traced_poly.lows.push_back(candidate->a);
}
@@ -3229,8 +3252,8 @@ void PrintObject::bridge_over_infill()
36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
traced_poly.highs.push_back(candidate->b);
} else {
traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
traced_poly.highs.push_back(candidate->b - Point{bridging_flow.scaled_spacing() / 2, 0});
traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
traced_poly.highs.push_back(candidate->b - Point{scan_spacing / 2, 0});
traced_poly.highs.push_back(candidate->b);
}
segment_added = true;
@@ -3238,9 +3261,9 @@ void PrintObject::bridge_over_infill()
}
if (!segment_added) {
// Zero overlapping segments, we just close this polygon
traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
// Orca: No section continues this strip; close at its right edge.
traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows));
new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
traced_poly.lows.clear();
@@ -3255,9 +3278,9 @@ void PrintObject::bridge_over_infill()
for (const auto &segment : polygon_slice) {
if (used_segments.find(&segment) == used_segments.end()) {
TracedPoly &new_tp = current_traced_polys.emplace_back();
new_tp.lows.push_back(segment.a - Point{bridging_flow.scaled_spacing() / 2, 0});
new_tp.lows.push_back(segment.a - Point{scan_spacing / 2, 0});
new_tp.lows.push_back(segment.a);
new_tp.highs.push_back(segment.b - Point{bridging_flow.scaled_spacing() / 2, 0});
new_tp.highs.push_back(segment.b - Point{scan_spacing / 2, 0});
new_tp.highs.push_back(segment.b);
}
}
@@ -3364,7 +3387,10 @@ void PrintObject::bridge_over_infill()
total_fill_area = closing(total_fill_area, float(SCALED_EPSILON));
expansion_area = closing(expansion_area, float(SCALED_EPSILON));
expansion_area = intersection(expansion_area, deep_infill_area);
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
// Orca: Preserve the real lower-layer anchors for every candidate in this
// layer. Replacing this shared set for one pattern also changes later regions,
// and synthetic straight lines can claim support where no infill is printed.
const Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
#ifdef DEBUG_BRIDGE_OVER_INFILL
@@ -3375,6 +3401,9 @@ void PrintObject::bridge_over_infill()
std::vector<CandidateSurface> expanded_surfaces;
expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
const auto &region_config = candidate.region->region().config();
const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve ||
region_config.sparse_infill_pattern == ipOctagramSpiral;
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
@@ -3403,20 +3432,40 @@ void PrintObject::bridge_over_infill()
to_lines(area_to_be_bridge), to_lines(boundary_plines), to_lines(anchors), to_lines(expansion_area));
#endif
double bridging_angle = 0;
if (!anchors.empty()) {
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors),
candidate.region->region().config().sparse_infill_pattern.value,
candidate.region->region().config().infill_direction.value);
} else {
// use expansion boundaries as anchors.
// Also, use Infill pattern that is neutral for angle determination, since there are no infill lines.
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0);
double bridging_angle = -1.;
if (!anchors.empty() && turning_pattern) {
// Orca: Keep adjacent bridges over Hilbert/Octagram aligned despite
// their many local turning directions. Use the lower layer's rotation,
// since that is the infill supporting the bridge, not the current layer's.
for (const LayerRegion *lower_region : layer->lower_layer->regions()) {
// Orca: Apply the configured direction only if the same region has
// sparse infill below this bridge. A height modifier may put another
// pattern underneath, requiring the geometry-based fallback below.
if (&lower_region->region() != &candidate.region->region() ||
intersection(area_to_be_bridge, to_polygons(lower_region->fill_surfaces.filter_by_type(stInternal))).empty())
continue;
bridging_angle = calculate_infill_rotation_angle(po, layer->lower_layer->id(), region_config.infill_direction.value,
region_config.sparse_infill_rotate_template.value) + 0.5 * PI;
// Orca: Apply model alignment as infill generation does, then normalize
// the undirected bridge angle to [0, PI), including negative rotations.
if (region_config.align_infill_direction_to_model) {
const auto &m = po->trafo().matrix();
bridging_angle += std::atan2(double(m(1, 0)), double(m(0, 0)));
}
bridging_angle = std::fmod(bridging_angle, PI);
if (bridging_angle < 0.)
bridging_angle += PI;
break;
}
}
// Orca: A different region below (e.g. a height modifier) needs the actual anchor
// directions. When there are no sparse anchors, use the expansion boundaries.
if (bridging_angle < 0.)
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors.empty() ? boundary_plines : anchors));
// ORCA: Internal bridge angle override
// Orca: Preserve the user's absolute or relative internal bridge angle
// override after automatic direction selection.
if (candidate.region->region().config().internal_bridge_angle.value > 0) {
const auto &region_config = candidate.region->region().config();
const double custom_angle_rad = Geometry::deg2rad(region_config.internal_bridge_angle.value);
if (region_config.relative_bridge_angle.value)
bridging_angle += custom_angle_rad;
@@ -3429,11 +3478,19 @@ void PrintObject::bridge_over_infill()
}
}
// Orca: Changing the bridge direction must not change its physical supports.
// Extend to actual sparse infill or the existing boundary anchors, never to
// a synthetic grid that merely has the same nominal angle and spacing.
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10)));
}
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
// Orca: Use four samples per extrusion spacing for Hilbert/Octagram so the
// reconstructed boundary follows rounded anchors instead of cutting corners.
// Keep the original step for other patterns and at least one coordinate unit
// after integer division. This changes boundary accuracy, not infill density.
const coord_t scan_spacing = std::max(coord_t(1), flow.scaled_spacing() / (turning_pattern ? 4 : 1));
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
// Check collision with other expanded surfaces
{
@@ -3447,7 +3504,9 @@ void PrintObject::bridge_over_infill()
}
}
if (reconstruct) {
bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
// Orca: Retain the same sampling accuracy when matching a nearby
// bridge's direction; rebuilding must not lose the curved supports.
bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
}
}
@@ -3455,6 +3514,13 @@ void PrintObject::bridge_over_infill()
// bridging_area = opening(bridging_area, flow.scaled_spacing());
bridging_area = opening(bridging_area, flow.scaled_spacing() * 0.75);
bridging_area = closing(bridging_area, flow.scaled_spacing());
// Orca: Opening/closing can pull rounded bridge ends away from their real
// supports. Restore those contacts after smoothing, preserving the cleaned
// area and the selected angle; do not smooth the restored contacts again.
if (turning_pattern && !bridging_area.empty()) {
bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow,
bridging_angle, scan_spacing, true));
}
bridging_area = intersection(bridging_area, limiting_area);
bridging_area = intersection(bridging_area, total_fill_area);
bridging_area = diff(bridging_area, total_top_area);
+315
View File
@@ -0,0 +1,315 @@
#include "PublishSettings.hpp"
#include "PresetBundle.hpp"
#include "Preset.hpp"
#include "PrintConfig.hpp"
#include "MaterialType.hpp"
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <map>
#include <set>
namespace Slic3r {
std::string publish_base_key(const std::string &key)
{
const size_t pos = key.find('#');
return pos == std::string::npos ? key : key.substr(0, pos);
}
// Parse the trailing "#N" variant index ("retraction_length#2" -> 2). Returns -1 when the key
// carries no '#' separator or its suffix is malformed; mirrors the importer's strict parse
// (PresetBundle.cpp) so the export side rejects the same variants the receiver would skip.
static int publish_variant_index(const std::string &key, const std::string &base_key)
{
if (key.size() <= base_key.size() || key.compare(0, base_key.size(), base_key) != 0 || key[base_key.size()] != '#')
return -1;
const std::string suffix = key.substr(base_key.size() + 1);
if (suffix.empty())
return -1;
int idx = 0;
for (const char c : suffix) {
if (c < '0' || c > '9')
return -1;
idx = idx * 10 + (c - '0');
if (idx > 1000000) // overflow guard; real vector sizes are tiny
return -1;
}
return idx;
}
std::string normalize_filament_type(const std::string& type)
{
if (type.empty())
return type;
if (MaterialType::find(type) != nullptr)
return type;
// "PLA High Speed" -> "PLA": strip a space-separated modifier, but keep dash-separated
// types like "PA-CF" / "PETG-CF" intact (they are distinct materials, not modifiers).
const size_t sep = type.find(' ');
if (sep != std::string::npos) {
const std::string base = type.substr(0, sep);
if (MaterialType::find(base) != nullptr)
return base;
}
return type;
}
void make_publish_universal(DynamicPrintConfig &config)
{
// Lists: empty => compatible with every printer / every print preset. Conditions:
// empty so a leftover expression left behind by the baseline clone can never
// re-narrow the match (see is_compatible_with_printer, Preset.cpp:840). All four
// keys exist on filament presets; nil-guard for hand-crafted future schemas.
if (auto *opt = config.opt<ConfigOptionStrings>("compatible_printers", false))
opt->values.clear();
if (auto *opt = config.opt<ConfigOptionStrings>("compatible_prints", false))
opt->values.clear();
if (auto *opt = config.opt<ConfigOptionString>("compatible_printers_condition", false))
opt->value.clear();
if (auto *opt = config.opt<ConfigOptionString>("compatible_prints_condition", false))
opt->value.clear();
}
std::string publish_material_base_name(const std::string &preset_name)
{
if (preset_name.empty())
return preset_name;
const size_t at = preset_name.find('@');
std::string base = (at == std::string::npos) ? preset_name : preset_name.substr(0, at);
boost::trim_right(base);
return base;
}
const std::set<std::string>& publish_structural_keys()
{
// Non-publishable keys: the *_settings_id keys are also in PresetCollection::skipped_in_dirty
// (Preset.cpp) / stripped from configs (profile_print_params_same); publishing them would
// rewrite the user's preset inheritance/structure.
static const std::set<std::string> structural_keys = {
"printer_settings_id", "filament_settings_id", "print_settings_id",
"sla_print_settings_id", "sla_material_settings_id",
"compatible_printers", "compatible_prints",
"compatible_printers_condition", "compatible_prints_condition",
"default_filament_profile", "default_print_profile",
"default_sla_print_profile", "default_sla_material_profile",
"extruder_count", "bed_shape",
"inherits", "inherits_group",
"printer_technology", "printer_model", "printer_variant",
"physical_printer_settings_id", "filament_ids",
"different_settings_to_system"
};
return structural_keys;
}
const std::set<std::string>& publish_mixed_keys()
{
// Must match PresetBundle's s_project_options mixed-color group (PresetBundle.cpp): these
// are project-level parallel per-slot arrays, not filament-preset options, so the import
// material pass applies them into project_config instead of a filament preset config.
static const std::set<std::string> mixed_keys = {
"filament_is_mixed",
"filament_mixed_components",
"filament_mixed_sublayer_ratios",
"filament_mixed_gradient",
"filament_mixed_gradient_range",
"filament_mixed_gradient_curve",
"filament_mixed_gradient_per_part"
};
return mixed_keys;
}
// The printer tab's "Retraction" optgroup (TabPrinter::build_fff, Tab.cpp), in tab order.
// KEEP IN SYNC with that optgroup: the published-3MF printer allowlist is built from these
// lists, so any key shown there must be publishable here (and vice versa).
const std::vector<PublishablePrinterOption>& publishable_printer_retraction_options()
{
static const std::vector<PublishablePrinterOption> options = {
{ "retraction_length", "printer_extruder_retraction#length" },
{ "retract_restart_extra", "printer_extruder_retraction#extra-length-on-restart" },
{ "retraction_speed", "printer_extruder_retraction#retraction-speed" },
{ "deretraction_speed", "printer_extruder_retraction#deretraction-speed" },
{ "retraction_minimum_travel", "printer_extruder_retraction#travel-distance-threshold" },
{ "retract_when_changing_layer", "printer_extruder_retraction#retract-on-layer-change" },
{ "wipe", "printer_extruder_retraction#wipe-while-retracting" },
{ "wipe_distance", "printer_extruder_retraction#wipe-distance" },
{ "retract_before_wipe", "printer_extruder_retraction#retract-amount-before-wipe" },
{ "retract_after_wipe", "printer_extruder_retraction#retract-amount-after-wipe" },
};
return options;
}
// The printer tab's "Z-Hop" optgroup (TabPrinter::build_fff, Tab.cpp), in tab order. KEEP IN
// SYNC with that optgroup, same as publishable_printer_retraction_options().
const std::vector<PublishablePrinterOption>& publishable_printer_z_hop_options()
{
static const std::vector<PublishablePrinterOption> options = {
{ "retract_lift_enforce", "printer_extruder_z_hop#on-surfaces" },
{ "z_hop_types", "printer_extruder_z_hop#z-hop-type" },
{ "z_hop", "printer_extruder_z_hop#z-hop-height" },
{ "travel_slope", "printer_extruder_z_hop#traveling-angle" },
{ "retract_lift_above", "printer_extruder_z_hop#only-lift-z-above" },
{ "retract_lift_below", "printer_extruder_z_hop#only-lift-z-below" },
};
return options;
}
const std::set<std::string>& publishable_printer_keys()
{
// Union of the two optgroups; "Retraction when switching material" keys are excluded
// (toolchange retraction is device/profile territory, not a publishable behavior tweak).
static const std::set<std::string> printer_keys = [] {
std::set<std::string> keys;
for (const PublishablePrinterOption &opt : publishable_printer_retraction_options())
keys.insert(opt.key);
for (const PublishablePrinterOption &opt : publishable_printer_z_hop_options())
keys.insert(opt.key);
return keys;
}();
return printer_keys;
}
std::vector<std::string> collect_dirty_settings_keys(const PresetBundle& bundle)
{
std::set<std::string> keys;
// Union the dirty keys of each collection's edited preset (filaments may span multiple
// slots); feeds only the Publish dialog's pre-check.
for (const std::string& key : bundle.prints.current_dirty_options(true))
keys.insert(key);
for (const std::string& key : bundle.printers.current_dirty_options(true))
keys.insert(key);
for (const std::string& key : bundle.filaments.current_dirty_options(true))
keys.insert(key);
return std::vector<std::string>(keys.begin(), keys.end());
}
DynamicPrintConfig filter_published_config(
const DynamicPrintConfig &full_config,
const std::vector<std::string> &published_keys,
const std::vector<PublishedMaterialEntry> &material_keys)
{
DynamicPrintConfig filtered;
std::set<std::string> base_keys_to_include;
// Never masked (whole-vector serialization): identity, plate geometry, process keys and
// printer keys without a "#N" variant.
std::set<std::string> mask_exempt_keys;
// Material entries: base key -> author slots whose values must survive; other slots are
// masked to their defaults so a publish (partial or full) does not leak unrelated slot
// data.
std::map<std::string, std::set<int>> slot_mask_map;
// 1. Mandatory material identity & slot count keys for 3MF validation/normalization
// (filament_ids: exported for validation, denylisted on apply - see publish_structural_keys).
static const std::vector<std::string> s_material_identity_keys = {
"filament_colour",
"filament_type",
"filament_vendor",
"filament_ids",
"filament_diameter",
"filament_self_index",
"filament_extruder_variant"
};
for (const std::string &key : s_material_identity_keys) {
base_keys_to_include.insert(key);
mask_exempt_keys.insert(key);
}
// 2. Published plate / bed geometry keys (wipe tower positioning)
static const std::vector<std::string> s_plate_geometry_keys = {
"wipe_tower_x",
"wipe_tower_y",
"wipe_tower_rotation_angle"
};
for (const std::string &key : s_plate_geometry_keys) {
base_keys_to_include.insert(key);
mask_exempt_keys.insert(key);
}
// 3. Process and printer published keys. Printer per-extruder keys carry a "#N" variant
// (e.g. retraction_length#2): mask the base to the author's extruder index so a partial
// publish does not serialize every extruder's value (same slot-masking as the material side).
const std::set<std::string> &printer_keys = publishable_printer_keys();
for (const std::string &key : published_keys) {
const std::string base_key = publish_base_key(key);
if (base_key.empty())
continue;
base_keys_to_include.insert(base_key);
if (printer_keys.count(base_key) != 0) {
const int variant_idx = publish_variant_index(key, base_key);
if (variant_idx >= 0)
slot_mask_map[base_key].insert(variant_idx);
else
mask_exempt_keys.insert(base_key); // bare printer key or malformed variant: whole vector
} else {
mask_exempt_keys.insert(base_key); // process key: whole vector
}
}
// 4. Material-specific published keys. Both partial (entry.keys) and full-publish
// (entry.full_keys) entries mask to the author's slot on export (see the copy loop below);
// slot-less entries (hand-crafted files) stay unmasked (whole vector).
for (const PublishedMaterialEntry &entry : material_keys) {
for (const std::string &key : entry.keys) {
const std::string base_key = publish_base_key(key);
if (base_key.empty())
continue;
base_keys_to_include.insert(base_key);
if (entry.slot >= 0)
slot_mask_map[base_key].insert(entry.slot);
}
for (const std::string &key : entry.full_keys) {
const std::string base_key = publish_base_key(key);
if (base_key.empty())
continue;
base_keys_to_include.insert(base_key);
if (entry.slot >= 0)
slot_mask_map[base_key].insert(entry.slot);
}
}
// Masking restores every non-published slot of a vector option with the option default, so
// a partial publish does not leak unrelated slot data. can_mask_slots reports whether a key
// is maskable at all (vector option plus a registered default of the same type); an
// unmaskable key is dropped from the payload entirely instead of shipping the author's
// whole vector.
auto can_mask_slots = [](const ConfigOption &opt, const ConfigOptionDef *def) -> bool {
if (def == nullptr || !def->default_value || def->default_value->type() != opt.type())
return false;
const auto *vec = dynamic_cast<const ConfigOptionVectorBase *>(&opt);
const auto *default_vec = dynamic_cast<const ConfigOptionVectorBase *>(def->default_value.get());
return vec != nullptr && vec->size() > 0 && default_vec != nullptr && !default_vec->empty();
};
auto mask_slots = [](ConfigOption &opt, const ConfigOptionDef *def, const std::set<int> &keep_slots) {
auto *vec = dynamic_cast<ConfigOptionVectorBase*>(&opt);
for (size_t idx = 0; idx < vec->size(); ++idx)
if (keep_slots.count(static_cast<int>(idx)) == 0)
vec->set_at(def->default_value.get(), idx, 0);
};
// Copy the selected options from full_config into the filtered config.
for (const std::string &key : base_keys_to_include) {
const ConfigOption *opt = full_config.option(key);
if (opt == nullptr)
continue;
const auto mask_it = slot_mask_map.find(key);
const bool needs_masking = mask_exempt_keys.count(key) == 0 && mask_it != slot_mask_map.end() && !mask_it->second.empty();
if (needs_masking && !can_mask_slots(*opt, print_config_def.get(key))) {
BOOST_LOG_TRIVIAL(warning) << "publish: dropping unmaskable key \"" << key
<< "\" from the published payload (no usable option default)";
continue;
}
ConfigOption *cloned = opt->clone();
if (needs_masking)
mask_slots(*cloned, print_config_def.get(key), mask_it->second);
filtered.set_key_value(key, cloned);
}
return filtered;
}
} // namespace Slic3r
+99
View File
@@ -0,0 +1,99 @@
#pragma once
#include <set>
#include <string>
#include <vector>
namespace Slic3r {
class PresetBundle;
// Strip a trailing "#N" variant suffix ("retraction_length#2" -> "retraction_length").
std::string publish_base_key(const std::string &key);
// Structural keys that are never applied onto the receiver's presets when loading a published
// 3MF (single source of truth for the denylist); applying them would rewrite the user's preset
// inheritance/structure. filament_ids is still exported via the identity list (3MF validation
// needs it) - exported, never applied.
const std::set<std::string>& publish_structural_keys();
// The mixed-color filament project keys (parallel per-slot arrays, see PresetBundle's
// s_project_options). Import applies them into project_config, not a filament preset.
const std::set<std::string>& publish_mixed_keys();
// One row of the printer tab's "Retraction" / "Z-Hop" optgroups (config key + tab icon id).
struct PublishablePrinterOption {
const char *key; // config key, e.g. "retraction_length"
const char *icon; // tab icon id, e.g. "printer_extruder_retraction#length"
};
// The printer tab's "Retraction" / "Z-Hop" optgroup options, in tab order.
const std::vector<PublishablePrinterOption>& publishable_printer_retraction_options();
const std::vector<PublishablePrinterOption>& publishable_printer_z_hop_options();
// Union of the two optgroup option lists; printer keys apply on import only if their base
// key is in this allowlist.
const std::set<std::string>& publishable_printer_keys();
// Union of setting keys differing from the base/system preset across the current print,
// printer and filament presets (feeds the Publish dialog's pre-check).
std::vector<std::string> collect_dirty_settings_keys(const PresetBundle& bundle);
// Per-slot published material keys, applied positionally (author slot N -> receiver slot N).
// The identity fields drive the created copy's naming and grouping on Full entries, the
// notification labels, and the partial type gate (publish_type) is the author's explicit
// opt-in for requiring a material type.
struct PublishedMaterialEntry {
std::string filament_type; // material family, e.g. "PLA" (may be empty)
std::string filament_vendor; // e.g. "Generic", "Bambu" (may be empty)
std::string filament_id; // stable material id, e.g. "GFL99" (may be empty)
// Unique preset id of the author's slot preset (e.g. Orca Filament Library "setting_id").
// Not matched against the receiver's library; carried so identical Full entries within one
// load share one created instance (within-load dedup key).
std::string setting_id;
// Canonical name of the author's slot preset (e.g. "Generic PLA @System"). On Full import
// it names the created copy after its "@variant" tail is stripped; never matched against
// the receiver's library.
std::string preset_name;
// 0-based author filament slot; -1 (hand-crafted files) is skipped.
int slot{-1};
std::vector<std::string> keys;
// "Full Publish": the whole filament preset (full_keys) is published. On the receiver Full
// Publish always creates a standalone parentless copy (libslic3r's "Detach from parent"),
// universally compatible and project-embedded only - never written to the user's library.
// Identical Full entries within one load share one created instance (within-load dedup).
bool full{false};
// All non-structural filament keys of the author's slot preset; values travel in the file
// config, masked to the author's slot index.
std::vector<std::string> full_keys;
// Vendor-agnostic (MaterialType) filament type the author requires for this slot; on a
// partial entry's mismatch the slot is replaced with a same-type filament. Full entries
// consult no gate.
bool publish_type{false};
std::string publish_type_value;
// Required filament colour, applied on load regardless of the type match.
bool publish_color{false};
std::string color;
// Import-side only, never serialized: the authored slot sits past the receiver's physical
// capacity, so the entry is appended as an empty mixed-filament placeholder (virtual tail
// slot; the GUI flags it for the user to assign components).
bool mixed_placeholder{false};
};
// "PLA High Speed" -> "PLA" (strip a space modifier); dash types like "PA-CF" are kept intact.
std::string normalize_filament_type(const std::string& type);
class DynamicPrintConfig;
// Clear the compatibility lists/conditions on a filament config so it is universally
// compatible once detached (empty lists + empty conditions = compatible with everything).
void make_publish_universal(DynamicPrintConfig &config);
// Naming base for a detached published-material copy: "Generic PLA @System" -> "Generic PLA"
// (truncate/right-trim at the first '@' tail). Empty result means "fall back to identity".
std::string publish_material_base_name(const std::string &preset_name);
// Minimal DynamicPrintConfig for a published 3MF export: only the selected published keys,
// material keys, identity fields and plate geometry keys.
DynamicPrintConfig filter_published_config(
const DynamicPrintConfig &full_config,
const std::vector<std::string> &published_keys,
const std::vector<PublishedMaterialEntry> &material_keys);
}
+3 -1
View File
@@ -1007,7 +1007,9 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
bool SLAPrintObject::invalidate_all_steps()
{
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
const bool inherited_invalidated = Inherited::invalidate_all_steps();
const bool print_invalidated = m_print->invalidate_all_steps();
return inherited_invalidated || print_invalidated;
}
double SLAPrintObject::get_elevation() const {
+4
View File
@@ -97,6 +97,8 @@ set(SLIC3R_GUI_SOURCES
GUI/CloneDialog.hpp
GUI/ConfigManipulation.cpp
GUI/ConfigManipulation.hpp
GUI/ConfigValueFormatter.cpp
GUI/ConfigValueFormatter.hpp
GUI/ConfigWizard.cpp
GUI/ConfigWizard.hpp
GUI/ConfigWizard_private.hpp
@@ -451,6 +453,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Project.hpp
GUI/PublishDialog.cpp
GUI/PublishDialog.hpp
GUI/PublishSettingsDialog.cpp
GUI/PublishSettingsDialog.hpp
GUI/PurgeModeDialog.cpp
GUI/PurgeModeDialog.hpp
GUI/RammingChart.cpp
+245
View File
@@ -0,0 +1,245 @@
#include "ConfigValueFormatter.hpp"
#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "I18N.hpp"
#include "GUI.hpp"
#include "Field.hpp"
namespace Slic3r {
namespace GUI {
std::string get_pure_opt_key(const std::string& opt_key)
{
std::string pure_key = opt_key;
const int pos = pure_key.find("#");
if (pos > 0)
boost::erase_tail(pure_key, pure_key.size() - pos);
return pure_key;
}
wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill, int idx)
{
const ConfigOptionDef& def = config.def()->options.at(opt_key);
const std::vector<std::string>& names = def.enum_labels;//ConfigOptionEnum<T>::get_enum_names();
int val = 0;
if (idx >= 0)
val = dynamic_cast<const ConfigOptionInts*>(config.option(opt_key))->get_at(idx);
else
val = config.option(opt_key)->getInt();
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
// So we should "convert" val to the correct one
if (is_infill) {
for (auto key_val : *def.enum_keys_map)
if (int(key_val.second) == val) {
auto it = std::find(def.enum_values.begin(), def.enum_values.end(), key_val.first);
if (it == def.enum_values.end())
return "";
return from_u8(_utf8(names[it - def.enum_values.begin()]));
}
return _L("Undefined");
}
return from_u8(_utf8(names[val]));
}
wxString get_full_label(const std::string& opt_key, const DynamicPrintConfig& config)
{
const std::string pure_key = get_pure_opt_key(opt_key);
auto option = config.option(pure_key);
if (!option || option->is_nil())
return _L("N/A");
const ConfigOptionDef* opt = config.def()->get(pure_key);
return opt->full_label.empty() ? opt->label : opt->full_label;
}
wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config)
{
int orig_opt_idx = -1;
int opt_idx = -1;
int pos = opt_key.find("#");
std::string temp_str = opt_key;
if (pos > 0) {
boost::erase_head(temp_str, pos + 1);
orig_opt_idx = std::atoi(temp_str.c_str());
}
opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
const std::string pure_key = get_pure_opt_key(opt_key);
auto option = config.option(pure_key);
if (!option) {
return _L("N/A");
}
auto opt_vector = dynamic_cast<const ConfigOptionVectorBase *>(option);
if ((option->is_scalar() && option->is_nil()) ||
(option->is_vector() && opt_vector && opt_idx >= 0 && opt_idx < opt_vector->size() && opt_vector->is_nil(opt_idx)))
return _L("N/A");
wxString out;
const ConfigOptionDef* opt = config.def()->get(pure_key);
bool is_nullable = opt->nullable;
switch (opt->type) {
case coInt:
return from_u8((boost::format("%1%") % config.opt_int(pure_key)).str());
case coInts: {
if (is_nullable) {
auto values = config.opt<ConfigOptionIntsNullable>(pure_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
}
else {
auto values = config.opt<ConfigOptionInts>(pure_key);
if (orig_opt_idx >= 0 && orig_opt_idx < values->size()) {
return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
}
else {
std::string value_str;
for (int i = 0; i < values->size(); i++) {
value_str += std::to_string(values->get_at(i));
if (i != values->size() - 1) {
value_str += ",";
}
}
return from_u8(value_str);
}
}
return _L("Undefined");
}
case coBool:
return config.opt_bool(pure_key) ? "true" : "false";
case coBools: {
if (is_nullable) {
auto values = config.opt<ConfigOptionBoolsNullable>(pure_key);
if (opt_idx < values->size())
return values->get_at(opt_idx) ? "true" : "false";
}
else {
auto values = config.opt<ConfigOptionBools>(pure_key);
if (opt_idx < values->size())
return values->get_at(opt_idx) ? "true" : "false";
}
return _L("Undefined");
}
case coPercent:
return from_u8((boost::format("%1%%%") % int(config.optptr(pure_key)->getFloat())).str());
case coPercents: {
if (is_nullable) {
auto values = config.opt<ConfigOptionPercentsNullable>(pure_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
}
else {
auto values = config.opt<ConfigOptionPercents>(pure_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
}
return _L("Undefined");
}
case coFloat:
return double_to_string(config.opt_float(pure_key));
case coFloats: {
if (is_nullable) {
auto values = config.opt<ConfigOptionFloatsNullable>(pure_key);
if (opt_idx < values->size())
return double_to_string(values->get_at(opt_idx));
}
else {
auto values = config.opt<ConfigOptionFloats>(pure_key);
if (values && opt_idx < values->size())
return double_to_string(values->get_at(opt_idx));
}
return _L("Undefined");
}
case coString:
return from_u8(config.opt_string(pure_key));
case coStrings: {
const ConfigOptionStrings* strings = config.opt<ConfigOptionStrings>(pure_key);
if (strings) {
if (pure_key == "compatible_printers" || pure_key == "compatible_prints") {
if (strings->empty())
return _L("All");
for (size_t id = 0; id < strings->size(); id++)
out += from_u8(strings->get_at(id)) + "\n";
out.RemoveLast(1);
return out;
}
if (!strings->empty() && opt_idx < strings->values.size())
return from_u8(strings->get_at(opt_idx));
}
break;
}
case coFloatOrPercent: {
const ConfigOptionFloatOrPercent* opt = config.opt<ConfigOptionFloatOrPercent>(pure_key);
if (opt)
out = double_to_string(opt->value) + (opt->percent ? "%" : "");
return out;
}
case coEnum: {
return get_string_from_enum(pure_key, config,
pure_key == "top_surface_pattern" ||
pure_key == "bottom_surface_pattern" ||
pure_key == "internal_solid_infill_pattern" ||
pure_key == "sparse_infill_pattern" ||
pure_key == "ironing_pattern" ||
pure_key == "support_ironing_pattern" ||
pure_key == "support_pattern" ||
pure_key == "support_interface_pattern")
;
}
case coEnums: {
return get_string_from_enum(pure_key, config,
pure_key == "top_surface_pattern" ||
pure_key == "bottom_surface_pattern" ||
pure_key == "internal_solid_infill_pattern" ||
pure_key == "sparse_infill_pattern" ||
pure_key == "ironing_pattern" ||
pure_key == "support_ironing_pattern" ||
pure_key == "support_pattern" ||
pure_key == "support_interface_pattern"
, opt_idx);
}
case coPoint: {
Vec2d val = config.opt<ConfigOptionPoint>(pure_key)->value;
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
}
case coPoints: {
//BBS: add bed_exclude_area
if (pure_key == "printable_area" || pure_key == "thumbnails") {
ConfigOptionPoints points = *config.option<ConfigOptionPoints>(pure_key);
//BuildVolume build_volume = {points.values, 0.};
return get_thumbnails_string(points.values);
}
else if (pure_key == "bed_exclude_area") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(pure_key)->values);
}
else if (pure_key == "head_wrap_detect_zone") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(pure_key)->values);
}
else if (pure_key == "wrapping_exclude_area") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(pure_key)->values);
}
Vec2d val = config.opt<ConfigOptionPoints>(pure_key)->get_at(opt_idx);
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
}
default:
break;
}
return out;
}
} // namespace GUI
} // namespace Slic3r
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <string>
#include <wx/string.h>
namespace Slic3r {
class DynamicPrintConfig;
namespace GUI {
// Human-readable value of opt_key (may carry a "#<index>" suffix) in config.
wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config);
// Full label of opt_key; "N/A" when the option is not set.
wxString get_full_label(const std::string& opt_key, const DynamicPrintConfig& config);
// Strip the "#<index>" suffix (if any) from the option key.
std::string get_pure_opt_key(const std::string& opt_key);
// Localized label of the currently selected value of an enum option.
wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill = false, int idx = -1);
} // namespace GUI
} // namespace Slic3r
+3 -1
View File
@@ -1,3 +1,5 @@
#include <limits>
#include <nlohmann/json.hpp>
#include "DevMapping.h"
#include "DevFilaSystem.h"
@@ -270,7 +272,7 @@ namespace Slic3r
std::set<int> picked_tar;
for (int k = 0; k < distance_map.size(); k++)
{
float min_val = INT_MAX;
float min_val = std::numeric_limits<float>::max();
int picked_src_idx = -1;
int picked_tar_idx = -1;
for (int i = 0; i < distance_map.size(); i++)
+2 -1
View File
@@ -2714,7 +2714,8 @@ void ColourPicker::set_value(const boost::any& value, bool change_event)
auto field = dynamic_cast<wxColourPickerCtrl*>(window);
#ifdef __WXMSW__
wxColour clr = (clr_str.IsEmpty() || !clr.IsOk()) ? wxTransparentColour : clr_str;
const wxColour parsed_clr(clr_str);
wxColour clr = (clr_str.IsEmpty() || !parsed_clr.IsOk()) ? wxTransparentColour : parsed_clr;
field->SetColour(clr);
draw_bmp_btn(field, clr);
#else
+2
View File
@@ -628,8 +628,10 @@ private:
void on_button_click(wxCommandEvent &WXUNUSED(ev));
void save_colors_to_config();
private:
#if !defined(__linux__) && !defined(__LINUX__)
wxColourData* m_clrData{nullptr};
wxColourPickerWidget* m_picker_widget{nullptr};
#endif
};
class PointCtrl : public Field {
+425 -1
View File
@@ -1,16 +1,66 @@
#include <wx/dcmemory.h>
#include <wx/dcgraph.h>
#include <wx/graphics.h>
#include <wx/settings.h>
#include <wx/window.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <numeric>
#include <string>
#include <tuple>
#include "EncodedFilament.hpp"
#include "FilamentBitmapUtils.hpp"
#include "GUI_App.hpp"
#include "GuiColor.hpp"
#include "I18N.hpp"
#include "Widgets/Label.hpp"
#include "Widgets/StateColor.hpp"
#include "libslic3r/FilamentMixer.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r { namespace GUI {
// Barycentric utilities for a ternary (triangle) ratio picker.
double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c)
{
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
{
double total = tri_signed_area2(v0, v1, v2);
if (std::abs(total) < 1e-9) return false;
double s0 = tri_signed_area2(p, v1, v2) / total;
double s1 = tri_signed_area2(v0, p, v2) / total;
double s2 = 1.0 - s0 - s1;
return s0 >= -0.001 && s1 >= -0.001 && s2 >= -0.001;
}
void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
double& w0, double& w1, double& w2)
{
double total = std::abs(tri_signed_area2(v0, v1, v2));
if (total < 1e-9) { w0 = w1 = w2 = 1.0 / 3.0; return; }
w0 = std::abs(tri_signed_area2(p, v1, v2)) / total;
w1 = std::abs(tri_signed_area2(v0, p, v2)) / total;
w2 = 1.0 - w0 - w1;
w0 = std::clamp(w0, 0.0, 1.0);
w1 = std::clamp(w1, 0.0, 1.0);
w2 = std::clamp(w2, 0.0, 1.0);
double s = w0 + w1 + w2;
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
}
TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
{
double w0, w1, w2;
tri_barycentric(p, v0, v1, v2, w0, w1, w2);
return {w0 * v0.x + w1 * v1.x + w2 * v2.x,
w0 * v0.y + w1 * v1.y + w2 * v2.y};
}
void fill_gradient_rect_east(wxDC& dc, const wxRect& rect, const wxColour& from, const wxColour& to)
{
if (rect.width <= 0 || rect.height <= 0) return;
@@ -73,7 +123,7 @@ std::vector<wxColour> sample_gradient_ramp(const wxColour& first,
// Resolve the curve a gradient slot is sampled with, mirroring the slicer's fallback in
// ToolOrdering: a custom curve wins, otherwise a straight line between gradient_range's
// endpoints, otherwise the 0.10 -> 0.90 default.
static Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot)
Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot)
{
const auto* curve_opt = cfg.option<ConfigOptionStrings>("filament_mixed_gradient_curve");
if (curve_opt && slot < curve_opt->values.size() && !curve_opt->values[slot].empty()) {
@@ -449,4 +499,378 @@ void recompute_mixed_slot_colors(std::vector<wxColour>& colors,
}
}
namespace {
// Layout ratios of the gradient plot rect, copied from GradientCurveEditor so the read-only
// preview and the interactive editor stay pixel-identical. Plot rect is square 1:1; the
// right/bottom margins host the axis arrows and labels.
constexpr double kPlotLeftRatio = 0.0316;
constexpr double kPlotRightRatio = 0.6766;
constexpr double kPlotTopRatio = 0.1529;
constexpr double kPlotBottomRatio = 0.8474;
constexpr int kGridDivisions = 9; // 10 grid lines including the outer borders.
constexpr int kStrokeAxis = 2; // axis line width (px, no DPI scaling)
constexpr int kAxisArrowHalf = 5; // half-base of the axis arrow triangle (DIP)
constexpr int kAxisArrowLen = 10; // length of the axis arrow triangle (DIP)
constexpr int kPointRadius = 4; // anchor outer radius (DIP)
constexpr float kBgSimilarThreshold = 15.0f;
constexpr int kOutlineExtraDip = 2;
constexpr double kTriangleMarginDip = 20.0;
// Quadratic blend that never goes out of gamut, matching MixedFilamentDialog::blend_colors.
wxColour lerp_blend(const wxColour& a, const wxColour& b, double ratio_a)
{
unsigned char r, g, bl;
Slic3r::filament_mixer_lerp(a.Red(), a.Green(), a.Blue(),
b.Red(), b.Green(), b.Blue(),
static_cast<float>(1.0 - ratio_a), &r, &g, &bl);
return wxColour(r, g, bl);
}
// DIP conversion for these free functions: unlike the wxWindow member FromDIP, it needs the
// window parameter explicitly; nullptr picks the app's default DPI like the Publish dialog does.
int dip_px(int v) { return wxWindow::FromDIP(v, nullptr); }
} // namespace
wxRect mixed_gradient_plot_rect(const wxSize& sz)
{
const int x = static_cast<int>(std::lround(sz.x * kPlotLeftRatio));
const int y = static_cast<int>(std::lround(sz.y * kPlotTopRatio));
const int x2 = static_cast<int>(std::lround(sz.x * kPlotRightRatio));
const int y2 = static_cast<int>(std::lround(sz.y * kPlotBottomRatio));
const int side = std::max(1, std::min(x2 - x, y2 - y));
return wxRect(x, y, side, side);
}
void draw_mixed_gradient_plot(wxDC& raw_dc, const wxSize& canvas,
const std::vector<MixedGradientCurve>& curves,
const std::vector<wxPoint2DDouble>& anchors,
const MixedGradientTheme& theme)
{
// Draw into an internal opaque buffer so wxGCDC text/curves anti-alias against a solid
// background (never a transparent one), then blit the finished image onto the caller's
// buffered paint DC. wxGCDC cannot wrap a generic wxDC&, so the buffer is always a
// wxMemoryDC -- the one type wxGCDC accepts on every platform.
if (canvas.x <= 0 || canvas.y <= 0)
return;
const wxRect rc = mixed_gradient_plot_rect(canvas);
if (rc.width <= 0 || rc.height <= 0)
return;
wxBitmap buf(canvas);
wxMemoryDC memdc(buf);
memdc.SetBackground(wxBrush(theme.background));
memdc.Clear();
wxGCDC dc(memdc);
wxGraphicsContext* gc = dc.GetGraphicsContext();
// 10x10 light grid (10 lines including outer borders, 9 equal divisions).
dc.SetPen(wxPen(theme.grid, 1));
for (int i = 0; i <= kGridDivisions; ++i) {
const int x = rc.x + rc.width * i / kGridDivisions;
const int y = rc.y + rc.height * i / kGridDivisions;
dc.DrawLine(x, rc.y, x, rc.y + rc.height);
dc.DrawLine(rc.x, y, rc.x + rc.width, y);
}
// Set the label font first so text width measurements drive arrow / label placement.
wxFont label_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
label_font.SetPointSize(std::max(7, label_font.GetPointSize() - 1));
dc.SetFont(label_font);
const wxString axis_y_title = _L("Material Ratio");
const wxString axis_x_title = _L("Model Height");
const wxString pct_text = wxT("100%");
const wxSize x_title_sz = dc.GetTextExtent(axis_x_title);
const wxSize y_title_sz = dc.GetTextExtent(axis_y_title);
wxFont strong_font = label_font;
strong_font.SetWeight(wxFONTWEIGHT_SEMIBOLD);
dc.SetFont(strong_font);
const wxSize pct_text_sz = dc.GetTextExtent(pct_text);
dc.SetFont(label_font);
// Axes (grey 700) with filled triangle arrows. Y-axis extends above the plot top to the
// canvas top edge; X-axis extends past the plot right toward the canvas right edge.
const int arrow_half = dip_px(kAxisArrowHalf);
const int arrow_len = dip_px(kAxisArrowLen);
dc.SetPen(wxPen(theme.axis, kStrokeAxis));
dc.SetBrush(wxBrush(theme.axis));
const int y_axis_x = rc.x;
const int y_title_pct_gap = dip_px(1);
const int y_title_bottom_pad = dip_px(2);
const int y_title_y = std::max(0, rc.y - y_title_sz.y - y_title_pct_gap - pct_text_sz.y - y_title_bottom_pad);
const int y_arrow_tip_y = y_title_y;
const int y_arrow_ty = y_arrow_tip_y + arrow_len;
dc.DrawLine(y_axis_x, y_arrow_ty, y_axis_x, rc.y + rc.height);
{
wxPoint tri[3] = {
wxPoint(y_axis_x, y_arrow_tip_y),
wxPoint(y_axis_x - arrow_half, y_arrow_ty),
wxPoint(y_axis_x + arrow_half, y_arrow_ty),
};
dc.DrawPolygon(3, tri);
}
const int x_axis_y = rc.y + rc.height;
const int x_label_gap = dip_px(4);
const int x_edge_pad = dip_px(6);
const int x_arrow_ideal = rc.x + rc.width + dip_px(10);
const int x_arrow_max = canvas.x - x_title_sz.x - x_label_gap - x_edge_pad - arrow_len;
const int x_arrow_tx = std::max(rc.x + rc.width + arrow_len, std::min(x_arrow_ideal, x_arrow_max));
const int x_arrow_tip_x = x_arrow_tx + arrow_len;
const int x_title_x = x_arrow_tip_x + x_label_gap;
dc.DrawLine(rc.x, x_axis_y, x_arrow_tx, x_axis_y);
{
wxPoint tri[3] = {
wxPoint(x_arrow_tip_x, x_axis_y),
wxPoint(x_arrow_tx, x_axis_y - arrow_half),
wxPoint(x_arrow_tx, x_axis_y + arrow_half),
};
dc.DrawPolygon(3, tri);
}
// Labels: "Material Ratio" and the leading "100%" share the same left x; the trailing
// "Model Height" follows the X-axis arrow tip (already clamped to make room).
const int label_left_x = y_axis_x + dip_px(10);
dc.SetTextForeground(theme.label);
dc.DrawText(axis_y_title, label_left_x, y_title_y);
dc.SetFont(strong_font);
dc.SetTextForeground(theme.label_strong);
dc.DrawText(pct_text, label_left_x, y_title_y + y_title_sz.y + y_title_pct_gap);
dc.DrawText(pct_text, rc.x + rc.width - pct_text_sz.x, x_axis_y);
dc.SetFont(label_font);
dc.SetTextForeground(theme.label);
dc.DrawText(axis_x_title, x_title_x, x_axis_y - x_title_sz.y / 2);
if (!gc)
return;
// Outline only when the curve colour is perceptually close to the background; otherwise the
// plain filament colour reads fine and the extra stroke would look heavy.
auto needs_outline = [&](const wxColour& c) {
return calc_color_distance(c, theme.background) < kBgSimilarThreshold;
};
// Only the geometry goes through the graphics context: dc.DrawLines() takes integer wxPoint
// and would quantize the curve back to whole pixels. The pen is still set on the dc, which
// forwards it here while keeping its own cached state in sync for later dc drawing.
auto draw_polyline = [&](const MixedGradientCurve& curve) {
if (curve.points.size() < 2)
return;
dc.SetPen(wxPen(curve.colour, dip_px(curve.stroke_dip)));
gc->StrokeLines(curve.points.size(), curve.points.data());
};
for (const MixedGradientCurve& curve : curves) {
if (needs_outline(curve.colour))
draw_polyline({curve.points, theme.outline, curve.stroke_dip + kOutlineExtraDip});
draw_polyline(curve);
}
// Control points: hollow circle with axis-colour border, theme-aware fill, drawn with a
// sub-pixel centre so the ring stays centred on the curve.
if (!anchors.empty()) {
const double r = dip_px(kPointRadius);
dc.SetPen(wxPen(theme.axis, 1));
dc.SetBrush(wxBrush(theme.point_fill));
for (const wxPoint2DDouble& p : anchors)
gc->DrawEllipse(p.m_x - r, p.m_y - r, r * 2, r * 2);
}
memdc.SelectObject(wxNullBitmap);
raw_dc.DrawBitmap(buf, 0, 0);
}
void draw_mixed_ratio_blend_bar(wxDC& dc, const wxRect& rect, const wxColour& first,
const wxColour& second, double second_fraction)
{
if (rect.width <= 0 || rect.height <= 0)
return;
for (int x = 0; x < rect.width; ++x) {
const double t = rect.width > 1 ? double(x) / rect.width : 0.0;
const wxColour c = lerp_blend(first, second, 1.0 - t);
dc.SetPen(wxPen(c));
dc.DrawLine(rect.x + x, rect.y, rect.x + x, rect.y + rect.height);
}
// Fixed in both themes, like the triangle picker's drag handle: the divider is drawn over
// blended filament colour, so it has to keep its contrast against data rather than chrome.
const int div_x = rect.x + static_cast<int>(second_fraction * rect.width);
dc.SetPen(wxPen(wxColour(80, 80, 80), dip_px(4)));
dc.DrawLine(div_x, rect.y, div_x, rect.y + rect.height);
dc.SetPen(wxPen(*wxWHITE, dip_px(2)));
dc.DrawLine(div_x, rect.y, div_x, rect.y + rect.height);
}
void draw_mixed_ratio_segments(wxDC& dc, const wxRect& rect, const std::vector<wxColour>& colours,
const std::vector<double>& shares)
{
const size_t n = std::min(colours.size(), shares.size());
if (n == 0 || rect.width <= 0 || rect.height <= 0)
return;
std::vector<double> norm = shares;
double total = 0.0;
for (double s : norm)
total += s;
if (total <= 0.0) {
norm.assign(n, 1.0 / n);
total = 1.0;
}
auto share_to_px = [&](double share_sum) { return rect.x + int(std::lround(share_sum / total * double(rect.width))); };
int x0 = rect.x;
std::vector<wxRect> segs(n);
for (size_t i = 0; i < n; ++i) {
int x1 = rect.x + rect.width;
if (i + 1 < n)
x1 = share_to_px(std::accumulate(norm.begin(), norm.begin() + i + 1, 0.0));
segs[i] = wxRect(x0, rect.y, std::max(1, x1 - x0), rect.height);
x0 = segs[i].GetRight() + 1;
}
for (size_t i = 0; i < n; ++i) {
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(colours[i]));
dc.DrawRectangle(segs[i]);
}
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#ACACAC")), 1));
dc.DrawRectangle(rect);
}
namespace {
struct TriCacheKey
{
int w, h;
int c0r, c0g, c0b, c1r, c1g, c1b, c2r, c2g, c2b;
int bg_r, bg_g, bg_b, ol_r, ol_g, ol_b;
bool operator<(const TriCacheKey& o) const
{
return std::tie(w, h, c0r, c0g, c0b, c1r, c1g, c1b, c2r, c2g, c2b, bg_r, bg_g, bg_b, ol_r, ol_g, ol_b) <
std::tie(o.w, o.h, o.c0r, o.c0g, o.c0b, o.c1r, o.c1g, o.c1b, o.c2r, o.c2g, o.c2b, o.bg_r, o.bg_g, o.bg_b, o.ol_r, o.ol_g, o.ol_b);
}
};
std::map<TriCacheKey, wxBitmap>& tri_cache()
{
static std::map<TriCacheKey, wxBitmap> cache;
return cache;
}
} // namespace
std::array<TriPoint, 3> mixed_triangle_vertices(const wxSize& size, double margin_dip)
{
const double pw = size.GetWidth(), ph = size.GetHeight();
const double margin = dip_px(int(margin_dip));
const double avail = std::min(pw, ph) - 2.0 * margin;
const double side = avail;
const double tri_h = side * std::sqrt(3.0) / 2.0;
const double cx = pw / 2.0;
const double top_y = (ph - tri_h) / 2.0;
return {{{cx, top_y}, {cx - side / 2.0, top_y + tri_h}, {cx + side / 2.0, top_y + tri_h}}};
}
void draw_mixed_triangle_picker(wxDC& dc, const wxSize& size, const std::array<wxColour, 3>& colours,
const std::array<double, 3>& weights, const MixedTriangleTheme& theme)
{
if (size.GetWidth() <= 0 || size.GetHeight() <= 0)
return;
const std::array<TriPoint, 3> v = mixed_triangle_vertices(size, kTriangleMarginDip);
dc.SetBrush(wxBrush(theme.background));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight());
const wxColour& c0 = colours[0];
const wxColour& c1 = colours[1];
const wxColour& c2 = colours[2];
const TriCacheKey key{size.GetWidth(), size.GetHeight(),
c0.Red(), c0.Green(), c0.Blue(),
c1.Red(), c1.Green(), c1.Blue(),
c2.Red(), c2.Green(), c2.Blue(),
theme.background.Red(), theme.background.Green(), theme.background.Blue(),
theme.outline.Red(), theme.outline.Green(), theme.outline.Blue()};
wxBitmap& bmp = tri_cache()[key];
if (!bmp.IsOk()) {
bmp = wxBitmap(size.GetWidth(), size.GetHeight(), 24);
wxMemoryDC mdc(bmp);
mdc.SetBrush(wxBrush(theme.background));
mdc.SetPen(*wxTRANSPARENT_PEN);
mdc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight());
const int min_y = int(std::min({v[0].y, v[1].y, v[2].y}));
const int max_y = int(std::max({v[0].y, v[1].y, v[2].y}));
const int min_x = int(std::min({v[0].x, v[1].x, v[2].x}));
const int max_x = int(std::max({v[0].x, v[1].x, v[2].x}));
for (int py = min_y; py <= max_y; ++py) {
for (int px = min_x; px <= max_x; ++px) {
const TriPoint p = {double(px), double(py)};
if (!tri_contains(p, v[0], v[1], v[2]))
continue;
double w0, w1, w2;
tri_barycentric(p, v[0], v[1], v[2], w0, w1, w2);
unsigned char mr, mg, mb;
if (w0 + w1 > 1e-6) {
float t01 = float(w1 / (w0 + w1));
Slic3r::filament_mixer_lerp(c0.Red(), c0.Green(), c0.Blue(), c1.Red(), c1.Green(), c1.Blue(), t01, &mr, &mg, &mb);
Slic3r::filament_mixer_lerp(mr, mg, mb, c2.Red(), c2.Green(), c2.Blue(), float(w2), &mr, &mg, &mb);
} else {
mr = c2.Red(); mg = c2.Green(); mb = c2.Blue();
}
mdc.SetPen(wxPen(wxColour(mr, mg, mb)));
mdc.DrawPoint(px, py);
}
}
mdc.SetPen(wxPen(theme.outline, 1));
mdc.SetBrush(*wxTRANSPARENT_BRUSH);
const wxPoint pts[3] = {{int(v[0].x), int(v[0].y)}, {int(v[1].x), int(v[1].y)}, {int(v[2].x), int(v[2].y)}};
mdc.DrawPolygon(3, pts);
mdc.SelectObject(wxNullBitmap);
// Keep the cache from growing without bound across DPI/size changes.
if (tri_cache().size() > 6) {
auto& cache = tri_cache();
cache.erase(cache.begin());
}
}
dc.DrawBitmap(bmp, 0, 0);
// Published-ratio marker (read-only twin of the editor's drag handle).
const double w0 = weights[0], w1 = weights[1], w2 = weights[2];
const int hx = int(w0 * v[0].x + w1 * v[1].x + w2 * v[2].x);
const int hy = int(w0 * v[0].y + w1 * v[1].y + w2 * v[2].y);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(wxPen(theme.ring, dip_px(2)));
dc.DrawCircle(hx, hy, dip_px(5));
}
void draw_mixed_triangle_labels(wxDC& dc, const wxSize& size, const std::array<double, 3>& weights,
const MixedTriangleTheme& theme)
{
const std::array<TriPoint, 3> v = mixed_triangle_vertices(size, kTriangleMarginDip);
dc.SetFont(::Label::Body_12);
dc.SetTextForeground(theme.label);
// "Ratio" title, sitting above the top vertex.
const wxString title = _L("Ratio");
dc.DrawText(title, dip_px(2), std::max(0, int(v[0].y - dc.GetTextExtent(title).GetHeight() - dip_px(4))));
for (int i = 0; i < 3; ++i) {
const wxString text = wxString::Format("%d%%", int(std::lround(weights[i] * 100.0)));
const wxSize tsz = dc.GetTextExtent(text);
int lx = int(v[i].x - tsz.GetWidth() / 2.0);
int ly = (i == 0) ? int(v[i].y - tsz.GetHeight() - dip_px(4)) : int(v[i].y + dip_px(3));
ly = std::clamp(ly, 0, size.GetHeight() - tsz.GetHeight());
lx = std::clamp(lx, 0, size.GetWidth() - tsz.GetWidth());
dc.DrawText(text, lx, ly);
}
}
}} // namespace Slic3r::GUI
+94
View File
@@ -5,6 +5,9 @@
#include <wx/colour.h>
#include <wx/dc.h>
#include <wx/gdicmn.h>
#include <wx/geometry.h>
#include <wx/graphics.h>
#include <array>
#include <vector>
// Orca: forward-declare so the header is self-contained outside libslic3r_gui's
@@ -13,6 +16,16 @@ namespace Slic3r { class DynamicPrintConfig; struct GradientCurve; }
namespace Slic3r { namespace GUI {
// Barycentric utilities for a ternary (triangle) ratio picker, shared by the mixed-filament
// editor and the Publish dialog's read-only definition preview.
struct TriPoint { double x, y; };
double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c);
bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2);
void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
double& w0, double& w1, double& w2);
TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2);
// Fills a rect with a west->east linear gradient by drawing solid 1px columns.
// Use instead of wxDC::GradientFillLinear, whose CoreGraphics (CGShading) backend
// fails to render on some macOS builds; solid fills are unaffected.
@@ -51,6 +64,12 @@ std::vector<wxColour> sample_gradient_ramp(const wxColour& first,
// destination's height in pixels.
std::vector<wxColour> mixed_gradient_ramp(const Slic3r::DynamicPrintConfig& cfg, size_t slot, int steps);
// Resolve the curve a gradient slot is sampled with: the custom curve wins when it has at
// least two points, otherwise a straight line between gradient_range's endpoints, otherwise
// the 0.10 -> 0.90 default. Mirrors the slicer's ToolOrdering fallback so every preview
// agrees with what gets sliced. Always returns a two-point curve.
Slic3r::GradientCurve mixed_gradient_curve(const Slic3r::DynamicPrintConfig& cfg, size_t slot);
// Fill rect with a ramp, ramp.front() along the bottom edge.
void fill_gradient_ramp_rect(wxDC& dc, const wxRect& rect, const std::vector<wxColour>& ramp);
@@ -63,6 +82,81 @@ wxBitmap create_gradient_ramp_bitmap(const std::vector<wxColour>& ramp, const wx
void recompute_mixed_slot_colors(std::vector<wxColour>& colors,
const Slic3r::DynamicPrintConfig& cfg);
// --- Gradient plot (shared by GradientCurveEditor and the Publish dialog's read-only
// preview). The plot is a square 1:1 rect laid out with the editor's ratios so both
// render identically; curves are drawn as sub-pixel anti-aliased polylines through
// wxGCDC so they never quantize to whole pixels.
// One curve of the plot: screen-space sub-pixel points already mapped into the plot
// rect, the stroke colour and the stroke width in DIP.
struct MixedGradientCurve
{
std::vector<wxPoint2DDouble> points;
wxColour colour;
int stroke_dip;
};
// Theme tokens, resolved by the caller through StateColor::darkModeColorFor.
struct MixedGradientTheme
{
wxColour background; // for near-background outline detection
wxColour grid; // grid line
wxColour axis; // axis + arrow fill
wxColour label; // "Material Ratio" / "Model Height"
wxColour label_strong; // "100%"
wxColour outline; // near-background curve lift
wxColour point_fill; // anchor fill
};
// Square 1:1 plot rect inside `canvas`, using the editor's plot ratios.
wxRect mixed_gradient_plot_rect(const wxSize& canvas);
// Draw the whole plot (grid, axes + arrowheads, axis labels, each curve with an optional
// near-background outline, and anchor circles). `anchors` are empty when the caller has
// none to show. `dc` is the caller's buffered paint DC; a wxGCDC is created inside so the
// geometry gets anti-aliased.
void draw_mixed_gradient_plot(wxDC& dc, const wxSize& canvas,
const std::vector<MixedGradientCurve>& curves,
const std::vector<wxPoint2DDouble>& anchors,
const MixedGradientTheme& theme);
// --- Ratio bar (2-component continuous blend + divider, matching MixedFilamentDialog).
// Colours blend first->second across the rect; the divider marks `second_fraction` of the
// rect's width (the second component's share, 0..1).
void draw_mixed_ratio_blend_bar(wxDC& dc, const wxRect& rect, const wxColour& first,
const wxColour& second, double second_fraction);
// Fallback ratio bar for N>2 non-gradient slots: one solid segment per component,
// widths proportional to shares. Label text (the "NN%" inside wide-enough segments) is
// the caller's concern.
void draw_mixed_ratio_segments(wxDC& dc, const wxRect& rect, const std::vector<wxColour>& colours,
const std::vector<double>& shares);
// --- Triangle picker (3-component), shared by MixedFilamentDialog and the Publish preview.
struct MixedTriangleTheme
{
wxColour background;
wxColour outline; // triangle border
wxColour ring; // drag-handle ring
wxColour label; // "Ratio" title + per-vertex labels
};
// The three vertices of the read-only/miniature triangle inside a `size` square panel,
// with `margin_dip` inset. Order: top, bottom-left, bottom-right.
std::array<TriPoint, 3> mixed_triangle_vertices(const wxSize& size, double margin_dip = 20.0);
// Draw background, the cached barycentric fill, the outline and the drag-handle marker.
// `weights` are the three barycentric shares (sum 1). The "Ratio" title and per-vertex
// percentage labels are drawn by the caller so the interactive editor can keep its own
// live child labels while the read-only preview draws them as text.
void draw_mixed_triangle_picker(wxDC& dc, const wxSize& size, const std::array<wxColour, 3>& colours,
const std::array<double, 3>& weights, const MixedTriangleTheme& theme);
// Draw the "Ratio" title plus one "NN%" label per vertex (used by the read-only preview;
// the interactive editor positions its own live labels instead).
void draw_mixed_triangle_labels(wxDC& dc, const wxSize& size, const std::array<double, 3>& weights,
const MixedTriangleTheme& theme);
}} // namespace Slic3r::GUI
#endif // slic3r_GUI_FilamentBitmapUtils_hpp_
+2 -223
View File
@@ -598,7 +598,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
#ifdef WIN32
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
static void register_win32_device_notification_event()
{
@@ -6808,7 +6808,7 @@ bool GUI_App::check_preset_parent_available(const std::pair<std::string, std::ma
void GUI_App::add_pending_vendor_preset(const std::pair<std::string, std::map<std::string, std::string>>& preset_data)
{
Preset::Type type;
Preset::Type type = Preset::Type::TYPE_INVALID;
if (preset_data.second.at(BBL_JSON_KEY_TYPE) == PRESET_IOT_PRINT_TYPE)
type = Preset::Type::TYPE_PRINT;
else if (preset_data.second.at(BBL_JSON_KEY_TYPE) == PRESET_IOT_PRINTER_TYPE)
@@ -7769,21 +7769,6 @@ void GUI_App::stop_http_server()
m_http_server.stop();
}
void GUI_App::switch_staff_pick(bool on)
{
mainframe->m_webview->SendDesignStaffpick(on);
}
bool GUI_App::switch_language()
{
if (select_language()) {
recreate_GUI(_L("Switching application language") + dots);
return true;
} else {
return false;
}
}
#ifdef __linux__
static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguageInfo* language,
const wxLanguageInfo* system_language)
@@ -7878,72 +7863,6 @@ int GUI_App::GetSingleChoiceIndex(const wxString& message,
#endif
}
// select language from the list of installed languages
bool GUI_App::select_language()
{
wxArrayString translations = wxTranslations::Get()->GetAvailableTranslations(SLIC3R_APP_KEY);
std::vector<const wxLanguageInfo*> language_infos;
language_infos.emplace_back(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH));
for (size_t i = 0; i < translations.GetCount(); ++ i) {
const wxLanguageInfo *langinfo = wxLocale::FindLanguageInfo(translations[i]);
if (langinfo != nullptr)
language_infos.emplace_back(langinfo);
}
sort_remove_duplicates(language_infos);
std::sort(language_infos.begin(), language_infos.end(), [](const wxLanguageInfo* l, const wxLanguageInfo* r) { return l->Description < r->Description; });
wxArrayString names;
names.Alloc(language_infos.size());
// Some valid language should be selected since the application start up.
const wxString active_language_code = current_language_code();
const wxLanguageInfo* active_language_info = wxLocale::FindLanguageInfo(active_language_code);
const wxLanguage current_language = active_language_info != nullptr ? wxLanguage(active_language_info->Language) : wxLanguage(m_wxLocale->GetLanguage());
const wxString active_lang_prefix = active_language_code.BeforeFirst('_');
int init_selection = -1;
int init_selection_alt = -1;
int init_selection_default = -1;
for (size_t i = 0; i < language_infos.size(); ++ i) {
if (wxLanguage(language_infos[i]->Language) == current_language)
// The dictionary matches the active language and country.
init_selection = i;
else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == active_lang_prefix) ||
// if the active language is Slovak, mark the Czech language as active.
(language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && active_lang_prefix == "sk"))
// The dictionary matches the active language, it does not necessarily match the country.
init_selection_alt = i;
if (language_infos[i]->CanonicalName.BeforeFirst('_') == "en")
// This will be the default selection if the active language does not match any dictionary.
init_selection_default = i;
names.Add(language_infos[i]->Description);
}
if (init_selection == -1)
// This is the dictionary matching the active language.
init_selection = init_selection_alt;
if (init_selection != -1)
// This is the language to highlight in the choice dialog initially.
init_selection_default = init_selection;
const long index = GetSingleChoiceIndex(_L("Select the language"), _L("Language"), names, init_selection_default);
// Try to load a new language.
if (index != -1 && (init_selection == -1 || init_selection != index)) {
const wxLanguageInfo *new_language_info = language_infos[index];
if (this->load_language(new_language_info->CanonicalName, false)) {
// Save language at application config.
// Which language to save as the selected dictionary language?
// 1) Hopefully the language set to wxTranslations by this->load_language(), but that API is weird and we don't want to rely on its
// stability in the future:
// wxTranslations::Get()->GetBestTranslation(SLIC3R_APP_KEY, wxLANGUAGE_ENGLISH);
// 2) Current locale language may not match the dictionary name, see GH issue #3901
// m_wxLocale->GetCanonicalName()
// 3) new_language_info->CanonicalName is a safe bet. It points to a valid dictionary name.
app_config->set("language", new_language_info->CanonicalName.ToUTF8().data());
return true;
}
}
return false;
}
// Load gettext translation files and activate them at the start of the application,
// based on the "language" key stored in the application config.
@@ -8330,146 +8249,6 @@ void GUI_App::show_ip_address_enter_dialog_handler(wxCommandEvent& evt)
show_modal_ip_address_enter_dialog(mode == -1?false:true, title);
}
//void GUI_App::add_config_menu(wxMenuBar *menu)
//void GUI_App::add_config_menu(wxMenu *menu)
//{
// auto local_menu = new wxMenu();
// wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
//
// const auto config_wizard_name = _(ConfigWizard::name(true));
// const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Open %s"))) % config_wizard_name).str());
// // Cmd+, is standard on OS X - what about other operating systems?
// if (is_editor()) {
// local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
// local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for Configuration Updates"), _L("Check for configuration updates"));
// local_menu->AppendSeparator();
// }
// local_menu->Append(config_id_base + ConfigMenuPreferences, _L("Preferences") + dots +
//#ifdef __APPLE__
// "\tCtrl+,",
//#else
// "\tCtrl+P",
//#endif
// _L("Application preferences"));
// wxMenu* mode_menu = nullptr;
// if (is_editor()) {
// local_menu->AppendSeparator();
// mode_menu = new wxMenu();
// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _L("Simple"), _L("Simple Mode"));
// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _L("Advanced"), _L("Advanced Mode"));
// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple);
// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced);
//
// local_menu->AppendSubMenu(mode_menu, _L("Mode"), wxString::Format(_L("%s Mode"), SLIC3R_APP_NAME));
// }
// local_menu->AppendSeparator();
// local_menu->Append(config_id_base + ConfigMenuLanguage, _L("Language"));
// if (is_editor()) {
// local_menu->AppendSeparator();
// }
//
// local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
// switch (event.GetId() - config_id_base) {
// case ConfigMenuWizard:
// run_wizard(ConfigWizard::RR_USER);
// break;
// case ConfigMenuUpdate:
// check_updates(true);
// break;
//#ifdef __linux__
// case ConfigMenuDesktopIntegration:
// show_desktop_integration_dialog();
// break;
//#endif
// case ConfigMenuSnapshots:
// //BBS do not support task snapshot
// break;
// case ConfigMenuPreferences:
// {
// //BBS GUI refactor: remove unuse layout logic
// //bool app_layout_changed = false;
// {
// // the dialog needs to be destroyed before the call to recreate_GUI()
// // or sometimes the application crashes into wxDialogBase() destructor
// // so we put it into an inner scope
// PreferencesDialog dlg(mainframe);
// dlg.ShowModal();
// //BBS GUI refactor: remove unuse layout logic
// //app_layout_changed = dlg.settings_layout_changed();
// if (dlg.seq_top_layer_only_changed())
// this->plater_->refresh_print();
//
// if (dlg.recreate_GUI()) {
// recreate_GUI(_L("Restart application") + dots);
// return;
// }
//#ifdef _WIN32
// if (is_editor()) {
// if (app_config->get("associate_3mf") == "true")
// associate_3mf_files();
// if (app_config->get("associate_stl") == "true")
// associate_stl_files();
// }
// else {
// if (app_config->get("associate_gcode") == "true")
// associate_gcode_files();
// }
//#endif // _WIN32
// }
// //BBS GUI refactor: remove unuse layout logic
// /*if (app_layout_changed) {
// // hide full main_sizer for mainFrame
// mainframe->GetSizer()->Show(false);
// mainframe->update_layout();
// mainframe->select_tab(size_t(0));
// }*/
// break;
// }
// case ConfigMenuLanguage:
// {
// /* Before change application language, let's check unsaved changes on 3D-Scene
// * and draw user's attention to the application restarting after a language change
// */
// {
// // the dialog needs to be destroyed before the call to switch_language()
// // or sometimes the application crashes into wxDialogBase() destructor
// // so we put it into an inner scope
// wxString title = is_editor() ? wxString(SLIC3R_APP_NAME) : wxString(GCODEVIEWER_APP_NAME);
// title += " - " + _L("Choose language");
// //wxMessageDialog dialog(nullptr,
// MessageDialog dialog(nullptr,
// _L("Switching the language requires application restart.\n") + "\n\n" +
// _L("Do you want to continue?"),
// title,
// wxICON_QUESTION | wxOK | wxCANCEL);
// if (dialog.ShowModal() == wxID_CANCEL)
// return;
// }
//
// switch_language();
// break;
// }
// case ConfigMenuFlashFirmware:
// //BBS FirmwareDialog::run(mainframe);
// break;
// default:
// break;
// }
// });
//
// using std::placeholders::_1;
//
// if (mode_menu != nullptr) {
// auto modfn = [this](int mode, wxCommandEvent&) { if (get_mode() != mode) save_mode(mode); };
// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comSimple, _1), config_id_base + ConfigMenuModeSimple);
// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comAdvanced, _1), config_id_base + ConfigMenuModeAdvanced);
// }
//
// // BBS
// //menu->Append(local_menu, _L("Configuration"));
// menu->AppendSubMenu(local_menu, _L("Configuration"));
//}
void GUI_App::open_presetbundledialog(size_t open_on_tab, const std::string& highlight_option)
{
bool app_layout_changed = false;
-3
View File
@@ -569,7 +569,6 @@ public:
void start_http_server(const std::string& provider = ORCA_CLOUD_PROVIDER);
void start_http_server(int port, const std::string& provider = ORCA_CLOUD_PROVIDER);
void stop_http_server();
void switch_staff_pick(bool on);
void on_show_check_privacy_dlg(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
void show_check_privacy_dlg(wxCommandEvent& evt);
@@ -583,7 +582,6 @@ public:
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
void update_ui_from_settings();
bool switch_language();
bool load_language(wxString language, bool initial);
Tab* get_tab(Preset::Type type);
@@ -801,7 +799,6 @@ private:
bool window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
void window_pos_sanitize(wxTopLevelWindow* window);
void window_pos_center(wxTopLevelWindow *window);
bool select_language();
// Dynamic printer agent selection - internal helpers for switch_printer_agent
// and the plugin load/unload callbacks (init_plugin_gui_wiring).
+5 -4
View File
@@ -69,6 +69,8 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std
HANDLE handlesrc = nullptr;
HANDLE handledst = nullptr;
CopyFileResult ret = SUCCESS;
DWORD size = 0;
DWORD dwRead = 0, dwWrite = 0;
handlesrc = CreateFile(src.wc_str(),
GENERIC_READ,
@@ -96,13 +98,12 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std
goto __finished;
}
DWORD size=GetFileSize(handlesrc,NULL);
size = GetFileSize(handlesrc,NULL);
buff = new char[size+1];
DWORD dwRead=0,dwWrite;
result = ReadFile(handlesrc, buff, size, &dwRead, NULL);
if (!result) {
DWORD errCode = GetLastError();
error_message = "Error: " + errCode;
error_message = "Error: " + std::to_string(errCode);
ret = FAIL_COPY_FILE;
goto __finished;
}
@@ -110,7 +111,7 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std
result = WriteFile(handledst,buff,size,&dwWrite,NULL);
if (!result) {
DWORD errCode = GetLastError();
error_message = "Error: " + errCode;
error_message = "Error: " + std::to_string(errCode);
ret = FAIL_COPY_FILE;
goto __finished;
}
+1 -1
View File
@@ -342,7 +342,7 @@ bool GLGizmoBrimEars::on_mouse(const wxMouseEvent& mouse_event)
// concludes that the event was not intended for it, it should return false.
bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (action != SLAGizmoEventType::MouseWheelDown || action != SLAGizmoEventType::MouseWheelUp || action != SLAGizmoEventType::Moving) {
if (action != SLAGizmoEventType::MouseWheelDown && action != SLAGizmoEventType::MouseWheelUp && action != SLAGizmoEventType::Moving) {
apply_radius_change();
}
+1 -1
View File
@@ -122,7 +122,7 @@ bool GLGizmoFdmSupports::on_init()
{ctrl + _L("Mouse wheel"), _L("Gap area")}
};
memset(&m_print_instance, 0, sizeof(m_print_instance));
m_print_instance = PrintInstance();
return true;
}
@@ -702,7 +702,7 @@ bool GizmoObjectManipulation::reset_zero_button(ImGuiWrapper *imgui_wrapper, bo
for (int i = 0; i < number; i++)
{
char buf[3][64] = {0};
char buf[3][64] = {};
float buf_size[3] = {0};
for (int j = 0; j < 3; j++) {
ImGui::DataTypeFormatString(buf[j], IM_ARRAYSIZE(buf[j]), ImGuiDataType_Double, (void *) &vec[i][j], "%.2f");
+45 -186
View File
@@ -1,4 +1,5 @@
#include "GradientCurveEditor.hpp"
#include "FilamentBitmapUtils.hpp"
#include "GUI_App.hpp"
#include "GuiColor.hpp"
#include "I18N.hpp"
@@ -19,23 +20,13 @@ namespace GUI {
wxDEFINE_EVENT(wxEVT_GRADIENT_CURVE_CHANGED, wxCommandEvent);
namespace {
// Layout ratios of the plot rect within the widget, taken from a 214 x 180 px reference drawing.
// Plot rect occupies the upper-left region; right + bottom margins host axis arrows / labels.
constexpr double kPlotLeftRatio = 0.0316;
constexpr double kPlotRightRatio = 0.6766;
constexpr double kPlotTopRatio = 0.1529;
constexpr double kPlotBottomRatio = 0.8474;
constexpr int kGridDivisions = 9; // 10 grid lines including the outer borders.
// Hit / stroke (DIP).
// Hit / stroke (DIP). The plot-rect ratios, grid divisions, axis/arrow geometry and the
// near-background outline threshold now live in FilamentBitmapUtils so the read-only Publish
// preview and this editor stay pixel-identical.
constexpr int kHitRadius = 6;
constexpr int kCurveHitRadius = 5;
constexpr int kPointRadius = 4; // anchor outer radius (DIP)
constexpr int kStrokeUnselected = 2;
constexpr int kStrokeSelected = 4;
constexpr int kStrokeAxis = 2; // axis line width (px, no DPI scaling - matches kGridColor pen and 2DBed convention)
constexpr int kAxisArrowHalf = 5; // half-base of the axis arrow triangle (DIP)
constexpr int kAxisArrowLen = 10; // length of the axis arrow triangle (DIP)
// Light-mode design tokens. Resolved through StateColor::darkModeColorFor()
// at paint time so the editor follows the app theme (#EEEEEE -> #4C4C55, #6B6B6B ->
@@ -46,12 +37,6 @@ const wxColour kAxisColor (107, 107, 107); // #6B6B6B grey 700
const wxColour kLabelMuted (107, 107, 107); // #6B6B6B grey 700
const wxColour kLabelStrong ( 38, 46, 48); // #262E30 grey 900
const wxColour kOutlineColor(172, 172, 172); // #ACACAC dimmed elements
// LAB (DeltaE76) threshold for "curve color is too close to the background": below it the curve
// gets a subtle outline so it does not visually vanish, otherwise it is drawn plain. Looser than
// the 5.0 of FlushPredict::is_similar_color, so a pastel pink on white still gets an outline.
constexpr float kBgSimilarThreshold = 15.0f;
constexpr int kOutlineExtraDip = 2;
} // namespace
GradientCurveEditor::GradientCurveEditor(wxWindow* parent,
@@ -177,15 +162,8 @@ void GradientCurveEditor::emit_changed()
wxRect GradientCurveEditor::plot_rect() const
{
const wxSize sz = GetClientSize();
const int x = static_cast<int>(std::lround(sz.x * kPlotLeftRatio));
const int y = static_cast<int>(std::lround(sz.y * kPlotTopRatio));
const int x2 = static_cast<int>(std::lround(sz.x * kPlotRightRatio));
const int y2 = static_cast<int>(std::lround(sz.y * kPlotBottomRatio));
// Force square 1:1 so X/Y axes share the same scale and grid cells stay square. Anchor at
// the top-left so the "100%" labels on the bottom/right still align with the plot edges.
const int side = std::max(1, std::min(x2 - x, y2 - y));
return wxRect(x, y, side, side);
// Square 1:1 plot, shared with the Publish dialog's read-only preview.
return mixed_gradient_plot_rect(GetClientSize());
}
wxPoint2DDouble GradientCurveEditor::data_to_px_f(double x, double y) const
@@ -330,171 +308,52 @@ void GradientCurveEditor::on_paint(wxPaintEvent& /*evt*/)
raw_dc.SetBackground(wxBrush(bg));
raw_dc.Clear();
// Render through wxGCDC so curves, arrows and anchor circles get anti-aliased; the buffered
// DC is the actual back buffer that gets blitted to the window.
wxGCDC dc(raw_dc);
// The curve and its anchors are drawn straight on the graphics context so their
// coordinates stay sub-pixel accurate (see data_to_px_f).
wxGraphicsContext* gc = dc.GetGraphicsContext();
const wxRect rc = plot_rect();
if (rc.width <= 0 || rc.height <= 0)
return;
// 10x10 light grid (10 lines including outer borders, 9 equal divisions).
dc.SetPen(wxPen(grid_color, 1));
for (int i = 0; i <= kGridDivisions; ++i) {
const int x = rc.x + rc.width * i / kGridDivisions;
const int y = rc.y + rc.height * i / kGridDivisions;
dc.DrawLine(x, rc.y, x, rc.y + rc.height);
dc.DrawLine(rc.x, y, rc.x + rc.width, y);
}
// Set the label font first so text width measurements drive arrow / label placement.
wxFont label_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
label_font.SetPointSize(std::max(7, label_font.GetPointSize() - 1));
dc.SetFont(label_font);
const wxString axis_y_title = _L("Material Ratio");
const wxString axis_x_title = _L("Model Height");
const wxString pct_text = wxT("100%");
const wxSize x_title_sz = dc.GetTextExtent(axis_x_title);
const wxSize y_title_sz = dc.GetTextExtent(axis_y_title);
wxFont strong_font = label_font;
strong_font.SetWeight(wxFONTWEIGHT_SEMIBOLD);
dc.SetFont(strong_font);
const wxSize pct_text_sz = dc.GetTextExtent(pct_text);
dc.SetFont(label_font);
// Axes (grey 700) with filled triangle arrows. Y-axis extends above the plot top to the
// canvas top edge; X-axis extends past the plot right toward the canvas right edge.
const int arrow_half = FromDIP(kAxisArrowHalf);
const int arrow_len = FromDIP(kAxisArrowLen);
const wxSize sz = GetClientSize();
dc.SetPen(wxPen(axis_color, kStrokeAxis));
dc.SetBrush(wxBrush(axis_color));
// Y-axis: vertical line at plot_left, from arrow tip near canvas top down to plot bottom.
const int y_axis_x = rc.x;
const int y_title_pct_gap = FromDIP(1);
const int y_title_bottom_pad = FromDIP(2);
const int y_title_y = std::max(0, rc.y - y_title_sz.y - y_title_pct_gap - pct_text_sz.y - y_title_bottom_pad);
const int y_arrow_tip_y = y_title_y;
const int y_arrow_ty = y_arrow_tip_y + arrow_len;
dc.DrawLine(y_axis_x, y_arrow_ty, y_axis_x, rc.y + rc.height);
{
wxPoint tri[3] = {
wxPoint(y_axis_x, y_arrow_tip_y),
wxPoint(y_axis_x - arrow_half, y_arrow_ty),
wxPoint(y_axis_x + arrow_half, y_arrow_ty),
// Render the plot (grid, axes, labels, curves, anchors) through the shared painter so the
// interactive editor and the Publish dialog's read-only preview stay pixel-identical. The
// curves are handed over as sub-pixel polylines and anti-alias inside the helper.
std::vector<MixedGradientCurve> curves;
std::vector<wxPoint2DDouble> anchors;
if (m_points.size() >= 2) {
auto color_for_curve = [&](int curve_idx) -> wxColour {
wxColour c = (curve_idx == 0) ? m_color_low : m_color_high;
// Transparent filaments (alpha == 0, e.g. #FFFFFF00) would be invisible.
if (c.Alpha() == 0)
c.Set(c.Red(), c.Green(), c.Blue(), 150);
return c;
};
dc.DrawPolygon(3, tri);
}
// X-axis arrow tip: stays just past the plot ideally, but is clamped so the trailing
// "Material Ratio" label still fits inside the canvas without overlapping the arrow.
const int x_axis_y = rc.y + rc.height;
const int x_label_gap = FromDIP(4);
const int x_edge_pad = FromDIP(6);
const int x_arrow_ideal = rc.x + rc.width + FromDIP(10);
const int x_arrow_max = sz.x - x_title_sz.x - x_label_gap - x_edge_pad - arrow_len;
const int x_arrow_tx = std::max(rc.x + rc.width + arrow_len,
std::min(x_arrow_ideal, x_arrow_max));
const int x_arrow_tip_x = x_arrow_tx + arrow_len;
const int x_title_x = x_arrow_tip_x + x_label_gap;
dc.DrawLine(rc.x, x_axis_y, x_arrow_tx, x_axis_y);
{
wxPoint tri[3] = {
wxPoint(x_arrow_tip_x, x_axis_y),
wxPoint(x_arrow_tx, x_axis_y - arrow_half),
wxPoint(x_arrow_tx, x_axis_y + arrow_half),
auto build_polyline = [&](int curve_idx) -> std::vector<wxPoint2DDouble> {
const int samples = std::max(128, plot_rect().width * 2);
std::vector<wxPoint2DDouble> poly;
poly.reserve(samples + 1);
for (int s = 0; s <= samples; ++s) {
const double x = double(s) / samples;
const double y0 = sample_curve_y(x);
const double vy = to_visual_y(curve_idx, y0);
poly.push_back(data_to_px_f(x, vy));
}
return poly;
};
dc.DrawPolygon(3, tri);
}
// Labels.
// "Model Height" and "100%" share the same left x; the gap is larger than the
// axis-arrow half-base so the text never visually touches the Y-axis arrow.
const int label_left_x = y_axis_x + FromDIP(10);
dc.SetTextForeground(label_muted);
dc.DrawText(axis_y_title, label_left_x, y_title_y);
dc.SetFont(strong_font);
dc.SetTextForeground(label_strong);
dc.DrawText(pct_text, label_left_x, y_title_y + y_title_sz.y + y_title_pct_gap);
// Bottom-right "100%" sits under the right end of the plot; "Material Ratio" follows the
// X-axis arrow tip (placement was already clamped above to leave room).
dc.DrawText(pct_text, rc.x + rc.width - pct_text_sz.x, x_axis_y);
dc.SetFont(label_font);
dc.SetTextForeground(label_muted);
dc.DrawText(axis_x_title, x_title_x, x_axis_y - x_title_sz.y / 2);
if (m_points.size() < 2 || !gc)
return;
auto color_for_curve = [&](int curve_idx) -> wxColour {
wxColour c = (curve_idx == 0) ? m_color_low : m_color_high;
// Transparent filaments (alpha == 0, e.g. #FFFFFF00) would be invisible.
// Lift alpha so the curve stays visible while still hinting at transparency.
if (c.Alpha() == 0)
c.Set(c.Red(), c.Green(), c.Blue(), 150);
return c;
};
auto build_polyline = [&](int curve_idx) -> std::vector<wxPoint2DDouble> {
const int samples = std::max(128, rc.width * 2);
std::vector<wxPoint2DDouble> poly;
poly.reserve(samples + 1);
for (int s = 0; s <= samples; ++s) {
const double x = double(s) / samples;
const double y0 = sample_curve_y(x);
const double vy = to_visual_y(curve_idx, y0);
poly.push_back(data_to_px_f(x, vy));
// Draw unselected first so the selected curve sits on top.
const int other = 1 - m_selected_curve;
for (const int idx : {other, m_selected_curve}) {
std::vector<wxPoint2DDouble> pts = build_polyline(idx);
if (pts.empty())
continue;
curves.push_back({std::move(pts), color_for_curve(idx), idx == m_selected_curve ? kStrokeSelected : kStrokeUnselected});
}
return poly;
};
// Only the geometry goes through the graphics context: dc.DrawLines() takes integer wxPoint
// and would quantize the curve back to whole pixels. The pen is still set on the dc, which
// forwards it here while keeping its own cached state in sync for later dc drawing.
auto draw_polyline = [&](const std::vector<wxPoint2DDouble>& poly, const wxColour& col, int stroke_dip) {
dc.SetPen(wxPen(col, FromDIP(stroke_dip)));
gc->StrokeLines(poly.size(), poly.data());
};
// Outline only when the curve color is perceptually close to the background; otherwise
// the plain filament color reads fine and the extra stroke would look heavy.
auto needs_outline = [&](const wxColour& c) {
return calc_color_distance(c, bg) < kBgSimilarThreshold;
};
auto draw_one = [&](int curve_idx, int stroke_dip) {
const auto poly = build_polyline(curve_idx);
const wxColour col = color_for_curve(curve_idx);
if (needs_outline(col))
draw_polyline(poly, outline_color, stroke_dip + kOutlineExtraDip);
draw_polyline(poly, col, stroke_dip);
};
// Draw unselected first so the selected curve sits on top.
const int other = 1 - m_selected_curve;
draw_one(other, kStrokeUnselected);
draw_one(m_selected_curve, kStrokeSelected);
// Control points (selected curve only): hollow circle with axis-color border, theme-aware fill.
// Drawn on the graphics context with a sub-pixel center so the ring stays centered on the
// curve instead of drifting up to half a pixel off it; pen and brush go through the dc for
// the same reason as in draw_polyline above.
const double r = FromDIP(kPointRadius);
dc.SetPen(wxPen(axis_color, 1));
dc.SetBrush(wxBrush(point_fill));
for (size_t i = 0; i < m_points.size(); ++i) {
const double vy = to_visual_y(m_selected_curve, m_points[i].y);
const wxPoint2DDouble p = data_to_px_f(m_points[i].x, vy);
gc->DrawEllipse(p.m_x - r, p.m_y - r, r * 2, r * 2);
// Control points (selected curve only).
anchors.reserve(m_points.size());
for (size_t i = 0; i < m_points.size(); ++i) {
const double vy = to_visual_y(m_selected_curve, m_points[i].y);
anchors.push_back(data_to_px_f(m_points[i].x, vy));
}
}
const MixedGradientTheme theme{bg, grid_color, axis_color, label_muted, label_strong, outline_color, point_fill};
draw_mixed_gradient_plot(raw_dc, GetClientSize(), curves, anchors, theme);
}
void GradientCurveEditor::on_left_down(wxMouseEvent& evt)
+1 -1
View File
@@ -790,7 +790,7 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) {
void IMSlider::show_tooltip(const std::string tooltip) {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 6 * m_scale, 3 * m_scale });
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, { 3 * m_scale });
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * m_scale);
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
ImGui::PushStyleColor(ImGuiCol_Border, { 0,0,0,0 });
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
+1
View File
@@ -174,6 +174,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ ctrl + "O", L("Open Project") },
{ ctrl + "S", L("Save Project") },
{ ctrl + shift + "S", L("Save Project as")},
{ ctrl + shift + "E", L("Publish 3MF") },
// File>Import
{ ctrl + "I", L("Import geometry data from STL/STEP/3MF/OBJ/AMF files") },
// File>Export
+50 -97
View File
@@ -39,7 +39,6 @@
#include "Plater.hpp"
#include "WebViewDialog.hpp"
#include "../Utils/Process.hpp"
#include "format.hpp"
// BBS
#include "PartPlate.hpp"
#include "Preferences.hpp"
@@ -49,11 +48,9 @@
#include "../Utils/NetworkAgentFactory.hpp"
#include "../Utils/PrintHost.hpp"
#include <fstream>
#include <string_view>
#include "GUI_App.hpp"
#include "UnsavedChangesDialog.hpp"
#include "PublishSettingsDialog.hpp"
#include "MsgDialog.hpp"
#include "Notebook.hpp"
#include "GUI_Factories.hpp"
@@ -743,6 +740,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
if (m_plater) { m_plater->add_file(); }
return;
}
if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'E') {
if (can_export_model()) publish_project();
return;
}
evt.Skip();
});
@@ -1590,7 +1591,7 @@ void MainFrame::register_win32_callbacks()
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
@@ -1630,7 +1631,7 @@ void MainFrame::register_win32_callbacks()
{
static constexpr int device_count = 1;
RAWINPUTDEVICE devices[device_count] = { 0 };
RAWINPUTDEVICE devices[device_count] = {};
// multi-axis mouse (SpaceNavigator, etc.)
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x08;
@@ -1739,6 +1740,22 @@ bool MainFrame::save_project_as(const wxString& filename)
return ret;
}
void MainFrame::publish_project()
{
if (m_plater == nullptr)
return;
// Seed the dialog from the session selection (a remembered state or a freshly loaded
// published 3MF); a null pointer means "fresh", keeping the dirty defaults.
std::vector<std::string> pending_keys;
std::vector<Slic3r::PublishedMaterialEntry> pending_material;
const bool has_prior = m_plater->get_pending_published(pending_keys, pending_material);
PublishSettingsDialog dlg(this, has_prior ? &pending_keys : nullptr, has_prior ? &pending_material : nullptr);
if (dlg.ShowModal() != wxID_OK)
return;
m_plater->set_pending_published(dlg.GetPublishedKeys(), dlg.GetPublishedMaterialKeys());
m_plater->export_published_3mf(dlg.GetPublishedKeys(), dlg.GetPublishedMaterialKeys());
}
bool MainFrame::can_upload() const
{
return true;
@@ -2834,6 +2851,20 @@ void MainFrame::init_menubar_as_editor()
[this](){return m_plater != nullptr && can_save_as(); }, this);
#endif
// BBS: publish
fileMenu->AppendSeparator();
auto publish_handler = [this](wxCommandEvent&) { publish_project(); };
#ifndef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Publish 3MF") + dots + "\t" + ctrl + shift + "E", _L("Export a 3MF file with the selected settings embedded"),
publish_handler, "menu_publish", nullptr,
[this](){return can_export_model(); }, this);
#else
append_menu_item(fileMenu, wxID_ANY, _L("Publish 3MF") + dots + "\t" + ctrl + shift + "E", _L("Export a 3MF file with the selected settings embedded"),
publish_handler, "", nullptr,
[this](){return can_export_model(); }, this);
#endif
fileMenu->AppendSeparator();
@@ -3244,98 +3275,9 @@ void MainFrame::init_menubar_as_editor()
auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\t" + ctrl + "P", "");
#endif
//auto printer_item = new wxMenuItem(parent_menu, ConfigMenuPrinter + config_id_base, _L("Printer"), "");
//auto language_item = new wxMenuItem(parent_menu, ConfigMenuLanguage + config_id_base, _L("Switch Language"), "");
// parent_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) {
// switch (event.GetId() - config_id_base) {
// //case ConfigMenuLanguage:
// //{
// // /* Before change application language, let's check unsaved changes on 3D-Scene
// // * and draw user's attention to the application restarting after a language change
// // */
// // {
// // // the dialog needs to be destroyed before the call to switch_language()
// // // or sometimes the application crashes into wxDialogBase() destructor
// // // so we put it into an inner scope
// // wxString title = _L("Language selection");
// // wxMessageDialog dialog(nullptr,
// // _L("Switching the language requires application restart.\n") + "\n\n" +
// // _L("Do you want to continue?"),
// // title,
// // wxICON_QUESTION | wxOK | wxCANCEL);
// // if (dialog.ShowModal() == wxID_CANCEL)
// // return;
// // }
//
// // wxGetApp().switch_language();
// // break;
// //}
// //case ConfigMenuWizard:
// //{
// // wxGetApp().run_wizard(ConfigWizard::RR_USER);
// // break;
// //}
// case ConfigMenuPrinter:
// {
// wxGetApp().params_dialog()->Popup();
// wxGetApp().get_tab(Preset::TYPE_PRINTER)->restore_last_select_item();
// break;
// }
// case ConfigMenuPreferences:
// {
// CallAfter([this] {
// PreferencesDialog dlg(this);
// dlg.ShowModal();
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
//#else
// if (dlg.seq_top_layer_only_changed())
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// plater()->refresh_print();
//#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
//#ifdef _WIN32
// /*
// if (wxGetApp().app_config()->get("associate_3mf") == "true")
// wxGetApp().associate_3mf_files();
// if (wxGetApp().app_config()->get("associate_stl") == "true")
// wxGetApp().associate_stl_files();
// /*if (wxGetApp().app_config()->get("associate_step") == "true")
// wxGetApp().associate_step_files();*/
//#endif // _WIN32
//#endif
// });
// break;
// }
// default:
// break;
// }
// });
#ifdef __APPLE__
wxString about_title = wxString::Format(_L("&About %s"), SLIC3R_APP_FULL_NAME);
//auto about_item = new wxMenuItem(parent_menu, OrcaSlicerMenuAbout + bambu_studio_id_base, about_title, "");
//parent_menu->Bind(wxEVT_MENU, [this, bambu_studio_id_base](wxEvent& event) {
// switch (event.GetId() - bambu_studio_id_base) {
// case OrcaSlicerMenuAbout:
// Slic3r::GUI::about();
// break;
// case OrcaSlicerMenuPreferences:
// CallAfter([this] {
// PreferencesDialog dlg(this);
// dlg.ShowModal();
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
//#else
// if (dlg.seq_top_layer_only_changed())
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// plater()->refresh_print();
// });
// break;
// default:
// break;
// }
//});
//parent_menu->Insert(0, about_item);
append_menu_item(
parent_menu, wxID_ANY, _L(about_title), "",
[](wxCommandEvent &) { Slic3r::GUI::about();},
@@ -4212,15 +4154,23 @@ std::wstring MainFrame::FileHistory::GetThumbnailUrl(int index) const
return wss.str();
}
bool MainFrame::FileHistory::GetPublished(int index) const
{
return index >= 0 && index < static_cast<int>(m_published_files.size()) && m_published_files[index];
}
void MainFrame::FileHistory::AddFileToHistory(const wxString &file)
{
if (this->m_fileMaxFiles == 0)
return;
wxFileHistory::AddFileToHistory(file);
if (m_load_called)
if (m_load_called) {
m_thumbnails.push_front(bbs_3mf_get_thumbnail(into_u8(file).c_str()));
else
m_published_files.push_front(bbs_3mf_is_published(into_u8(file)));
} else {
m_thumbnails.push_front("");
m_published_files.push_front(false);
}
}
void MainFrame::FileHistory::RemoveFileFromHistory(size_t i)
@@ -4229,6 +4179,7 @@ void MainFrame::FileHistory::RemoveFileFromHistory(size_t i)
return;
wxFileHistory::RemoveFileFromHistory(i);
m_thumbnails.erase(m_thumbnails.begin() + i);
m_published_files.erase(m_published_files.begin() + i);
}
size_t MainFrame::FileHistory::FindFileInHistory(const wxString & file)
@@ -4244,6 +4195,7 @@ void MainFrame::FileHistory::LoadThumbnails()
if (!thumbnail.empty()) {
m_thumbnails[i] = thumbnail;
}
m_published_files[i] = bbs_3mf_is_published(into_u8(GetHistoryFile(i)));
}
});
m_load_called = true;
@@ -4264,6 +4216,7 @@ void MainFrame::get_recent_projects(boost::property_tree::wptree &tree, int imag
std::wstring proj = m_recent_projects.GetHistoryFile(i).ToStdWstring();
item.put(L"project_name", proj.substr(proj.find_last_of(L"/\\") + 1));
item.put(L"path", proj);
item.put(L"published", m_recent_projects.GetPublished(i) ? L"1" : L"0");
boost::system::error_code ec;
std::time_t t = boost::filesystem::last_write_time(proj, ec);
if (!ec) {
+4
View File
@@ -178,6 +178,7 @@ class MainFrame : public DPIFrame
{
FileHistory(int max) : wxFileHistory(max) {}
std::wstring GetThumbnailUrl(int index) const;
bool GetPublished(int index) const;
virtual void AddFileToHistory(const wxString &file);
virtual void RemoveFileFromHistory(size_t i);
@@ -188,6 +189,7 @@ class MainFrame : public DPIFrame
void SetMaxFiles(int max);
private:
std::deque<std::string> m_thumbnails;
std::deque<bool> m_published_files; // parallel to m_thumbnails: is it a published 3mf?
bool m_load_called = false;
};
@@ -341,6 +343,8 @@ public:
bool can_upload() const;
void save_project();
bool save_project_as(const wxString& filename = wxString());
// Open the Publish dialog and export the selected settings as a published 3MF.
void publish_project();
void add_to_recent_projects(const wxString& filename);
void get_recent_projects(boost::property_tree::wptree &tree, int images);
+1 -1
View File
@@ -71,7 +71,7 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl3 *media_ctrl, const w
auto ip = str.find(' ', ik);
if (ip == wxString::npos) ip = str.Length();
auto v = str.Mid(ik, ip - ik);
if (k == "T:" && v.Length() == 8) {
if (strcmp(k, "T:") == 0 && v.Length() == 8) {
long h = 0,m = 0,s = 0;
v.Left(2).ToLong(&h);
v.Mid(3, 2).ToLong(&m);
+2 -2
View File
@@ -297,7 +297,7 @@ void MeshClipper::recalculate_triangles()
// it so it lies on our line. This will be the figure to subtract
// from the cut. The coordinates must not overflow after the transform,
// make the rectangle a bit smaller.
const coord_t size = (std::numeric_limits<coord_t>::max()/2 - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4;
const coord_t size = (double(std::numeric_limits<coord_t>::max()/2) - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4;
Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})};
ep.front().rotate(angle);
ep.front().translate(scale_(-e * a), scale_(-e * b));
@@ -352,7 +352,7 @@ void MeshClipper::recalculate_triangles()
// To prevent overflow after scaling, downscale the input if needed:
double extra_scale = 1.;
coord_t limit = coord_t(std::min(std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_x)), std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_y))));
coord_t limit = coord_t(std::min(double(std::numeric_limits<coord_t>::max()) / (2. * std::max(1., scale_x)), double(std::numeric_limits<coord_t>::max()) / (2. * std::max(1., scale_y))));
coord_t max_coord = 0;
for (const Point& pt : exp.contour)
max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y())));
+13 -129
View File
@@ -845,23 +845,8 @@ wxBoxSizer* MixedFilamentDialog::create_ratio_slider()
m_ratio_bar->Bind(wxEVT_PAINT, [this](wxPaintEvent&) {
wxBufferedPaintDC dc(m_ratio_bar);
wxSize sz = m_ratio_bar->GetClientSize();
wxColour col_a = comp_colour(0), col_b = comp_colour(1);
for (int x = 0; x < sz.GetWidth(); ++x) {
double t = (double)x / sz.GetWidth();
wxColour c = blend_colors(col_a, col_b, 1.0 - t);
dc.SetPen(wxPen(c));
dc.DrawLine(x, 0, x, sz.GetHeight());
}
int div_x = (int)(ratio(1) / 100.0 * sz.GetWidth());
// Fixed in both themes, like the triangle picker's drag handle: the divider is drawn over
// blended filament colour, so it has to keep its contrast against data rather than chrome.
dc.SetPen(wxPen(wxColour(80, 80, 80), FromDIP(4)));
dc.DrawLine(div_x, 0, div_x, sz.GetHeight());
dc.SetPen(wxPen(*wxWHITE, FromDIP(2)));
dc.DrawLine(div_x, 0, div_x, sz.GetHeight());
draw_mixed_ratio_blend_bar(dc, wxRect(0, 0, sz.GetWidth(), sz.GetHeight()),
comp_colour(0), comp_colour(1), ratio(1) / 100.0);
});
m_ratio_bar->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) {
@@ -917,52 +902,8 @@ wxBoxSizer* MixedFilamentDialog::create_ratio_slider()
}
// ---- Triangle (ternary) ratio picker ----
// Barycentric coordinate utilities
struct TriPoint { double x, y; };
static double tri_signed_area2(TriPoint a, TriPoint b, TriPoint c)
{
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
static bool tri_contains(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
{
double total = tri_signed_area2(v0, v1, v2);
if (std::abs(total) < 1e-9) return false;
double s0 = tri_signed_area2(p, v1, v2) / total;
double s1 = tri_signed_area2(v0, p, v2) / total;
double s2 = 1.0 - s0 - s1;
return s0 >= -0.001 && s1 >= -0.001 && s2 >= -0.001;
}
static void tri_barycentric(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2,
double& w0, double& w1, double& w2)
{
double total = std::abs(tri_signed_area2(v0, v1, v2));
if (total < 1e-9) { w0 = w1 = w2 = 1.0 / 3.0; return; }
w0 = std::abs(tri_signed_area2(p, v1, v2)) / total;
w1 = std::abs(tri_signed_area2(v0, p, v2)) / total;
w2 = 1.0 - w0 - w1;
w0 = std::clamp(w0, 0.0, 1.0);
w1 = std::clamp(w1, 0.0, 1.0);
w2 = std::clamp(w2, 0.0, 1.0);
double s = w0 + w1 + w2;
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
}
static TriPoint tri_clamp(TriPoint p, TriPoint v0, TriPoint v1, TriPoint v2)
{
double w0, w1, w2;
tri_barycentric(p, v0, v1, v2, w0, w1, w2);
w0 = std::clamp(w0, 0.0, 1.0);
w1 = std::clamp(w1, 0.0, 1.0);
w2 = std::clamp(w2, 0.0, 1.0);
double s = w0 + w1 + w2;
if (s > 0) { w0 /= s; w1 /= s; w2 /= s; }
return {w0 * v0.x + w1 * v1.x + w2 * v2.x,
w0 * v0.y + w1 * v1.y + w2 * v2.y};
}
// The barycentric utilities (TriPoint, tri_contains, tri_barycentric, tri_clamp) live in
// FilamentBitmapUtils so the Publish dialog can mirror this picker read-only.
wxBoxSizer* MixedFilamentDialog::create_triangle_picker()
{
@@ -996,72 +937,15 @@ wxBoxSizer* MixedFilamentDialog::create_triangle_picker()
wxSize sz = m_triangle_panel->GetClientSize();
auto [v0, v1, v2] = get_vertices();
wxColour tri_bg = StateColor::darkModeColorFor(*wxWHITE);
dc.SetBrush(wxBrush(tri_bg));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(0, 0, sz.GetWidth(), sz.GetHeight());
wxColour c0 = comp_colour(0), c1 = comp_colour(1), c2 = comp_colour(2);
const bool cache_valid = m_tri_cache_bmp.IsOk() &&
m_tri_cache_size == sz &&
m_tri_cache_c0 == c0 && m_tri_cache_c1 == c1 && m_tri_cache_c2 == c2;
if (!cache_valid) {
int min_y = (int)std::min({v0.y, v1.y, v2.y});
int max_y = (int)std::max({v0.y, v1.y, v2.y});
int min_x = (int)std::min({v0.x, v1.x, v2.x});
int max_x = (int)std::max({v0.x, v1.x, v2.x});
m_tri_cache_bmp = wxBitmap(sz.GetWidth(), sz.GetHeight(), 24);
wxMemoryDC mdc(m_tri_cache_bmp);
mdc.SetBrush(wxBrush(tri_bg));
mdc.SetPen(*wxTRANSPARENT_PEN);
mdc.DrawRectangle(0, 0, sz.GetWidth(), sz.GetHeight());
for (int py = min_y; py <= max_y; ++py) {
for (int px = min_x; px <= max_x; ++px) {
TriPoint p = {(double)px, (double)py};
if (!tri_contains(p, v0, v1, v2)) continue;
double w0, w1, w2;
tri_barycentric(p, v0, v1, v2, w0, w1, w2);
unsigned char mr, mg, mb;
if (w0 + w1 > 1e-6) {
float t01 = static_cast<float>(w1 / (w0 + w1));
Slic3r::filament_mixer_lerp(c0.Red(), c0.Green(), c0.Blue(),
c1.Red(), c1.Green(), c1.Blue(),
t01, &mr, &mg, &mb);
float t2 = static_cast<float>(w2);
Slic3r::filament_mixer_lerp(mr, mg, mb,
c2.Red(), c2.Green(), c2.Blue(),
t2, &mr, &mg, &mb);
} else {
mr = c2.Red(); mg = c2.Green(); mb = c2.Blue();
}
mdc.SetPen(wxPen(wxColour(mr, mg, mb)));
mdc.DrawPoint(px, py);
}
}
mdc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#CECECE")), 1));
mdc.SetBrush(*wxTRANSPARENT_BRUSH);
wxPoint pts[3] = {{(int)v0.x, (int)v0.y}, {(int)v1.x, (int)v1.y}, {(int)v2.x, (int)v2.y}};
mdc.DrawPolygon(3, pts);
mdc.SelectObject(wxNullBitmap);
m_tri_cache_c0 = c0; m_tri_cache_c1 = c1; m_tri_cache_c2 = c2;
m_tri_cache_size = sz;
}
dc.DrawBitmap(m_tri_cache_bmp, 0, 0);
// Drag handle (always redrawn on top of cached bitmap)
double hx = m_tri_wx * v0.x + m_tri_wy * v1.x + m_tri_wz * v2.x;
double hy = m_tri_wx * v0.y + m_tri_wy * v1.y + m_tri_wz * v2.y;
int handle_r = FromDIP(5);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(wxPen(wxColour("#262E30"), FromDIP(2)));
dc.DrawCircle((int)hx, (int)hy, handle_r);
// Draw the background, cached barycentric fill, outline and drag-handle marker through the
// shared picker painter (same geometry the read-only Publish preview uses).
draw_mixed_triangle_picker(dc, sz,
{comp_colour(0), comp_colour(1), comp_colour(2)},
{m_tri_wx, m_tri_wy, m_tri_wz},
{StateColor::darkModeColorFor(*wxWHITE),
StateColor::darkModeColorFor(wxColour("#CECECE")),
StateColor::darkModeColorFor(wxColour("#262E30")),
StateColor::darkModeColorFor(COLOR_LABEL_MUTED)});
if (m_result.ratios.size() >= 3) {
dc.SetFont(::Label::Body_10);
-4
View File
@@ -169,10 +169,6 @@ private:
// Triangle picker drag point (barycentric weights)
double m_tri_wx{0.333}, m_tri_wy{0.333}, m_tri_wz{0.334};
// Cached triangle color bitmap (invalidated when colors or size change)
wxBitmap m_tri_cache_bmp;
wxColour m_tri_cache_c0, m_tri_cache_c1, m_tri_cache_c2;
wxSize m_tri_cache_size;
std::array<RatioLabelPanel*, 3> m_triangle_ratio_labels{nullptr, nullptr, nullptr};
};
+1 -1
View File
@@ -498,7 +498,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
static ImVec2 last_win_size(0.0f, 0.0f);
bool shown = true;
if (imgui.begin(_L("3Dconnexion settings"), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse || ImGuiWindowFlags_NoTitleBar)) {
if (imgui.begin(_L("3Dconnexion settings"), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar)) {
if (shown) {
ImVec2 win_size = ImGui::GetWindowSize();
if (last_win_size.x != win_size.x || last_win_size.y != win_size.y) {
+10 -36
View File
@@ -3083,7 +3083,7 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
}
bool retval = false;
if (this->activate_existing(notification.get())) {
if (m_initialized) { // ignore update action - it cant be initialized if canvas and imgui context is not ready
if (m_initialized && m_imgui_ready) {
if (notification->get_type() == NotificationType::SlicingWarning) {
m_pop_notifications.back()->append(notification->get_data().ori_text);
} else {
@@ -3129,6 +3129,10 @@ void NotificationManager::stop_delayed_notifications_of_type(const NotificationT
void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay_width, float bottom_margin, float right_margin)
{
// Notifications render inside an ImGui frame, so the font atlas is built from this point on
// and pushed notifications may safely measure their text.
m_imgui_ready = true;
sort_notifications();
float bottom_up_last_y = bottom_margin; // ORCA dont scale margins
@@ -3339,17 +3343,7 @@ size_t NotificationManager::get_notification_count() const
void NotificationManager::bbl_show_plateinfo_notification(const std::string &text)
{
NotificationData data{NotificationType::BBLPlateInfo, NotificationLevel::PrintInfoNotificationLevel, BBL_NOTICE_MAX_INTERVAL, text};
for (std::unique_ptr<PopNotification> &notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::BBLPlateInfo) {
notification->reinit();
notification->update(data);
return;
}
}
auto notification = std::make_unique<NotificationManager::PopNotification>(data, m_id_provider, m_evt_handler);
push_notification_data(std::move(notification), 0);
push_notification_data(data, 0);
}
void NotificationManager::bbl_close_3mf_warn_notification()
@@ -3360,20 +3354,10 @@ void NotificationManager::bbl_close_3mf_warn_notification()
}
}
void NotificationManager::bbl_show_3mf_warn_notification(const std::string &text)
void NotificationManager::bbl_show_3mf_warn_notification(const std::string &text, NotificationLevel level)
{
NotificationData data{NotificationType::BBL3MFInfo, NotificationLevel::ErrorNotificationLevel, BBL_NOTICE_MAX_INTERVAL, text};
for (std::unique_ptr<PopNotification> &notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::BBL3MFInfo) {
notification->reinit();
notification->update(data);
return;
}
}
auto notification = std::make_unique<NotificationManager::PopNotification>(data, m_id_provider, m_evt_handler);
push_notification_data(std::move(notification), 0);
NotificationData data{NotificationType::BBL3MFInfo, level, BBL_NOTICE_MAX_INTERVAL, text};
push_notification_data(data, 0);
}
void NotificationManager::bbl_close_plateinfo_notification()
@@ -3388,17 +3372,7 @@ void NotificationManager::bbl_close_plateinfo_notification()
void NotificationManager::bbl_show_preview_only_notification(const std::string &text)
{
NotificationData data{NotificationType::BBLPreviewOnlyMode, NotificationLevel::WarningNotificationLevel, 0, text};
for (std::unique_ptr<PopNotification> &notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::BBLPreviewOnlyMode) {
notification->reinit();
notification->update(data);
return;
}
}
auto notification = std::make_unique<NotificationManager::PopNotification>(data, m_id_provider, m_evt_handler);
push_notification_data(std::move(notification), 0);
push_notification_data(data, 0);
}
void NotificationManager::bbl_close_preview_only_notification()
+12 -2
View File
@@ -378,7 +378,9 @@ public:
void bbl_close_plateinfo_notification();
//BBS-- 3mf warning
void bbl_show_3mf_warn_notification(const std::string &text);
// level defaults to the historical error styling; callers reporting informational
// 3MF load notices (published settings) pass WarningNotificationLevel instead.
void bbl_show_3mf_warn_notification(const std::string &text, NotificationLevel level = NotificationLevel::ErrorNotificationLevel);
void bbl_close_3mf_warn_notification();
//BBS--preview only mode
@@ -1052,6 +1054,11 @@ private:
bool m_is_dark = false;
// set by init(), until false notifications are only added not updated and frame is not requested after push
bool m_initialized{ false };
// set by render_notifications() on the first rendered frame. m_initialized only proves the
// manager exists, not that the ImGui context can measure text: the font atlas is built lazily
// in ImGuiWrapper::new_frame() on the first GL render, so updating a notification before that
// (PopNotification::init -> count_spaces -> ImGui::CalcTextSize) dereferences a null font.
bool m_imgui_ready{ false };
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
wxEvtHandler* m_evt_handler;
// Cache of IDs to identify and reuse ImGUI windows.
@@ -1076,7 +1083,10 @@ private:
NotificationType::ProgressBar,
NotificationType::PrintHostUpload,
NotificationType::SimplifySuggestion,
NotificationType::ValidateWarning
NotificationType::ValidateWarning,
// A published file load can produce several distinct 3MF warnings (invalid values,
// skipped settings, changed slots); let them stack rather than clobber each other.
NotificationType::BBL3MFInfo
};
//prepared (basic) notifications
// non-static so its not loaded too early. If static, the translations wont load correctly.
-1
View File
@@ -66,7 +66,6 @@ class ParamsPanel : public wxPanel
{
#if __WXOSX__
wxWindow* m_tmp_panel;
int m_size_move = -1;
#endif // __WXOSX__
private:
+1 -1
View File
@@ -1112,7 +1112,7 @@ void PartPlate::show_tooltip(const std::string tooltip)
{
const auto scale = m_plater->get_current_canvas3D()->get_scale();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale});
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, {3 * scale});
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * scale);
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0});
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
+399 -38
View File
@@ -1,12 +1,10 @@
#include "Plater.hpp"
#include "../Utils/NetworkAgent.hpp"
#include "../Utils/NetworkAgentFactory.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r_version.h"
#include <cstddef>
#include <algorithm>
#include <chrono>
#include <numeric>
#include <limits>
#include <optional>
@@ -16,8 +14,6 @@
#include <vector>
#include <string>
#include <regex>
#include <future>
#include <thread>
#include <atomic>
#include <mutex>
#include <boost/algorithm/string.hpp>
@@ -69,7 +65,6 @@
#include "libslic3r/Format/bbs_3mf.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/SLA/Hollowing.hpp"
#include "libslic3r/SLA/SupportPoint.hpp"
#include "libslic3r/SLA/ReprojectPointsOnMesh.hpp"
#include "libslic3r/Polygon.hpp"
@@ -78,16 +73,15 @@
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PublishSettings.hpp"
#include "slic3r/Utils/CrealityPrint.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/ObjColorUtils.hpp"
// For stl export
#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp"
#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GuiColor.hpp"
#include "GUI_ObjectList.hpp"
#ifdef __WXGTK__
#include "LinuxDisplayBackend.hpp"
@@ -123,7 +117,6 @@
#include "SendMultiMachinePage.hpp"
#include "SendToPrinter.hpp"
#include "PublishDialog.hpp"
#include "ModelMall.hpp"
#include "ConfigWizard.hpp"
#include "SyncAmsInfoDialog.hpp"
#include "../Utils/ASCIIFolding.hpp"
@@ -149,7 +142,6 @@
#include "ParamsDialog.hpp"
#include "ImageDPIFrame.hpp"
#include "Widgets/Label.hpp"
#include "Widgets/RoundedRectangle.hpp"
#include "Widgets/RadioGroup.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/Button.hpp"
@@ -6715,6 +6707,12 @@ struct Plater::priv
SendToPrinterDialog* m_send_to_sdcard_dlg = nullptr;
PublishDialog *m_publish_dlg = nullptr;
// Session-level stash of the last published selection. Written on publish and on
// loading a published 3MF; read when the Publish dialog is opened.
bool m_has_pending_published{false};
std::vector<std::string> m_pending_published_keys;
std::vector<Slic3r::PublishedMaterialEntry> m_pending_material_keys;
// Data
Slic3r::DynamicPrintConfig *config; // FIXME: leak?
Slic3r::Print fff_print;
@@ -6939,7 +6937,10 @@ struct Plater::priv
// BBS: backup & restore
using LoadProgressCallback = std::function<bool(int, const wxString&)>;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi = false);
std::vector<size_t> load_files(const std::vector<fs::path>& input_files,
LoadStrategy strategy,
bool ask_multi = false,
bool* published_out = nullptr);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool split_object = false, bool auto_drop = true);
// Texture-to-color import: a mesh loaded with UVs + a texture map gets its faces clustered
@@ -6967,7 +6968,7 @@ struct Plater::priv
std::function<bool()> cancel_callback = {});
fs::path get_export_file_path(GUI::FileType file_type);
wxString get_export_file(GUI::FileType file_type);
wxString get_export_file(GUI::FileType file_type, const wxString& title = {}, bool published = false);
// BBS
void load_auxiliary_files();
@@ -8275,7 +8276,10 @@ void read_binary_stl(const std::string& filename, std::string& model_id, std::st
}
// BBS: backup & restore
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi)
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files,
LoadStrategy strategy,
bool ask_multi,
bool* published_out)
{
std::vector<size_t> empty_result;
bool dlg_cont = true;
@@ -8355,6 +8359,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
const float INPUT_FILES_RATIO = 0.7;
const float INIT_MODEL_RATIO = 0.75;
const float CENTER_AROUND_ORIGIN_RATIO = 0.8;
const float LOAD_MODEL_RATIO = 0.9;
for (size_t i = 0; i < input_files.size(); ++i) {
@@ -8395,6 +8400,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
DynamicPrintConfig config;
Semver file_version;
En3mfType en_3mf_file_type = En3mfType::From_BBS;
// BBS: a "published" 3MF carries a flag plus the author-selected setting keys;
// on load keep the user's current presets and overlay only those keys. Declared
// here (outside the config block below) so it stays alive for the embedded-preset
// gate, the metadata strip and the preset overlay after the block closes.
PublishedConfig published_config;
{
DynamicPrintConfig config_loaded;
@@ -8422,6 +8432,107 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
<< boost::format(", plate_data.size %1%, project_preset.size %2%, is_bbs_or_orca_3mf %3%, file_version %4% \n") % plate_data.size() %
project_presets.size() % (en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) % file_version.to_string();
// BBS: a "published" 3MF carries a flag plus the author-selected setting keys;
// on load keep the user's current presets and overlay only those keys. Parsed
// here (before the version/fallback chain below) because a published file has
// no project_settings.config: its values travel in the published_config
// metadata payload, which must fill config_loaded before the chain decides
// whether to import geometry only.
if (model.model_info != nullptr) {
auto published_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_TAG);
if (published_it != model.model_info->metadata_items.end() && is_published_3mf_flag(published_it->second)) {
published_config.published = true;
auto keys_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_KEYS_TAG);
if (keys_it != model.model_info->metadata_items.end()) {
try {
auto j = nlohmann::json::parse(keys_it->second);
if (j.is_array())
for (const auto& k : j)
if (k.is_string())
published_config.published_keys.emplace_back(k.get<std::string>());
} catch (...) {
// Ignore malformed published_keys; the project still loads normally.
}
}
auto material_keys_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_MATERIAL_TAG);
if (material_keys_it != model.model_info->metadata_items.end()) {
try {
auto jm = nlohmann::json::parse(material_keys_it->second);
if (jm.is_array())
for (const auto& m : jm) {
try {
// Malformed entries are isolated so one bad item
// cannot discard valid entries that follow it.
if (!m.is_object())
continue;
PublishedMaterialEntry entry;
const auto mat_it = m.find("material");
if (mat_it != m.end() && mat_it->is_object()) {
const auto& mat = *mat_it;
if (mat.contains("filament_type") && mat["filament_type"].is_string())
entry.filament_type = mat["filament_type"].get<std::string>();
if (mat.contains("filament_vendor") && mat["filament_vendor"].is_string())
entry.filament_vendor = mat["filament_vendor"].get<std::string>();
if (mat.contains("filament_id") && mat["filament_id"].is_string())
entry.filament_id = mat["filament_id"].get<std::string>();
if (mat.contains("setting_id") && mat["setting_id"].is_string())
entry.setting_id = mat["setting_id"].get<std::string>();
if (mat.contains("name") && mat["name"].is_string())
entry.preset_name = mat["name"].get<std::string>();
}
if (m.contains("slot") && m["slot"].is_number_integer())
entry.slot = m["slot"].get<int>();
const auto entry_keys_it = m.find("keys");
if (entry_keys_it != m.end() && entry_keys_it->is_array())
for (const auto& k : *entry_keys_it)
if (k.is_string())
entry.keys.emplace_back(k.get<std::string>());
// Fields always written by the current exporter.
if (m.contains("full") && m["full"].is_boolean())
entry.full = m["full"].get<bool>();
const auto entry_full_keys_it = m.find("full_keys");
if (entry_full_keys_it != m.end() && entry_full_keys_it->is_array())
for (const auto& k : *entry_full_keys_it)
if (k.is_string())
entry.full_keys.emplace_back(k.get<std::string>());
if (m.contains("publish_type") && m["publish_type"].is_boolean())
entry.publish_type = m["publish_type"].get<bool>();
if (m.contains("type") && m["type"].is_string())
entry.publish_type_value = m["type"].get<std::string>();
if (m.contains("publish_color") && m["publish_color"].is_boolean())
entry.publish_color = m["publish_color"].get<bool>();
if (m.contains("color") && m["color"].is_string())
entry.color = m["color"].get<std::string>();
published_config.material_keys.emplace_back(std::move(entry));
} catch (const nlohmann::json::exception&) {
// Ignore only this malformed material entry.
}
}
} catch (const nlohmann::json::exception&) {
// Ignore malformed published_material_keys; the project still loads normally.
}
}
// Rebuild the published values from the metadata payload: a published
// file carries no project_settings.config, so config_loaded is filled
// from here; a missing or malformed payload leaves it empty and the
// fallback chain below imports the geometry only.
auto payload_it = model.model_info->metadata_items.find(ORCA_PUBLISHED_CONFIG_TAG);
if (payload_it != model.model_info->metadata_items.end()) {
try {
ConfigSubstitutions payload_substitutions =
config_loaded.load_from_ini_string(payload_it->second, ForwardCompatibilitySubstitutionRule::Enable);
config_substitutions.substitutions.insert(config_substitutions.substitutions.end(),
std::make_move_iterator(payload_substitutions.begin()),
std::make_move_iterator(payload_substitutions.end()));
} catch (...) {
// Ignore malformed published_config; the project still loads normally.
}
}
}
}
// 1. add extruder for prusa model if the number of existing extruders is not enough
// 2. add extruder for BBS or Other model if only import geometry
if (en_3mf_file_type == En3mfType::From_Prusa || (load_model && !load_config)) {
@@ -8585,7 +8696,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
text += "\n";
log_and_show_3mf_info(text, bambu_project_title);
}
} else if (load_config) {
} else if (load_config && !published_config.published) {
// BambuStudio version is older or same as our SLIC3R_VERSION
wxString text = _L("The 3MF was created by BambuStudio. Some settings may differ from OrcaSlicer.");
log_and_show_3mf_info(text, bambu_project_title);
@@ -8645,7 +8756,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
Semver old_version(1, 5, 9);
if ((en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) && (file_version < old_version) && load_model && load_config && !config_loaded.empty()) {
// A published 3MF has no project config to migrate: skip the old-version
// translations even if a slicer tag slipped through classification.
if ((en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) && (file_version < old_version) && !published_config.published && load_model && load_config && !config_loaded.empty()) {
translate_old = true;
partplate_list.get_plate_size(current_width, current_depth, current_height);
}
@@ -8667,8 +8780,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
// BBS:: project embedded presets
if ((project_presets.size() > 0) && load_config) {
// BBS:: project embedded presets (skipped for published projects: the author's
// embedded presets must not pollute the receiver's library, the overlay applies
// the published keys to the receiver's own presets instead).
if ((project_presets.size() > 0) && load_config && !published_config.published) {
// load project embedded presets
PresetsConfigSubstitutions preset_substitutions;
PresetBundle & preset_bundle = *wxGetApp().preset_bundle;
@@ -8724,6 +8839,19 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
// BBS: a "published" 3MF loads as a new project: its path must not become the
// project filename (Save/Ctrl-S would overwrite the shared file), and the
// published metadata is consumed above and stripped so a later save is a normal
// unpublished 3MF.
if (published_out != nullptr && published_config.published)
*published_out = true;
if (published_config.published && load_config && this->model.model_info != nullptr) {
this->model.model_info->metadata_items.erase(ORCA_PUBLISHED_TAG);
this->model.model_info->metadata_items.erase(ORCA_PUBLISHED_KEYS_TAG);
this->model.model_info->metadata_items.erase(ORCA_PUBLISHED_MATERIAL_TAG);
this->model.model_info->metadata_items.erase(ORCA_PUBLISHED_CONFIG_TAG);
}
if (load_config) {
if (!config.empty()) {
Preset::normalize(config);
@@ -8732,7 +8860,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
{
// BBS: modify the prime tower params for old version file
Semver old_version3(2, 0, 0);
if ((en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) && file_version < old_version3) {
if ((en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) && !published_config.published && file_version < old_version3) {
double old_filament_prime_volume = 0.;
int filament_count = 0;
{
@@ -8782,7 +8910,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
auto choise = wxGetApp().app_config->get("no_warn_when_modified_gcodes");
if (choise.empty() || choise != "true") {
if (!published_config.published && (choise.empty() || choise != "true")) {
// BBS: first validate the printer
// validate the system profiles
std::set<std::string> modified_gcodes;
@@ -8827,12 +8955,56 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (wipe_tower_y_opt)
file_wipe_tower_y = *wipe_tower_y_opt;
// Convert the printer's filament ids to Orca ids before loading.
if (auto* agent = wxGetApp().getAgent()) {
if (auto* ids = config.opt<ConfigOptionStrings>("filament_ids"))
for (std::string& id : ids->values)
id = agent->to_orca_filament_id(id);
}
preset_bundle->load_config_model(filename.string(), std::move(config), file_version);
preset_bundle->load_config_model(filename.string(), std::move(config), file_version, &published_config);
// Mixed-filament definitions that collided with one of the
// receiver's real slots were relocated during the preset load.
// Re-point the freshly parsed model's extruder references and
// color painting from the author's slot numbers to where each
// definition landed, so volumes colored with a mix follow it.
// Runs before the objects are handed over to the plater below.
if (load_model && !published_config.mixed_slot_relocations.empty())
Slic3r::remap_model_filament_slots(model, published_config.mixed_slot_relocations);
// BBS: notify the user about published settings that could not be applied.
if (!published_config.skipped_keys.empty()) {
NotificationManager* notify_manager = q->get_notification_manager();
std::string message = _u8L("Some published settings could not be applied:");
for (const std::string& key : published_config.skipped_keys)
message += "\n-" + key;
// Informational: the load succeeded, these keys were skipped.
notify_manager
->bbl_show_3mf_warn_notification(message,
NotificationManager::NotificationLevel::WarningNotificationLevel);
}
// BBS: notify the user about slot materials that were replaced while
// loading a published project (type mismatch / no same-type match).
if (!published_config.material_replacements.empty()) {
NotificationManager* notify_manager = q->get_notification_manager();
std::string message = _u8L("Some filament slots were changed:");
for (const std::string& replacement : published_config.material_replacements)
message += "\n-" + replacement;
// Informational: the load succeeded, the slots were adapted.
notify_manager
->bbl_show_3mf_warn_notification(message,
NotificationManager::NotificationLevel::WarningNotificationLevel);
}
// Remember the imported published selection so the Publish dialog is
// pre-seeded with the file's settings. Stored after the load so any
// per-slot relocations are already reflected in material_keys.
if (published_config.published) {
this->m_has_pending_published = true;
this->m_pending_published_keys = published_config.published_keys;
this->m_pending_material_keys = published_config.material_keys;
}
ConfigOption* bed_type_opt = preset_bundle->project_config.option("curr_bed_type");
if (bed_type_opt != nullptr) {
@@ -8973,7 +9145,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
dynamic_map->value = false;
}
// Update filament combobox after loading config
wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);
if (published_config.published) {
q->update_filament_colors_in_full_config();
wxGetApp().plater()->sidebar().update_all_preset_comboboxes();
wxGetApp().plater()->sidebar().update_dynamic_filament_list();
} else {
wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);
}
// The loaded project supplies nozzle_volume_type; refresh the sidebar
// nozzle-count badges against it.
if (auto *nozzle_volumes = wxGetApp().preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")) {
@@ -9726,7 +9904,7 @@ fs::path Plater::priv::get_export_file_path(GUI::FileType file_type)
return output_file;
}
wxString Plater::priv::get_export_file(GUI::FileType file_type)
wxString Plater::priv::get_export_file(GUI::FileType file_type, const wxString& title, bool published)
{
wxString wildcard;
switch (file_type) {
@@ -9768,8 +9946,11 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
}
case FT_3MF:
{
output_file.replace_extension("3mf");
dlg_title = _L("Save file as");
// A published export is suggested as "<name>.published.3mf" so the role is visible in the
// dialog and in the recent-files list. This is only a pre-filled suggestion; the user's
// typed filename wins, keeping a plain ".3mf" output fully valid.
output_file.replace_extension(published ? "published.3mf" : "3mf");
dlg_title = title.empty() ? _L("Save file as") : title;
break;
}
case FT_OBJ:
@@ -10007,6 +10188,11 @@ void Plater::priv::reset(bool apply_presets_change)
clear_warnings();
// A new project must not inherit the previous project's published selection (Feature A/B).
m_has_pending_published = false;
m_pending_published_keys.clear();
m_pending_material_keys.clear();
set_project_filename("");
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " call set_project_filename: empty";
@@ -15107,14 +15293,15 @@ void Plater::load_project(wxString const& filename2,
if (strategy & LoadStrategy::Restore)
input_paths.push_back(into_u8(originfile));
std::vector<size_t> res = load_files(input_paths, strategy);
bool loaded_published = false;
std::vector<size_t> res = load_files(input_paths, strategy, false, &loaded_published);
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
// if res is empty no data has been loaded
if (!res.empty() && (load_restore || !(strategy & LoadStrategy::Silence))) {
if (!res.empty() && !loaded_published && (load_restore || !(strategy & LoadStrategy::Silence))) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " call set_project_filename: " << (load_restore ? originfile : filename);
p->set_project_filename(load_restore ? originfile : filename);
if (load_restore && originfile.IsEmpty()) {
@@ -15125,6 +15312,15 @@ void Plater::load_project(wxString const& filename2,
if (using_exported_file()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " using ecported set project filename: " << filename;
p->set_project_filename(filename);
} else if (loaded_published && !res.empty()) {
// A "published" 3MF loads as a new project: its path must not become the project
// filename (Save/Ctrl-S prompts for a destination instead of overwriting it);
// reset() already cleared the project name, so restore the default title and keep
// the file in recents. Only on a successful load (res not empty): a failed or
// cancelled load must not pollute "Recently opened".
p->set_project_name(_L("Untitled"));
if (!filename.IsEmpty())
wxGetApp().mainframe->add_to_recent_projects(filename);
}
}
@@ -16782,22 +16978,12 @@ void Plater::force_update_all_plate_thumbnails()
}
// BBS: backup
std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi) {
std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, LoadStrategy strategy, bool ask_multi, bool* published_out) {
//BBS: wish to reset state when load a new file
p->m_slice_all_only_has_gcode = false;
//BBS: wish to reset all plates stats item selected state when load a new file
p->preview->get_canvas3d()->reset_select_plate_toolbar_selection();
return p->load_files(input_files, strategy, ask_multi);
}
// To be called when providing a list of files to the GUI slic3r on command line.
std::vector<size_t> Plater::load_files(const std::vector<std::string>& input_files, LoadStrategy strategy, bool ask_multi)
{
std::vector<fs::path> paths;
paths.reserve(input_files.size());
for (const std::string& path : input_files)
paths.emplace_back(path);
return p->load_files(paths, strategy, ask_multi);
return p->load_files(input_files, strategy, ask_multi, published_out);
}
bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
@@ -18026,7 +18212,6 @@ void Plater::send_gcode_finish(wxString name)
auto out_str = GUI::format(_L("The file %s has been sent to the printer's storage space and can be viewed on the printer."), name);
p->notification_manager->push_exporting_finished_notification(out_str, "", false);
}
void Plater::export_core_3mf()
{
wxString path = p->get_export_file(FT_3MF);
@@ -18035,6 +18220,182 @@ void Plater::export_core_3mf()
export_3mf(path_u8, SaveStrategy::Silence);
}
// Export the current project as a "published" 3MF: a pure export that never touches the
// project's file name, dirty state, backup path or title, and attaches the published metadata
// to the model only for the duration of the export (a later Save Project is a normal 3MF).
int Plater::export_published_3mf(const std::vector<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& material_keys)
{
wxString path = p->get_export_file(FT_3MF, _L("Publish 3MF file as:"), true);
if (path.empty() || path == "<cancel>")
return wxID_CANCEL;
nlohmann::json j = nlohmann::json::array();
for (const std::string& key : published_keys)
j.push_back(key);
nlohmann::json jm = nlohmann::json::array();
for (const Slic3r::PublishedMaterialEntry& e : material_keys)
jm.push_back({{"material",
{{"filament_type", e.filament_type},
{"filament_vendor", e.filament_vendor},
{"filament_id", e.filament_id},
{"setting_id", e.setting_id},
{"name", e.preset_name}}},
{"slot", e.slot},
{"keys", e.keys},
{"full", e.full},
{"full_keys", e.full_keys},
{"publish_type", e.publish_type},
{"type", e.publish_type_value},
{"publish_color", e.publish_color},
{"color", e.color}});
Model& model = this->model();
// Save the previous metadata so it can be restored after the export, keeping the in-memory
// project pristine (the published flag lives only in the exported file).
const bool had_model_info = (model.model_info != nullptr);
const bool had_published = had_model_info &&
(model.model_info->metadata_items.find(ORCA_PUBLISHED_TAG) != model.model_info->metadata_items.end());
const bool had_published_keys = had_model_info && (model.model_info->metadata_items.find(ORCA_PUBLISHED_KEYS_TAG) !=
model.model_info->metadata_items.end());
const bool had_material_keys = had_model_info && (model.model_info->metadata_items.find(ORCA_PUBLISHED_MATERIAL_TAG) !=
model.model_info->metadata_items.end());
const bool had_payload = had_model_info &&
(model.model_info->metadata_items.find(ORCA_PUBLISHED_CONFIG_TAG) != model.model_info->metadata_items.end());
const std::string prev_published = had_published ? model.model_info->metadata_items.at(ORCA_PUBLISHED_TAG) : std::string();
const std::string prev_published_keys = had_published_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_KEYS_TAG) :
std::string();
const std::string prev_material_keys = had_material_keys ? model.model_info->metadata_items.at(ORCA_PUBLISHED_MATERIAL_TAG) :
std::string();
const std::string prev_payload = had_payload ? model.model_info->metadata_items.at(ORCA_PUBLISHED_CONFIG_TAG) : std::string();
// export_3mf() assigns archive paths to previously unsaved SVGs. Preserve those fields too,
// otherwise a publish changes what a later normal project save writes.
std::vector<std::pair<std::string*, std::string>> previous_svg_paths;
for (ModelObject* object : model.objects)
for (ModelVolume* volume : object->volumes)
if (volume != nullptr && volume->emboss_shape.has_value() && volume->emboss_shape->svg_file.has_value()) {
std::string* path_in_3mf = &volume->emboss_shape->svg_file->path_in_3mf;
previous_svg_paths.emplace_back(path_in_3mf, *path_in_3mf);
}
auto restore_temporary_state = [&]() {
for (const auto& [path_in_3mf, previous_path] : previous_svg_paths)
*path_in_3mf = previous_path;
if (!had_model_info) {
model.model_info = nullptr;
} else {
if (had_published)
model.model_info->metadata_items[ORCA_PUBLISHED_TAG] = prev_published;
else
model.model_info->metadata_items.erase(ORCA_PUBLISHED_TAG);
if (had_published_keys)
model.model_info->metadata_items[ORCA_PUBLISHED_KEYS_TAG] = prev_published_keys;
else
model.model_info->metadata_items.erase(ORCA_PUBLISHED_KEYS_TAG);
if (had_material_keys)
model.model_info->metadata_items[ORCA_PUBLISHED_MATERIAL_TAG] = prev_material_keys;
else
model.model_info->metadata_items.erase(ORCA_PUBLISHED_MATERIAL_TAG);
if (had_payload)
model.model_info->metadata_items[ORCA_PUBLISHED_CONFIG_TAG] = prev_payload;
else
model.model_info->metadata_items.erase(ORCA_PUBLISHED_CONFIG_TAG);
}
};
bool state_restored = false;
auto restore_now = [&]() {
if (state_restored)
return;
restore_temporary_state();
state_restored = true;
};
ScopeGuard restore_guard(restore_now);
if (model.model_info == nullptr)
model.model_info = std::make_shared<ModelInfo>();
model.model_info->metadata_items[ORCA_PUBLISHED_TAG] = "1";
model.model_info->metadata_items[ORCA_PUBLISHED_KEYS_TAG] = j.dump();
model.model_info->metadata_items[ORCA_PUBLISHED_MATERIAL_TAG] = jm.dump();
int ret = -1;
try {
// Minimal published export: filter full_config to the published keys, material keys,
// identity fields and plate geometry keys, and omit the project config file, the
// project-embedded preset dumps and the OrcaSlicer version tag from the archive. The
// filtered values are serialized into the published_config metadata payload instead, so
// OrcaSlicer versions without the publish feature fall back to importing the geometry only
// (keeping the receiver's presets) while new versions rebuild the config from the payload.
DynamicPrintConfig full_cfg = wxGetApp().preset_bundle->full_config_secure();
DynamicPrintConfig filtered_cfg = filter_published_config(full_cfg, published_keys, material_keys);
std::string payload;
for (const std::string& key : filtered_cfg.keys()) {
// A value containing a newline would break the INI written below (read_ini throws),
// so load_from_ini_string discards the whole settings block on import. Skip such
// keys instead of silently dropping every setting.
std::string value = filtered_cfg.opt_serialize(key);
if (value.find('\n') != std::string::npos) {
BOOST_LOG_TRIVIAL(warning) << "publish: dropping key \"" << key
<< "\" from the published payload (value contains a newline)";
continue;
}
payload += key + " = " + value + "\n";
}
model.model_info->metadata_items[ORCA_PUBLISHED_CONFIG_TAG] = std::move(payload);
// Same file layout as save_project(), plus Silence (so export_3mf does not set the project
// filename on success, keeping this a pure export like export_core_3mf()) and MinimalPublished.
auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::Silence | SaveStrategy::MinimalPublished;
bool full_pathnames = wxGetApp().app_config->get_bool("export_sources_full_pathnames");
if (full_pathnames)
save_strategy = save_strategy | SaveStrategy::FullPathSources;
ret = export_3mf(into_path(path), save_strategy, -1, nullptr);
} catch (...) {
restore_now();
MessageDialog(this,
_L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs "
"have the file open."),
_L("Publish"), wxOK | wxICON_WARNING)
.ShowModal();
return wxID_CANCEL;
}
if (ret < 0) {
restore_now();
MessageDialog(this,
_L("Failed to export the published 3MF file.\nPlease check whether the folder exists online or if other programs "
"have the file open."),
_L("Publish"), wxOK | wxICON_WARNING)
.ShowModal();
return wxID_CANCEL;
}
restore_now();
// Register the exported file in the "Recently opened" list
wxGetApp().mainframe->add_to_recent_projects(path);
return wxID_YES;
}
bool Plater::get_pending_published(std::vector<std::string>& out_keys,
std::vector<Slic3r::PublishedMaterialEntry>& out_material) const
{
if (!p->m_has_pending_published)
return false;
out_keys = p->m_pending_published_keys;
out_material = p->m_pending_material_keys;
return true;
}
void Plater::set_pending_published(const std::vector<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& material_keys)
{
p->m_has_pending_published = true;
p->m_pending_published_keys = published_keys;
p->m_pending_material_keys = material_keys;
}
Preset *get_printer_preset(const MachineObject *obj)
{
if (!obj)
+8 -3
View File
@@ -407,9 +407,7 @@ public:
bool preview_zip_archive(const boost::filesystem::path& archive_path);
// BBS: restore
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig, bool ask_multi = false);
// To be called when providing a list of files to the GUI slic3r on command line.
std::vector<size_t> load_files(const std::vector<std::string>& input_files, LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig, bool ask_multi = false);
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig, bool ask_multi = false, bool* published_out = nullptr);
// to be called on drag and drop
bool load_files(const wxArrayString& filenames);
@@ -519,6 +517,13 @@ public:
void export_gcode_3mf(bool export_all = false);
void send_gcode_finish(wxString name);
void export_core_3mf();
// Export a "published" 3MF embedding the author-selected settings in the file metadata; a
// pure export that leaves the in-memory project untouched.
int export_published_3mf(const std::vector<std::string>& published_keys, const std::vector<Slic3r::PublishedMaterialEntry>& material_keys);
// Session-level stash of the last published selection, seeded into the Publish dialog on
// open and written on publish or on loading a published 3MF
bool get_pending_published(std::vector<std::string>& out_keys, std::vector<Slic3r::PublishedMaterialEntry>& out_material) const;
void set_pending_published(const std::vector<std::string>& published_keys, const std::vector<Slic3r::PublishedMaterialEntry>& material_keys);
static TriangleMesh combine_mesh_fff(const ModelObject& mo, int instance_id, std::function<void(const std::string&)> notify_func = {});
void export_stl(bool extended = false, bool selection_only = false, bool multi_stls = false, FileType file_type = FT_STL);
//BBS: remove amf
+3 -20
View File
@@ -507,26 +507,14 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox(wxString title, wxS
}
}
// the dialog needs to be destroyed before the call to switch_language()
// or sometimes the application crashes into wxDialogBase() destructor
// so we put it into an inner scope
MessageDialog msg_wingow(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"),
L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL);
if (msg_wingow.ShowModal() == wxID_CANCEL) {
MessageDialog msg_window(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"),
_L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL);
if (msg_window.ShowModal() == wxID_CANCEL) {
combobox->SetSelection(m_current_language_selected);
return;
}
}
auto check = [](bool yes_or_no) {
// if (yes_or_no)
// return true;
int act_btns = ActionButtons::SAVE;
return wxGetApp().check_and_keep_current_preset_changes(_L("Switching application language"),
_L("Switching application language while some presets are modified."), act_btns);
};
m_current_language_selected = combobox->GetSelection();
if (m_current_language_selected >= 0 && m_current_language_selected < vlist.size()) {
m_pending_language = vlist[m_current_language_selected]->CanonicalName.ToUTF8().data();
@@ -1031,11 +1019,6 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
app_config->set_bool(param, checkbox->GetValue());
app_config->save();
// if (param == "staff_pick_switch") {
// bool pbool = app_config->get("staff_pick_switch") == "true";
// wxGetApp().switch_staff_pick(pbool);
// }
if (param == "sync_user_preset") {
bool sync = app_config->get("sync_user_preset") == "true" ? true : false;
if (sync) {
+1 -1
View File
@@ -1803,7 +1803,7 @@ static void* get_function(const char* name)
return function;
#if defined(_MSC_VER) || defined(_WIN32)
function = GetProcAddress(module, name);
function = reinterpret_cast<void*>(GetProcAddress(module, name));
#else
function = dlsym(module, name);
#endif
File diff suppressed because it is too large Load Diff
+286
View File
@@ -0,0 +1,286 @@
#pragma once
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#include "Widgets/TabCtrl.hpp"
#include "libslic3r/PublishSettings.hpp"
#include <wx/wx.h>
#include <wx/colour.h>
#include <wx/scrolwin.h>
#include <wx/menu.h>
#include <utility>
#include <vector>
#include <string>
// Forward declarations (all are global classes, see Widgets/TextInput.hpp and
// Widgets/StaticLine.hpp).
class TextInput;
class StaticLine;
namespace Slic3r { namespace GUI {
struct PublishMaterialIdentity
{
std::string type;
std::string vendor;
std::string id;
};
// One unmet dependency of an enabled mixed-filament slot: the component filament the mix uses
// would ship without its material (either the component slot is not enabled at all, or it is
// enabled with neither "Full Publish" nor the "Type" requirement checked). Slots are 0-based.
struct MixedDependencyIssue
{
enum class Reason { Disabled, MaterialNotPublished };
size_t mixed_slot{0};
size_t component_slot{0};
Reason reason{Reason::Disabled};
};
// Dialog letting a model author select which settings get embedded in a 3MF. Nested tab layout
// mirroring the Process settings (Printer / Filament / Process outer tabs, category or material
// tabs inside each). Dirty settings are pre-checked and shown bold; on OK the print rows become
// "orca_published_keys" and the material rows become "orca_published_material_keys".
class PublishSettingsDialog : public DPIDialog
{
public:
// Optional published selection (Feature A/B): when the caller supplies one (either a
// remembered session selection or the payload of a freshly loaded published 3MF) the dialog
// is seeded from it, overriding the dirty-default pre-check. A non-null pointer to an empty
// selection means "publish nothing" (an intentional empty state); a null pointer means "no
// remembered selection" (keep the dirty defaults).
PublishSettingsDialog(wxWindow* parent = nullptr,
const std::vector<std::string>* published_keys = nullptr,
const std::vector<Slic3r::PublishedMaterialEntry>* material_keys = nullptr);
~PublishSettingsDialog();
// The selected print/printer setting keys (in display order); printer keys carry a '#N'
// per-extruder suffix.
std::vector<std::string> GetPublishedKeys() const;
// The selected keys grouped per material section (base keys, no '#N' suffix).
std::vector<Slic3r::PublishedMaterialEntry> GetPublishedMaterialKeys() const;
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_sys_color_changed() override;
private:
void fit_to_content();
void refresh_mixed_tab_bitmaps();
// Which part of the settings the row/category came from.
enum class Section { Print, Printer, Material };
// One selectable setting row: a checkbox (setting name) plus a value label and an optional
// grey unit label. key is the full config key, possibly with a "#N" variant suffix
// (print/printer rows); material rows carry the base key.
enum class RowKind {
Setting, // a regular setting key
Color, // material colour requirement (filament_colour)
Type, // material type requirement (read-only text)
};
struct Row
{
std::string key;
wxString category;
wxString subcategory;
wxString label;
wxString value;
wxString unit;
wxString section_title; // outer tab title, for filter matching
Section section{Section::Print};
RowKind kind{RowKind::Setting};
size_t outer_index{0};
size_t inner_index{0};
bool dirty{false}; // matches a dirty base key: pre-checked + bold
bool matches_filter{false}; // survives the active filter (computed by apply_filter)
wxCheckBox* check{nullptr};
wxStaticText* value_label{nullptr};
wxStaticText* unit_label{nullptr};
wxStaticBitmap* color_chip{nullptr}; // Color rows only; swatch next to the value
wxSizerItem* item{nullptr}; // sizer item of this row's h-sizer in its tab list sizer
};
// An optgroup heading. Rows store indices into m_rows.
struct Subcategory
{
wxString title;
::StaticLine* header{nullptr}; // null when the title is empty
wxSizerItem* item{nullptr};
std::vector<size_t> rows;
};
// An inner TabCtrl page with its material controls and content.
struct Category
{
wxString title;
Section section{Section::Print};
size_t group{0}; // index into m_sections / outer page
size_t source_index{0}; // stable source page or material slot index
wxPanel* page{nullptr};
wxScrolledWindow* scroll{nullptr};
wxBoxSizer* list_sizer{nullptr};
wxStaticText* info{nullptr};
wxPoint scroll_pos{0, 0};
wxStaticBitmap* filament_color_chip{nullptr};
wxStaticText* title_label{nullptr}; // material title (static text; Full Publish carries the label elsewhere)
// "Enable": while unchecked nothing of this slot is exported and everything below the
// header row is hidden. For physical slots the Full Publish toggle sits on a second
// line (full_line_item) visible only when enabled; for mixed slots Enable alone implies
// publishing the mix definition, so no Full Publish widget exists at all.
wxCheckBox* enable_check{nullptr};
wxSizerItem* full_line_item{nullptr}; // sizer item of the Full Publish line (physical slots only)
// "Full Publish": while checked, the whole slot preset is serialized and its rows
// (incl. Color/Type) are disabled.
wxCheckBox* full_check{nullptr};
// True for a mixed-color filament slot: no Material/Retraction rows; Enable publishes
// the slot's gradient/ratio definition as a whole.
bool is_mixed{false};
// Material identity, only for Section::Material categories.
std::string filament_type;
std::string filament_vendor;
std::string filament_id;
// The author's 0-based filament slot this material section represents.
size_t filament_slot{0};
std::vector<Subcategory> subs;
std::vector<size_t> rows; // flattened rows of this category
};
// Frozen snapshot of a mixed filament slot's definition for the read-only visualization
// painted on the slot's page. Plain data only: the paint handler must never touch the
// config. For gradient slots the curve is pre-sampled (t, ratio) pairs, where ratio is the
// first component's share over model height; anchors carry the raw control points.
struct MixedVisualSpec
{
bool valid{false};
bool is_gradient{false};
std::vector<wxColour> component_colours; // colour per component, in config order
std::vector<double> ratios; // sublayer shares summing to ~1 (non-gradient)
std::vector<double> tri_weights; // 3-component mixes: barycentric shares
std::vector<std::pair<double, double>> gradient_samples;
std::vector<std::pair<double, double>> gradient_anchors;
};
// One outer TabCtrl page. Category entries are its inner tabs.
struct SectionGroup
{
wxString title; // _L("Printer") / _L("Filament") / _L("Process")
Section kind{Section::Print}; // maps 1:1 to the display group
std::string icon_name; // "printer" / "filament" / "process"
ScalableBitmap icon_bmp; // tab icon next to the title; rescaled on DPI change
wxPanel* page{nullptr};
TabCtrl* tabs{nullptr};
// Second tab strip, below the main one, listing only the mixed-color filament slots.
// Present on the Material section only (null elsewhere).
TabCtrl* mixed_tabs{nullptr};
wxPanel* page_host{nullptr};
wxBoxSizer* page_host_sizer{nullptr};
int selected_inner{-1};
// Selected mixed tab (index into mixed_categories), valid while a mixed slot page is shown.
int selected_mixed{-1};
std::vector<size_t> categories; // indices into m_categories (physical slots)
std::vector<size_t> mixed_categories; // indices into m_categories (mixed slots)
};
void build_option_model();
// Seed the dialog from a published selection (print/printer keys + per-slot material keys):
// the supplied selection is authoritative - it is applied after the dirty pre-check and
// overrides it, so deselected dirty keys stay off. Rows/slots not present in the selection
// are left unselected. Unknown or out-of-range entries are skipped gracefully.
void apply_selection(const std::vector<std::string>& published_keys,
const std::vector<Slic3r::PublishedMaterialEntry>& material_keys);
// Frozen snapshot of a mixed slot's definition for the page visualization, resolved from
// the full config once at dialog-build time. Gradient slots pre-sample exactly what the
// slicer will print: the custom curve wins over the gradient_range endpoints over the
// 0.10 -> 0.90 default (the resolution FilamentBitmapUtils::mixed_gradient_curve mirrors).
static MixedVisualSpec make_mixed_visual_spec(const Slic3r::DynamicPrintConfig& full, size_t slot);
void apply_filter(const wxString& filter_text);
// Menu-only pseudo filters: show only the checked ("Filter selected") or only the
// unchecked ("Filter non-selected") rows. The search box keeps the user's text.
void apply_pseudo_filter(bool selected_only);
// Recompute row matches and visibility for the active filter mode. filter is the lowered
// search text; it is ignored by the pseudo modes.
void refresh_filter(const wxString& filter);
void select_all(bool value);
void select_visible(bool value);
void show_menu(wxMouseEvent& evt);
void set_row_bold(Row& row, bool bold);
// "Full Publish" toggled: disables/enables the material's rows.
void on_full_toggle(size_t category_index);
// "Enable" toggled on a material slot: reveals/hides everything below the header and, for a
// mixed slot, auto-selects its component filaments' "Enable" + "Full Publish" toggles.
void on_enable_toggle(size_t category_index);
// Whether a category currently publishes something, driving its tab's indicator dot.
// Print/Printer: any row checked. Material: the slot's "Enable" is on.
bool category_has_selection(const Category& cat) const;
// Recompute the indicator dot on every outer/inner tab from the current selection state.
void refresh_tab_indicators();
// Unmet dependencies of enabled mixed-filament slots, one record per (mix, component) pair:
// "Enable" not checked on the component, or enabled with neither "Full Publish" nor the
// "Type" requirement row checked. Colour is deliberately ignored (the receiver renders the
// mix from its own components' colours). Sorted by mixed slot, then component slot.
std::vector<MixedDependencyIssue> unpublished_mixed_components() const;
// Read-only visualization of a mixed slot's definition (a stacked ratio bar, or the
// Material Ratio vs Model Height graph for a gradient), inserted above the info hint
// inside the category's scroll area.
void add_mixed_visual(size_t category_index, const MixedVisualSpec& spec);
// Return/create the fixed outer page for a Section kind.
size_t section_group_for(Section kind);
size_t category_index_for(const wxString& title,
Section section,
size_t group,
size_t source_index,
const PublishMaterialIdentity& identity = PublishMaterialIdentity(),
bool is_mixed = false);
size_t subcategory_index_for(size_t category_index, const wxString& title, const wxString& icon);
void add_row_ui(const std::string& key,
const wxString& label,
const wxString& value,
const wxString& unit,
size_t category_index,
size_t subcategory_index,
RowKind kind = RowKind::Setting);
// The non-structural filament keys of a slot's preset, for a "Full Publish" entry.
std::vector<std::string> full_keys_for_slot() const;
void save_scroll_position(Category& category);
void show_outer_page(size_t section_index);
void show_inner_page(size_t section_index, int inner_index);
void show_mixed_page(size_t section_index, int mixed_index);
void on_outer_tab_changed(wxCommandEvent& event);
void on_inner_tab_changed(size_t section_index, wxCommandEvent& event);
void on_mixed_tab_changed(size_t section_index, wxCommandEvent& event);
bool row_is_visible(const Row& row) const;
void apply_visibility();
void bind_tab_events();
TabCtrl* m_outer_tabs{nullptr};
wxPanel* m_outer_host{nullptr};
wxBoxSizer* m_outer_host_sizer{nullptr};
int m_selected_outer{-1};
wxBoxSizer* m_fb_sizer{nullptr}; // "All"/"None" buttons sizer
// Active filter mode: free text from the search box, or one of the menu's pseudo filters.
enum class FilterMode { Text, SelectedOnly, UnselectedOnly };
FilterMode m_filter_mode{FilterMode::Text};
TextInput* m_filter_box{nullptr};
wxTextCtrl* m_filter_ctrl{nullptr};
wxStaticBitmap* m_menu_button{nullptr};
// Shown while a pseudo filter is active (the search box keeps the user's text, so the chip
// carries the visible state); clicking it returns to text filtering.
wxStaticText* m_pseudo_chip{nullptr};
wxString m_info_nonsel;
wxString m_info_allsel;
wxString m_info_empty;
wxString m_info_mix; // body hint shown for a mixed slot (published as a whole)
ScalableBitmap m_search;
ScalableBitmap m_menu;
std::vector<Row> m_rows;
std::vector<Category> m_categories;
std::vector<SectionGroup> m_sections;
};
}} // namespace Slic3r::GUI
+7 -7
View File
@@ -814,13 +814,13 @@ wxBoxSizer* SendMultiMachinePage::create_item_title(wxString title, wxWindow* pa
wxBoxSizer* m_sizer_title = new wxBoxSizer(wxHORIZONTAL);
auto m_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0);
m_title->SetForegroundColour(DESIGN_GRAY800_COLOR);
m_title->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR);
m_title->SetFont(::Label::Head_13);
m_title->Wrap(-1);
m_title->SetToolTip(tooltip);
auto m_line = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
m_line->SetBackgroundColour(DESIGN_GRAY400_COLOR);
m_line->SetBackgroundColour(SEND_DESIGN_GRAY400_COLOR);
m_sizer_title->Add(m_title, 0, wxALIGN_CENTER | wxALL, 3);
m_sizer_title->Add(0, 0, 0, wxLEFT, 9);
@@ -843,7 +843,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_checkbox(wxString title, wxWindow*
m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8);
auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0);
checkbox_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
checkbox_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
checkbox_title->SetFont(::Label::Body_13);
auto size = checkbox_title->GetTextExtent(title);
@@ -867,12 +867,12 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin
{
wxBoxSizer* sizer_input = new wxBoxSizer(wxHORIZONTAL);
auto input_title = new wxStaticText(parent, wxID_ANY, str_before);
input_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
input_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
input_title->SetFont(::Label::Body_13);
input_title->SetToolTip(tooltip);
input_title->Wrap(-1);
auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER);
auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, SEND_DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER);
StateColor input_bg(std::pair<wxColour, int>(wxColour("#F0F0F1"), StateColor::Disabled), std::pair<wxColour, int>(*wxWHITE, StateColor::Enabled));
input->SetBackgroundColor(input_bg);
input->GetTextCtrl()->SetValue(app_config->get(param));
@@ -880,7 +880,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin
input->GetTextCtrl()->SetValidator(validator);
auto second_title = new wxStaticText(parent, wxID_ANY, str_after, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
second_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
second_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
second_title->SetFont(::Label::Body_13);
second_title->SetToolTip(tooltip);
second_title->Wrap(-1);
@@ -1337,7 +1337,7 @@ wxPanel* SendMultiMachinePage::create_page()
m_tip_text->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1));
m_tip_text->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1));
m_tip_text->SetLabel(_L("Please select the devices you would like to manage here (up to 6 devices)"));
m_tip_text->SetForegroundColour(DESIGN_GRAY800_COLOR);
m_tip_text->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR);
m_tip_text->SetFont(::Label::Head_20);
m_tip_text->Wrap(-1);
+9 -9
View File
@@ -22,15 +22,15 @@ namespace GUI {
#define SEND_LEFT_DEV_STATUS 250
#define SEND_LEFT_TAKS_STATUS 180
#define DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248)
#define DESIGN_GRAY900_COLOR wxColour(38, 46, 48)
#define DESIGN_GRAY800_COLOR wxColour(50, 58, 61)
#define DESIGN_GRAY600_COLOR wxColour(144, 144, 144)
#define DESIGN_GRAY400_COLOR wxColour(166, 169, 170)
#define DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1)
#define DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1)
#define DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1)
#define DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1)
#define SEND_DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248)
#define SEND_DESIGN_GRAY900_COLOR wxColour(38, 46, 48)
#define SEND_DESIGN_GRAY800_COLOR wxColour(50, 58, 61)
#define SEND_DESIGN_GRAY600_COLOR wxColour(144, 144, 144)
#define SEND_DESIGN_GRAY400_COLOR wxColour(166, 169, 170)
#define SEND_DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1)
#define SEND_DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1)
#define SEND_DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1)
#define SEND_DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1)
+1 -1
View File
@@ -678,7 +678,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
wxBoxSizer *loading_Sizer = new wxBoxSizer(wxHORIZONTAL);
m_gif_ctrl = new wxAnimationCtrl(m_loading_page, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxDefaultSize, wxAC_DEFAULT_STYLE);
auto gif_path = Slic3r::var("loading.gif").c_str();
const wxString gif_path = from_u8(Slic3r::var("loading.gif"));
if (m_gif_ctrl->LoadFile(gif_path)){
m_gif_ctrl->SetSize(m_gif_ctrl->GetAnimation().GetSize());
m_gif_ctrl->Play();
+8 -17
View File
@@ -7,6 +7,7 @@
#include "libslic3r/FilamentMixer.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PublishSettings.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "Search.hpp"
@@ -5699,26 +5700,16 @@ if (is_marlin_flavor)
optgroup->append_single_option_line("extruder_offset", "printer_extruder_basic_information#extruder-offset-position", extruder_idx);
//BBS: don't show retract related config menu in machine page
// These optgroups are built from publishable_printer_retraction/z_hop_options() so the
// published-3MF printer allowlist (their union in libslic3r/PublishSettings.hpp) can
// never drift from what the machine page actually shows.
optgroup = page->new_optgroup(L("Retraction"), L"param_retraction");
optgroup->append_single_option_line("retraction_length", "printer_extruder_retraction#length", extruder_idx);
optgroup->append_single_option_line("retract_restart_extra", "printer_extruder_retraction#extra-length-on-restart", extruder_idx);
optgroup->append_single_option_line("retraction_speed", "printer_extruder_retraction#retraction-speed", extruder_idx);
optgroup->append_single_option_line("deretraction_speed", "printer_extruder_retraction#deretraction-speed", extruder_idx);
optgroup->append_single_option_line("retraction_minimum_travel", "printer_extruder_retraction#travel-distance-threshold", extruder_idx);
optgroup->append_single_option_line("retract_when_changing_layer", "printer_extruder_retraction#retract-on-layer-change", extruder_idx);
optgroup->append_single_option_line("wipe", "printer_extruder_retraction#wipe-while-retracting", extruder_idx);
optgroup->append_single_option_line("wipe_distance", "printer_extruder_retraction#wipe-distance", extruder_idx);
optgroup->append_single_option_line("retract_before_wipe", "printer_extruder_retraction#retract-amount-before-wipe", extruder_idx);
// Orca
optgroup->append_single_option_line("retract_after_wipe", "printer_extruder_retraction#retract-amount-after-wipe", extruder_idx);
for (const PublishablePrinterOption& opt : publishable_printer_retraction_options())
optgroup->append_single_option_line(opt.key, opt.icon, extruder_idx);
optgroup = page->new_optgroup(L("Z-Hop"), L"param_extruder_lift_enforcement");
optgroup->append_single_option_line("retract_lift_enforce", "printer_extruder_z_hop#on-surfaces", extruder_idx);
optgroup->append_single_option_line("z_hop_types", "printer_extruder_z_hop#z-hop-type", extruder_idx);
optgroup->append_single_option_line("z_hop", "printer_extruder_z_hop#z-hop-height", extruder_idx);
optgroup->append_single_option_line("travel_slope", "printer_extruder_z_hop#traveling-angle", extruder_idx);
optgroup->append_single_option_line("retract_lift_above", "printer_extruder_z_hop#only-lift-z-above", extruder_idx);
optgroup->append_single_option_line("retract_lift_below", "printer_extruder_z_hop#only-lift-z-below", extruder_idx);
for (const PublishablePrinterOption& opt : publishable_printer_z_hop_options())
optgroup->append_single_option_line(opt.key, opt.icon, extruder_idx);
optgroup = page->new_optgroup(L("Retraction when switching material"), L"param_retraction_material_change");
optgroup->append_single_option_line("retract_length_toolchange", "printer_extruder_retraction#retraction-when-switching-materials", extruder_idx);
+1 -2
View File
@@ -30,7 +30,6 @@
#include <memory>
//#include "BedShapeDialog.hpp"
#include "Event.hpp"
#include "wxExtensions.hpp"
#include "ConfigManipulation.hpp"
#include "OptionsGroup.hpp"
@@ -38,7 +37,6 @@
//BBS: GUI refactor
#include "Notebook.hpp"
#include "ParamsPanel.hpp"
#include "Widgets/RoundedRectangle.hpp"
#include "Widgets/TextInput.hpp"
#include "Widgets/CheckBox.hpp" // ORCA
@@ -472,6 +470,7 @@ protected:
std::string m_last_sparse_infill_rotate_template_value;
ConfigManipulation get_config_manipulation();
friend class EditGCodeDialog;
friend class PublishSettingsDialog;
};
class TabPrint : public Tab
+1 -223
View File
@@ -12,6 +12,7 @@
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Color.hpp"
#include "format.hpp"
#include "ConfigValueFormatter.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "Tab.hpp"
@@ -22,7 +23,6 @@
#include "MsgDialog.hpp"
#include "PresetComboBoxes.hpp"
#include "Widgets/RoundedRectangle.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/DialogButtons.hpp"
#include "Widgets/HyperLink.hpp"
@@ -571,14 +571,6 @@ void DiffModel::Clear()
}
static std::string get_pure_opt_key(std::string opt_key)
{
const int pos = opt_key.find("#");
if (pos > 0)
boost::erase_tail(opt_key, opt_key.size() - pos);
return opt_key;
}
// ----------------------------------------------------------------------------
// DiffViewCtrl
// ----------------------------------------------------------------------------
@@ -1212,32 +1204,6 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s
return true;
}
wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill = false, int idx = -1)
{
const ConfigOptionDef& def = config.def()->options.at(opt_key);
const std::vector<std::string>& names = def.enum_labels;//ConfigOptionEnum<T>::get_enum_names();
int val = 0;
if (idx >= 0)
val = dynamic_cast<const ConfigOptionInts*>(config.option(opt_key))->get_at(idx);
else
val = config.option(opt_key)->getInt();
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
// So we should "convert" val to the correct one
if (is_infill) {
for (auto key_val : *def.enum_keys_map)
if (int(key_val.second) == val) {
auto it = std::find(def.enum_values.begin(), def.enum_values.end(), key_val.first);
if (it == def.enum_values.end())
return "";
return from_u8(_utf8(names[it - def.enum_values.begin()]));
}
return _L("Undefined");
}
return from_u8(_utf8(names[val]));
}
// BBS
#if 0
static size_t get_id_from_opt_key(std::string opt_key)
@@ -1251,194 +1217,6 @@ static size_t get_id_from_opt_key(std::string opt_key)
}
#endif
static wxString get_full_label(std::string opt_key, const DynamicPrintConfig& config)
{
opt_key = get_pure_opt_key(opt_key);
auto option = config.option(opt_key);
if (!option || option->is_nil())
return _L("N/A");
const ConfigOptionDef* opt = config.def()->get(opt_key);
return opt->full_label.empty() ? opt->label : opt->full_label;
}
static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config)
{
int orig_opt_idx = -1;
int opt_idx = -1;
int pos = opt_key.find("#");
std::string temp_str = opt_key;
if (pos > 0) {
boost::erase_head(temp_str, pos + 1);
orig_opt_idx = static_cast<size_t>(atoi(temp_str.c_str()));
}
opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
opt_key = get_pure_opt_key(opt_key);
auto option = config.option(opt_key);
if (!option) {
return _L("N/A");
}
auto opt_vector = dynamic_cast<const ConfigOptionVectorBase *>(option);
if ((option->is_scalar() && config.option(opt_key)->is_nil()) ||
(option->is_vector() && opt_vector && opt_idx >= 0 && opt_idx < opt_vector->size() && opt_vector->is_nil(opt_idx)))
return _L("N/A");
wxString out;
const ConfigOptionDef* opt = config.def()->get(opt_key);
bool is_nullable = opt->nullable;
switch (opt->type) {
case coInt:
return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str());
case coInts: {
if (is_nullable) {
auto values = config.opt<ConfigOptionIntsNullable>(opt_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
}
else {
auto values = config.opt<ConfigOptionInts>(opt_key);
if (orig_opt_idx >= 0 && orig_opt_idx < values->size()) {
return from_u8((boost::format("%1%") % values->get_at(opt_idx)).str());
}
else {
std::string value_str;
for (int i = 0; i < values->size(); i++) {
value_str += std::to_string(values->get_at(i));
if (i != values->size() - 1) {
value_str += ",";
}
}
return from_u8(value_str);
}
}
return _L("Undefined");
}
case coBool:
return config.opt_bool(opt_key) ? "true" : "false";
case coBools: {
if (is_nullable) {
auto values = config.opt<ConfigOptionBoolsNullable>(opt_key);
if (opt_idx < values->size())
return values->get_at(opt_idx) ? "true" : "false";
}
else {
auto values = config.opt<ConfigOptionBools>(opt_key);
if (opt_idx < values->size())
return values->get_at(opt_idx) ? "true" : "false";
}
return _L("Undefined");
}
case coPercent:
return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str());
case coPercents: {
if (is_nullable) {
auto values = config.opt<ConfigOptionPercentsNullable>(opt_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
}
else {
auto values = config.opt<ConfigOptionPercents>(opt_key);
if (opt_idx < values->size())
return from_u8((boost::format("%1%%%") % values->get_at(opt_idx)).str());
}
return _L("Undefined");
}
case coFloat:
return double_to_string(config.opt_float(opt_key));
case coFloats: {
if (is_nullable) {
auto values = config.opt<ConfigOptionFloatsNullable>(opt_key);
if (opt_idx < values->size())
return double_to_string(values->get_at(opt_idx));
}
else {
auto values = config.opt<ConfigOptionFloats>(opt_key);
if (values && opt_idx < values->size())
return double_to_string(values->get_at(opt_idx));
}
return _L("Undefined");
}
case coString:
return from_u8(config.opt_string(opt_key));
case coStrings: {
const ConfigOptionStrings* strings = config.opt<ConfigOptionStrings>(opt_key);
if (strings) {
if (opt_key == "compatible_printers" || opt_key == "compatible_prints") {
if (strings->empty())
return _L("All");
for (size_t id = 0; id < strings->size(); id++)
out += from_u8(strings->get_at(id)) + "\n";
out.RemoveLast(1);
return out;
}
if (!strings->empty() && opt_idx < strings->values.size())
return from_u8(strings->get_at(opt_idx));
}
break;
}
case coFloatOrPercent: {
const ConfigOptionFloatOrPercent* opt = config.opt<ConfigOptionFloatOrPercent>(opt_key);
if (opt)
out = double_to_string(opt->value) + (opt->percent ? "%" : "");
return out;
}
case coEnum: {
return get_string_from_enum(opt_key, config,
opt_key == "top_surface_pattern" ||
opt_key == "bottom_surface_pattern" ||
opt_key == "internal_solid_infill_pattern" ||
opt_key == "sparse_infill_pattern" ||
opt_key == "ironing_pattern" ||
opt_key == "support_ironing_pattern" ||
opt_key == "support_pattern" ||
opt_key == "support_interface_pattern")
;
}
case coEnums: {
return get_string_from_enum(opt_key, config,
opt_key == "top_surface_pattern" ||
opt_key == "bottom_surface_pattern" ||
opt_key == "internal_solid_infill_pattern" ||
opt_key == "sparse_infill_pattern" ||
opt_key == "ironing_pattern" ||
opt_key == "support_ironing_pattern" ||
opt_key == "support_pattern" ||
opt_key == "support_interface_pattern"
, opt_idx);
}
case coPoint: {
Vec2d val = config.opt<ConfigOptionPoint>(opt_key)->value;
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
}
case coPoints: {
//BBS: add bed_exclude_area
if (opt_key == "printable_area" || opt_key == "thumbnails") {
ConfigOptionPoints points = *config.option<ConfigOptionPoints>(opt_key);
//BuildVolume build_volume = {points.values, 0.};
return get_thumbnails_string(points.values);
}
else if (opt_key == "bed_exclude_area") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
}
else if (opt_key == "head_wrap_detect_zone") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
}
else if (opt_key == "wrapping_exclude_area") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
}
Vec2d val = config.opt<ConfigOptionPoints>(opt_key)->get_at(opt_idx);
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
}
default:
break;
}
return out;
}
void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header)
{
PresetCollection* presets = dependent_presets;
+81 -6
View File
@@ -23,6 +23,7 @@
#include <wx/textdlg.h>
#include <wx/wx.h>
#include <wx/weakref.h>
#include <wx/display.h>
#include <wx/fileconf.h>
#include <wx/file.h>
@@ -562,6 +563,78 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
m_ProfileJson["filament"][fName]["selected"] = 1;
}
}
else if (strCmd == "check_for_new_printers") {
json response = json::object();
response["command"] = "check_new_printers_result";
// Guide pages currently send sequence_id as a number, while older
// pages may send it as a string. Preserve the value without
// forcing either representation.
if (j.contains("sequence_id"))
response["sequence_id"] = j["sequence_id"];
else
response["sequence_id"] = "";
if (!m_MainPtr->preset_updater) {
response["error"] = "Printer update service is unavailable.";
wxString strJS = wxString::Format("HandleStudio(%s)", response.dump(-1, ' ', true));
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
} else {
// Orca: enumerate vendors directly from disk rather than from m_ProfileJson["model"]
// — a vendor with no machine models (e.g. a filament-only bundle, or a test fixture
// like "test123" with an empty machine_model_list) never gets a "vendor" entry
// pushed into "model" by LoadProfileFamily(), so it would be invisible to the
// request body and get endlessly re-offered by the server. Scan both the system dir
// (already-installed vendors) and the bundled resources dir (shipped-but-not-yet-
// installed vendors), same as LoadProfileData() does when building loaded_vendors.
std::set<std::string> system_vendors;
for (const auto& dir : {vendor_dir, rsrc_vendor_dir}) {
if (!boost::filesystem::exists(dir))
continue;
for (const auto& entry : boost::filesystem::directory_iterator(dir)) {
if (!boost::filesystem::is_directory(entry) && boost::iequals(entry.path().extension().string(), ".json"))
system_vendors.insert(entry.path().stem().string());
}
}
// Orca: check_new_vendors() is async (network + confirmation dialog + download
// all happen off the calling thread apart from the dialog itself); guard against
// this dialog being closed before the callback fires.
wxWeakRef<GuideFrame> weak_this(this);
try {
m_MainPtr->preset_updater->check_new_vendors(
system_vendors, [weak_this, response](std::vector<std::string> installed_vendors, bool declined) mutable {
if (!weak_this)
return;
// Orca: append the newly installed vendor(s) into the in-memory
// profile data (instead of a full LoadProfileData() rescan of every
// vendor) and push the refreshed list to the webview, the same way
// request_userguide_profile does, so the printer list picks them up
// without needing to reopen the guide.
for (const auto& vendor_id : installed_vendors) {
weak_this->LoadProfileFamily(vendor_id, (weak_this->vendor_dir / (vendor_id + ".json")).string());
}
if (!installed_vendors.empty()) {
json profile_response = json::object();
profile_response["command"] = "response_userguide_profile";
profile_response["sequence_id"] = "10001";
profile_response["response"] = weak_this->m_ProfileJson;
wxString profileJS = wxString::Format("HandleStudio(%s)", profile_response.dump(-1, ' ', true));
weak_this->RunScript(profileJS);
}
response["vendors"] = installed_vendors;
response["declined"] = declined;
wxString strJS = wxString::Format("HandleStudio(%s)", response.dump(-1, ' ', true));
weak_this->RunScript(strJS);
});
} catch (const std::exception &e) {
BOOST_LOG_TRIVIAL(warning) << "Failed to check for new printers: " << e.what();
response["error"] = "Failed to check for new printers.";
wxString strJS = wxString::Format("HandleStudio(%s)", response.dump(-1, ' ', true));
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
}
}
}
else if (strCmd == "user_guide_finish") {
SaveProfile();
@@ -1644,13 +1717,15 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath
OneModel["materials"] = pm["default_materials"];
// wxString strCoverPath = wxString::Format("%s\\%s\\%s_cover.png", strFolder, strVendor, std::string(s1.mb_str()));
std::string cover_file = s1 + "_cover.png";
boost::filesystem::path cover_path = boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/profiles/" / strVendor / cover_file).make_preferred();
std::string cover_file = s1 + "_cover.png";
boost::filesystem::path cover_path = boost::filesystem::absolute(vendor_dir / cover_file).make_preferred();
BOOST_LOG_TRIVIAL(info) << "[WebGuideDialog] " << cover_path;
if (!boost::filesystem::exists(cover_path)) {
cover_path =
(boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") /
cover_file)
.make_preferred();
cover_path = boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/profiles/" / strVendor / cover_file)
.make_preferred();
if (!boost::filesystem::exists(cover_path))
cover_path = (boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") / cover_file)
.make_preferred();
}
OneModel["cover"] = cover_path.string();
+35
View File
@@ -162,6 +162,15 @@ void Button::SetCenter(bool isCenter)
{
this->isCenter = isCenter; }
void Button::SetIndicator(bool on)
{
if (m_show_indicator == on)
return;
m_show_indicator = on;
messureSize();
Refresh();
}
void Button::SetVertical(bool vertical)
{
this->vertical = vertical;
@@ -324,6 +333,13 @@ void Button::render(wxDC& dc)
szContent.x -= d;
}
}
if (m_show_indicator) {
const int dot = FromDIP(6);
if (vertical)
szContent.y += dot + FromDIP(6);
else
szContent.x += dot + FromDIP(6);
}
// move to center
wxRect rcContent = { {0, 0}, size };
if (isCenter) {
@@ -364,6 +380,17 @@ void Button::render(wxDC& dc)
#endif
dc.DrawText(text, pt);
}
if (m_show_indicator) {
const int dot = FromDIP(6); // diameter
wxPoint dot_pt;
dot_pt.x = pt.x + (text.IsEmpty() ? 0 : textSize.x) + FromDIP(6) + dot / 2;
// Centre on the content vertically; a bitmap-only (empty-label) tab has no text row.
dot_pt.y = text.IsEmpty() ? rcContent.y + rcContent.height / 2 : pt.y + textSize.y / 2;
const wxColour c = StateColor::darkModeColorFor(m_indicator_color);
dc.SetBrush(wxBrush(c));
dc.SetPen(wxPen(c));
dc.DrawCircle(dot_pt, dot / 2);
}
}
void Button::messureSize()
@@ -388,6 +415,14 @@ void Button::messureSize()
if (szIcon.y > szContent.y) szContent.y = szIcon.y;
}
}
if (m_show_indicator) {
// Indicator dot sits to the right of the label: its diameter plus the gap from the text.
const int dot = FromDIP(6);
if (vertical)
szContent.y += dot + FromDIP(6);
else
szContent.x += dot + FromDIP(6);
}
wxSize size = szContent + paddingSize * 2;
if (minSize.GetHeight() > 0)
size.SetHeight(minSize.GetHeight());
+29 -21
View File
@@ -4,28 +4,30 @@
#include "../wxExtensions.hpp"
#include "StaticBox.hpp"
#include <wx/tipwin.h>
#include <wx/colour.h>
class ButtonProps
{
public:
static int ChoiceButtonGap(){return 10;};
static int WindowButtonGap(){return 10;};
static int ChoiceButtonGap() { return 10; };
static int WindowButtonGap() { return 10; };
};
enum class ButtonStyle{
enum class ButtonStyle {
Regular,
Confirm,
Alert,
Disabled,
};
enum class ButtonType{
Compact , // Font10 FullyRounded For spaces with less areas
Window , // Font12 FullyRounded For regular buttons in windows and not related with parameter boxes
Choice , // Font14 Semi-Rounded For dialog/window choice buttons
enum class ButtonType {
Compact, // Font10 FullyRounded For spaces with less areas
Window, // Font12 FullyRounded For regular buttons in windows and not related with parameter boxes
Choice, // Font14 Semi-Rounded For dialog/window choice buttons
Parameter, // Font14 Semi-Rounded For buttons that near parameter boxes
Icon , // ------ Semi-Rounded For buttons that only has icons. icons should be 16x16 and iconSize has to be defined as 16 while creation of button
Expanded , // Font14 Semi-Rounded For full length buttons. ex. buttons in static box
Icon, // ------ Semi-Rounded For buttons that only has icons. icons should be 16x16 and iconSize has to be defined as 16 while
// creation of button
Expanded, // Font14 Semi-Rounded For full length buttons. ex. buttons in static box
};
class Button : public StaticBox
@@ -36,15 +38,17 @@ class Button : public StaticBox
wxSize paddingSize;
ScalableBitmap active_icon;
StateColor text_color;
StateColor text_color;
bool pressedDown = false;
bool m_selected = true;
bool canFocus = true;
bool canFocus = true;
bool isCenter = true;
bool vertical = false;
bool m_show_indicator = false;
wxColour m_indicator_color = wxColour("#009688");
static const int buttonWidth = 200;
static const int buttonWidth = 200;
static const int buttonHeight = 50;
public:
@@ -68,19 +72,23 @@ public:
void SetStyle(const ButtonStyle style /*= ButtonStyle::Regular*/, const ButtonType type /*= ButtonType::None*/);
void SetTextColor(StateColor const &color);
void SetTextColor(StateColor const& color);
void SetTextColorNormal(wxColor const &color);
void SetTextColorNormal(wxColor const& color);
void SetSelected(bool selected = true) { m_selected = selected; }
// Show a small coloured dot to the right of the label (used by TabCtrl tabs to flag that
// the tab's category has a selected/toggled setting).
void SetIndicator(bool on);
// Only meant to be used by inspector, not public API
ButtonStyle GetStyle() const { return m_style; }
ButtonType GetType() const { return m_type; }
bool IsSelected() const { return m_selected; }
ButtonType GetType() const { return m_type; }
bool IsSelected() const { return m_selected; }
bool Enable(bool enable = true) override;
void EnableTooltipEvenDisabled();// The tip will be shown even if the button is disabled
void EnableTooltipEvenDisabled(); // The tip will be shown even if the button is disabled
void SetCanFocus(bool canFocus) override;
@@ -104,7 +112,7 @@ protected:
private:
bool m_has_style = false;
ButtonStyle m_style;
ButtonType m_type;
ButtonType m_type;
void paintEvent(wxPaintEvent& evt);
@@ -115,10 +123,10 @@ private:
// some useful events
void mouseDown(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void mouseCaptureLost(wxMouseCaptureLostEvent &event);
void keyDownUp(wxKeyEvent &event);
void mouseCaptureLost(wxMouseCaptureLostEvent& event);
void keyDownUp(wxKeyEvent& event);
//
//
void sendButtonEvent();
// parent motion
+1 -1
View File
@@ -999,7 +999,7 @@ void FanControlPopupNew::init_names(MachineObject* obj) {
radio_btn_name[AIR_DUCT::AIR_DUCT_HEATING_INTERNAL_FILT] = _L("Heating");
radio_btn_name[AIR_DUCT::AIR_DUCT_EXHAUST] = _L("Exhaust");
radio_btn_name[AIR_DUCT::AIR_DUCT_FULL_COOLING] = _L("Full Cooling");
radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = L("Init");
radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = _L("Init");
air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_CHAMBER] = _L("Chamber");
air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_INNERLOOP] = _L("Innerloop");
+7
View File
@@ -7,6 +7,7 @@ BEGIN_EVENT_TABLE(StaticBox, wxWindow)
// catch paint events
//EVT_ERASE_BACKGROUND(StaticBox::eraseEvent)
EVT_SIZE(StaticBox::sizeEvent)
EVT_PAINT(StaticBox::paintEvent)
END_EVENT_TABLE()
@@ -140,6 +141,12 @@ void StaticBox::eraseEvent(wxEraseEvent& evt)
#endif
}
void StaticBox::sizeEvent(wxSizeEvent& evt)
{
Refresh();
evt.Skip();
}
void StaticBox::paintEvent(wxPaintEvent& evt)
{
// depending on your system you may need to look at double-buffered dcs

Some files were not shown because too many files have changed in this diff Show More