Merge branch 'main' into enh-port-edit-gcode-dlg

This commit is contained in:
Ocraftyone 2024-01-21 14:12:39 -05:00 committed by GitHub
commit 11fd73a90d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2622 additions and 745 deletions

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-13 17:34+0100\n" "POT-Creation-Date: 2024-01-19 18:27+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -3758,7 +3758,7 @@ msgstr ""
msgid "Size:" msgid "Size:"
msgstr "" msgstr ""
#, possible-c-format, possible-boost-format #, possible-boost-format
msgid "" msgid ""
"Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please " "Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please "
"separate the conflicted objects farther (%s <-> %s)." "separate the conflicted objects farther (%s <-> %s)."
@ -3830,6 +3830,15 @@ msgstr ""
msgid "Resolution" msgid "Resolution"
msgstr "" msgstr ""
msgid "Enable"
msgstr ""
msgid "Hostname or IP"
msgstr ""
msgid "Custom camera source"
msgstr ""
msgid "Show \"Live Video\" guide page." msgid "Show \"Live Video\" guide page."
msgstr "" msgstr ""
@ -4605,6 +4614,9 @@ msgstr ""
msgid "Camera Setting" msgid "Camera Setting"
msgstr "" msgstr ""
msgid "Switch Camera View"
msgstr ""
msgid "Control" msgid "Control"
msgstr "" msgstr ""
@ -8126,6 +8138,29 @@ msgid ""
"bottom shell layers" "bottom shell layers"
msgstr "" msgstr ""
msgid "Apply gap fill"
msgstr ""
msgid ""
"Enables gap fill for the selected surfaces. The minimum gap length that will "
"be filled can be controlled from the filter out tiny gaps option below.\n"
"\n"
"Options:\n"
"1. Everywhere: Applies gap fill to top, bottom and internal solid surfaces\n"
"2. Top and Bottom surfaces: Applies gap fill to top and bottom surfaces "
"only\n"
"3. Nowhere: Disables gap fill\n"
msgstr ""
msgid "Everywhere"
msgstr ""
msgid "Top and bottom surfaces"
msgstr ""
msgid "Nowhere"
msgstr ""
msgid "Force cooling for overhang and bridge" msgid "Force cooling for overhang and bridge"
msgstr "" msgstr ""
@ -8562,6 +8597,18 @@ msgid ""
"thickness (top+bottom solid layers)" "thickness (top+bottom solid layers)"
msgstr "" msgstr ""
msgid "Further reduce solid infill on walls (experimental)"
msgstr ""
msgid ""
"Further reduces any solid infill applied to walls. As there will be very "
"limited infill supporting solid surfaces, make sure that you are using "
"adequate number of walls to support the part on sloping surfaces.\n"
"\n"
"For heavily sloped surfaces this option is not suitable as it will generate "
"too thin of a top layer and should be disabled."
msgstr ""
msgid "Top surface pattern" msgid "Top surface pattern"
msgstr "" msgstr ""
@ -9840,9 +9887,6 @@ msgid ""
"model and save printing time, but make slicing and G-code generating slower" "model and save printing time, but make slicing and G-code generating slower"
msgstr "" msgstr ""
msgid "Enable"
msgstr ""
msgid "Filename format" msgid "Filename format"
msgstr "" msgstr ""
@ -12557,3 +12601,151 @@ msgid ""
"Message body: \"%1%\"\n" "Message body: \"%1%\"\n"
"Error: \"%2%\"" "Error: \"%2%\""
msgstr "" msgstr ""
#: resources/data/hints.ini: [hint:Precise wall]
msgid "Precise wall\nDid you know that turning on precise wall can improve precision and layer consistency?"
msgstr ""
#: resources/data/hints.ini: [hint:Sandwich mode]
msgid "Sandwich mode\nDid you know that you can use sandwich mode (inner-outer-inner) to improve precision and layer consistency if your model doesn't have very steep overhangs?"
msgstr ""
#: resources/data/hints.ini: [hint:Chamber temperature]
msgid "Chamber temperature\nDid you know that OrcaSlicer supports chamber temperature?"
msgstr ""
#: resources/data/hints.ini: [hint:Calibration]
msgid "Calibration\nDid you know that calibrating your printer can do wonders? Check out our beloved calibration solution in OrcaSlicer."
msgstr ""
#: resources/data/hints.ini: [hint:Auxiliary fan]
msgid "Auxiliary fan\nDid you know that OrcaSlicer supports Auxiliary part cooling fan?"
msgstr ""
#: resources/data/hints.ini: [hint:Air filtration]
msgid "Air filtration/Exhuast Fan\nDid you know that OrcaSlicer can support Air filtration/Exhuast Fan?"
msgstr ""
#: resources/data/hints.ini: [hint:G-code window]
msgid "G-code window\nYou can turn on/off the G-code window by pressing the <b>C</b> key."
msgstr ""
#: resources/data/hints.ini: [hint:Switch workspaces]
msgid "Switch workspaces\nYou can switch between <b>Prepare</b> and <b>Preview</b> workspaces by pressing the <b>Tab</b> key."
msgstr ""
#: resources/data/hints.ini: [hint:How to use keyboard shortcuts]
msgid "How to use keyboard shortcuts\nDid you know that Orca Slicer offers a wide range of keyboard shortcuts and 3D scene operations."
msgstr ""
#: resources/data/hints.ini: [hint:Reverse on odd]
msgid "Reverse on odd\nDid you know that <b>Reverse on odd</b> feature can significantly improve the surface quality of your overhangs?"
msgstr ""
#: resources/data/hints.ini: [hint:Cut Tool]
msgid "Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool?"
msgstr ""
#: resources/data/hints.ini: [hint:Fix Model]
msgid "Fix Model\nDid you know that you can fix a corrupted 3D model to avoid a lot of slicing problems on the Windows system?"
msgstr ""
#: resources/data/hints.ini: [hint:Timelapse]
msgid "Timelapse\nDid you know that you can generate a timelapse video during each print?"
msgstr ""
#: resources/data/hints.ini: [hint:Auto-Arrange]
msgid "Auto-Arrange\nDid you know that you can auto-arrange all objects in your project?"
msgstr ""
#: resources/data/hints.ini: [hint:Auto-Orient]
msgid "Auto-Orient\nDid you know that you can rotate objects to an optimal orientation for printing by a simple click?"
msgstr ""
#: resources/data/hints.ini: [hint:Lay on Face]
msgid "Lay on Face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the <b>F</b> key."
msgstr ""
#: resources/data/hints.ini: [hint:Object List]
msgid "Object List\nDid you know that you can view all objects/parts in a list and change settings for each object/part?"
msgstr ""
#: resources/data/hints.ini: [hint:Search Functionality]
msgid "Search Functionality\nDid you know that you use the Search tool to quickly find a specific Orca Slicer setting?"
msgstr ""
#: resources/data/hints.ini: [hint:Simplify Model]
msgid "Simplify Model\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model."
msgstr ""
#: resources/data/hints.ini: [hint:Slicing Parameter Table]
msgid "Slicing Parameter Table\nDid you know that you can view all objects/parts on a table and change settings for each object/part?"
msgstr ""
#: resources/data/hints.ini: [hint:Split to Objects/Parts]
msgid "Split to Objects/Parts\nDid you know that you can split a big object into small ones for easy colorizing or printing?"
msgstr ""
#: resources/data/hints.ini: [hint:Subtract a Part]
msgid "Subtract a Part\nDid you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Orca Slicer."
msgstr ""
#: resources/data/hints.ini: [hint:STEP]
msgid "STEP\nDid you know that you can improve your print quality by slicing a STEP file instead of an STL?\nOrca Slicer supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try!"
msgstr ""
#: resources/data/hints.ini: [hint:Z seam location]
msgid "Z seam location\nDid you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!"
msgstr ""
#: resources/data/hints.ini: [hint:Fine-tuning for flow rate]
msgid "Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned for even better-looking prints? Depending on the material, you can improve the overall finish of the printed model by doing some fine-tuning."
msgstr ""
#: resources/data/hints.ini: [hint:Split your prints into plates]
msgid "Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts."
msgstr ""
#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height]
msgid "Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!"
msgstr ""
#: resources/data/hints.ini: [hint:Support painting]
msgid "Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it."
msgstr ""
#: resources/data/hints.ini: [hint:Different types of supports]
msgid "Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!"
msgstr ""
#: resources/data/hints.ini: [hint:Printing Silk Filament]
msgid "Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results."
msgstr ""
#: resources/data/hints.ini: [hint:Brim for better adhesion]
msgid "Brim for better adhesion\nDid you know that when printing models have a small contact interface with the printing surface, it's recommended to use a brim?"
msgstr ""
#: resources/data/hints.ini: [hint:Set parameters for multiple objects]
msgid "Set parameters for multiple objects\nDid you know that you can set slicing parameters for all selected objects at one time?"
msgstr ""
#: resources/data/hints.ini: [hint:Stack objects]
msgid "Stack objects\nDid you know that you can stack objects as a whole one?"
msgstr ""
#: resources/data/hints.ini: [hint:Flush into support/objects/infill]
msgid "Flush into support/objects/infill\nDid you know that you can save the wasted filament by flushing them into support/objects/infill during filament change?"
msgstr ""
#: resources/data/hints.ini: [hint:Improve strength]
msgid "Improve strength\nDid you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?"
msgstr ""
#: resources/data/hints.ini: [hint:When need to print with the printer door opened]
msgid "When need to print with the printer door opened\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature. More info about this in the Wiki."
msgstr ""
#: resources/data/hints.ini: [hint:Avoid warping]
msgid "Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping."
msgstr ""

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Orca Slicer\n" "Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-13 17:34+0100\n" "POT-Creation-Date: 2024-01-21 14:28+0100\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Guislain Cyril, Thomas Lété\n" "Language-Team: Guislain Cyril, Thomas Lété\n"
@ -759,7 +759,7 @@ msgstr "Annuler les modifications apportées à la police."
#, boost-format #, boost-format
msgid "Font \"%1%\" can't be selected." msgid "Font \"%1%\" can't be selected."
msgstr "La police « %1% » ne peut pas être sélectionnée." msgstr "La police « %1% » ne peut pas être sélectionnée."
msgid "Operation" msgid "Operation"
msgstr "Opération" msgstr "Opération"
@ -845,7 +845,7 @@ msgstr "Impossible de supprimer le dernier style existant."
#, boost-format #, boost-format
msgid "Are you sure you want to permanently remove the \"%1%\" style?" msgid "Are you sure you want to permanently remove the \"%1%\" style?"
msgstr "Êtes-vous sûr de vouloir supprimer définitivement le style « %1% » ?" msgstr "Êtes-vous sûr de vouloir supprimer définitivement le style « %1% » ?"
#, boost-format #, boost-format
msgid "Delete \"%1%\" style." msgid "Delete \"%1%\" style."
@ -1015,8 +1015,8 @@ msgid ""
"Can't load exactly same font(\"%1%\"). Aplication selected a similar " "Can't load exactly same font(\"%1%\"). Aplication selected a similar "
"one(\"%2%\"). You have to specify font for enable edit text." "one(\"%2%\"). You have to specify font for enable edit text."
msgstr "" msgstr ""
"Impossible de charger exactement la même police (« %1% »). Lapplication a " "Impossible de charger exactement la même police (« %1% »). Lapplication a "
"sélectionné une police similaire (« %2% »). Vous devez spécifier la police " "sélectionné une police similaire (« %2% »). Vous devez spécifier la police "
"pour permettre lédition du texte." "pour permettre lédition du texte."
msgid "No symbol" msgid "No symbol"
@ -2995,8 +2995,8 @@ msgid ""
"A desiccant status lower than two bars indicates that desiccant may be " "A desiccant status lower than two bars indicates that desiccant may be "
"inactive. Please change the desiccant.(The bars: higher the better.)" "inactive. Please change the desiccant.(The bars: higher the better.)"
msgstr "" msgstr ""
"Un état du dessicant inférieur à deux barres indique que le dessicant est " "Un état du dessicateur inférieur à deux barres indique que le dessicateur "
"peut-être inactif. Veuillez changer le déshydratant. (Plus c'est élevé, " "est peut-être inactif. Veuillez changer le déshydratant. (Plus c'est élevé, "
"mieux c'est.)" "mieux c'est.)"
msgid "" msgid ""
@ -3005,7 +3005,7 @@ msgid ""
"process. During this time, the indicator may not represent the chamber " "process. During this time, the indicator may not represent the chamber "
"accurately." "accurately."
msgstr "" msgstr ""
"Remarque: Lorsque le couvercle est ouvert ou que le sachet de dessinateur " "Remarque: Lorsque le couvercle est ouvert ou que le sachet de dessicateur "
"est changé, cela peut prendre plusieurs heures ou une nuit pour absorber " "est changé, cela peut prendre plusieurs heures ou une nuit pour absorber "
"l'humidité. Les basses températures ralentissent également le processus. " "l'humidité. Les basses températures ralentissent également le processus. "
"Pendant ce temps, l'indicateur pourrait ne pas représenter l'humidité dans " "Pendant ce temps, l'indicateur pourrait ne pas représenter l'humidité dans "
@ -3423,8 +3423,8 @@ msgid ""
"Alternate extra wall only works with ensure vertical shell thickness " "Alternate extra wall only works with ensure vertical shell thickness "
"disabled. " "disabled. "
msgstr "" msgstr ""
"La paroi supplémentaire alternée ne fonctionne que si « Assurer lépaisseur " "La paroi supplémentaire alternée ne fonctionne que si « Assurer lépaisseur "
"verticale de la coque » est désactivé. " "verticale de la coque » est désactivé. "
msgid "" msgid ""
"Change these settings automatically? \n" "Change these settings automatically? \n"
@ -3433,9 +3433,9 @@ msgid ""
"No - Dont use alternate extra wall" "No - Dont use alternate extra wall"
msgstr "" msgstr ""
"Modifier ces paramètres automatiquement ? \n" "Modifier ces paramètres automatiquement ? \n"
"Oui - Désactiver « Assurer lépaisseur verticale de la coque » et activer " "Oui - Désactiver « Assurer lépaisseur verticale de la coque » et activer « "
"« Paroi supplémentaire alternée »\n" "Paroi supplémentaire alternée »\n"
"Non - Ne pas utiliser « Paroi supplémentaire alternée »" "Non - Ne pas utiliser « Paroi supplémentaire alternée »"
msgid "" msgid ""
"Prime tower does not work when Adaptive Layer Height or Independent Support " "Prime tower does not work when Adaptive Layer Height or Independent Support "
@ -4148,6 +4148,15 @@ msgstr "Passer en LIVE"
msgid "Resolution" msgid "Resolution"
msgstr "Résolution" msgstr "Résolution"
msgid "Enable"
msgstr "Activer"
msgid "Hostname or IP"
msgstr "Nom dhôte ou IP"
msgid "Custom camera source"
msgstr "Source caméra personnalisée"
msgid "Show \"Live Video\" guide page." msgid "Show \"Live Video\" guide page."
msgstr "Afficher la page de guide « Vidéo en direct »." msgstr "Afficher la page de guide « Vidéo en direct »."
@ -4968,6 +4977,9 @@ msgstr "Carte SD"
msgid "Camera Setting" msgid "Camera Setting"
msgstr "Réglage de la Caméra" msgstr "Réglage de la Caméra"
msgid "Switch Camera View"
msgstr "Changer la vue de la caméra"
msgid "Control" msgid "Control"
msgstr "Contrôle" msgstr "Contrôle"
@ -6591,7 +6603,7 @@ msgid "Busy"
msgstr "Occupé" msgstr "Occupé"
msgid "Bambu Cool Plate" msgid "Bambu Cool Plate"
msgstr "Plaque Bambu Cool Plate" msgstr "Plateau Cool Plate"
msgid "PLA Plate" msgid "PLA Plate"
msgstr "Plaque PLA" msgstr "Plaque PLA"
@ -6603,7 +6615,7 @@ msgid "Bambu Smooth PEI Plate"
msgstr "Bambu Smooth PEI Plate" msgstr "Bambu Smooth PEI Plate"
msgid "High temperature Plate" msgid "High temperature Plate"
msgstr "Bambu High Temperature Plate" msgstr "Plateau haute température"
msgid "Bambu Textured PEI Plate" msgid "Bambu Textured PEI Plate"
msgstr "Bambu Textured PEI Plate" msgstr "Bambu Textured PEI Plate"
@ -6827,8 +6839,8 @@ msgid ""
"Caution to use! Flow calibration on Textured PEI Plate may fail due to the " "Caution to use! Flow calibration on Textured PEI Plate may fail due to the "
"scattered surface." "scattered surface."
msgstr "" msgstr ""
"Attention à lutilisation ! La calibration du débit sur le plateau Bambu " "Attention à lutilisation ! La calibration du débit sur le plateau PEI "
"Dual-Sided Textured PEI peut échouer en raison de la surface texturée." "texturé double face peut échouer en raison de la surface texturée."
msgid "Automatic flow calibration using Micro Lidar" msgid "Automatic flow calibration using Micro Lidar"
msgstr "Calibration automatique du débit à laide du Micro-Lidar" msgstr "Calibration automatique du débit à laide du Micro-Lidar"
@ -7112,6 +7124,10 @@ msgstr "Générateur de paroi"
msgid "Walls and surfaces" msgid "Walls and surfaces"
msgstr "Parois et surfaces" msgstr "Parois et surfaces"
msgid "Small Area Infill Flow Compensation (experimental)"
msgstr ""
"Compensation des débits de remplissage des petites zones (expérimental)"
msgid "Bridging" msgid "Bridging"
msgstr "Ponts" msgstr "Ponts"
@ -7258,17 +7274,16 @@ msgstr ""
"sur le plateau Engineering." "sur le plateau Engineering."
msgid "Smooth PEI Plate / High Temp Plate" msgid "Smooth PEI Plate / High Temp Plate"
msgstr "Bambu Smooth PEI Plate / High Temp Plate" msgstr "Plateau PEI lisse / Plateau haute température"
msgid "" msgid ""
"Bed temperature when Smooth PEI Plate/High temperature plate is installed. " "Bed temperature when Smooth PEI Plate/High temperature plate is installed. "
"Value 0 means the filament does not support to print on the Smooth PEI Plate/" "Value 0 means the filament does not support to print on the Smooth PEI Plate/"
"High Temp Plate" "High Temp Plate"
msgstr "" msgstr ""
"Température du plateau lorsque le plateau Bambu Smooth PEI Plate/High " "Température du plateau lorsque le Plateau PEI lisse / haute température est "
"Temperature Plate est installé. Une valeur à 0 signifie que le filament ne " "installé. Une valeur à 0 signifie que le filament ne prend pas en charge "
"prend pas en charge l'impression sur le plateau Bambu Smooth PEI Plate/High " "l'impression sur le plateau PEI lisse/haute température"
"Temperature Plate"
msgid "Textured PEI Plate" msgid "Textured PEI Plate"
msgstr "Plaque PEI texturée" msgstr "Plaque PEI texturée"
@ -8564,7 +8579,7 @@ msgid ""
"when prime tower is enabled." "when prime tower is enabled."
msgstr "" msgstr ""
"Lutilisation de diamètres de buses et de filaments différents nest pas " "Lutilisation de diamètres de buses et de filaments différents nest pas "
"autorisée lorsque loption « prime tower » est activée." "autorisée lorsque loption « prime tower » est activée."
msgid "" msgid ""
"The Wipe Tower is currently only supported with the relative extruder " "The Wipe Tower is currently only supported with the relative extruder "
@ -9011,6 +9026,39 @@ msgstr ""
"que l'épaisseur de la coque inférieure est absolument déterminée par les " "que l'épaisseur de la coque inférieure est absolument déterminée par les "
"couches de la coque inférieure" "couches de la coque inférieure"
msgid "Apply gap fill"
msgstr "Remplissage des trous"
msgid ""
"Enables gap fill for the selected surfaces. The minimum gap length that will "
"be filled can be controlled from the filter out tiny gaps option below.\n"
"\n"
"Options:\n"
"1. Everywhere: Applies gap fill to top, bottom and internal solid surfaces\n"
"2. Top and Bottom surfaces: Applies gap fill to top and bottom surfaces "
"only\n"
"3. Nowhere: Disables gap fill\n"
msgstr ""
"Active le remplissage des trous pour les surfaces sélectionnées. La longueur "
"minimale du trou qui sera comblé peut être contrôlée à laide de loption "
 Filtrer les petits trous » ci-dessous.\n"
