mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 15:21:09 +00:00
Merge branch 'main' into ui-fixes-100
This commit is contained in:
@@ -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 ""
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "フィラメントを選択"
|
||||
|
||||
|
||||
@@ -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 "필라멘트 선택"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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ą"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "Выбрать материал"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "เลือกเส้นพลาสติก"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "Вибрати філамент"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 "选择耗材"
|
||||
|
||||
|
||||
@@ -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 "選擇線材"
|
||||
|
||||
|
||||
@@ -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 |
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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={};
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -425,7 +425,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context)
|
||||
else
|
||||
c = *context;
|
||||
|
||||
STACKFRAME64 sf = {0};
|
||||
STACKFRAME64 sf = {};
|
||||
DWORD imageType;
|
||||
|
||||
//intel X86
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 ¶ms = 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
+1437
-145
File diff suppressed because it is too large
Load Diff
@@ -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);*/
|
||||
|
||||
|
||||
@@ -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
@@ -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 §ion : 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 ®ion_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 ®ion_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);
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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++)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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> ¬ification : 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> ¬ification : 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> ¬ification : 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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -66,7 +66,6 @@ class ParamsPanel : public wxPanel
|
||||
{
|
||||
#if __WXOSX__
|
||||
wxWindow* m_tmp_panel;
|
||||
int m_size_move = -1;
|
||||
#endif // __WXOSX__
|
||||
|
||||
private:
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
Reference in New Issue
Block a user