"\n"
"Options :\n"
"1. Partout : Applique le remplissage des trous aux surfaces solides "
"supérieures, inférieures et internes.\n"
"2. Surfaces supérieure et inférieure : Remplissage des trous uniquement sur "
"les surfaces supérieures et inférieures.\n"
"3. Nulle part : Désactive le remplissage des trous\n"
msgid "Everywhere"
msgstr "Partout"
msgid "Top and bottom surfaces"
msgstr "Surfaces supérieure et inférieure"
msgid "Nowhere"
msgstr "Nulle part"
msgid "Force cooling for overhang and bridge" msgid "Force cooling for overhang and bridge"
msgstr "Forcer la ventilation pour les surplombs et ponts" msgstr "Forcer la ventilation pour les surplombs et ponts"
@ -9150,7 +9198,7 @@ msgstr ""
"Si une surface supérieure doit être imprimée et quelle est partiellement " "Si une surface supérieure doit être imprimée et quelle est partiellement "
"couverte par une autre couche, elle ne sera pas considérée comme une couche " "couverte par une autre couche, elle ne sera pas considérée comme une couche "
"supérieure si sa largeur est inférieure à cette valeur. Cela peut être utile " "supérieure si sa largeur est inférieure à cette valeur. Cela peut être utile "
"pour ne pas déclencher loption « un périmètre sur le dessus » sur des " "pour ne pas déclencher loption « un périmètre sur le dessus » sur des "
"surfaces qui ne devraient être couvertes que par des périmètres. Cette " "surfaces qui ne devraient être couvertes que par des périmètres. Cette "
"valeur peut être un mm ou un % de la largeur dextrusion du périmètre.\n" "valeur peut être un mm ou un % de la largeur dextrusion du périmètre.\n"
"Attention : Si cette option est activée, des artefacts peuvent être créés si " "Attention : Si cette option est activée, des artefacts peuvent être créés si "
@ -9376,6 +9424,15 @@ msgstr "Par couche"
msgid "By object" msgid "By object"
msgstr "Par objet" msgstr "Par objet"
msgid "Layer order"
msgstr "Ordre des couches"
msgid "Print order within a single layer"
msgstr "Ordre dimpression au sein dune même couche"
msgid "As object list"
msgstr "En tant que liste dobjets"
msgid "Slow printing down for better layer cooling" msgid "Slow printing down for better layer cooling"
msgstr "Impression lente pour un meilleur refroidissement des couches" msgstr "Impression lente pour un meilleur refroidissement des couches"
@ -9596,6 +9653,25 @@ msgstr ""
"garantir l'épaisseur verticale de la coque (couches solides " "garantir l'épaisseur verticale de la coque (couches solides "
"supérieure+inférieure)." "supérieure+inférieure)."
msgid "Further reduce solid infill on walls (experimental)"
msgstr "Réduire davantage le remplissage solide des parois (expérimental)"
msgid ""
"Further reduces any solid infill applied to walls. As there will be very "
"limited infill supporting solid surfaces, make sure that you are using "
"adequate number of walls to support the part on sloping surfaces.\n"
"\n"
"For heavily sloped surfaces this option is not suitable as it will generate "
"too thin of a top layer and should be disabled."
msgstr ""
"Réduit encore davantage les remplissages solides appliqués aux parois. Étant "
"donné que le remplissage des surfaces solides sera très limité, assurez-vous "
"que vous utilisez un nombre suffisant de parois pour soutenir la partie sur "
"les surfaces inclinées.\n"
"\n"
"Pour les surfaces fortement inclinées, cette option nest pas adaptée car "
"elle génère une couche supérieure trop fine et doit être désactivée."
msgid "Top surface pattern" msgid "Top surface pattern"
msgstr "Motif de surface supérieure" msgstr "Motif de surface supérieure"
@ -10887,6 +10963,28 @@ msgstr ""
msgid "This G-code will be used as a custom code" msgid "This G-code will be used as a custom code"
msgstr "Ce G-code sera utilisé comme code personnalisé" msgstr "Ce G-code sera utilisé comme code personnalisé"
msgid "Enable Flow Compensation"
msgstr "Activer la compensation de débit"
msgid "Enable flow compensation for small infill areas"
msgstr ""
"Activer la compensation des débits pour les petites zones de remplissage"
msgid "Flow Compensation Model"
msgstr "Modèle de compensation de débit"
msgid ""
"Flow Compensation Model, used to adjust the flow for small infill areas. The "
"model is expressed as a comma separated pair of values for extrusion length "
"and flow correction factors, one per line, in the following format: "
"\"1.234,5.678\""
msgstr ""
"Modèle de compensation du débit, utilisé pour ajuster le débit pour les "
"petites zones de remplissage. Le modèle est exprimé sous la forme dune "
"paire de valeurs séparées par des virgules pour la longueur dextrusion et "
"les facteurs de correction du débit, une par ligne, dans le format suivant : "
 1.234,5.678 »"
msgid "Maximum speed X" msgid "Maximum speed X"
msgstr "Vitesse maximale X" msgstr "Vitesse maximale X"
@ -11246,9 +11344,6 @@ msgstr ""
"peut réduire les rétractions pour les modèles complexes et économiser du " "peut réduire les rétractions pour les modèles complexes et économiser du "
"temps dimpression, mais ralentit la découpe et la génération du G-code." "temps dimpression, mais ralentit la découpe et la génération du G-code."
msgid "Enable"
msgstr "Activer"
msgid "Filename format" msgid "Filename format"
msgstr "Format du nom de fichier" msgstr "Format du nom de fichier"
@ -11332,8 +11427,8 @@ msgstr ""
"manière, le remplissage est coincé verticalement entre les parois, ce qui " "manière, le remplissage est coincé verticalement entre les parois, ce qui "
"permet dobtenir des impressions plus solides. \n" "permet dobtenir des impressions plus solides. \n"
"\n" "\n"
"Lorsque cette option est activée, loption « assurer lépaisseur verticale " "Lorsque cette option est activée, loption « assurer lépaisseur verticale "
"de la coque » doit être désactivée. \n" "de la coque » doit être désactivée. \n"
"\n" "\n"
"Il nest pas recommandé dutiliser le remplissage par éclairs avec cette " "Il nest pas recommandé dutiliser le remplissage par éclairs avec cette "
"option, car il y a peu de remplissage pour ancrer les périmètres " "option, car il y a peu de remplissage pour ancrer les périmètres "
@ -11461,7 +11556,7 @@ msgid ""
"parameter: \"Z hop upper boundary\"" "parameter: \"Z hop upper boundary\""
msgstr "" msgstr ""
"Le saut de Z ne sera effectif que si Z est supérieur à cette valeur et " "Le saut de Z ne sera effectif que si Z est supérieur à cette valeur et "
"inférieur au paramètre : « Limite supérieure du saut de Z »" "inférieur au paramètre : « Limite supérieure du saut de Z »"
msgid "Z hop upper boundary" msgid "Z hop upper boundary"
msgstr "Limite supérieure du saut de Z" msgstr "Limite supérieure du saut de Z"
@ -11471,7 +11566,7 @@ msgid ""
"the parameter: \"Z hop lower boundary\" and is below this value" "the parameter: \"Z hop lower boundary\" and is below this value"
msgstr "" msgstr ""
"Si cette valeur est positive, le saut de Z ne sera effectif que si Z est " "Si cette valeur est positive, le saut de Z ne sera effectif que si Z est "
"supérieur au paramètre : « Limite inférieure de Z hop » et quil est " "supérieur au paramètre : « Limite inférieure de Z hop » et quil est "
"inférieur à cette valeur." "inférieur à cette valeur."
msgid "Z hop type" msgid "Z hop type"
@ -11758,7 +11853,7 @@ msgid ""
"Smooth Spiral smoothes out X and Y moves as wellresulting in no visible seam " "Smooth Spiral smoothes out X and Y moves as wellresulting in no visible seam "
"at all, even in the XY directions on walls that are not vertical" "at all, even in the XY directions on walls that are not vertical"
msgstr "" msgstr ""
 Spirale lisse » lisse également les mouvements X et Y, de sorte quaucune " Spirale lisse » lisse également les mouvements X et Y, de sorte quaucune "
"couture nest visible, même dans les directions XY sur des parois qui ne " "couture nest visible, même dans les directions XY sur des parois qui ne "
"sont pas verticales." "sont pas verticales."
@ -12649,8 +12744,8 @@ msgid ""
"Wipe tower is only compatible with relative mode. It is recommended on most " "Wipe tower is only compatible with relative mode. It is recommended on most "
"printers. Default is checked" "printers. Default is checked"
msgstr "" msgstr ""
"Lextrusion relative est recommandée lors de lutilisation de loption " "Lextrusion relative est recommandée lors de lutilisation de loption « "
"« label_objects ». Certains extrudeurs fonctionnent mieux avec cette option " "label_objects ». Certains extrudeurs fonctionnent mieux avec cette option "
"non verrouillée (mode dextrusion absolu). La tour dessuyage nest " "non verrouillée (mode dextrusion absolu). La tour dessuyage nest "
"compatible quavec le mode relatif. Il est recommandé sur la plupart des " "compatible quavec le mode relatif. Il est recommandé sur la plupart des "
"imprimantes. Loption par défaut est cochée" "imprimantes. Loption par défaut est cochée"
@ -12767,11 +12862,11 @@ msgstr ""
"\n" "\n"
"REMARQUE : les surfaces inférieures et supérieures ne sont pas affectées par " "REMARQUE : les surfaces inférieures et supérieures ne sont pas affectées par "
"cette valeur afin déviter les lacunes visuelles sur le côté du modèle. " "cette valeur afin déviter les lacunes visuelles sur le côté du modèle. "
"Réglez le « seuil dune paroi » dans les paramètres avancés ci-dessous pour " "Réglez le « seuil dune paroi » dans les paramètres avancés ci-dessous pour "
"ajuster la sensibilité de ce qui est considéré comme une surface supérieure. " "ajuster la sensibilité de ce qui est considéré comme une surface supérieure. "
"Le « seuil dune paroi » nest visible que si ce paramètre est supérieur à " "Le « seuil dune paroi » nest visible que si ce paramètre est supérieur à "
"la valeur par défaut de 0,5 ou si loption « surfaces supérieures à une " "la valeur par défaut de 0,5 ou si loption « surfaces supérieures à une "
"paroi » est activée." "paroi » est activée."
msgid "First layer minimum wall width" msgid "First layer minimum wall width"
msgstr "Largeur minimale de la paroi de la première couche" msgstr "Largeur minimale de la paroi de la première couche"
@ -13644,11 +13739,9 @@ msgstr "Filament"
msgid "Start temp: " msgid "Start temp: "
msgstr "Temp. de début: " msgstr "Temp. de début: "
msgid "End temp: " msgid "End temp: "
msgstr "Temp. de fin: " msgstr "Temp. de fin: "
msgid "Temp step: " msgid "Temp step: "
msgstr "Intervalle de temp. : " msgstr "Intervalle de temp. : "
@ -13946,7 +14039,7 @@ msgstr ""
msgid "" msgid ""
"\"Bambu\" or \"Generic\" can not be used as a Vendor for custom filaments." "\"Bambu\" or \"Generic\" can not be used as a Vendor for custom filaments."
msgstr "" msgstr ""
 Bambu » ou « Générique » ne peuvent pas être utilisés comme fournisseur de " Bambu » ou « Générique » ne peuvent pas être utilisés comme fournisseur de "
"filaments personnalisés." "filaments personnalisés."
msgid "Filament type is not selected, please reselect type." msgid "Filament type is not selected, please reselect type."
@ -13993,10 +14086,10 @@ msgstr ""
msgid "" msgid ""
"We would rename the presets as \"Vendor Type Serial @printer you " "We would rename the presets as \"Vendor Type Serial @printer you "
"selected\". \n" "selected\". \n"
"To add preset for more prinetrs, Please go to printer selection" "To add preset for more printers, Please go to printer selection"
msgstr "" msgstr ""
"Nous renommerions les préréglages en « Fournisseur Type Série @imprimante " "Nous renommerions les préréglages en « Vendor Type Serial @printer you "
"que vous avez sélectionnée ». \n" "selected ». \n"
"Pour ajouter des préréglages pour dautres imprimantes, veuillez aller à la " "Pour ajouter des préréglages pour dautres imprimantes, veuillez aller à la "
"sélection de limprimante." "sélection de limprimante."
@ -14112,7 +14205,7 @@ msgid ""
"You have entered an illegal input in the printable area section on the first " "You have entered an illegal input in the printable area section on the first "
"page. Please check before creating it." "page. Please check before creating it."
msgstr "" msgstr ""
"Vous avez introduit une donnée illégale dans la section « zone imprimable » " "Vous avez introduit une donnée illégale dans la section « zone imprimable » "
"de la première page. Veuillez vérifier avant de la créer." "de la première page. Veuillez vérifier avant de la créer."
msgid "The custom printer or model is not inputed, place input." msgid "The custom printer or model is not inputed, place input."
@ -14578,7 +14671,7 @@ msgid ""
"Message body: \"%2%\"" "Message body: \"%2%\""
msgstr "" msgstr ""
"Statut HTTP : %1%\n" "Statut HTTP : %1%\n"
"Corps du message : « %2% »" "Corps du message : « %2% »"
#, boost-format #, boost-format
msgid "" msgid ""
@ -14587,8 +14680,8 @@ msgid ""
"Error: \"%2%\"" "Error: \"%2%\""
msgstr "" msgstr ""
"Lanalyse de la réponse de lhôte a échoué.\n" "Lanalyse de la réponse de lhôte a échoué.\n"
"Corps du message : « %1% »\n" "Corps du message : « %1% »\n"
"Erreur : « %2% »" "Erreur : « %2% »"
#, boost-format #, boost-format
msgid "" msgid ""
@ -14597,8 +14690,21 @@ msgid ""
"Error: \"%2%\"" "Error: \"%2%\""
msgstr "" msgstr ""
"Lénumération des imprimantes hôtes a échoué.\n" "Lénumération des imprimantes hôtes a échoué.\n"
"Corps du message : « %1% »\n" "Corps du message : « %1% »\n"
"Erreur : « %2% »" "Erreur : « %2% »"
#~ msgid "End end: "
#~ msgstr "Temp. de fin: "
#~ msgid ""
#~ "We would rename the presets as \"Vendor Type Serial @printer you "
#~ "selected\". \n"
#~ "To add preset for more prinetrs, Please go to printer selection"
#~ msgstr ""
#~ "Nous renommerions les préréglages en « Fournisseur Type Série @imprimante "
#~ "que vous avez sélectionnée ». \n"
#~ "Pour ajouter des préréglages pour dautres imprimantes, veuillez aller à "
#~ "la sélection de limprimante."
#~ msgid "The Config can not be loaded." #~ msgid "The Config can not be loaded."
#~ msgstr "La configuration ne peut pas être chargée." #~ msgstr "La configuration ne peut pas être chargée."

View file

@ -84,9 +84,21 @@ var LangText = {
t89: "Open Containing Folder", t89: "Open Containing Folder",
t90: "3D Model", t90: "3D Model",
t91: "Download 3D models", t91: "Download 3D models",
t92: "Bambu Christmas Cabin", "t92": "Create by",
t93: "Printer Connection", "t93": "Remixed by",
t94: "Please set up your printer connection to view the device.", "t94": "Shared by",
"t95": "Model Information",
"t96": "Accessories",
"t97": "Profile Information",
"t98": "Model name",
"t100":"Model description",
"t101":"BOM",
"t102":"Assembly Guide",
"t103":"Other",
"t104":"Profile name",
"t105":"Profile Author",
"t106":"Profile description",
orca1: "Edit Project Info",
}, },
es_ES: { es_ES: {
t1: "Bienvenido a Orca Slicer", t1: "Bienvenido a Orca Slicer",
@ -173,9 +185,20 @@ var LangText = {
t89: "Abrir carpeta contenedora", t89: "Abrir carpeta contenedora",
t90: "Modelo 3D", t90: "Modelo 3D",
t91: "Descargar modelos 3D", t91: "Descargar modelos 3D",
t92: "Cabina Bambú de Navidad", "t92": "Creado por",
t93: "Conexión de Impresora", "t93": "Remixado por",
t94: "Por favor, configure la conexión de red de la impresora para encontrarla.", "t94": "Compartido por",
"t95": "Información del modelo",
"t96": "Accesorios",
"t97": "Información del perfil",
"t98": "Nombre del modelo",
"t100": "Descripción del modelo",
"t101": "Lista de materiales",
"t102": "Guía de ensamblaje",
"t103": "Otros",
"t104": "Nombre del perfil",
"t105": "Autor del perfil",
"t106": "Descripción del perfil",
}, },
de_DE: { de_DE: {
t1: "Willkommen im Orca Slicer", t1: "Willkommen im Orca Slicer",
@ -262,7 +285,20 @@ var LangText = {
t89: "Enthaltenden Ordner öffnen", t89: "Enthaltenden Ordner öffnen",
t90: "3D-Modell", t90: "3D-Modell",
t91: "3D-Modelle herunterladen", t91: "3D-Modelle herunterladen",
t92: "Bambu Weihnachtshütte", "t92": "Erstellt von",
"t93": "Remixed von",
"t94": "Geteilt von",
"t95": "Modellinformationen",
"t96": "Zubehör",
"t97": "Profilinformationen",
"t98": "Modellname",
"t100": "Modellbeschreibung",
"t101": "Stückliste",
"t102": "Montageanleitung",
"t103": "Andere",
"t104": "Profilname",
"t105": "Profilautor",
"t106": "Profilbeschreibung",
}, },
cs_CZ: { cs_CZ: {
t1: "Vítejte v Orca Slicer", t1: "Vítejte v Orca Slicer",
@ -349,7 +385,20 @@ var LangText = {
t89: "Otevřít složku obsahující", t89: "Otevřít složku obsahující",
t90: "3D model", t90: "3D model",
t91: "Stáhnout 3D modely", t91: "Stáhnout 3D modely",
t92: "Vánoční kabina Bambu", "t92": "Vytvořil",
"t93": "Přepracováno",
"t94": "Sdíleno",
"t95": "Informace o modelu",
"t96": "Příslušenství",
"t97": "Informace o profilu",
"t98": "Název modelu",
"t100":"Popis modelu",
"t101":"Seznam součástek (BOM)",
"t102":"Průvodce sestavením",
"t103":"Jiné",
"t104":"Název profilu",
"t105":"Autor profilu",
"t106":"Popis profilu",
}, },
fr_FR: { fr_FR: {
t1: "Bienvenue sur Orca Slicer", t1: "Bienvenue sur Orca Slicer",
@ -436,9 +485,20 @@ var LangText = {
t89: "Ouvrir le dossier contenant", t89: "Ouvrir le dossier contenant",
t90: "Modèle 3D", t90: "Modèle 3D",
t91: "Télécharger des modèles 3D", t91: "Télécharger des modèles 3D",
t92: "Cabane de Noël Bambu", "t92": "Créé par",
t93: "Connexion à l'imprimante", "t93": "Remixé par",
t94: "Veuillez configurer la connexion de votre imprimante pour afficher l'interface.", "t94": "Partagé par",
"t95": "Informations sur le modèle",
"t96": "Accessoires",
"t97": "Informations de profil",
"t98": "Nom du modèle",
"t100": "Description du modèle",
"t101": "BOM",
"t102": "Guide d'assemblage",
"t103": "Autre",
"t104": "Nom du profil",
"t105": "Auteur du profil",
"t106": "Description du profil",
t109: "Filaments du système", t109: "Filaments du système",
t110: "Filaments personnalisés", t110: "Filaments personnalisés",
t111: "Créer un nouveau filament", t111: "Créer un nouveau filament",
@ -544,7 +604,20 @@ var LangText = {
t89: "打开所在的文件夹", t89: "打开所在的文件夹",
t90: "3D 模型", t90: "3D 模型",
t91: "下载3D模型", t91: "下载3D模型",
t92: "Bambu圣诞小屋", "t92": "创作",
"t93": "修改",
"t94": "分享",
"t95": "模型信息",
"t96": "附件",
"t97": "配置信息",
"t98": "模型名称",
"t100":"模型介绍",
"t101":"物料清单",
"t102":"装备指导",
"t103":"其他",
"t104":"配置名称",
"t105":"配置作者",
"t106":"配置介绍",
wk1: "快速入门指南", wk1: "快速入门指南",
wk2: "本文介绍了Orca Slicer的最基本用法。它指导用户配置软件创建项目并逐步完成第一个打印任务。", wk2: "本文介绍了Orca Slicer的最基本用法。它指导用户配置软件创建项目并逐步完成第一个打印任务。",
wk3: "基于项目的工作流", wk3: "基于项目的工作流",
@ -561,6 +634,7 @@ var LangText = {
wk14: "与STL相比STEP带来了更多有效的信息。由于STEP的高精度切片时可以生成更多的圆弧路径。STEP还包括模型每个零件的装配关系可分割模型后恢复装配视图。", wk14: "与STL相比STEP带来了更多有效的信息。由于STEP的高精度切片时可以生成更多的圆弧路径。STEP还包括模型每个零件的装配关系可分割模型后恢复装配视图。",
wk15: "3D文本", wk15: "3D文本",
wk16: "使用3D文本工具用户可以轻松地在项目中创建各种3D文本形状使模型更加个性化。Orca Slicer提供了数十种字体并支持粗体和斜体样式使文本具有更大的灵活性。", wk16: "使用3D文本工具用户可以轻松地在项目中创建各种3D文本形状使模型更加个性化。Orca Slicer提供了数十种字体并支持粗体和斜体样式使文本具有更大的灵活性。",
orca1: "编辑项目信息",
}, },
zh_TW: { zh_TW: {
t1: "歡迎使用 Orca Slicer", t1: "歡迎使用 Orca Slicer",

View file

@ -66,3 +66,8 @@ a
{ {
background-color:#36363C; background-color:#36363C;
} }
#AddModelInfoBtn:hover
{
color: #000;
}

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="-3 -3 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0769 6.84171C12.0769 6.58678 12.2835 6.38017 12.5385 6.38017C12.7934 6.38017 13 6.58678 13 6.84171V10.6923C13 11.9668 11.9668 13 10.6923 13H3.30769C2.03323 13 1 11.9668 1 10.6923V3.30769C1 2.03323 2.03323 1 3.30769 1H7.7006C7.95553 1 8.16214 1.20661 8.16214 1.46154C8.16214 1.71647 7.95553 1.92308 7.7006 1.92308H3.30769C2.54309 1.92308 1.92308 2.54309 1.92308 3.30769V10.6923C1.92308 11.4569 2.54309 12.0769 3.30769 12.0769H10.6923C11.4569 12.0769 12.0769 11.4571 12.0769 10.6923V6.84171ZM11.8775 1.74946C12.0528 1.57007 12.3392 1.5634 12.5226 1.73431C12.706 1.90541 12.7191 2.19171 12.5523 2.37885L7.51689 7.77975C7.34309 7.96617 7.05102 7.97644 6.86442 7.80264C6.67801 7.62885 6.66773 7.33678 6.84153 7.15018L11.8775 1.74946Z" fill="#6B6B6B"/>
</svg>

After

Width:  |  Height:  |  Size: 865 B

View file

@ -26,6 +26,7 @@
<div id="EmptyArea"> <div id="EmptyArea">
<div><img src="img/null.png"></div> <div><img src="img/null.png"></div>
<div>no model information</div> <div>no model information</div>
<div id="AddModelInfoBtn" class="trans TextS1" tid='orca1' onClick="OnClickEditProjectInfo()">Edit Project Info</div>
</div> </div>
<div id="WholeArea"> <div id="WholeArea">
@ -42,6 +43,9 @@
<div id="Profile_ProcessBar" class="LeftProcessBar" onclick="OnMenuClick('Model_Profile');"> <div id="Profile_ProcessBar" class="LeftProcessBar" onclick="OnMenuClick('Model_Profile');">
<img class="LeftTipIcon ProfileIcon" src="img/profile_h.svg" /><span class="trans" tid='t97'>Profile Information</span> <img class="LeftTipIcon ProfileIcon" src="img/profile_h.svg" /><span class="trans" tid='t97'>Profile Information</span>
</div> </div>
<div id="Edit_ProcessBar" class="LeftProcessBar" onclick="OnClickEditProjectInfo();">
<img class="LeftTipIcon" src="img/edit.svg" /><span class="trans" tid='orca1'>Edit Project Info</span>
</div>
</div> </div>
<div id="LeftEmptyBlock" style="height: 100%;width:280px;">&nbsp;</div> <div id="LeftEmptyBlock" style="height: 100%;width:280px;">&nbsp;</div>

View file

@ -153,7 +153,7 @@ body
position:fixed; position:fixed;
top: 24px; top: 24px;
width: 264px; width: 264px;
height: 120px; height: 160px;
flex-shrink: 0; flex-shrink: 0;
} }
@ -376,3 +376,19 @@ body
background-color: rgba(255,0,0,.5)!important; background-color: rgba(255,0,0,.5)!important;
} }
#AddModelInfoBtn
{
border-width: 1px;
border-style: solid;
padding: 0px 10px;
border-radius: 6px;
line-height: 26px;
height: 26px;
margin-top: 20px;
cursor: pointer;
}
#AddModelInfoBtn:hover
{
background-color:#CDCECE;
}

View file

@ -580,7 +580,14 @@ function OnClickOpenImage( F_ID )
$("img#"+F_ID).click(); $("img#"+F_ID).click();
} }
function OnClickEditProjectInfo()
{
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="edit_project_info";
SendWXMessage( JSON.stringify(tSend) );
}

View file

@ -157,6 +157,8 @@ set(lisbslic3r_sources
GCode/PrintExtents.hpp GCode/PrintExtents.hpp
GCode/RetractWhenCrossingPerimeters.cpp GCode/RetractWhenCrossingPerimeters.cpp
GCode/RetractWhenCrossingPerimeters.hpp GCode/RetractWhenCrossingPerimeters.hpp
GCode/SmallAreaInfillFlowCompensator.cpp
GCode/SmallAreaInfillFlowCompensator.hpp
GCode/SpiralVase.cpp GCode/SpiralVase.cpp
GCode/SpiralVase.hpp GCode/SpiralVase.hpp
GCode/SeamPlacer.cpp GCode/SeamPlacer.cpp

View file

@ -1849,6 +1849,15 @@ static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(c
//BBS: add sort logic for seq-print //BBS: add sort logic for seq-print
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order) std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order)
{ {
auto find_object_index = [](const Model& model, const ModelObject* obj) {
for (int index = 0; index < model.objects.size(); index++)
{
if (model.objects[index] == obj)
return index;
}
return -1;
};
// Build up map from ModelInstance* to PrintInstance* // Build up map from ModelInstance* to PrintInstance*
std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance; std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance;
model_instance_to_print_instance.reserve(print.num_object_instances()); model_instance_to_print_instance.reserve(print.num_object_instances());
@ -1856,10 +1865,16 @@ std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Pri
for (const PrintInstance &print_instance : print_object->instances()) for (const PrintInstance &print_instance : print_object->instances())
{ {
if (init_order) if (init_order)
const_cast<ModelInstance*>(print_instance.model_instance)->arrange_order = print_instance.model_instance->id().id; const_cast<ModelInstance*>(print_instance.model_instance)->arrange_order = find_object_index(print.model(), print_object->model_object());
model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance); model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance);
} }
std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first->arrange_order < r.first->arrange_order; }); std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first->arrange_order < r.first->arrange_order; });
if (init_order) {
// Re-assign the arrange_order so each instance has a unique order number
for (int k = 0; k < model_instance_to_print_instance.size(); k++) {
const_cast<ModelInstance*>(model_instance_to_print_instance[k].first)->arrange_order = k + 1;
}
}
std::vector<const PrintInstance*> instances; std::vector<const PrintInstance*> instances;
instances.reserve(model_instance_to_print_instance.size()); instances.reserve(model_instance_to_print_instance.size());
@ -1980,6 +1995,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
} else } else
m_enable_extrusion_role_markers = false; m_enable_extrusion_role_markers = false;
if (!print.config().small_area_infill_flow_compensation_model.empty())
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
// if thumbnail type of BTT_TFT, insert above header // if thumbnail type of BTT_TFT, insert above header
// if not, it is inserted under the header in its normal spot // if not, it is inserted under the header in its normal spot
const GCodeThumbnailsFormat m_gcode_thumbnail_format = print.full_print_config().opt_enum<GCodeThumbnailsFormat>("thumbnails_format"); const GCodeThumbnailsFormat m_gcode_thumbnail_format = print.full_print_config().opt_enum<GCodeThumbnailsFormat>("thumbnails_format");
@ -2215,8 +2233,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there. // Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders()); this->set_extruders(tool_ordering.all_extruders());
// Order object instances using a nearest neighbor search. print_object_instances_ordering =
print_object_instances_ordering = chain_print_object_instances(print); // By default, order object instances using a nearest neighbor search.
print.config().print_order == PrintOrder::Default ? chain_print_object_instances(print)
// Otherwise same order as the object list
: sort_object_instances_by_model_order(print);
} }
if (initial_extruder_id == (unsigned int)-1) { if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print! // Nothing to print!
@ -2540,9 +2561,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// another one, set first layer temperatures. This happens before the Z move // another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures. // is triggered, so machine has more time to reach such temperatures.
this->placeholder_parser().set("current_object_idx", int(finished_objects)); this->placeholder_parser().set("current_object_idx", int(finished_objects));
//BBS: remove printing_by_object_gcode std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id);
//std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id);
std::string printing_by_object_gcode;
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, printing_by_object_gcode, initial_extruder_id, false); this->_print_first_layer_bed_temperature(file, print, printing_by_object_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, printing_by_object_gcode, initial_extruder_id, false); this->_print_first_layer_extruder_temperatures(file, print, printing_by_object_gcode, initial_extruder_id, false);
@ -4020,7 +4039,7 @@ LayerResult GCode::process_layer(
std::vector<InstanceToPrint> instances_to_print; std::vector<InstanceToPrint> instances_to_print;
bool has_prime_tower = print.config().enable_prime_tower bool has_prime_tower = print.config().enable_prime_tower
&& print.extruders().size() > 1 && print.extruders().size() > 1
&& (print.config().print_sequence == PrintSequence::ByLayer && ((print.config().print_sequence == PrintSequence::ByLayer && print.config().print_order == PrintOrder::Default)
|| (print.config().print_sequence == PrintSequence::ByObject && print.objects().size() == 1)); || (print.config().print_sequence == PrintSequence::ByObject && print.objects().size() == 1));
if (has_prime_tower) { if (has_prime_tower) {
int plate_idx = print.get_plate_index(); int plate_idx = print.get_plate_index();
@ -5268,15 +5287,25 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
for (const Line& line : path.polyline.lines()) { for (const Line& line : path.polyline.lines()) {
const double line_length = line.length() * SCALING_FACTOR; const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length; path_length += line_length;
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
description += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode += m_writer.extrude_to_xy( gcode += m_writer.extrude_to_xy(
this->point_to_gcode(line.b), this->point_to_gcode(line.b),
e_per_mm * line_length, dE,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion()); GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
} }
} else { } else {
// BBS: start to generate gcode from arc fitting data which includes line and arc // BBS: start to generate gcode from arc fitting data which includes line and arc
const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result; const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result;
for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) { for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) {
std::string tempDescription = description;
switch (fitting_result[fitting_index].path_type) { switch (fitting_result[fitting_index].path_type) {
case EMovePathType::Linear_move: { case EMovePathType::Linear_move: {
size_t start_index = fitting_result[fitting_index].start_point_index; size_t start_index = fitting_result[fitting_index].start_point_index;
@ -5285,10 +5314,19 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]); const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
const double line_length = line.length() * SCALING_FACTOR; const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length; path_length += line_length;
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode += m_writer.extrude_to_xy( gcode += m_writer.extrude_to_xy(
this->point_to_gcode(line.b), this->point_to_gcode(line.b),
e_per_mm * line_length, dE,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion()); GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
} }
break; break;
} }
@ -5298,12 +5336,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR; const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point); const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
path_length += arc_length; path_length += arc_length;
auto dE = e_per_mm * arc_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(arc_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, arc_length);
}
}
gcode += m_writer.extrude_arc_to_xy( gcode += m_writer.extrude_arc_to_xy(
this->point_to_gcode(arc.end_point), this->point_to_gcode(arc.end_point),
center_offset, center_offset,
e_per_mm * arc_length, dE,
arc.direction == ArcDirection::Arc_Dir_CCW, arc.direction == ArcDirection::Arc_Dir_CCW,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion()); GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
break; break;
} }
default: default:
@ -5325,6 +5372,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role()); pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role());
for (size_t i = 1; i < new_points.size(); i++) { for (size_t i = 1; i < new_points.size(); i++) {
std::string tempDescription = description;
const ProcessedPoint &processed_point = new_points[i]; const ProcessedPoint &processed_point = new_points[i];
const ProcessedPoint &pre_processed_point = new_points[i-1]; const ProcessedPoint &pre_processed_point = new_points[i-1];
Vec2d p = this->point_to_gcode_quantized(processed_point.p); Vec2d p = this->point_to_gcode_quantized(processed_point.p);
@ -5363,8 +5411,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += m_writer.set_speed(new_speed, "", comment); gcode += m_writer.set_speed(new_speed, "", comment);
last_set_speed = new_speed; last_set_speed = new_speed;
} }
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode += gcode +=
m_writer.extrude_to_xy(p, e_per_mm * line_length, GCodeWriter::full_gcode_comment ? description : ""); m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
prev = p; prev = p;

View file

@ -23,6 +23,7 @@
#include "GCode/ExtrusionProcessor.hpp" #include "GCode/ExtrusionProcessor.hpp"
#include "GCode/PressureEqualizer.hpp" #include "GCode/PressureEqualizer.hpp"
#include "GCode/SmallAreaInfillFlowCompensator.hpp"
#include <memory> #include <memory>
#include <map> #include <map>
@ -536,6 +537,8 @@ private:
std::unique_ptr<WipeTowerIntegration> m_wipe_tower; std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
std::unique_ptr<SmallAreaInfillFlowCompensator> m_small_area_infill_flow_compensator;
// Heights (print_z) at which the skirt has already been extruded. // Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done; std::vector<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
@ -598,6 +601,7 @@ private:
friend class WipeTowerIntegration; friend class WipeTowerIntegration;
friend class PressureEqualizer; friend class PressureEqualizer;
friend class Print; friend class Print;
friend class SmallAreaInfillFlowCompensator;
}; };
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order = false); std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order = false);

View file

@ -0,0 +1,88 @@
// Modify the flow of extrusion lines inversely proportional to the length of
// the extrusion line. When infill lines get shorter the flow rate will auto-
// matically be reduced to mitigate the effect of small infill areas being
// over-extruded.
// Based on original work by Alexander Þór licensed under the GPLv3:
// https://github.com/Alexander-T-Moss/Small-Area-Flow-Comp
#include <math.h>
#include <cstring>
#include <cfloat>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "SmallAreaInfillFlowCompensator.hpp"
namespace Slic3r {
bool nearly_equal(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
SmallAreaInfillFlowCompensator::SmallAreaInfillFlowCompensator(const Slic3r::GCodeConfig& config)
{
for (auto& line : config.small_area_infill_flow_compensation_model.values) {
std::istringstream iss(line);
std::string value_str;
double eLength = 0.0;
if (std::getline(iss, value_str, ',')) {
try {
eLength = std::stod(value_str);
if (std::getline(iss, value_str, ',')) {
eLengths.push_back(eLength);
flowComps.push_back(std::stod(value_str));
}
} catch (...) {
std::stringstream ss;
ss << "Error parsing data point in small area infill compensation model:" << line << std::endl;
throw Slic3r::InvalidArgument(ss.str());
}
}
}
for (int i = 0; i < eLengths.size(); i++) {
if (i == 0) {
if (!nearly_equal(eLengths[i], 0.0)) {
throw Slic3r::InvalidArgument("First extrusion length for small area infill compensation model must be 0");
}
} else {
if (nearly_equal(eLengths[i], 0.0)) {
throw Slic3r::InvalidArgument("Only the first extrusion length for small area infill compensation model can be 0");
}
if (eLengths[i] <= eLengths[i - 1]) {
throw Slic3r::InvalidArgument("Extrusion lengths for subsequent points must be increasing");
}
}
}
if (!flowComps.empty() && !nearly_equal(flowComps.back(), 1.0)) {
throw Slic3r::InvalidArgument("Final compensation factor for small area infill flow compensation model must be 1.0");
}
flowModel.set_points(eLengths, flowComps);
}
double SmallAreaInfillFlowCompensator::flow_comp_model(const double line_length)
{
if (line_length == 0 || line_length > max_modified_length()) {
return 1.0;
}
return flowModel(line_length);
}
double SmallAreaInfillFlowCompensator::modify_flow(const double line_length, const double dE, const ExtrusionRole role)
{
if (role == ExtrusionRole::erSolidInfill || role == ExtrusionRole::erTopSolidInfill || role == ExtrusionRole::erBottomSurface) {
return dE * flow_comp_model(line_length);
}
return dE;
}
} // namespace Slic3r

View file

@ -0,0 +1,35 @@
#ifndef slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_
#define slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "../ExtrusionEntity.hpp"
#include "spline/spline.h"
namespace Slic3r {
class SmallAreaInfillFlowCompensator
{
public:
SmallAreaInfillFlowCompensator() = delete;
explicit SmallAreaInfillFlowCompensator(const Slic3r::GCodeConfig& config);
~SmallAreaInfillFlowCompensator() = default;
double modify_flow(const double line_length, const double dE, const ExtrusionRole role);
private:
// Model points
std::vector<double> eLengths;
std::vector<double> flowComps;
// TODO: Cubic Spline
tk::spline flowModel;
double flow_comp_model(const double line_length);
double max_modified_length() { return eLengths.back(); }
};
} // namespace Slic3r
#endif /* slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_ */

View file

@ -836,7 +836,7 @@ end:
// BBS: backup all in one dir // BBS: backup all in one dir
std::string Model::get_auxiliary_file_temp_path() std::string Model::get_auxiliary_file_temp_path()
{ {
return get_backup_path("/Auxiliaries"); return get_backup_path("Auxiliaries");
} }
// BBS: backup dir // BBS: backup dir

View file

@ -786,7 +786,7 @@ static std::vector<std::string> s_Preset_print_options {
"independent_support_layer_height", "independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers", "support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang", "support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance", "filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body", "sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width", "ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
@ -817,6 +817,7 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder", "wiping_volumes_extruders","wipe_tower_bridging", "single_extruder_multi_material_priming", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder", "wiping_volumes_extruders","wipe_tower_bridging", "single_extruder_multi_material_priming",
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
"small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model",
}; };
static std::vector<std::string> s_Preset_filament_options { static std::vector<std::string> s_Preset_filament_options {

View file

@ -314,6 +314,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
//|| opt_key == "resolution" //|| opt_key == "resolution"
//BBS: when enable arc fitting, we must re-generate perimeter //BBS: when enable arc fitting, we must re-generate perimeter
|| opt_key == "enable_arc_fitting" || opt_key == "enable_arc_fitting"
|| opt_key == "print_order"
|| opt_key == "wall_sequence") { || opt_key == "wall_sequence") {
osteps.emplace_back(posPerimeters); osteps.emplace_back(posPerimeters);
osteps.emplace_back(posEstimateCurledExtrusions); osteps.emplace_back(posEstimateCurledExtrusions);
@ -1043,6 +1044,7 @@ boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.
StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
{ {
std::vector<unsigned int> extruders = this->extruders(); std::vector<unsigned int> extruders = this->extruders();
unsigned int nozzles = m_config.nozzle_diameter.size();
if (m_objects.empty()) if (m_objects.empty())
return {std::string()}; return {std::string()};
@ -1050,7 +1052,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (extruders.empty()) if (extruders.empty())
return { L("No extrusions under current settings.") }; return { L("No extrusions under current settings.") };
if (extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) { if (nozzles < 2 && extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) {
auto ret = check_multi_filament_valid(*this); auto ret = check_multi_filament_valid(*this);
if (!ret.string.empty()) if (!ret.string.empty())
{ {

View file

@ -195,6 +195,12 @@ static t_config_enum_values s_keys_map_PrintSequence {
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintSequence) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintSequence)
static t_config_enum_values s_keys_map_PrintOrder{
{ "default", int(PrintOrder::Default) },
{ "as_obj_list", int(PrintOrder::AsObjectList)},
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintOrder)
static t_config_enum_values s_keys_map_SlicingMode { static t_config_enum_values s_keys_map_SlicingMode {
{ "regular", int(SlicingMode::Regular) }, { "regular", int(SlicingMode::Regular) },
{ "even_odd", int(SlicingMode::EvenOdd) }, { "even_odd", int(SlicingMode::EvenOdd) },
@ -1171,6 +1177,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple; def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<PrintSequence>(PrintSequence::ByLayer)); def->set_default_value(new ConfigOptionEnum<PrintSequence>(PrintSequence::ByLayer));
def = this->add("print_order", coEnum);
def->label = L("Layer order");
def->tooltip = L("Print order within a single layer");
def->enum_keys_map = &ConfigOptionEnum<PrintOrder>::get_enum_values();
def->enum_values.push_back("default");
def->enum_values.push_back("as_obj_list");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("As object list"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PrintOrder>(PrintOrder::Default));
def = this->add("slow_down_for_layer_cooling", coBools); def = this->add("slow_down_for_layer_cooling", coBools);
def->label = L("Slow printing down for better layer cooling"); def->label = L("Slow printing down for better layer cooling");
def->tooltip = L("Enable this option to slow printing speed down to make the final layer time not shorter than " def->tooltip = L("Enable this option to slow printing speed down to make the final layer time not shorter than "
@ -2668,6 +2685,26 @@ def = this->add("filament_loading_speed", coFloats);
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("")); def->set_default_value(new ConfigOptionString(""));
def = this->add("small_area_infill_flow_compensation", coBool);
def->label = L("Enable Flow Compensation");
def->tooltip = L("Enable flow compensation for small infill areas");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("small_area_infill_flow_compensation_model", coStrings);
def->label = L("Flow Compensation Model");
def->tooltip = L(
"Flow Compensation Model, used to adjust the flow for small infill "
"areas. The model is expressed as a comma separated pair of values for "
"extrusion length and flow correction factors, one per line, in the "
"following format: \"1.234,5.678\"");
def->mode = comAdvanced;
def->gui_flags = "serialized";
def->multiline = true;
def->full_width = true;
def->height = 15;
def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"});
{ {
struct AxisDefault { struct AxisDefault {
std::string name; std::string name;

View file

@ -106,6 +106,13 @@ enum class PrintSequence {
Count, Count,
}; };
enum class PrintOrder
{
Default,
AsObjectList,
Count,
};
enum class SlicingMode enum class SlicingMode
{ {
// Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons. // Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons.
@ -920,6 +927,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<WallSequence>, wall_sequence)) ((ConfigOptionEnum<WallSequence>, wall_sequence))
((ConfigOptionBool, is_infill_first)) ((ConfigOptionBool, is_infill_first))
((ConfigOptionBool, small_area_infill_flow_compensation))
) )
PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE(
@ -1067,6 +1075,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, enable_filament_ramming)) ((ConfigOptionBool, enable_filament_ramming))
((ConfigOptionBool, support_multi_bed_types)) ((ConfigOptionBool, support_multi_bed_types))
// Small Area Infill Flow Compensation
((ConfigOptionStrings, small_area_infill_flow_compensation_model))
) )
// This object is mapped to Perl as Slic3r::Config::Print. // This object is mapped to Perl as Slic3r::Config::Print.
@ -1098,6 +1108,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInts, overhang_fan_speed)) ((ConfigOptionInts, overhang_fan_speed))
((ConfigOptionEnumsGeneric, overhang_fan_threshold)) ((ConfigOptionEnumsGeneric, overhang_fan_threshold))
((ConfigOptionEnum<PrintSequence>,print_sequence)) ((ConfigOptionEnum<PrintSequence>,print_sequence))
((ConfigOptionEnum<PrintOrder>, print_order))
((ConfigOptionInts, first_layer_print_sequence)) ((ConfigOptionInts, first_layer_print_sequence))
((ConfigOptionBools, slow_down_for_layer_cooling)) ((ConfigOptionBools, slow_down_for_layer_cooling))
((ConfigOptionInts, close_fan_the_first_x_layers)) ((ConfigOptionInts, close_fan_the_first_x_layers))

View file

@ -928,6 +928,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wipe_on_loops" || opt_key == "wipe_on_loops"
|| opt_key == "wipe_speed") { || opt_key == "wipe_speed") {
steps.emplace_back(posPerimeters); steps.emplace_back(posPerimeters);
} else if (
opt_key == "small_area_infill_flow_compensation"
|| opt_key == "small_area_infill_flow_compensation_model") {
steps.emplace_back(posSlice);
} else if (opt_key == "gap_infill_speed" } else if (opt_key == "gap_infill_speed"
|| opt_key == "filter_out_gap_fill" ) { || opt_key == "filter_out_gap_fill" ) {
// Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero. // Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero.

View file

@ -33,17 +33,29 @@ wxDEFINE_EVENT(EVT_AUXILIARY_IMPORT, wxCommandEvent);
wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_COVER, wxCommandEvent); wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_COVER, wxCommandEvent);
wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_DELETE, wxCommandEvent); wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_DELETE, wxCommandEvent);
wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_RENAME, wxCommandEvent); wxDEFINE_EVENT(EVT_AUXILIARY_UPDATE_RENAME, wxCommandEvent);
wxDEFINE_EVENT(EVT_AUXILIARY_DONE, wxCommandEvent);
const std::vector<std::string> license_list = { const std::vector<std::string> license_list = {
"BSD License", "",
"Apache License", "CC0",
"GPL License", "BY",
"LGPL License", "BY-SA",
"MIT License", "BY-ND",
"CC License" "BY-NC",
"BY-NC-SA",
"BY-NC-ND",
}; };
static std::shared_ptr<ModelInfo> ensure_model_info()
{
auto& model = wxGetApp().plater()->model();
if (model.model_info == nullptr) {
model.model_info = std::make_shared<ModelInfo>();
}
return model.model_info;
}
AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, AuxiliaryFolderType type, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, AuxiliaryFolderType type, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
{ {
m_type = type; m_type = type;
@ -344,7 +356,7 @@ void AuFile::on_input_enter(wxCommandEvent &evt)
} }
auto existing = false; auto existing = false;
auto dir = m_file_path.branch_path(); auto dir = m_file_path.parent_path();
auto new_fullname = new_file_name + m_file_path.extension().string(); auto new_fullname = new_file_name + m_file_path.extension().string();
@ -454,14 +466,12 @@ void AuFile::on_mouse_left_up(wxMouseEvent &evt)
void AuFile::on_set_cover() void AuFile::on_set_cover()
{ {
if (wxGetApp().plater()->model().model_info == nullptr) { wxGetApp().plater()->model().model_info = std::make_shared<ModelInfo>(); }
fs::path path(into_path(m_file_name)); fs::path path(into_path(m_file_name));
wxGetApp().plater()->model().model_info->cover_file = path.string(); ensure_model_info()->cover_file = path.string();
//wxGetApp().plater()->model().model_info->cover_file = m_file_name.ToStdString(); //wxGetApp().plater()->model().model_info->cover_file = m_file_name.ToStdString();
auto full_path = m_file_path.branch_path(); auto full_path = m_file_path.parent_path();
auto full_root_path = full_path.branch_path(); auto full_root_path = full_path.parent_path();
auto full_root_path_str = encode_path(full_root_path.string().c_str()); auto full_root_path_str = encode_path(full_root_path.string().c_str());
auto dir = wxString::Format("%s/.thumbnails", full_root_path_str); auto dir = wxString::Format("%s/.thumbnails", full_root_path_str);
@ -505,8 +515,8 @@ void AuFile::on_set_delete()
auto is_fine = fs::remove(bfs_path); auto is_fine = fs::remove(bfs_path);
if (m_cover) { if (m_cover) {
auto full_path = m_file_path.branch_path(); auto full_path = m_file_path.parent_path();
auto full_root_path = full_path.branch_path(); auto full_root_path = full_path.parent_path();
auto full_root_path_str = encode_path(full_root_path.string().c_str()); auto full_root_path_str = encode_path(full_root_path.string().c_str());
auto dir = wxString::Format("%s/.thumbnails", full_root_path_str); auto dir = wxString::Format("%s/.thumbnails", full_root_path_str);
fs::path dir_path(dir.ToStdWstring()); fs::path dir_path(dir.ToStdWstring());
@ -520,8 +530,11 @@ void AuFile::on_set_delete()
if (fs::exists(fs::path(middle_img_path))) { fs::remove(fs::path(middle_img_path)); } if (fs::exists(fs::path(middle_img_path))) { fs::remove(fs::path(middle_img_path)); }
} }
if (wxGetApp().plater()->model().model_info == nullptr) { wxGetApp().plater()->model().model_info = std::make_shared<ModelInfo>(); } if (wxGetApp().plater()->model().model_info != nullptr) {
if (wxGetApp().plater()->model().model_info->cover_file == m_file_name) { wxGetApp().plater()->model().model_info->cover_file = ""; } if (wxGetApp().plater()->model().model_info->cover_file == m_file_name) {
wxGetApp().plater()->model().model_info->cover_file = "";
}
}
if (is_fine) { if (is_fine) {
auto evt = wxCommandEvent(EVT_AUXILIARY_UPDATE_DELETE); auto evt = wxCommandEvent(EVT_AUXILIARY_UPDATE_DELETE);
@ -669,6 +682,7 @@ void AuFolderPanel::update(std::vector<fs::path> paths)
} }
m_gsizer_content->Layout(); m_gsizer_content->Layout();
Layout(); Layout();
Refresh();
} }
void AuFolderPanel::msw_rescale() void AuFolderPanel::msw_rescale()
@ -820,9 +834,22 @@ void AuxiliaryPanel::init_bitmap()
void AuxiliaryPanel::init_tabpanel() void AuxiliaryPanel::init_tabpanel()
{ {
auto m_side_tools = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(220), FromDIP(18))); StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Disabled),
std::pair<wxColour, int>(wxColour(0, 137, 123), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(38, 166, 154), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 150, 136), StateColor::Normal));
auto back_btn = new Button(this, _L("Back"), "assemble_return", wxBORDER_NONE | wxBU_LEFT | wxBU_EXACTFIT);
back_btn->SetSize(wxSize(FromDIP(220), FromDIP(18)));
back_btn->SetBackgroundColor(btn_bg_green);
back_btn->SetCornerRadius(0);
back_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxEvent& e) {
auto event = wxCommandEvent(EVT_AUXILIARY_DONE);
event.SetEventObject(m_parent);
wxPostEvent(m_parent, event);
});
wxBoxSizer *sizer_side_tools = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sizer_side_tools = new wxBoxSizer(wxVERTICAL);
sizer_side_tools->Add(m_side_tools, 1, wxEXPAND, 0); sizer_side_tools->Add(back_btn, 1, wxEXPAND, 0);
m_tabpanel = new Tabbook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, sizer_side_tools, wxNB_LEFT | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME); m_tabpanel = new Tabbook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, sizer_side_tools, wxNB_LEFT | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
m_tabpanel->SetBackgroundColour(wxColour("#FEFFFF")); m_tabpanel->SetBackgroundColour(wxColour("#FEFFFF"));
m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [this](wxBookCtrlEvent &e) { ; }); m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [this](wxBookCtrlEvent &e) { ; });
@ -872,20 +899,7 @@ bool AuxiliaryPanel::Show(bool show) { return wxPanel::Show(show); }
void AuxiliaryPanel::init_auxiliary() void AuxiliaryPanel::init_auxiliary()
{ {
Model &model = wxGetApp().plater()->model(); Model &model = wxGetApp().plater()->model();
m_root_dir = encode_path(model.get_auxiliary_file_temp_path().c_str()); Reload(encode_path(model.get_auxiliary_file_temp_path().c_str()), {});
if (wxDirExists(m_root_dir)) {
fs::path path_to_del(m_root_dir.ToStdWstring());
try {
fs::remove_all(path_to_del);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
}
}
fs::path top_dir_path(m_root_dir.ToStdWstring());
fs::create_directory(top_dir_path);
for (auto folder : s_default_folders) create_folder(folder);
} }
void AuxiliaryPanel::on_import_file(wxCommandEvent &event) void AuxiliaryPanel::on_import_file(wxCommandEvent &event)
@ -947,7 +961,7 @@ void AuxiliaryPanel::on_import_file(wxCommandEvent &event)
boost::system::error_code ec; boost::system::error_code ec;
if (!fs::copy_file(src_bfs_path, fs::path(dir_path.ToStdWstring()), fs::copy_option::overwrite_if_exists, ec)) continue; if (!fs::copy_file(src_bfs_path, fs::path(dir_path.ToStdWstring()), fs::copy_options::overwrite_existing, ec)) continue;
Slic3r::put_other_changes(); Slic3r::put_other_changes();
// add in file list // add in file list
@ -987,76 +1001,22 @@ std::string AuxiliaryPanel::replaceSpace(std::string s, std::string ts, std::str
return s; return s;
} }
void AuxiliaryPanel::Reload(wxString aux_path) void AuxiliaryPanel::Reload(wxString aux_path, std::map<std::string, std::vector<json>> paths)
{ {
fs::path new_aux_path(aux_path.ToStdWstring());
try {
fs::remove_all(fs::path(m_root_dir.ToStdWstring()));
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory " << m_root_dir.c_str();
}
m_root_dir = aux_path; m_root_dir = aux_path;
m_paths_list.clear(); m_paths_list.clear();
// Check new path. If not exist, create a new one.
if (!fs::exists(new_aux_path)) {
fs::create_directory(new_aux_path);
// Create default folders if they are not loaded
for (auto folder : s_default_folders) {
wxString folder_path = aux_path + "/" + folder;
if (fs::exists(folder_path.ToStdWstring())) continue;
fs::create_directory(folder_path.ToStdWstring());
}
update_all_panel();
m_designer_panel->update_info();
return;
}
// Load from new path for (const auto & path : paths) {
std::vector<fs::path> dir_cache; m_paths_list[path.first] = std::vector<fs::path>{};
fs::directory_iterator iter_end; for (const auto & j : path.second) {
m_paths_list[path.first].push_back(j["_filepath"]);
for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) {
wxString path = iter->path().generic_wstring();
dir_cache.push_back(iter->path());
} }
for (auto dir : dir_cache) {
for (fs::directory_iterator iter(dir); iter != iter_end; iter++) {
if (fs::is_directory(iter->path())) continue;
wxString file_path = iter->path().generic_wstring();
//auto file_path_str = encode_path(file_path.c_str());
for (auto folder : s_default_folders) {
auto idx = file_path.find(folder.ToStdString());
if (idx != std::string::npos) {
auto iter = m_paths_list.find(folder.ToStdString());
auto file_path_str = fs::path(file_path.ToStdWstring());
if (iter != m_paths_list.end()) {
m_paths_list[folder.ToStdString()].push_back(file_path_str);
break;
} else {
m_paths_list[folder.ToStdString()] = std::vector<fs::path>{file_path_str};
break;
}
}
}
}
}
// Create default folders if they are not loaded
wxDataViewItemArray default_items;
for (auto folder : s_default_folders) {
wxString folder_path = aux_path + "/" + folder;
if (fs::exists(folder_path.ToStdWstring())) continue;
fs::create_directory(folder_path.ToStdWstring());
} }
update_all_panel(); update_all_panel();
update_all_cover(); update_all_cover();
m_designer_panel->update_info(); m_designer_panel->update_info();
m_tabpanel->SetSelection(0);
} }
void AuxiliaryPanel::update_all_panel() void AuxiliaryPanel::update_all_panel()
@ -1121,22 +1081,21 @@ void AuxiliaryPanel::update_all_cover()
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1)); m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_sizer_model_name->Add(m_imput_model_name, 0, wxALIGN_CENTER, 0); m_sizer_model_name->Add(m_imput_model_name, 0, wxALIGN_CENTER, 0);
/*
wxBoxSizer *m_sizer_license = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *m_sizer_license = new wxBoxSizer(wxHORIZONTAL);
auto m_text_license = new wxStaticText(this, wxID_ANY, _L("License"), wxDefaultPosition, wxSize(120, -1), 0); auto m_text_license = new wxStaticText(this, wxID_ANY, _L("License"), wxDefaultPosition, wxSize(180, -1), 0);
m_text_license->Wrap(-1); m_text_license->Wrap(-1);
m_sizer_license->Add(m_text_license, 0, wxALIGN_CENTER, 0); m_sizer_license->Add(m_text_license, 0, wxALIGN_CENTER, 0);
m_combo_license = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(450, -1), 0, NULL, wxCB_READONLY); m_combo_license = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(450), -1), 0, NULL, wxCB_READONLY);
m_sizer_license->Add(m_combo_license, 0, wxALIGN_CENTER, 0); m_sizer_license->Add(m_combo_license, 0, wxALIGN_CENTER, 0);
*/
m_sizer_body->Add( 0, 0, 0, wxTOP, FromDIP(50) ); m_sizer_body->Add( 0, 0, 0, wxTOP, FromDIP(50) );
m_sizer_body->Add(m_sizer_designer, 0, wxLEFT, FromDIP(50)); m_sizer_body->Add(m_sizer_designer, 0, wxLEFT, FromDIP(50));
m_sizer_body->Add( 0, 0, 0, wxTOP, FromDIP(20)); m_sizer_body->Add( 0, 0, 0, wxTOP, FromDIP(20));
m_sizer_body->Add(m_sizer_model_name, 0, wxLEFT, FromDIP(50)); m_sizer_body->Add(m_sizer_model_name, 0, wxLEFT, FromDIP(50));
//m_sizer_body->Add(0, 0, 0, wxTOP, FromDIP(20)); m_sizer_body->Add(0, 0, 0, wxTOP, FromDIP(20));
//m_sizer_body->Add(m_sizer_license, 0, wxLEFT, FromDIP(50)); m_sizer_body->Add(m_sizer_license, 0, wxLEFT, FromDIP(50));
//init_license_list(); init_license_list();
SetSizer(m_sizer_body); SetSizer(m_sizer_body);
Layout(); Layout();
@ -1144,52 +1103,35 @@ void AuxiliaryPanel::update_all_cover()
m_input_designer->Bind(wxEVT_TEXT, &DesignerPanel::on_input_enter_designer, this); m_input_designer->Bind(wxEVT_TEXT, &DesignerPanel::on_input_enter_designer, this);
m_imput_model_name->Bind(wxEVT_TEXT, &DesignerPanel::on_input_enter_model, this); m_imput_model_name->Bind(wxEVT_TEXT, &DesignerPanel::on_input_enter_model, this);
//m_combo_license->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(DesignerPanel::on_select_license), NULL, this); m_combo_license->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &DesignerPanel::on_select_license, this);
} }
DesignerPanel::~DesignerPanel() DesignerPanel::~DesignerPanel()
{ {
//m_combo_license->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(DesignerPanel::on_select_license), NULL, this);
} }
void DesignerPanel::init_license_list() void DesignerPanel::init_license_list()
{ {
/*
wxArrayString text_licese; wxArrayString text_licese;
for (int i = 0; i < license_list.size(); i++) { for (int i = 0; i < license_list.size(); i++) {
text_licese.Add(license_list[i]); text_licese.Add(license_list[i]);
} }
m_combo_license->Set(text_licese); m_combo_license->Set(text_licese);
*/
} }
void DesignerPanel::on_select_license(wxCommandEvent&evt) void DesignerPanel::on_select_license(wxCommandEvent&evt)
{ {
int selected = evt.GetInt(); int selected = evt.GetInt();
if (selected >= 0 && selected < license_list.size()) { if (selected >= 0 && selected < license_list.size()) {
if (wxGetApp().plater()->model().model_info == nullptr) { ensure_model_info()->license = license_list[selected];
wxGetApp().plater()->model().model_info = std::make_shared<ModelInfo>();
}
if (wxGetApp().plater()->model().model_info != nullptr) {
wxGetApp().plater()->model().model_info->license = license_list[selected];
}
} }
} }
bool DesignerPanel::Show(bool show) bool DesignerPanel::Show(bool show)
{ {
if ( wxGetApp().plater()->model().design_info != nullptr) { if (show) update_info();
wxString text = wxString::FromUTF8(wxGetApp().plater()->model().design_info->Designer);
m_input_designer->GetTextCtrl()->SetValue(text);
}
if (wxGetApp().plater()->model().model_info != nullptr) {
wxString text = wxString::FromUTF8(wxGetApp().plater()->model().model_info->model_name);
m_imput_model_name->GetTextCtrl()->SetValue(text);
}
return wxPanel::Show(show); return wxPanel::Show(show);
} }
void DesignerPanel::on_input_enter_designer(wxCommandEvent &evt) void DesignerPanel::on_input_enter_designer(wxCommandEvent &evt)
{ {
@ -1200,9 +1142,7 @@ void DesignerPanel::on_input_enter_designer(wxCommandEvent &evt)
void DesignerPanel::on_input_enter_model(wxCommandEvent &evt) void DesignerPanel::on_input_enter_model(wxCommandEvent &evt)
{ {
auto text = evt.GetString(); auto text = evt.GetString();
if (wxGetApp().plater()->model().model_info) { ensure_model_info()->model_name = std::string(text.ToUTF8().data());
wxGetApp().plater()->model().model_info->model_name = std::string(text.ToUTF8().data());
}
} }
void DesignerPanel::update_info() void DesignerPanel::update_info()
@ -1215,10 +1155,13 @@ void DesignerPanel::update_info()
} }
if (wxGetApp().plater()->model().model_info != nullptr) { if (wxGetApp().plater()->model().model_info != nullptr) {
wxString text = wxString::FromUTF8(wxGetApp().plater()->model().model_info->model_name); m_imput_model_name->GetTextCtrl()->SetValue(wxString::FromUTF8(wxGetApp().plater()->model().model_info->model_name));
m_imput_model_name->GetTextCtrl()->SetValue(text); if (!m_combo_license->SetStringSelection(wxString::FromUTF8(wxGetApp().plater()->model().model_info->license))) {
m_combo_license->SetSelection(0);
}
} else { } else {
m_imput_model_name->GetTextCtrl()->SetValue(wxEmptyString); m_imput_model_name->GetTextCtrl()->SetValue(wxEmptyString);
m_combo_license->SetSelection(0);
} }
} }
@ -1226,6 +1169,7 @@ void DesignerPanel::msw_rescale()
{ {
m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1)); m_input_designer->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1)); m_imput_model_name->GetTextCtrl()->SetSize(wxSize(FromDIP(450), -1));
m_combo_license->SetSize(wxSize(FromDIP(450), -1));
} }
}} // namespace Slic3r::GUI }} // namespace Slic3r::GUI

View file

@ -181,7 +181,7 @@ public:
::TextInput* m_input_designer {nullptr}; ::TextInput* m_input_designer {nullptr};
::TextInput* m_imput_model_name {nullptr}; ::TextInput* m_imput_model_name {nullptr};
//wxComboBox* m_combo_license {nullptr}; ComboBox* m_combo_license {nullptr};
bool Show(bool show) override; bool Show(bool show) override;
void init_license_list(); void init_license_list();
void on_input_enter_designer(wxCommandEvent &evt); void on_input_enter_designer(wxCommandEvent &evt);
@ -232,7 +232,7 @@ public:
void create_folder(wxString name = wxEmptyString); void create_folder(wxString name = wxEmptyString);
std::string replaceSpace(std::string s, std::string ts, std::string ns); std::string replaceSpace(std::string s, std::string ts, std::string ns);
void on_import_file(wxCommandEvent &event); void on_import_file(wxCommandEvent &event);
void Reload(wxString aux_path); void Reload(wxString aux_path, std::map<std::string, std::vector<json>> paths);
void update_all_panel(); void update_all_panel();
void update_all_cover(); void update_all_cover();
@ -242,6 +242,7 @@ wxDECLARE_EVENT(EVT_AUXILIARY_IMPORT, wxCommandEvent);
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_COVER, wxCommandEvent); wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_COVER, wxCommandEvent);
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_DELETE, wxCommandEvent); wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_DELETE, wxCommandEvent);
wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_RENAME, wxCommandEvent); wxDECLARE_EVENT(EVT_AUXILIARY_UPDATE_RENAME, wxCommandEvent);
wxDECLARE_EVENT(EVT_AUXILIARY_DONE, wxCommandEvent);
}} // namespace Slic3r::GUI }} // namespace Slic3r::GUI
#endif #endif

View file

@ -661,9 +661,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_speed", "ironing_angle" }) for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_speed", "ironing_angle" })
toggle_line(el, has_ironing); toggle_line(el, has_ironing);
// bool have_sequential_printing = (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject); bool have_sequential_printing = (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject);
// for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) // for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" })
// toggle_field(el, have_sequential_printing); // toggle_field(el, have_sequential_printing);
toggle_field("print_order", !have_sequential_printing);
bool have_ooze_prevention = config->opt_bool("ooze_prevention"); bool have_ooze_prevention = config->opt_bool("ooze_prevention");
toggle_field("standby_temperature_delta", have_ooze_prevention); toggle_field("standby_temperature_delta", have_ooze_prevention);
@ -742,6 +743,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
apply(config, &new_conf); apply(config, &new_conf);
} }
toggle_line("timelapse_type", is_BBL_Printer); toggle_line("timelapse_type", is_BBL_Printer);
bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation");
toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation);
} }
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)

View file

@ -7304,10 +7304,10 @@ void GLCanvas3D::_render_overlays()
auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
auto curr_print_seq = curr_plate->get_real_print_seq(); auto curr_print_seq = curr_plate->get_real_print_seq();
bool sequential_print = (curr_print_seq == PrintSequence::ByObject); const Print* print = fff_print();
bool sequential_print = (curr_print_seq == PrintSequence::ByObject) || print->config().print_order == PrintOrder::AsObjectList;
std::vector<const ModelInstance*> sorted_instances; std::vector<const ModelInstance*> sorted_instances;
if (sequential_print) { if (sequential_print) {
const Print* print = fff_print();
if (print) { if (print) {
for (const PrintObject *print_object : print->objects()) for (const PrintObject *print_object : print->objects())
{ {

View file

@ -2656,7 +2656,7 @@ bool GUI_App::on_init_inner()
sidebar().obj_list()->init(); sidebar().obj_list()->init();
//sidebar().aux_list()->init_auxiliary(); //sidebar().aux_list()->init_auxiliary();
//mainframe->m_auxiliary->init_auxiliary(); mainframe->m_project->init_auxiliary();
// update_mode(); // !!! do that later // update_mode(); // !!! do that later
SetTopWindow(mainframe); SetTopWindow(mainframe);

View file

@ -1545,26 +1545,6 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
} }
if (type & itObject) { if (type & itObject) {
int curr_obj_id = m_objects_model->GetIdByItem(event.GetItem());
PartPlateList& partplate_list = wxGetApp().plater()->get_partplate_list();
int from_plate = partplate_list.find_instance(curr_obj_id, 0);
if (from_plate == -1) {
event.Veto();
return;
}
auto curr_plate_seq = partplate_list.get_plate(from_plate)->get_print_seq();
if (curr_plate_seq == PrintSequence::ByDefault) {
auto curr_preset_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
if (curr_preset_config.has("print_sequence"))
curr_plate_seq = curr_preset_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence")->value;
}
if (curr_plate_seq != PrintSequence::ByObject) {
//drag forbidden under bylayer mode
event.Veto();
return;
}
m_dragged_data.init(m_objects_model->GetIdByItem(item), type); m_dragged_data.init(m_objects_model->GetIdByItem(item), type);
} }
else if (type & itVolume){ else if (type & itVolume){

View file

@ -717,7 +717,12 @@ bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::s
static double get_grabber_mean_size(const BoundingBoxf3& bb) static double get_grabber_mean_size(const BoundingBoxf3& bb)
{ {
#if ENABLE_FIXED_GRABBER
// Orca: make grabber larger
return 32. * GLGizmoBase::INV_ZOOM;
#else
return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.; return (bb.size().x() + bb.size().y() + bb.size().z()) / 30.;
#endif
} }
indexed_triangle_set GLGizmoCut3D::its_make_groove_plane() indexed_triangle_set GLGizmoCut3D::its_make_groove_plane()
@ -2503,13 +2508,13 @@ void GLGizmoCut3D::add_horizontal_shift(float shift)
void GLGizmoCut3D::render_color_marker(float size, const ImU32& color) void GLGizmoCut3D::render_color_marker(float size, const ImU32& color)
{ {
ImGui::SameLine();
const float radius = 0.5f * size; const float radius = 0.5f * size;
ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos;
pos.x += size; pos.x += radius;
pos.y += 1.25f * radius; pos.y += 1.4f * radius;
ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6); ImGui::GetCurrentWindow()->DrawList->AddNgonFilled(pos, radius, color, 6);
m_imgui->text(" "); m_imgui->text(" ");
ImGui::SameLine();
} }
void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance) void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in_val, const float& init_val, float& in_tolerance)
@ -2730,20 +2735,27 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, floa
// render "After Cut" section // render "After Cut" section
float label_width = 0; ImVec2 label_size;
for (const wxString &label : {_L("Upper part"), _L("Lower part")}) { for (const wxString &label : {_L("Upper part"), _L("Lower part")}) {
const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f); const ImVec2 text_size = ImGuiWrapper::calc_text_size(label);
if (label_width < width) if (label_size.x < text_size.x)
label_width = width; label_size.x = text_size.x;
if (label_size.y < text_size.y)
label_size.y = text_size.y;
} }
auto render_part_action_line = [this, label_width, &connectors](const wxString &label, const wxString &suffix, bool &keep_part, const float marker_size = label_size.y;
const float h_shift = marker_size + label_size.x + m_imgui->scaled(2.f);
auto render_part_action_line = [this, h_shift, marker_size, &connectors](const wxString &label, const wxString &suffix, bool &keep_part,
bool &place_on_cut_part, bool &rotate_part) { bool &place_on_cut_part, bool &rotate_part) {
bool keep = true; bool keep = true;
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
render_color_marker(marker_size, ImGuiWrapper::to_ImU32(suffix == "##upper" ? UPPER_PART_COLOR : LOWER_PART_COLOR));
m_imgui->text(label); m_imgui->text(label);
ImGui::SameLine(label_width); ImGui::SameLine(h_shift);
m_imgui->disabled_begin(!connectors.empty() || m_keep_as_parts); m_imgui->disabled_begin(!connectors.empty() || m_keep_as_parts);
m_imgui->bbl_checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); m_imgui->bbl_checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep);

View file

@ -51,6 +51,7 @@ static unsigned int GLOBAL_PLATE_INDEX = 0;
static const double LOGICAL_PART_PLATE_GAP = 1. / 5.; static const double LOGICAL_PART_PLATE_GAP = 1. / 5.;
static const int PARTPLATE_ICON_SIZE = 16; static const int PARTPLATE_ICON_SIZE = 16;
static const int PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE = 12;
static const int PARTPLATE_ICON_GAP_TOP = 3; static const int PARTPLATE_ICON_GAP_TOP = 3;
static const int PARTPLATE_ICON_GAP_LEFT = 3; static const int PARTPLATE_ICON_GAP_LEFT = 3;
static const int PARTPLATE_ICON_GAP_Y = 5; static const int PARTPLATE_ICON_GAP_Y = 5;
@ -571,6 +572,42 @@ void PartPlate::calc_vertex_for_number(int index, bool one_number, GLModel &buff
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n";
} }
void PartPlate::calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int index, PickingModel &model) {
model.reset();
auto bed_ext = get_extents(m_shape);
auto factor = bed_ext.size()(1) / 200.0;
wxCoord w, h;
h = int(factor * 16);
ExPolygon poly;
Vec2d p = bed_ext[3];
float offset_x = 1;
h = PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE;
p += Vec2d(0, PARTPLATE_TEXT_OFFSET_Y + h);
if (texture && texture->get_width() > 0 && texture->get_height()) {
w = int(factor * (texture->get_original_width() * 16) / texture->get_height()) + 1;
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) - h )});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) )});
if (!init_model_from_poly(model.model, poly, GROUND_Z))
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n";
} else {
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x ), scale_(p(1) - h )});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))});
poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) )});
if (!init_model_from_poly(model.model, poly, GROUND_Z))
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n";
}
init_raycaster_from_model(model);
}
void PartPlate::calc_vertex_for_icons(int index, PickingModel &model) void PartPlate::calc_vertex_for_icons(int index, PickingModel &model)
{ {
model.reset(); model.reset();
@ -975,6 +1012,13 @@ void PartPlate::render_icons(bool bottom, bool only_name, int hover_id)
render_icon_texture(m_lock_icon.model, m_partplate_list->m_lockopen_texture); render_icon_texture(m_lock_icon.model, m_partplate_list->m_lockopen_texture);
} }
if (hover_id == 6) {
render_icon_texture(m_plate_name_edit_icon.model, m_partplate_list->m_plate_name_edit_hovered_texture);
show_tooltip(_u8L("Edit current plate name"));
}
else
render_icon_texture(m_plate_name_edit_icon.model, m_partplate_list->m_plate_name_edit_texture);
if (m_partplate_list->render_plate_settings) { if (m_partplate_list->render_plate_settings) {
if (hover_id == 5) { if (hover_id == 5) {
if (get_bed_type() == BedType::btDefault && get_print_seq() == PrintSequence::ByDefault && get_first_layer_print_sequence().empty()) if (get_bed_type() == BedType::btDefault && get_print_seq() == PrintSequence::ByDefault && get_first_layer_print_sequence().empty())
@ -1272,6 +1316,9 @@ void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas)
register_model_for_picking(canvas, m_lock_icon, picking_id_component(4)); register_model_for_picking(canvas, m_lock_icon, picking_id_component(4));
if (m_partplate_list->render_plate_settings) if (m_partplate_list->render_plate_settings)
register_model_for_picking(canvas, m_plate_settings_icon, picking_id_component(5)); register_model_for_picking(canvas, m_plate_settings_icon, picking_id_component(5));
canvas.remove_raycasters_for_picking(SceneRaycaster::EType::Bed, picking_id_component(6));
register_model_for_picking(canvas, m_plate_name_edit_icon, picking_id_component(6));
} }
int PartPlate::picking_id_component(int idx) const int PartPlate::picking_id_component(int idx) const
@ -1782,6 +1829,11 @@ void PartPlate::generate_plate_name_texture()
if (!init_model_from_poly(m_plate_name_icon, poly, GROUND_Z)) if (!init_model_from_poly(m_plate_name_icon, poly, GROUND_Z))
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n";
auto canvas = this->m_partplate_list->m_plater->get_view3D_canvas3D();
canvas->remove_raycasters_for_picking(SceneRaycaster::EType::Bed, picking_id_component(6));
calc_vertex_for_plate_name_edit_icon(&m_name_texture, 0, m_plate_name_edit_icon);
register_model_for_picking(*canvas, m_plate_name_edit_icon, picking_id_component(6));
} }
void PartPlate::set_plate_name(const std::string& name) void PartPlate::set_plate_name(const std::string& name)
{ {
@ -3124,6 +3176,20 @@ void PartPlateList::generate_icon_textures()
} }
} }
// if (m_plate_name_edit_texture.get_id() == 0)
{
file_name = path + (m_is_dark ? "plate_name_edit_dark.svg" : "plate_name_edit.svg");
if (!m_plate_name_edit_texture.load_from_svg_file(file_name, true, false, false, icon_size)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name;
}
}
// if (m_plate_name_edit_hovered_texture.get_id() == 0)
{
file_name = path + (m_is_dark ? "plate_name_edit_hover_dark.svg" : "plate_name_edit_hover.svg");
if (!m_plate_name_edit_hovered_texture.load_from_svg_file(file_name, true, false, false, icon_size)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":load file %1% failed") % file_name;
}
}
std::string text_str = "01"; std::string text_str = "01";
wxFont* font = find_font(text_str,32); wxFont* font = find_font(text_str,32);
@ -3161,7 +3227,8 @@ void PartPlateList::release_icon_textures()
m_plate_settings_texture.reset(); m_plate_settings_texture.reset();
m_plate_settings_texture.reset(); m_plate_settings_texture.reset();
m_plate_settings_hovered_texture.reset(); m_plate_settings_hovered_texture.reset();
m_plate_name_edit_texture.reset();
m_plate_name_edit_hovered_texture.reset();
for (int i = 0;i < MAX_PLATE_COUNT; i++) { for (int i = 0;i < MAX_PLATE_COUNT; i++) {
m_idx_textures[i].reset(); m_idx_textures[i].reset();
} }

View file

@ -138,6 +138,7 @@ private:
PickingModel m_orient_icon; PickingModel m_orient_icon;
PickingModel m_lock_icon; PickingModel m_lock_icon;
PickingModel m_plate_settings_icon; PickingModel m_plate_settings_icon;
PickingModel m_plate_name_edit_icon;
GLModel m_plate_idx_icon; GLModel m_plate_idx_icon;
GLTexture m_texture; GLTexture m_texture;
@ -169,6 +170,7 @@ private:
void calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox); void calc_gridlines(const ExPolygon& poly, const BoundingBox& pp_bbox);
void calc_height_limit(); void calc_height_limit();
void calc_vertex_for_number(int index, bool one_number, GLModel &buffer); void calc_vertex_for_number(int index, bool one_number, GLModel &buffer);
void calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int index, PickingModel &model);
void calc_vertex_for_icons(int index, PickingModel &model); void calc_vertex_for_icons(int index, PickingModel &model);
// void calc_vertex_for_icons_background(int icon_count, GLModel &buffer); // void calc_vertex_for_icons_background(int icon_count, GLModel &buffer);
void render_background(bool force_default_color = false); void render_background(bool force_default_color = false);

View file

@ -62,6 +62,11 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this); Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this);
m_auxiliary = new AuxiliaryPanel(this);
m_auxiliary->Hide();
main_sizer->Add(m_auxiliary, wxSizerFlags().Expand().Proportion(1));
Bind(EVT_AUXILIARY_DONE, [this](wxCommandEvent& e) { update_model_data();});
SetSizer(main_sizer); SetSizer(main_sizer);
Layout(); Layout();
Fit(); Fit();
@ -91,25 +96,26 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
std::string model_author; std::string model_author;
std::string cover_file; std::string cover_file;
std::string description; std::string description;
std::map<std::string, std::vector<json>> files;
std::string p_name; std::string p_name;
std::string p_author; std::string p_author;
std::string p_description; std::string p_description;
std::string p_cover_file; std::string p_cover_file;
std::map<std::string, std::vector<json>> files;
Model model = wxGetApp().plater()->model(); Model model = wxGetApp().plater()->model();
license = model.model_info->license; auto model_info = model.model_info;
model_name = model.model_info->model_name; if (model_info != nullptr) {
cover_file = model.model_info->cover_file; license = model_info->license;
description = model.model_info->description; model_name = model_info->model_name;
update_type = model.model_info->origin; cover_file = model_info->cover_file;
description = model_info->description;
update_type = model_info->origin;
try { try {
if (!model.model_info->copyright.empty()) { if (!model_info->copyright.empty()) {
json copy_right = json::parse(model.model_info->copyright); json copy_right = json::parse(model_info->copyright);
if (copy_right.is_array()) { if (copy_right.is_array()) {
for (auto it = copy_right.begin(); it != copy_right.end(); it++) { for (auto it = copy_right.begin(); it != copy_right.end(); it++) {
@ -119,10 +125,10 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
} }
} }
} }
} } catch (...) {
catch (...) {
; ;
} }
}
if (model_author.empty() && model.design_info != nullptr) if (model_author.empty() && model.design_info != nullptr)
model_author = model.design_info->Designer; model_author = model.design_info->Designer;
@ -134,12 +140,44 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
p_author = model.profile_info->ProfileUserName; p_author = model.profile_info->ProfileUserName;
} }
//file info // file info
std::string file_path = encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str()); std::string file_path = encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str());
if (!file_path.empty()) { if (!file_path.empty()) {
files = Reload(file_path); files = Reload(file_path);
wxGetApp().CallAfter([this, file_path, files] { m_auxiliary->Reload(file_path, files); });
} else {
clear_model_info();
return;
} }
else {
bool has_content = false;
for (const string& v : {
update_type,
license,
model_name,
model_author,
cover_file,
description,
p_name,
p_author,
p_description,
p_cover_file,
}) {
if (!v.empty()) {
has_content = true;
break;
}
}
if (!has_content) {
for (const auto & file : files) {
if (!file.second.empty()) {
has_content = true;
break;
}
}
}
if (!has_content) {
// Nothing to show, just return
clear_model_info(); clear_model_info();
return; return;
} }
@ -180,6 +218,7 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
void ProjectPanel::msw_rescale() void ProjectPanel::msw_rescale()
{ {
m_auxiliary->msw_rescale();
} }
void ProjectPanel::on_size(wxSizeEvent &event) void ProjectPanel::on_size(wxSizeEvent &event)
@ -215,6 +254,9 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt)
else if (strCmd == "request_3mf_info") { else if (strCmd == "request_3mf_info") {
m_web_init_completed = true; m_web_init_completed = true;
} }
else if (strCmd == "edit_project_info") {
show_info_editor(true);
}
else if (strCmd == "debug_info") { else if (strCmd == "debug_info") {
//wxString msg = j["msg"]; //wxString msg = j["msg"];
//OutputDebugString(wxString::Format("Model_Web: msg = %s \r\n", msg)); //OutputDebugString(wxString::Format("Model_Web: msg = %s \r\n", msg));
@ -227,14 +269,24 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt)
} }
} }
void ProjectPanel::show_info_editor(bool show)
{
m_browser->Show(!show);
m_auxiliary->Show(show);
Layout();
}
void ProjectPanel::update_model_data() void ProjectPanel::update_model_data()
{ {
Model model = wxGetApp().plater()->model(); Model model = wxGetApp().plater()->model();
show_info_editor(false);
clear_model_info(); clear_model_info();
m_auxiliary->init_auxiliary();
//basics info //basics info
if (model.model_info == nullptr) //if (model.model_info == nullptr)
return; // return;
auto event = wxCommandEvent(EVT_PROJECT_RELOAD); auto event = wxCommandEvent(EVT_PROJECT_RELOAD);
event.SetEventObject(this); event.SetEventObject(this);
@ -258,7 +310,6 @@ std::map<std::string, std::vector<json>> ProjectPanel::Reload(wxString aux_path)
{ {
std::vector<fs::path> dir_cache; std::vector<fs::path> dir_cache;
fs::directory_iterator iter_end; fs::directory_iterator iter_end;
wxString m_root_dir;
std::map<std::string, std::vector<json>> m_paths_list; std::map<std::string, std::vector<json>> m_paths_list;
const static std::array<wxString, 5> s_default_folders = { const static std::array<wxString, 5> s_default_folders = {
@ -276,25 +327,17 @@ std::map<std::string, std::vector<json>> ProjectPanel::Reload(wxString aux_path)
fs::path new_aux_path(aux_path.ToStdWstring()); fs::path new_aux_path(aux_path.ToStdWstring());
try {
fs::remove_all(fs::path(m_root_dir.ToStdWstring()));
}
catch (...) {
BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory" << m_root_dir.c_str();
}
m_root_dir = aux_path;
// Check new path. If not exist, create a new one. // Check new path. If not exist, create a new one.
if (!fs::exists(new_aux_path)) { if (!fs::exists(new_aux_path)) {
fs::create_directory(new_aux_path); fs::create_directory(new_aux_path);
}
// Create default folders if they are not loaded // Create default folders if they are not loaded
for (auto folder : s_default_folders) { for (auto folder : s_default_folders) {
wxString folder_path = aux_path + "/" + folder; wxString folder_path = aux_path + "/" + folder;
if (fs::exists(folder_path.ToStdWstring())) continue; if (fs::exists(folder_path.ToStdWstring())) continue;
fs::create_directory(folder_path.ToStdWstring()); fs::create_directory(folder_path.ToStdWstring());
} }
return m_paths_list;
}
// Load from new path // Load from new path
for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) { for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) {
@ -321,6 +364,7 @@ std::map<std::string, std::vector<json>> ProjectPanel::Reload(wxString aux_path)
wxStat(file_name, &strucStat); wxStat(file_name, &strucStat);
wxFileOffset filelen = strucStat.st_size; wxFileOffset filelen = strucStat.st_size;
pfile_obj["_filepath"] = file_path;
pfile_obj["filename"] = wxGetApp().url_encode(file_path_obj.filename().string().c_str()); pfile_obj["filename"] = wxGetApp().url_encode(file_path_obj.filename().string().c_str());
pfile_obj["size"] = formatBytes((unsigned long)filelen); pfile_obj["size"] = formatBytes((unsigned long)filelen);

View file

@ -32,6 +32,7 @@
#include "Event.hpp" #include "Event.hpp"
#include "libslic3r/ProjectTask.hpp" #include "libslic3r/ProjectTask.hpp"
#include "wxExtensions.hpp" #include "wxExtensions.hpp"
#include "Auxiliary.hpp"
#define AUFILE_GREY700 wxColour(107, 107, 107) #define AUFILE_GREY700 wxColour(107, 107, 107)
#define AUFILE_GREY500 wxColour(158, 158, 158) #define AUFILE_GREY500 wxColour(158, 158, 158)
@ -63,10 +64,13 @@ private:
bool m_reload_already = {false}; bool m_reload_already = {false};
wxWebView* m_browser = {nullptr}; wxWebView* m_browser = {nullptr};
AuxiliaryPanel* m_auxiliary{nullptr};
wxString m_project_home_url; wxString m_project_home_url;
wxString m_root_dir; wxString m_root_dir;
static inline int m_sequence_id = 8000; static inline int m_sequence_id = 8000;
void show_info_editor(bool show);
public: public:
ProjectPanel(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL); ProjectPanel(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
@ -81,6 +85,7 @@ public:
void msw_rescale(); void msw_rescale();
void update_model_data(); void update_model_data();
void clear_model_info(); void clear_model_info();
void init_auxiliary() { m_auxiliary->init_auxiliary(); }
bool Show(bool show); bool Show(bool show);
void OnScriptMessage(wxWebViewEvent& evt); void OnScriptMessage(wxWebViewEvent& evt);

View file

@ -1324,8 +1324,11 @@ void Selection::scale_legacy(const Vec3d& scale, TransformationType transformati
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_scaling_factor(new_scale); v.set_instance_scaling_factor(new_scale);
// Restore mirror state
v.set_instance_mirror(m_cache.volumes_data[i].get_instance_transform().get_mirror());
} }
else { else {
const auto mirror = v.get_instance_mirror();
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) { if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
// Non-uniform scaling. Transform the scaling factors into the local coordinate system. // Non-uniform scaling. Transform the scaling factors into the local coordinate system.
// This is only possible, if the instance rotation is mulitples of ninety degrees. // This is only possible, if the instance rotation is mulitples of ninety degrees.
@ -1334,16 +1337,24 @@ void Selection::scale_legacy(const Vec3d& scale, TransformationType transformati
} }
else else
v.set_instance_scaling_factor(scale); v.set_instance_scaling_factor(scale);
// Restore mirror state
v.set_instance_mirror(mirror);
} }
// update the instance assemble transform // update the instance assemble transform
ModelObject* object = m_model->objects[v.object_idx()]; ModelObject* object = m_model->objects[v.object_idx()];
Geometry::Transformation assemble_transform = object->instances[v.instance_idx()]->get_assemble_transformation(); Geometry::Transformation assemble_transform = object->instances[v.instance_idx()]->get_assemble_transformation();
const auto mirror = assemble_transform.get_mirror();
assemble_transform.set_scaling_factor(v.get_instance_scaling_factor()); assemble_transform.set_scaling_factor(v.get_instance_scaling_factor());
assemble_transform.set_mirror(mirror);
object->instances[v.instance_idx()]->set_assemble_transformation(assemble_transform); object->instances[v.instance_idx()]->set_assemble_transformation(assemble_transform);
} }
else if (is_single_volume() || is_single_modifier()) else if (is_single_volume() || is_single_modifier()) {
const auto mirror = v.get_volume_transformation().get_mirror();
v.set_volume_scaling_factor(scale); v.set_volume_scaling_factor(scale);
// Restore mirror state
v.set_volume_mirror(mirror);
}
else { else {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
if (m_mode == Instance) { if (m_mode == Instance) {
@ -1354,6 +1365,8 @@ void Selection::scale_legacy(const Vec3d& scale, TransformationType transformati
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center)); v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_scaling_factor(new_scale); v.set_instance_scaling_factor(new_scale);
// Restore mirror state
v.set_instance_mirror(m_cache.volumes_data[i].get_instance_transform().get_mirror());
} }
else if (m_mode == Volume) { else if (m_mode == Volume) {
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3); Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3);
@ -1364,6 +1377,8 @@ void Selection::scale_legacy(const Vec3d& scale, TransformationType transformati
v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset); v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
} }
v.set_volume_scaling_factor(new_scale); v.set_volume_scaling_factor(new_scale);
// Restore mirror state
v.set_volume_mirror(m_cache.volumes_data[i].get_volume_transform().get_mirror());
} }
} }
} }

View file

@ -1989,6 +1989,14 @@ void TabPrint::build()
optgroup->append_single_option_line("reduce_crossing_wall"); optgroup->append_single_option_line("reduce_crossing_wall");
optgroup->append_single_option_line("max_travel_detour_distance"); optgroup->append_single_option_line("max_travel_detour_distance");
optgroup = page->new_optgroup(L("Small Area Infill Flow Compensation (experimental)"), L"param_advanced");
optgroup->append_single_option_line("small_area_infill_flow_compensation");
Option option = optgroup->get_option("small_area_infill_flow_compensation_model");
option.opt.full_width = true;
option.opt.is_code = true;
option.opt.height = 15;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Bridging"), L"param_advanced"); optgroup = page->new_optgroup(L("Bridging"), L"param_advanced");
optgroup->append_single_option_line("bridge_flow"); optgroup->append_single_option_line("bridge_flow");
optgroup->append_single_option_line("internal_bridge_flow"); optgroup->append_single_option_line("internal_bridge_flow");
@ -2198,6 +2206,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Special mode"), L"param_special"); optgroup = page->new_optgroup(L("Special mode"), L"param_special");
optgroup->append_single_option_line("slicing_mode"); optgroup->append_single_option_line("slicing_mode");
optgroup->append_single_option_line("print_sequence", "sequent-print"); optgroup->append_single_option_line("print_sequence", "sequent-print");
optgroup->append_single_option_line("print_order");
optgroup->append_single_option_line("spiral_mode", "spiral-vase"); optgroup->append_single_option_line("spiral_mode", "spiral-vase");
optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth"); optgroup->append_single_option_line("spiral_mode_smooth", "spiral-vase#smooth");
optgroup->append_single_option_line("spiral_mode_max_xy_smoothing", "spiral-vase#max-xy-smoothing"); optgroup->append_single_option_line("spiral_mode_max_xy_smoothing", "spiral-vase#max-xy-smoothing");
@ -2218,7 +2227,7 @@ void TabPrint::build()
optgroup->append_single_option_line("gcode_comments"); optgroup->append_single_option_line("gcode_comments");
optgroup->append_single_option_line("gcode_label_objects"); optgroup->append_single_option_line("gcode_label_objects");
optgroup->append_single_option_line("exclude_object"); optgroup->append_single_option_line("exclude_object");
Option option = optgroup->get_option("filename_format"); option = optgroup->get_option("filename_format");
// option.opt.full_width = true; // option.opt.full_width = true;
option.opt.is_code = true; option.opt.is_code = true;
option.opt.multiline = true; option.opt.multiline = true;

951
src/spline/spline.h Normal file
View file

@ -0,0 +1,951 @@
/*
* spline.h
*
* simple cubic spline interpolation library without external
* dependencies
*
* ---------------------------------------------------------------------
* Copyright (C) 2011, 2014, 2016, 2021 Tino Kluge (ttk448 at gmail.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ---------------------------------------------------------------------
*
*/
#ifndef TK_SPLINE_H
#define TK_SPLINE_H
#include <cstdio>
#include <cassert>
#include <cmath>
#include <vector>
#include <algorithm>
#ifdef HAVE_SSTREAM
#include <sstream>
#include <string>
#endif // HAVE_SSTREAM
// not ideal but disable unused-function warnings
// (we get them because we have implementations in the header file,
// and this is because we want to be able to quickly separate them
// into a cpp file if necessary)
#if !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
// unnamed namespace only because the implementation is in this
// header file and we don't want to export symbols to the obj files
namespace
{
namespace tk
{
// spline interpolation
class spline
{
public:
// spline types
enum spline_type {
linear = 10, // linear interpolation
cspline = 30, // cubic splines (classical C^2)
cspline_hermite = 31 // cubic hermite splines (local, only C^1)
};
// boundary condition type for the spline end-points
enum bd_type {
first_deriv = 1,
second_deriv = 2,
not_a_knot = 3
};
protected:
std::vector<double> m_x,m_y; // x,y coordinates of points
// interpolation parameters
// f(x) = a_i + b_i*(x-x_i) + c_i*(x-x_i)^2 + d_i*(x-x_i)^3
// where a_i = y_i, or else it won't go through grid points
std::vector<double> m_b,m_c,m_d; // spline coefficients
double m_c0; // for left extrapolation
spline_type m_type;
bd_type m_left, m_right;
double m_left_value, m_right_value;
bool m_made_monotonic;
void set_coeffs_from_b(); // calculate c_i, d_i from b_i
size_t find_closest(double x) const; // closest idx so that m_x[idx]<=x
public:
// default constructor: set boundary condition to be zero curvature
// at both ends, i.e. natural splines
spline(): m_type(cspline),
m_left(second_deriv), m_right(second_deriv),
m_left_value(0.0), m_right_value(0.0), m_made_monotonic(false)
{
;
}
spline(const std::vector<double>& X, const std::vector<double>& Y,
spline_type type = cspline,
bool make_monotonic = false,
bd_type left = second_deriv, double left_value = 0.0,
bd_type right = second_deriv, double right_value = 0.0
):
m_type(type),
m_left(left), m_right(right),
m_left_value(left_value), m_right_value(right_value),
m_made_monotonic(false) // false correct here: make_monotonic() sets it
{
this->set_points(X,Y,m_type);
if(make_monotonic) {
this->make_monotonic();
}
}
// modify boundary conditions: if called it must be before set_points()
void set_boundary(bd_type left, double left_value,
bd_type right, double right_value);
// set all data points (cubic_spline=false means linear interpolation)
void set_points(const std::vector<double>& x,
const std::vector<double>& y,
spline_type type=cspline);
// adjust coefficients so that the spline becomes piecewise monotonic
// where possible
// this is done by adjusting slopes at grid points by a non-negative
// factor and this will break C^2
// this can also break boundary conditions if adjustments need to
// be made at the boundary points
// returns false if no adjustments have been made, true otherwise
bool make_monotonic();
// evaluates the spline at point x
double operator() (double x) const;
double deriv(int order, double x) const;
// solves for all x so that: spline(x) = y
std::vector<double> solve(double y, bool ignore_extrapolation=true) const;
// returns the input data points
std::vector<double> get_x() const { return m_x; }
std::vector<double> get_y() const { return m_y; }
double get_x_min() const { assert(!m_x.empty()); return m_x.front(); }
double get_x_max() const { assert(!m_x.empty()); return m_x.back(); }
#ifdef HAVE_SSTREAM
// spline info string, i.e. spline type, boundary conditions etc.
std::string info() const;
#endif // HAVE_SSTREAM
};
namespace internal
{
// band matrix solver
class band_matrix
{
private:
std::vector< std::vector<double> > m_upper; // upper band
std::vector< std::vector<double> > m_lower; // lower band
public:
band_matrix() {}; // constructor
band_matrix(int dim, int n_u, int n_l); // constructor
~band_matrix() {}; // destructor
void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l
int dim() const; // matrix dimension
int num_upper() const
{
return (int)m_upper.size()-1;
}
int num_lower() const
{
return (int)m_lower.size()-1;
}
// access operator
double & operator () (int i, int j); // write
double operator () (int i, int j) const; // read
// we can store an additional diagonal (in m_lower)
double& saved_diag(int i);
double saved_diag(int i) const;
void lu_decompose();
std::vector<double> r_solve(const std::vector<double>& b) const;
std::vector<double> l_solve(const std::vector<double>& b) const;
std::vector<double> lu_solve(const std::vector<double>& b,
bool is_lu_decomposed=false);
};
double get_eps();
std::vector<double> solve_cubic(double a, double b, double c, double d,
int newton_iter=0);
} // namespace internal
// ---------------------------------------------------------------------
// implementation part, which could be separated into a cpp file
// ---------------------------------------------------------------------
// spline implementation
// -----------------------
void spline::set_boundary(spline::bd_type left, double left_value,
spline::bd_type right, double right_value)
{
assert(m_x.size()==0); // set_points() must not have happened yet
m_left=left;
m_right=right;
m_left_value=left_value;
m_right_value=right_value;
}
void spline::set_coeffs_from_b()
{
assert(m_x.size()==m_y.size());
assert(m_x.size()==m_b.size());
assert(m_x.size()>2);
size_t n=m_b.size();
if(m_c.size()!=n)
m_c.resize(n);
if(m_d.size()!=n)
m_d.resize(n);
for(size_t i=0; i<n-1; i++) {
const double h = m_x[i+1]-m_x[i];
// from continuity and differentiability condition
m_c[i] = ( 3.0*(m_y[i+1]-m_y[i])/h - (2.0*m_b[i]+m_b[i+1]) ) / h;
// from differentiability condition
m_d[i] = ( (m_b[i+1]-m_b[i])/(3.0*h) - 2.0/3.0*m_c[i] ) / h;
}
// for left extrapolation coefficients
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
}
void spline::set_points(const std::vector<double>& x,
const std::vector<double>& y,
spline_type type)
{
assert(x.size()==y.size());
assert(x.size()>=3);
// not-a-knot with 3 points has many solutions
if(m_left==not_a_knot || m_right==not_a_knot)
assert(x.size()>=4);
m_type=type;
m_made_monotonic=false;
m_x=x;
m_y=y;
int n = (int) x.size();
// check strict monotonicity of input vector x
for(int i=0; i<n-1; i++) {
assert(m_x[i]<m_x[i+1]);
}
if(type==linear) {
// linear interpolation
m_d.resize(n);
m_c.resize(n);
m_b.resize(n);
for(int i=0; i<n-1; i++) {
m_d[i]=0.0;
m_c[i]=0.0;
m_b[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
}
// ignore boundary conditions, set slope equal to the last segment
m_b[n-1]=m_b[n-2];
m_c[n-1]=0.0;
m_d[n-1]=0.0;
} else if(type==cspline) {
// classical cubic splines which are C^2 (twice cont differentiable)
// this requires solving an equation system
// setting up the matrix and right hand side of the equation system
// for the parameters b[]
int n_upper = (m_left == spline::not_a_knot) ? 2 : 1;
int n_lower = (m_right == spline::not_a_knot) ? 2 : 1;
internal::band_matrix A(n,n_upper,n_lower);
std::vector<double> rhs(n);
for(int i=1; i<n-1; i++) {
A(i,i-1)=1.0/3.0*(x[i]-x[i-1]);
A(i,i)=2.0/3.0*(x[i+1]-x[i-1]);
A(i,i+1)=1.0/3.0*(x[i+1]-x[i]);
rhs[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
}
// boundary conditions
if(m_left == spline::second_deriv) {
// 2*c[0] = f''
A(0,0)=2.0;
A(0,1)=0.0;
rhs[0]=m_left_value;
} else if(m_left == spline::first_deriv) {
// b[0] = f', needs to be re-expressed in terms of c:
// (2c[0]+c[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
A(0,0)=2.0*(x[1]-x[0]);
A(0,1)=1.0*(x[1]-x[0]);
rhs[0]=3.0*((y[1]-y[0])/(x[1]-x[0])-m_left_value);
} else if(m_left == spline::not_a_knot) {
// f'''(x[1]) exists, i.e. d[0]=d[1], or re-expressed in c:
// -h1*c[0] + (h0+h1)*c[1] - h0*c[2] = 0
A(0,0) = -(x[2]-x[1]);
A(0,1) = x[2]-x[0];
A(0,2) = -(x[1]-x[0]);
rhs[0] = 0.0;
} else {
assert(false);
}
if(m_right == spline::second_deriv) {
// 2*c[n-1] = f''
A(n-1,n-1)=2.0;
A(n-1,n-2)=0.0;
rhs[n-1]=m_right_value;
} else if(m_right == spline::first_deriv) {
// b[n-1] = f', needs to be re-expressed in terms of c:
// (c[n-2]+2c[n-1])(x[n-1]-x[n-2])
// = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
A(n-1,n-1)=2.0*(x[n-1]-x[n-2]);
A(n-1,n-2)=1.0*(x[n-1]-x[n-2]);
rhs[n-1]=3.0*(m_right_value-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
} else if(m_right == spline::not_a_knot) {
// f'''(x[n-2]) exists, i.e. d[n-3]=d[n-2], or re-expressed in c:
// -h_{n-2}*c[n-3] + (h_{n-3}+h_{n-2})*c[n-2] - h_{n-3}*c[n-1] = 0
A(n-1,n-3) = -(x[n-1]-x[n-2]);
A(n-1,n-2) = x[n-1]-x[n-3];
A(n-1,n-1) = -(x[n-2]-x[n-3]);
rhs[0] = 0.0;
} else {
assert(false);
}
// solve the equation system to obtain the parameters c[]
m_c=A.lu_solve(rhs);
// calculate parameters b[] and d[] based on c[]
m_d.resize(n);
m_b.resize(n);
for(int i=0; i<n-1; i++) {
m_d[i]=1.0/3.0*(m_c[i+1]-m_c[i])/(x[i+1]-x[i]);
m_b[i]=(y[i+1]-y[i])/(x[i+1]-x[i])
- 1.0/3.0*(2.0*m_c[i]+m_c[i+1])*(x[i+1]-x[i]);
}
// for the right extrapolation coefficients (zero cubic term)
// f_{n-1}(x) = y_{n-1} + b*(x-x_{n-1}) + c*(x-x_{n-1})^2
double h=x[n-1]-x[n-2];
// m_c[n-1] is determined by the boundary condition
m_d[n-1]=0.0;
m_b[n-1]=3.0*m_d[n-2]*h*h+2.0*m_c[n-2]*h+m_b[n-2]; // = f'_{n-2}(x_{n-1})
if(m_right==first_deriv)
m_c[n-1]=0.0; // force linear extrapolation
} else if(type==cspline_hermite) {
// hermite cubic splines which are C^1 (cont. differentiable)
// and derivatives are specified on each grid point
// (here we use 3-point finite differences)
m_b.resize(n);
m_c.resize(n);
m_d.resize(n);
// set b to match 1st order derivative finite difference
for(int i=1; i<n-1; i++) {
const double h = m_x[i+1]-m_x[i];
const double hl = m_x[i]-m_x[i-1];
m_b[i] = -h/(hl*(hl+h))*m_y[i-1] + (h-hl)/(hl*h)*m_y[i]
+ hl/(h*(hl+h))*m_y[i+1];
}
// boundary conditions determine b[0] and b[n-1]
if(m_left==first_deriv) {
m_b[0]=m_left_value;
} else if(m_left==second_deriv) {
const double h = m_x[1]-m_x[0];
m_b[0]=0.5*(-m_b[1]-0.5*m_left_value*h+3.0*(m_y[1]-m_y[0])/h);
} else if(m_left == not_a_knot) {
// f''' continuous at x[1]
const double h0 = m_x[1]-m_x[0];
const double h1 = m_x[2]-m_x[1];
m_b[0]= -m_b[1] + 2.0*(m_y[1]-m_y[0])/h0
+ h0*h0/(h1*h1)*(m_b[1]+m_b[2]-2.0*(m_y[2]-m_y[1])/h1);
} else {
assert(false);
}
if(m_right==first_deriv) {
m_b[n-1]=m_right_value;
m_c[n-1]=0.0;
} else if(m_right==second_deriv) {
const double h = m_x[n-1]-m_x[n-2];
m_b[n-1]=0.5*(-m_b[n-2]+0.5*m_right_value*h+3.0*(m_y[n-1]-m_y[n-2])/h);
m_c[n-1]=0.5*m_right_value;
} else if(m_right == not_a_knot) {
// f''' continuous at x[n-2]
const double h0 = m_x[n-2]-m_x[n-3];
const double h1 = m_x[n-1]-m_x[n-2];
m_b[n-1]= -m_b[n-2] + 2.0*(m_y[n-1]-m_y[n-2])/h1 + h1*h1/(h0*h0)
*(m_b[n-3]+m_b[n-2]-2.0*(m_y[n-2]-m_y[n-3])/h0);
// f'' continuous at x[n-1]: c[n-1] = 3*d[n-2]*h[n-2] + c[n-1]
m_c[n-1]=(m_b[n-2]+2.0*m_b[n-1])/h1-3.0*(m_y[n-1]-m_y[n-2])/(h1*h1);
} else {
assert(false);
}
m_d[n-1]=0.0;
// parameters c and d are determined by continuity and differentiability
set_coeffs_from_b();
} else {
assert(false);
}
// for left extrapolation coefficients
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
}
bool spline::make_monotonic()
{
assert(m_x.size()==m_y.size());
assert(m_x.size()==m_b.size());
assert(m_x.size()>2);
bool modified = false;
const int n=(int)m_x.size();
// make sure: input data monotonic increasing --> b_i>=0
// input data monotonic decreasing --> b_i<=0
for(int i=0; i<n; i++) {
int im1 = std::max(i-1, 0);
int ip1 = std::min(i+1, n-1);
if( ((m_y[im1]<=m_y[i]) && (m_y[i]<=m_y[ip1]) && m_b[i]<0.0) ||
((m_y[im1]>=m_y[i]) && (m_y[i]>=m_y[ip1]) && m_b[i]>0.0) ) {
modified=true;
m_b[i]=0.0;
}
}
// if input data is monotonic (b[i], b[i+1], avg have all the same sign)
// ensure a sufficient criteria for monotonicity is satisfied:
// sqrt(b[i]^2+b[i+1]^2) <= 3 |avg|, with avg=(y[i+1]-y[i])/h,
for(int i=0; i<n-1; i++) {
double h = m_x[i+1]-m_x[i];
double avg = (m_y[i+1]-m_y[i])/h;
if( avg==0.0 && (m_b[i]!=0.0 || m_b[i+1]!=0.0) ) {
modified=true;
m_b[i]=0.0;
m_b[i+1]=0.0;
} else if( (m_b[i]>=0.0 && m_b[i+1]>=0.0 && avg>0.0) ||
(m_b[i]<=0.0 && m_b[i+1]<=0.0 && avg<0.0) ) {
// input data is monotonic
double r = sqrt(m_b[i]*m_b[i]+m_b[i+1]*m_b[i+1])/std::fabs(avg);
if(r>3.0) {
// sufficient criteria for monotonicity: r<=3
// adjust b[i] and b[i+1]
modified=true;
m_b[i] *= (3.0/r);
m_b[i+1] *= (3.0/r);
}
}
}
if(modified==true) {
set_coeffs_from_b();
m_made_monotonic=true;
}
return modified;
}
// return the closest idx so that m_x[idx] <= x (return 0 if x<m_x[0])
size_t spline::find_closest(double x) const
{
std::vector<double>::const_iterator it;
it=std::upper_bound(m_x.begin(),m_x.end(),x); // *it > x
size_t idx = std::max( int(it-m_x.begin())-1, 0); // m_x[idx] <= x
return idx;
}
double spline::operator() (double x) const
{
// polynomial evaluation using Horner's scheme
// TODO: consider more numerically accurate algorithms, e.g.:
// - Clenshaw
// - Even-Odd method by A.C.R. Newbery
// - Compensated Horner Scheme
size_t n=m_x.size();
size_t idx=find_closest(x);
double h=x-m_x[idx];
double interpol;
if(x<m_x[0]) {
// extrapolation to the left
interpol=(m_c0*h + m_b[0])*h + m_y[0];
} else if(x>m_x[n-1]) {
// extrapolation to the right
interpol=(m_c[n-1]*h + m_b[n-1])*h + m_y[n-1];
} else {
// interpolation
interpol=((m_d[idx]*h + m_c[idx])*h + m_b[idx])*h + m_y[idx];
}
return interpol;
}
double spline::deriv(int order, double x) const
{
assert(order>0);
size_t n=m_x.size();
size_t idx = find_closest(x);
double h=x-m_x[idx];
double interpol;
if(x<m_x[0]) {
// extrapolation to the left
switch(order) {
case 1:
interpol=2.0*m_c0*h + m_b[0];
break;
case 2:
interpol=2.0*m_c0;
break;
default:
interpol=0.0;
break;
}
} else if(x>m_x[n-1]) {
// extrapolation to the right
switch(order) {
case 1:
interpol=2.0*m_c[n-1]*h + m_b[n-1];
break;
case 2:
interpol=2.0*m_c[n-1];
break;
default:
interpol=0.0;
break;
}
} else {
// interpolation
switch(order) {
case 1:
interpol=(3.0*m_d[idx]*h + 2.0*m_c[idx])*h + m_b[idx];
break;
case 2:
interpol=6.0*m_d[idx]*h + 2.0*m_c[idx];
break;
case 3:
interpol=6.0*m_d[idx];
break;
default:
interpol=0.0;
break;
}
}
return interpol;
}
std::vector<double> spline::solve(double y, bool ignore_extrapolation) const
{
std::vector<double> x; // roots for the entire spline
std::vector<double> root; // roots for each piecewise cubic
const size_t n=m_x.size();
// left extrapolation
if(ignore_extrapolation==false) {
root = internal::solve_cubic(m_y[0]-y,m_b[0],m_c0,0.0,1);
for(size_t j=0; j<root.size(); j++) {
if(root[j]<0.0) {
x.push_back(m_x[0]+root[j]);
}
}
}
// brute force check if piecewise cubic has roots in their resp. segment
// TODO: make more efficient
for(size_t i=0; i<n-1; i++) {
root = internal::solve_cubic(m_y[i]-y,m_b[i],m_c[i],m_d[i],1);
for(size_t j=0; j<root.size(); j++) {
double h = (i>0) ? (m_x[i]-m_x[i-1]) : 0.0;
double eps = internal::get_eps()*512.0*std::min(h,1.0);
if( (-eps<=root[j]) && (root[j]<m_x[i+1]-m_x[i]) ) {
double new_root = m_x[i]+root[j];
if(x.size()>0 && x.back()+eps > new_root) {
x.back()=new_root; // avoid spurious duplicate roots
} else {
x.push_back(new_root);
}
}
}
}
// right extrapolation
if(ignore_extrapolation==false) {
root = internal::solve_cubic(m_y[n-1]-y,m_b[n-1],m_c[n-1],0.0,1);
for(size_t j=0; j<root.size(); j++) {
if(0.0<=root[j]) {
x.push_back(m_x[n-1]+root[j]);
}
}
}
return x;
};
#ifdef HAVE_SSTREAM
std::string spline::info() const
{
std::stringstream ss;
ss << "type " << m_type << ", left boundary deriv " << m_left << " = ";
ss << m_left_value << ", right boundary deriv " << m_right << " = ";
ss << m_right_value << std::endl;
if(m_made_monotonic) {
ss << "(spline has been adjusted for piece-wise monotonicity)";
}
return ss.str();
}
#endif // HAVE_SSTREAM
namespace internal
{
// band_matrix implementation
// -------------------------
band_matrix::band_matrix(int dim, int n_u, int n_l)
{
resize(dim, n_u, n_l);
}
void band_matrix::resize(int dim, int n_u, int n_l)
{
assert(dim>0);
assert(n_u>=0);
assert(n_l>=0);
m_upper.resize(n_u+1);
m_lower.resize(n_l+1);
for(size_t i=0; i<m_upper.size(); i++) {
m_upper[i].resize(dim);
}
for(size_t i=0; i<m_lower.size(); i++) {
m_lower[i].resize(dim);
}
}
int band_matrix::dim() const
{
if(m_upper.size()>0) {
return (int)m_upper[0].size();
} else {
return 0;
}
}
// defines the new operator (), so that we can access the elements
// by A(i,j), index going from i=0,...,dim()-1
double & band_matrix::operator () (int i, int j)
{
int k=j-i; // what band is the entry
assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
assert( (-num_lower()<=k) && (k<=num_upper()) );
// k=0 -> diagonal, k<0 lower left part, k>0 upper right part
if(k>=0) return m_upper[k][i];
else return m_lower[-k][i];
}
double band_matrix::operator () (int i, int j) const
{
int k=j-i; // what band is the entry
assert( (i>=0) && (i<dim()) && (j>=0) && (j<dim()) );
assert( (-num_lower()<=k) && (k<=num_upper()) );
// k=0 -> diagonal, k<0 lower left part, k>0 upper right part
if(k>=0) return m_upper[k][i];
else return m_lower[-k][i];
}
// second diag (used in LU decomposition), saved in m_lower
double band_matrix::saved_diag(int i) const
{
assert( (i>=0) && (i<dim()) );
return m_lower[0][i];
}
double & band_matrix::saved_diag(int i)
{
assert( (i>=0) && (i<dim()) );
return m_lower[0][i];
}
// LR-Decomposition of a band matrix
void band_matrix::lu_decompose()
{
int i_max,j_max;
int j_min;
double x;
// preconditioning
// normalize column i so that a_ii=1
for(int i=0; i<this->dim(); i++) {
assert(this->operator()(i,i)!=0.0);
this->saved_diag(i)=1.0/this->operator()(i,i);
j_min=std::max(0,i-this->num_lower());
j_max=std::min(this->dim()-1,i+this->num_upper());
for(int j=j_min; j<=j_max; j++) {
this->operator()(i,j) *= this->saved_diag(i);
}
this->operator()(i,i)=1.0; // prevents rounding errors
}
// Gauss LR-Decomposition
for(int k=0; k<this->dim(); k++) {
i_max=std::min(this->dim()-1,k+this->num_lower()); // num_lower not a mistake!
for(int i=k+1; i<=i_max; i++) {
assert(this->operator()(k,k)!=0.0);
x=-this->operator()(i,k)/this->operator()(k,k);
this->operator()(i,k)=-x; // assembly part of L
j_max=std::min(this->dim()-1,k+this->num_upper());
for(int j=k+1; j<=j_max; j++) {
// assembly part of R
this->operator()(i,j)=this->operator()(i,j)+x*this->operator()(k,j);
}
}
}
}
// solves Ly=b
std::vector<double> band_matrix::l_solve(const std::vector<double>& b) const
{
assert( this->dim()==(int)b.size() );
std::vector<double> x(this->dim());
int j_start;
double sum;
for(int i=0; i<this->dim(); i++) {
sum=0;
j_start=std::max(0,i-this->num_lower());
for(int j=j_start; j<i; j++) sum += this->operator()(i,j)*x[j];
x[i]=(b[i]*this->saved_diag(i)) - sum;
}
return x;
}
// solves Rx=y
std::vector<double> band_matrix::r_solve(const std::vector<double>& b) const
{
assert( this->dim()==(int)b.size() );
std::vector<double> x(this->dim());
int j_stop;
double sum;
for(int i=this->dim()-1; i>=0; i--) {
sum=0;
j_stop=std::min(this->dim()-1,i+this->num_upper());
for(int j=i+1; j<=j_stop; j++) sum += this->operator()(i,j)*x[j];
x[i]=( b[i] - sum ) / this->operator()(i,i);
}
return x;
}
std::vector<double> band_matrix::lu_solve(const std::vector<double>& b,
bool is_lu_decomposed)
{
assert( this->dim()==(int)b.size() );
std::vector<double> x,y;
if(is_lu_decomposed==false) {
this->lu_decompose();
}
y=this->l_solve(b);
x=this->r_solve(y);
return x;
}
// machine precision of a double, i.e. the successor of 1 is 1+eps
double get_eps()
{
//return std::numeric_limits<double>::epsilon(); // __DBL_EPSILON__
return 2.2204460492503131e-16; // 2^-52
}
// solutions for a + b*x = 0
std::vector<double> solve_linear(double a, double b)
{
std::vector<double> x; // roots
if(b==0.0) {
if(a==0.0) {
// 0*x = 0
x.resize(1);
x[0] = 0.0; // any x solves it but we need to pick one
return x;
} else {
// 0*x + ... = 0, no solution
return x;
}
} else {
x.resize(1);
x[0] = -a/b;
return x;
}
}
// solutions for a + b*x + c*x^2 = 0
std::vector<double> solve_quadratic(double a, double b, double c,
int newton_iter=0)
{
if(c==0.0) {
return solve_linear(a,b);
}
// rescale so that we solve x^2 + 2p x + q = (x+p)^2 + q - p^2 = 0
double p=0.5*b/c;
double q=a/c;
double discr = p*p-q;
const double eps=0.5*internal::get_eps();
double discr_err = (6.0*(p*p)+3.0*fabs(q)+fabs(discr))*eps;
std::vector<double> x; // roots
if(fabs(discr)<=discr_err) {
// discriminant is zero --> one root
x.resize(1);
x[0] = -p;
} else if(discr<0) {
// no root
} else {
// two roots
x.resize(2);
x[0] = -p - sqrt(discr);
x[1] = -p + sqrt(discr);
}
// improve solution via newton steps
for(size_t i=0; i<x.size(); i++) {
for(int k=0; k<newton_iter; k++) {
double f = (c*x[i] + b)*x[i] + a;
double f1 = 2.0*c*x[i] + b;
// only adjust if slope is large enough
if(fabs(f1)>1e-8) {
x[i] -= f/f1;
}
}
}
return x;
}
// solutions for the cubic equation: a + b*x +c*x^2 + d*x^3 = 0
// this is a naive implementation of the analytic solution without
// optimisation for speed or numerical accuracy
// newton_iter: number of newton iterations to improve analytical solution
// see also
// gsl: gsl_poly_solve_cubic() in solve_cubic.c
// octave: roots.m - via eigenvalues of the Frobenius companion matrix
std::vector<double> solve_cubic(double a, double b, double c, double d,
int newton_iter)
{
if(d==0.0) {
return solve_quadratic(a,b,c,newton_iter);
}
// convert to normalised form: a + bx + cx^2 + x^3 = 0
if(d!=1.0) {
a/=d;
b/=d;
c/=d;
}
// convert to depressed cubic: z^3 - 3pz - 2q = 0
// via substitution: z = x + c/3
std::vector<double> z; // roots of the depressed cubic
double p = -(1.0/3.0)*b + (1.0/9.0)*(c*c);
double r = 2.0*(c*c)-9.0*b;
double q = -0.5*a - (1.0/54.0)*(c*r);
double discr=p*p*p-q*q; // discriminant
// calculating numerical round-off errors with assumptions:
// - each operation is precise but each intermediate result x
// when stored has max error of x*eps
// - only multiplication with a power of 2 introduces no new error
// - a,b,c,d and some fractions (e.g. 1/3) have rounding errors eps
// - p_err << |p|, q_err << |q|, ... (this is violated in rare cases)
// would be more elegant to use boost::numeric::interval<double>
const double eps = internal::get_eps();
double p_err = eps*((3.0/3.0)*fabs(b)+(4.0/9.0)*(c*c)+fabs(p));
double r_err = eps*(6.0*(c*c)+18.0*fabs(b)+fabs(r));
double q_err = 0.5*fabs(a)*eps + (1.0/54.0)*fabs(c)*(r_err+fabs(r)*3.0*eps)
+ fabs(q)*eps;
double discr_err = (p*p) * (3.0*p_err + fabs(p)*2.0*eps)
+ fabs(q) * (2.0*q_err + fabs(q)*eps) + fabs(discr)*eps;
// depending on the discriminant we get different solutions
if(fabs(discr)<=discr_err) {
// discriminant zero: one or two real roots
if(fabs(p)<=p_err) {
// p and q are zero: single root
z.resize(1);
z[0] = 0.0; // triple root
} else {
z.resize(2);
z[0] = 2.0*q/p; // single root
z[1] = -0.5*z[0]; // double root
}
} else if(discr>0) {
// three real roots: via trigonometric solution
z.resize(3);
double ac = (1.0/3.0) * acos( q/(p*sqrt(p)) );
double sq = 2.0*sqrt(p);
z[0] = sq * cos(ac);
z[1] = sq * cos(ac-2.0*M_PI/3.0);
z[2] = sq * cos(ac-4.0*M_PI/3.0);
} else if (discr<0.0) {
// single real root: via Cardano's fromula
z.resize(1);
double sgnq = (q >= 0 ? 1 : -1);
double basis = fabs(q) + sqrt(-discr);
double C = sgnq * pow(basis, 1.0/3.0); // c++11 has std::cbrt()
z[0] = C + p/C;
}
for(size_t i=0; i<z.size(); i++) {
// convert depressed cubic roots to original cubic: x = z - c/3
z[i] -= (1.0/3.0)*c;
// improve solution via newton steps
for(int k=0; k<newton_iter; k++) {
double f = ((z[i] + c)*z[i] + b)*z[i] + a;
double f1 = (3.0*z[i] + 2.0*c)*z[i] + b;
// only adjust if slope is large enough
if(fabs(f1)>1e-8) {
z[i] -= f/f1;
}
}
}
// ensure if a=0 we get exactly x=0 as root
// TODO: remove this fudge
if(a==0.0) {
assert(z.size()>0); // cubic should always have at least one root
double xmin=fabs(z[0]);
size_t imin=0;
for(size_t i=1; i<z.size(); i++) {
if(xmin>fabs(z[i])) {
xmin=fabs(z[i]);
imin=i;
}
}
z[imin]=0.0; // replace the smallest absolute value with 0
}
std::sort(z.begin(), z.end());
return z;
}
} // namespace internal
} // namespace tk
} // namespace
#if !defined(_MSC_VER)
#pragma GCC diagnostic pop
#endif
#endif /* TK_SPLINE_H */