diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d15c3e5c2..680a9515d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -507,6 +507,7 @@ add_custom_target(gettext_make_pot COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost -f "${BBL_L18N_DIR}/list.txt" -o "${BBL_L18N_DIR}/BambuStudio.pot" + COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Generate pot file from strings in the source tree" ) @@ -660,6 +661,8 @@ endfunction() add_subdirectory(src) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT BambuStudio_app_gui) +add_dependencies(gettext_make_pot hintsToPot) + # Perl bindings, currently only used for the unit / integration tests of libslic3r. # Also runs the unit / integration tests. #FIXME Port the tests into C++ to finally get rid of the Perl! diff --git a/bbl/i18n/BambuStudio.pot b/bbl/i18n/BambuStudio.pot index b7ae10719d..16cbd476dc 100644 --- a/bbl/i18n/BambuStudio.pot +++ b/bbl/i18n/BambuStudio.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,9 +21,15 @@ msgstr "" msgid "Supports Painting" msgstr "" +msgid "Alt + Mouse wheel" +msgstr "" + msgid "Section view" msgstr "" +msgid "Ctrl + Mouse wheel" +msgstr "" + msgid "Pen size" msgstr "" @@ -42,7 +48,7 @@ msgstr "" msgid "Shift + Left mouse button" msgstr "" -msgid "Erase painting" +msgid "Erase" msgstr "" msgid "Erase all painting" @@ -60,12 +66,6 @@ msgstr "" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "" - -msgid "Ctrl + Mouse wheel" -msgstr "" - msgid "Tool type" msgstr "" @@ -114,9 +114,6 @@ msgstr "" msgid "Paint" msgstr "" -msgid "Erase" -msgstr "" - msgid "Key 1~9" msgstr "" @@ -244,10 +241,10 @@ msgstr "" msgid "Movement:" msgstr "" -msgid "Rotation:" +msgid "Movement" msgstr "" -msgid "Height:" +msgid "Height" msgstr "" msgid "Keep upper part" @@ -351,10 +348,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -420,6 +417,9 @@ msgstr "" msgid "V" msgstr "" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "" @@ -547,6 +547,15 @@ msgstr "" msgid "*" msgstr "" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "" @@ -1039,6 +1048,42 @@ msgstr "" msgid "Line Type" msgstr "" +msgid "More" +msgstr "" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "" @@ -1075,7 +1120,10 @@ msgstr "" msgid "Tips" msgstr "" -msgid "AMS Settings" +msgid "Guide" +msgstr "" + +msgid "Retry" msgstr "" msgid "Calibrating AMS..." @@ -1184,6 +1232,9 @@ msgstr "" msgid "Unknown Failure" msgstr "" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "" @@ -1353,6 +1404,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "" + msgid "Insertion update" msgstr "" @@ -1389,6 +1443,19 @@ msgstr "" msgid "File" msgstr "" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1449,6 +1516,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, possible-boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "" @@ -1787,6 +1864,12 @@ msgstr "" msgid "Tool" msgstr "" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2006,6 +2089,12 @@ msgstr "" msgid "Explosion Ratio" msgstr "" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "" @@ -2107,6 +2196,9 @@ msgstr "" msgid "Slice" msgstr "" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "" @@ -2116,6 +2208,12 @@ msgstr "" msgid "Print all" msgstr "" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "" + msgid "Export sliced file" msgstr "" @@ -2141,6 +2239,9 @@ msgstr "" msgid "&About %s" msgstr "" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2426,17 +2527,33 @@ msgstr "" msgid "Initialize failed (No Device)!" msgstr "" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "" msgid "Loading..." msgstr "" -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, possible-c-format, possible-boost-format -msgid "Initialize failed [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, possible-c-format, possible-boost-format +msgid "Stopped [%d]!" msgstr "" msgid "Stopped." @@ -2485,7 +2602,7 @@ msgstr "" msgid "Download selected files from printer." msgstr "" -msgid "Management" +msgid "Select" msgstr "" msgid "Batch manage files." @@ -2510,16 +2627,13 @@ msgstr "" msgid "No files" msgstr "" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" +msgid "Open Folder" msgstr "" msgid "Download finished" @@ -2622,6 +2736,9 @@ msgstr "" msgid "Ludicrous" msgstr "" +msgid "Failed to connect to the server" +msgstr "" + msgid "Status" msgstr "" @@ -2634,15 +2751,9 @@ msgstr "" msgid "HMS" msgstr "" -msgid "Failed to connect to the server" -msgstr "" - msgid "Failed to connect to the printer" msgstr "" -msgid "OK" -msgstr "" - msgid "Yes" msgstr "" @@ -2706,9 +2817,6 @@ msgstr "" msgid "Goto download page." msgstr "" -msgid "More" -msgstr "" - msgid "Open Folder." msgstr "" @@ -2724,6 +2832,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "" msgstr[1] "" +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "" @@ -2929,6 +3049,11 @@ msgstr "" msgid "Restore" msgstr "" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, possible-c-format, possible-boost-format msgid "Loading file: %s" msgstr "" @@ -3035,6 +3160,9 @@ msgstr "" msgid "Another export job is running." msgstr "" +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3141,9 +3269,6 @@ msgstr "" msgid "Save Sliced file as:" msgstr "" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3213,6 +3338,12 @@ msgstr "" msgid "Second" msgstr "" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "" @@ -3243,6 +3374,12 @@ msgstr "" msgid "Units" msgstr "" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "" @@ -3282,6 +3419,9 @@ msgstr "" msgid "Backup interval" msgstr "" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "" @@ -3513,6 +3653,9 @@ msgstr "" msgid "Input access code" msgstr "" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "" @@ -3528,9 +3671,6 @@ msgstr "" msgid "Bamabu High Temperature Plate" msgstr "" -msgid "Send print job to" -msgstr "" - msgid "Refresh" msgstr "" @@ -3540,7 +3680,7 @@ msgstr "" msgid "Flow Calibration" msgstr "" -msgid "Send" +msgid "Can't connect to the printer" msgstr "" msgid "send completed" @@ -3605,12 +3745,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "" msgid "Modifying the device name" msgstr "" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "" @@ -3650,12 +3811,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -3835,6 +3997,9 @@ msgstr "" msgid "Printable space" msgstr "" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "" @@ -3859,6 +4024,9 @@ msgstr "" msgid "Pause G-code" msgstr "" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "" @@ -3903,9 +4071,6 @@ msgstr "" msgid "Set" msgstr "" -msgid "Custom G-code" -msgstr "" - msgid "Click to reset current value and attach to the global value." msgstr "" @@ -4354,23 +4519,6 @@ msgstr "" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4401,6 +4549,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "" @@ -4634,13 +4799,13 @@ msgstr "" #, possible-boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" #, possible-boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" #, possible-boost-format @@ -4651,11 +4816,11 @@ msgstr "" msgid "%1% is too tall, and collisions will be caused." msgstr "" -msgid " is too close to others, there will be collisions when printing.\n" +msgid " is too close to others, there may be collisions when printing.\n" msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" msgid "Prime Tower" @@ -4782,18 +4947,81 @@ msgstr "" msgid "Printer preset names" msgstr "" -msgid "Avoid crossing wall when travel" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" -msgid "Max travel detour distance" +msgid "Avoid crossing wall - Max detour length" msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" msgid "Other layers" @@ -5148,19 +5376,24 @@ msgid "Height to rod" msgstr "" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" msgid "Height to lid" msgstr "" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" msgid "Extruder Color" @@ -5335,6 +5568,9 @@ msgstr "" msgid "Adaptive Cubic" msgstr "" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5447,6 +5683,9 @@ msgid "" "nozzle, and what kind of filament can be printed" msgstr "" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "" @@ -5541,6 +5780,14 @@ msgid "" "acceleration to print" msgstr "" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "" @@ -5691,6 +5938,14 @@ msgstr "" msgid "Diameter of nozzle" msgstr "" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "" @@ -6382,10 +6637,6 @@ msgstr "" msgid " Object:" msgstr "" -#, possible-boost-format -msgid "Error at line %1%:\n" -msgstr "" - #, possible-c-format, possible-boost-format msgid "Support: generate toolpath at layer %d" msgstr "" @@ -6416,3 +6667,91 @@ msgstr "" #, possible-c-format, possible-boost-format msgid "Support: propagate branches at layer %d" msgstr "" + +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "3D Scene Operations\nDid you know how to control view and object/part selection with mouse and touchpanel in the 3D scene?" +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?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "Timelapse\nDid you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "Auto-Arrange\nDid you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "Auto-Orient\nDid you know that you can rotate objects to an optimal orientation for printing by a simple click?" +msgstr "" + +#: resources/data/hints.ini: [hint:Lay on Face] +msgid "Lay on Face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "Object List\nDid you know that you can view all objects/parts in a list and change settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "Simplify Model\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "Slicing Parameter Table\nDid you know that you can view all objects/parts on a table and change settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Split to Objects/Parts] +msgid "Split to Objects/Parts\nDid you know that you can split a big object into small ones for easy colorizing or printing?" +msgstr "" + +#: resources/data/hints.ini: [hint:Subtract a Part] +msgid "Subtract a Part\nDid you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "STEP\nDid you know that you can improve your print quality by slicing a STEP file instead of an STL?\nBambu Studio supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "Z seam location\nDid you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!" +msgstr "" + +#: resources/data/hints.ini: [hint:Fine-tuning for flow rate] +msgid "Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned for even better-looking prints? Depending on the material, you can improve the overall finish of the printed model by doing some fine-tuning." +msgstr "" + +#: resources/data/hints.ini: [hint:Split your prints into plates] +msgid "Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts." +msgstr "" + +#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height] +msgid "Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!" +msgstr "" + +#: resources/data/hints.ini: [hint:Support painting] +msgid "Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it." +msgstr "" + +#: resources/data/hints.ini: [hint:Different types of supports] +msgid "Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!" +msgstr "" + +#: resources/data/hints.ini: [hint:Printing Silk Filament] +msgid "Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results." +msgstr "" + +#: resources/data/hints.ini: [hint:Brim for better adhesion] +msgid "Brim for better adhesion\nDid you know that when printing models have a small contact interface with the printing surface, it's recommended to use a brim?" +msgstr "" + +#: resources/data/hints.ini: [hint:Set parameters for multiple objects] +msgid "Set parameters for multiple objects\nDid you know that you can set slicing parameters for all selected objects at one time?" +msgstr "" diff --git a/bbl/i18n/de/BambuStudio_de.po b/bbl/i18n/de/BambuStudio_de.po index 0e7c28e896..709c40d8f3 100644 --- a/bbl/i18n/de/BambuStudio_de.po +++ b/bbl/i18n/de/BambuStudio_de.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Supports aufmalen" +msgid "Alt + Mouse wheel" +msgstr "Alt + Mausrad" + msgid "Section view" msgstr "Abschnittsansicht" +msgid "Ctrl + Mouse wheel" +msgstr "Strg + Mausrad" + msgid "Pen size" msgstr "Pinselgröße" @@ -37,8 +43,8 @@ msgstr "Support blockieren" msgid "Shift + Left mouse button" msgstr "Gross + Linke Maustaste" -msgid "Erase painting" -msgstr "Gemaltes löschen" +msgid "Erase" +msgstr "löschen" msgid "Erase all painting" msgstr "Alles gemalte löschen" @@ -55,12 +61,6 @@ msgstr "Ausführen" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Pinselgröße festlegen" - -msgid "Ctrl + Mouse wheel" -msgstr "Strg + Mausrad" - msgid "Tool type" msgstr "Werkzeugtyp" @@ -112,9 +112,6 @@ msgstr "Pinselform" msgid "Paint" msgstr "malen" -msgid "Erase" -msgstr "löschen" - msgid "Key 1~9" msgstr "Schlüssel 1~9" @@ -242,11 +239,11 @@ msgstr "Schneiden" msgid "Movement:" msgstr "Bewegung:" -msgid "Rotation:" -msgstr "Drehung:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Höhe:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Oberen Teil behalten" @@ -353,10 +350,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -427,6 +424,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Version" @@ -568,6 +568,15 @@ msgstr "Spache" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Wähle eine G-Code Datei:" @@ -1080,6 +1089,42 @@ msgstr "Mehrfarbiger Druck" msgid "Line Type" msgstr "Linientyp" +msgid "More" +msgstr "Mehr" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Benutzerdefinierter G-Code" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Kein Drucker" @@ -1116,8 +1161,11 @@ msgstr "Entladen" msgid "Tips" msgstr "Tipps" -msgid "AMS Settings" -msgstr "AMS Einstellungen" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "AMS Kalibrieren..." @@ -1244,6 +1292,9 @@ msgstr "Bindungsfehler" msgid "Unknown Failure" msgstr "Unbekannter Fehler" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Abnormale Daten in der Druckdatei. Bitte slicen Sie erneut" @@ -1422,6 +1473,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "AMS Einstellungen" + msgid "Insertion update" msgstr "Update einfügen" @@ -1472,6 +1526,19 @@ msgstr "" msgid "File" msgstr "Datei" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1537,6 +1604,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Erfolgreich G-Code nach %1% exportiert" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Nullpunkt" @@ -1941,6 +2018,12 @@ msgstr "Fluss" msgid "Tool" msgstr "Werkzeug" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2160,6 +2243,12 @@ msgstr "Malwerkzeuge" msgid "Explosion Ratio" msgstr "Explosionsverhältnis" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Gesamtvolumen:" @@ -2266,6 +2355,9 @@ msgstr "Projekt" msgid "Slice" msgstr "Slice" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Alle Slicen" @@ -2275,6 +2367,12 @@ msgstr "Druckplatte slicen" msgid "Print all" msgstr "Alle drucken" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Senden" + msgid "Export sliced file" msgstr "Geslicete Datei exportieren" @@ -2300,6 +2398,9 @@ msgstr "Prüfen auf Update" msgid "&About %s" msgstr "&Über %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2585,18 +2686,34 @@ msgstr "Synchronisierung" msgid "Initialize failed (No Device)!" msgstr "Initialisierung fehlgeschlagen (Kein Gerät)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Initialisieren..." msgid "Loading..." msgstr "Laden..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Initialisierung fehlgeschlagen [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Gestoppt." @@ -2644,8 +2761,8 @@ msgstr "Herunterladen" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Management" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2669,17 +2786,14 @@ msgstr "Loading file list..." msgid "No files" msgstr "No files" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Open" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2781,6 +2895,9 @@ msgstr "Sport" msgid "Ludicrous" msgstr "Verrückt" +msgid "Failed to connect to the server" +msgstr "Verbindung zum Server fehlgeschlagen" + msgid "Status" msgstr "Status" @@ -2793,15 +2910,9 @@ msgstr "Update" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Verbindung zum Server fehlgeschlagen" - msgid "Failed to connect to the printer" msgstr "Verbindung zum Drucker fehlgeschlagen" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Ja" @@ -2865,9 +2976,6 @@ msgstr "Die Software hat eine neue Version." msgid "Goto download page." msgstr "Zur Download-Seite gehen." -msgid "More" -msgstr "Mehr" - msgid "Open Folder." msgstr "Öffne Ordner." @@ -2883,6 +2991,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d Objekt hat gemalte Farbe." msgstr[1] "%1$d Objekte haben gemalte Farbe." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Slice ok." @@ -3035,6 +3155,9 @@ msgstr "Filamentwechsel" msgid "Click to edit preset" msgstr "Klicken zum Bearbeiten der Voreinstellung" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Druckbetttyp" @@ -3093,6 +3216,11 @@ msgstr "" msgid "Restore" msgstr "Wiederherstellen" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Datei wird geladen: %s" @@ -3206,6 +3334,9 @@ msgstr "Das ausgewählte Objekt konnte nicht geteilt werden." msgid "Another export job is running." msgstr "Ein weiterer Exportauftrag läuft gerade." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3316,9 +3447,6 @@ msgstr "Speichere G-Code Datei als:" msgid "Save Sliced file as:" msgstr "Geslicte Datei speichern unter:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3395,6 +3523,12 @@ msgstr "Auswahl der Region" msgid "Second" msgstr "Sekunde" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Allgemeine Einstellungen" @@ -3425,6 +3559,12 @@ msgstr "Impirisch" msgid "Units" msgstr "Einheiten" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Benutzer-Synchronisation" @@ -3472,6 +3612,9 @@ msgstr "Automatische Datensicherung" msgid "Backup interval" msgstr "Sicherungsintervall" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Startseite und tägliche Tipps" @@ -3712,6 +3855,9 @@ msgstr "Anderes Gerät" msgid "Input access code" msgstr "Zugangscode eingeben" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Abmeldung erfolgreich." @@ -3727,9 +3873,6 @@ msgstr "Bambu technische Druckplatte" msgid "Bamabu High Temperature Plate" msgstr "Bambu Hochtemperaturdruckplatte" -msgid "Send print job to" -msgstr "Druckauftrag senden an" - msgid "Refresh" msgstr "Aktualisieren" @@ -3739,8 +3882,8 @@ msgstr "Druckbettnivellierung" msgid "Flow Calibration" msgstr "Durchfluss-Kalibrierung" -msgid "Send" -msgstr "Senden" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "senden abgeschlossen" @@ -3812,12 +3955,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Druckauftrag vorbereiten" msgid "Modifying the device name" msgstr "Den Gerätenamen ändern" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Drucker anmelden" @@ -3860,12 +4024,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4073,6 +4238,9 @@ msgstr "Filament End G-Code" msgid "Printable space" msgstr "Druckbarer Raum" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Zubehör" @@ -4097,6 +4265,9 @@ msgstr "Filamentwechsel G-Code" msgid "Pause G-code" msgstr "Pausen G-Code" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Bewegungsfähigkeit" @@ -4141,9 +4312,6 @@ msgstr "Alle" msgid "Set" msgstr "Setzen" -msgid "Custom G-code" -msgstr "Benutzerdefinierter G-Code" - msgid "Click to reset current value and attach to the global value." msgstr "" "Klicken Sie, um den aktuellen Wert zurückzusetzen und auf den globalen Wert " @@ -4624,23 +4792,6 @@ msgstr "Neue Version von Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4671,6 +4822,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "Das Speichern von Objekten in der 3mf ist fehlgeschlagen." @@ -4919,17 +5087,14 @@ msgstr "Schreibrückruf fehlgeschlagen" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% liegt zu nahe am Ausschlussbereich, beim Drucken kommt es zu Kollisionen." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% liegt zu nahe am Ausschlussbereich, beim Drucken kommt es zu Kollisionen." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4940,12 +5105,12 @@ msgstr "" msgid "%1% is too tall, and collisions will be caused." msgstr "%1% ist zu hoch und es kommt zu Kollisionen." -msgid " is too close to others, there will be collisions when printing.\n" -msgstr " zu nahe an anderen, es kommt beim Drucken zu Kollisionen.\n" +msgid " is too close to others, there may be collisions when printing.\n" +msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" -msgstr " ist zu nahe am Sperrbereich, es kommt beim Drucken zu Kollisionen.\n" +" is too close to exclusion area, there may be collisions when printing.\n" +msgstr "" msgid "Prime Tower" msgstr "Prime Turm" @@ -5097,23 +5262,84 @@ msgstr "" msgid "Printer preset names" msgstr "Drucker-Voreinstellungsnamen" -msgid "Avoid crossing wall when travel" -msgstr "Vermeiden Sie das Überqueren von Mauern während der Fahrt" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Fahren Sie einen Umweg und vermeiden Sie es, über die Wand zu fahren, was zu " "Tropfenbildung auf der Oberfläche führen kann" -msgid "Max travel detour distance" -msgstr "Maximale Umwegentfernung" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Maximale Umleitungsstrecke zur Vermeidung einer querenden Wand. Keine " -"Umleitung, wenn die Umleitungsstrecke größer als dieser Wert ist" msgid "Other layers" msgstr "Andere Schichten" @@ -5515,26 +5741,25 @@ msgid "Height to rod" msgstr "Höhe zur Stange" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Höhe des Abstandszylinders um den Extruder. Wird als Eingabe für die " -"automatische Anordnung verwendet, um Kollisionen zu vermeiden, wenn Objekt " -"für Objekt gedruckt wird" msgid "Height to lid" msgstr "Höhe zum Deckel" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Radius" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Abstandsradius um den Extruder. Wird als Eingabe für die automatische " -"Anordnung verwendet, um Kollisionen zu vermeiden, wenn Objekt für Objekt " -"gedruckt wird" msgid "Extruder Color" msgstr "Extruder Farbe" @@ -5739,6 +5964,9 @@ msgstr "Bienenwabe" msgid "Adaptive Cubic" msgstr "Adaptiv kubisch" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5881,6 +6109,9 @@ msgstr "" "Das metallische Material der Druckdüse. Dies bestimmt die Abriebfestigkeit " "der Druckdüse und welche Art von Filament gedruckt werden kann" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Gehärteter Stahl" @@ -5993,6 +6224,14 @@ msgstr "" "ob das Gerät den geräuschlosen Modus unterstützt, bei dem das Gerät eine " "geringere Beschleunigung zum Drucken verwendet" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Maximale Geschwindigkeit X" @@ -6155,6 +6394,14 @@ msgstr "Druckdüsendurchmesser" msgid "Diameter of nozzle" msgstr "Druckdüsendurchmesser" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Volumen der Druckdüse" @@ -6962,10 +7209,6 @@ msgstr "Slicen des Netzes" msgid " Object:" msgstr " Objekt:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Fehler auf Zeile %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support: Werkzeugpfad auf Ebene %d erzeugen" @@ -6997,6 +7240,250 @@ msgstr "Support: Löcher in Schicht %d repairieren" msgid "Support: propagate branches at layer %d" msgstr "Support: Verbreiten von Zweigen auf Ebene %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Gemaltes löschen" + +#~ msgid "Set pen size" +#~ msgstr "Pinselgröße festlegen" + +#~ msgid "Rotation:" +#~ msgstr "Drehung:" + +#~ msgid "Height:" +#~ msgstr "Höhe:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Initialisierung fehlgeschlagen [%d]!" + +#~ msgid "Management" +#~ msgstr "Management" + +#~ msgid "Open" +#~ msgstr "Open" + +#~ msgid "Send print job to" +#~ msgstr "Druckauftrag senden an" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% liegt zu nahe am Ausschlussbereich, beim Drucken kommt es zu " +#~ "Kollisionen." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% liegt zu nahe am Ausschlussbereich, beim Drucken kommt es zu " +#~ "Kollisionen." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr " zu nahe an anderen, es kommt beim Drucken zu Kollisionen.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ " ist zu nahe am Sperrbereich, es kommt beim Drucken zu Kollisionen.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Vermeiden Sie das Überqueren von Mauern während der Fahrt" + +#~ msgid "Max travel detour distance" +#~ msgstr "Maximale Umwegentfernung" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Maximale Umleitungsstrecke zur Vermeidung einer querenden Wand. Keine " +#~ "Umleitung, wenn die Umleitungsstrecke größer als dieser Wert ist" + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Höhe des Abstandszylinders um den Extruder. Wird als Eingabe für die " +#~ "automatische Anordnung verwendet, um Kollisionen zu vermeiden, wenn " +#~ "Objekt für Objekt gedruckt wird" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Abstandsradius um den Extruder. Wird als Eingabe für die automatische " +#~ "Anordnung verwendet, um Kollisionen zu vermeiden, wenn Objekt für Objekt " +#~ "gedruckt wird" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Fehler auf Zeile %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Dreiecke reduzieren" @@ -7028,9 +7515,6 @@ msgstr "Support: Verbreiten von Zweigen auf Ebene %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + Mausrad" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/en/BambuStudio_en.po b/bbl/i18n/en/BambuStudio_en.po index b530d84edd..1f2a6d7ef9 100644 --- a/bbl/i18n/en/BambuStudio_en.po +++ b/bbl/i18n/en/BambuStudio_en.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -13,9 +13,15 @@ msgstr "" msgid "Supports Painting" msgstr "Support Painting" +msgid "Alt + Mouse wheel" +msgstr "Alt + Mouse wheel" + msgid "Section view" msgstr "Section view" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + Mouse wheel" + msgid "Pen size" msgstr "Pen size" @@ -34,8 +40,8 @@ msgstr "Block supports" msgid "Shift + Left mouse button" msgstr "Shift + Left mouse button" -msgid "Erase painting" -msgstr "Erase painting" +msgid "Erase" +msgstr "Erase" msgid "Erase all painting" msgstr "Erase all painting" @@ -52,12 +58,6 @@ msgstr "Apply" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Set pen size" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + Mouse wheel" - msgid "Tool type" msgstr "Tool type" @@ -108,9 +108,6 @@ msgstr "Pen shape" msgid "Paint" msgstr "Paint" -msgid "Erase" -msgstr "Erase" - msgid "Key 1~9" msgstr "Key 1~9" @@ -238,11 +235,11 @@ msgstr "Cut" msgid "Movement:" msgstr "Movement:" -msgid "Rotation:" -msgstr "Rotation:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Height:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Keep upper part" @@ -348,10 +345,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -420,6 +417,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Version" @@ -557,6 +557,15 @@ msgstr "Language" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Select a G-code file:" @@ -1054,6 +1063,42 @@ msgstr "Multicolor Print" msgid "Line Type" msgstr "Line Type" +msgid "More" +msgstr "More" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Custom G-code" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "No printer" @@ -1090,8 +1135,11 @@ msgstr "Unload" msgid "Tips" msgstr "Tips" -msgid "AMS Settings" -msgstr "AMS Settings" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "Calibrating AMS..." @@ -1215,6 +1263,9 @@ msgstr "Binding failure" msgid "Unknown Failure" msgstr "Unknown Failure" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Abnormal print file data. Please slice again" @@ -1392,6 +1443,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "AMS Settings" + msgid "Insertion update" msgstr "Insertion update" @@ -1440,6 +1494,19 @@ msgstr "" msgid "File" msgstr "File" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1505,6 +1572,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Success! G-code exported to %1%" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Origin" @@ -1897,6 +1974,12 @@ msgstr "Flow" msgid "Tool" msgstr "Tool" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2116,6 +2199,12 @@ msgstr "Paint Toolbar" msgid "Explosion Ratio" msgstr "Explosion Ratio" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Total Volume:" @@ -2222,6 +2311,9 @@ msgstr "Project" msgid "Slice" msgstr "Slice" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Slice all" @@ -2231,6 +2323,12 @@ msgstr "Slice plate" msgid "Print all" msgstr "Print all" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Send" + msgid "Export sliced file" msgstr "Export Sliced File" @@ -2256,6 +2354,9 @@ msgstr "Check for Updates" msgid "&About %s" msgstr "&About %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2545,18 +2646,34 @@ msgstr "Synchronization" msgid "Initialize failed (No Device)!" msgstr "Initialization failed (No Device)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Initializing..." msgid "Loading..." msgstr "Loading..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Initalization failed [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Stopped." @@ -2604,7 +2721,7 @@ msgstr "Download" msgid "Download selected files from printer." msgstr "" -msgid "Management" +msgid "Select" msgstr "" msgid "Batch manage files." @@ -2629,16 +2746,13 @@ msgstr "" msgid "No files" msgstr "" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" +msgid "Open Folder" msgstr "" msgid "Download finished" @@ -2741,6 +2855,9 @@ msgstr "Sport" msgid "Ludicrous" msgstr "Ludicrous" +msgid "Failed to connect to the server" +msgstr "Failed to connect to the server" + msgid "Status" msgstr "Status" @@ -2753,15 +2870,9 @@ msgstr "Update" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Failed to connect to the server" - msgid "Failed to connect to the printer" msgstr "Failed to connect to the printer" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Yes" @@ -2825,9 +2936,6 @@ msgstr "An update is available!" msgid "Goto download page." msgstr "Go to download page" -msgid "More" -msgstr "More" - msgid "Open Folder." msgstr "Open Folder." @@ -2843,6 +2951,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d Object has color painting." msgstr[1] "%1$d Objects have color painting." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Slice complete" @@ -2993,6 +3113,9 @@ msgstr "Filament changes" msgid "Click to edit preset" msgstr "Click to edit preset" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Bed type" @@ -3050,6 +3173,11 @@ msgstr "" msgid "Restore" msgstr "Restore" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Loading file: %s" @@ -3160,6 +3288,9 @@ msgstr "The selected object couldn't be split." msgid "Another export job is running." msgstr "Another export job is running." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3268,9 +3399,6 @@ msgstr "Save G-code file as:" msgid "Save Sliced file as:" msgstr "Save Sliced file as:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3342,6 +3470,12 @@ msgstr "Region selection" msgid "Second" msgstr "Second" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "General Settings" @@ -3372,6 +3506,12 @@ msgstr "Imperial" msgid "Units" msgstr "Units" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "User sync" @@ -3417,6 +3557,9 @@ msgstr "Auto-Backup" msgid "Backup interval" msgstr "Backup interval" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Home page and daily tips" @@ -3649,6 +3792,9 @@ msgstr "Other Device" msgid "Input access code" msgstr "Input access code" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Log out successful." @@ -3664,9 +3810,6 @@ msgstr "Bambu Engineering Plate" msgid "Bamabu High Temperature Plate" msgstr "Bambu High Temperature Plate" -msgid "Send print job to" -msgstr "Send print job to" - msgid "Refresh" msgstr "Refresh" @@ -3676,8 +3819,8 @@ msgstr "Bed leveling" msgid "Flow Calibration" msgstr "Flow calibration" -msgid "Send" -msgstr "Send" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "Send complete" @@ -3746,12 +3889,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Preparing print job" msgid "Modifying the device name" msgstr "Modifying the device name" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Log in printer" @@ -3791,12 +3955,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -3998,6 +4163,9 @@ msgstr "Filament end G-code" msgid "Printable space" msgstr "Printable space" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Accessory" @@ -4022,6 +4190,9 @@ msgstr "Change filament G-code" msgid "Pause G-code" msgstr "Pause G-code" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Motion ability" @@ -4066,9 +4237,6 @@ msgstr "All" msgid "Set" msgstr "Set" -msgid "Custom G-code" -msgstr "Custom G-code" - msgid "Click to reset current value and attach to the global value." msgstr "Click to reset current value and attach to the global value." @@ -4535,23 +4703,6 @@ msgstr "New version of Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4582,6 +4733,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "Saving objects into the 3mf failed." @@ -4821,19 +4989,14 @@ msgstr "write callback failed" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% is too close to an exclusion area; there will be collisions when " -"printing." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% is too close to an exclusion area; there will be collisions when " -"printing." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4843,13 +5006,12 @@ msgstr "%1% is too close to others, and collisions may be caused." msgid "%1% is too tall, and collisions will be caused." msgstr "%1% is too tall, and collisions will be caused." -msgid " is too close to others, there will be collisions when printing.\n" -msgstr " is too close to others, there will be collisions when printing.\n" +msgid " is too close to others, there may be collisions when printing.\n" +msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" -" is too close to an exclusion area, there will be collisions when printing.\n" msgid "Prime Tower" msgstr "Prime Tower" @@ -4997,23 +5159,84 @@ msgstr "" msgid "Printer preset names" msgstr "Printer preset names" -msgid "Avoid crossing wall when travel" -msgstr "Avoid walls when traveling" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "This detours to avoid traveling across walls, which may cause blobs on the " "surface" -msgid "Max travel detour distance" -msgstr "Max travel detour distance" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Maximum detour distance for avoiding travel across walls. If the distance is " -"larger than this value, there will be no detour." msgid "Other layers" msgstr "Other layers" @@ -5404,24 +5627,25 @@ msgid "Height to rod" msgstr "Height to rod" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Height of the clearance cylinder around extruder: used as input for auto-" -"arranging to avoid collisions when printing object by object" msgid "Height to lid" msgstr "Height to lid" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Radius" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Clearance radius around extruder: used as input for auto-arranging to avoid " -"collisions when printing object by object" msgid "Extruder Color" msgstr "Extruder Color" @@ -5623,6 +5847,9 @@ msgstr "Honeycomb" msgid "Adaptive Cubic" msgstr "Adaptive Cubic" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5763,6 +5990,9 @@ msgstr "" "The metallic material of the nozzle: This determines the abrasive resistance " "of the nozzle and what kind of filament can be printed." +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Hardened steel" @@ -5869,6 +6099,14 @@ msgstr "" "Whether the machine supports silent mode in which machine uses lower " "acceleration to print more quietly" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Maximum speed X" @@ -6028,6 +6266,14 @@ msgstr "Nozzle diameter" msgid "Diameter of nozzle" msgstr "The diameter of the nozzle" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Nozzle volume" @@ -6817,10 +7063,6 @@ msgstr "Slicing mesh" msgid " Object:" msgstr " Object:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Error at line %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support: generate toolpath at layer %d" @@ -6852,6 +7094,243 @@ msgstr "Support: fix holes at layer %d" msgid "Support: propagate branches at layer %d" msgstr "Support: propagate branches at layer %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Erase painting" + +#~ msgid "Set pen size" +#~ msgstr "Set pen size" + +#~ msgid "Rotation:" +#~ msgstr "Rotation:" + +#~ msgid "Height:" +#~ msgstr "Height:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Initalization failed [%d]!" + +#~ msgid "Send print job to" +#~ msgstr "Send print job to" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% is too close to an exclusion area; there will be collisions when " +#~ "printing." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% is too close to an exclusion area; there will be collisions when " +#~ "printing." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr " is too close to others, there will be collisions when printing.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ " is too close to an exclusion area, there will be collisions when " +#~ "printing.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Avoid walls when traveling" + +#~ msgid "Max travel detour distance" +#~ msgstr "Max travel detour distance" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Maximum detour distance for avoiding travel across walls. If the distance " +#~ "is larger than this value, there will be no detour." + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Height of the clearance cylinder around extruder: used as input for auto-" +#~ "arranging to avoid collisions when printing object by object" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Clearance radius around extruder: used as input for auto-arranging to " +#~ "avoid collisions when printing object by object" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Error at line %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Reduce Triangles" @@ -6979,9 +7458,6 @@ msgstr "Support: propagate branches at layer %d" #~ msgid "Spiral mode" #~ msgstr "Spiral mode" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + Mouse wheel" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/es/BambuStudio_es.po b/bbl/i18n/es/BambuStudio_es.po index 8d441ddd8f..411945b272 100644 --- a/bbl/i18n/es/BambuStudio_es.po +++ b/bbl/i18n/es/BambuStudio_es.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Pintando Soportes" +msgid "Alt + Mouse wheel" +msgstr "Alt + Rueda del ratón" + msgid "Section view" msgstr "Vista de la sección" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + Rueda del ratón" + msgid "Pen size" msgstr "Tamaño del lápiz" @@ -37,8 +43,8 @@ msgstr "Bloquear soportes" msgid "Shift + Left mouse button" msgstr "Mayús + botón izquierdo del ratón" -msgid "Erase painting" -msgstr "Borrar lo pintado" +msgid "Erase" +msgstr "Borrar" msgid "Erase all painting" msgstr "Borrar todo lo pintado" @@ -55,12 +61,6 @@ msgstr "Realizar" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Ajustar el tamaño del lápiz" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + Rueda del ratón" - msgid "Tool type" msgstr "Tipo de herramienta" @@ -112,9 +112,6 @@ msgstr "Forma de lápiz" msgid "Paint" msgstr "Pintar" -msgid "Erase" -msgstr "Borrar" - msgid "Key 1~9" msgstr "Tecla 1~9" @@ -243,11 +240,11 @@ msgstr "Cortar" msgid "Movement:" msgstr "Movimiento:" -msgid "Rotation:" -msgstr "Rotación:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Altura:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Mantener la parte superior" @@ -354,10 +351,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -429,6 +426,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Versión" @@ -570,6 +570,15 @@ msgstr "Idioma" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Seleccione un archivo de código G:" @@ -1075,6 +1084,42 @@ msgstr "Impresión multicolor" msgid "Line Type" msgstr "Tipo de línea" +msgid "More" +msgstr "Más" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Código G personalizado" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Sin impresión" @@ -1111,8 +1156,11 @@ msgstr "Descargar" msgid "Tips" msgstr "Consejos" -msgid "AMS Settings" -msgstr "Ajustes del AMS" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "Calibración de AMS..." @@ -1239,6 +1287,9 @@ msgstr "Fallo en la vinculación" msgid "Unknown Failure" msgstr "Error desconocido" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Datos anormales del archivo de impresión. Por favor, procese de nuevo" @@ -1421,6 +1472,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "Ajustes del AMS" + msgid "Insertion update" msgstr "Actualización de la inserción" @@ -1469,6 +1523,19 @@ msgstr "" msgid "File" msgstr "Archivo" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1534,6 +1601,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Exportación de código G a %1% con éxito" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Origen" @@ -1939,6 +2016,12 @@ msgstr "Flujo" msgid "Tool" msgstr "Herramienta" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2158,6 +2241,12 @@ msgstr "Barra de herramientas de pintura" msgid "Explosion Ratio" msgstr "Ratio de Explosión" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Volumen total:" @@ -2264,6 +2353,9 @@ msgstr "Proyecto" msgid "Slice" msgstr "Laminar" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Laminar todo" @@ -2273,6 +2365,12 @@ msgstr "Laminar placa" msgid "Print all" msgstr "Imprimir todo" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Enviar" + msgid "Export sliced file" msgstr "Exportar archivo rebanado" @@ -2298,6 +2396,9 @@ msgstr "Comprobar Actualicaciones" msgid "&About %s" msgstr "&Acerca de %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2583,18 +2684,34 @@ msgstr "Sincronización" msgid "Initialize failed (No Device)!" msgstr "¡Inicialización fallida (No hay dispositivo)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Iniciando..." msgid "Loading..." msgstr "Cargando..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "¡Ha fallado la inicialización [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Detenido." @@ -2642,8 +2759,8 @@ msgstr "Descargar" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Management" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2667,17 +2784,14 @@ msgstr "Loading file list..." msgid "No files" msgstr "No files" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Open" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2779,6 +2893,9 @@ msgstr "Deportivo" msgid "Ludicrous" msgstr "Lúdico" +msgid "Failed to connect to the server" +msgstr "No se ha podido conectar con el servidor" + msgid "Status" msgstr "Estado" @@ -2791,15 +2908,9 @@ msgstr "Actualizar" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "No se ha podido conectar con el servidor" - msgid "Failed to connect to the printer" msgstr "No se ha podido conectar a la impresora" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Sí" @@ -2863,9 +2974,6 @@ msgstr "El software tiene una nueva versión." msgid "Goto download page." msgstr "Ir a la página de descargas." -msgid "More" -msgstr "Más" - msgid "Open Folder." msgstr "Abrir Carpeta." @@ -2881,6 +2989,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d El objeto tiene pintura de color." msgstr[1] "%1$d Los objetos tienen pintura de color." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Rebanado correcto." @@ -3035,6 +3155,9 @@ msgstr "Cambios de filamento" msgid "Click to edit preset" msgstr "Clic para cambiar el ajuste inicial" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Tipo de cama" @@ -3092,6 +3215,11 @@ msgstr "" msgid "Restore" msgstr "Restaurar" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Cargando archivo: %s" @@ -3207,6 +3335,9 @@ msgstr "El objeto seleccionado no ha podido ser dividido." msgid "Another export job is running." msgstr "Otro trabajo de exportación está en marcha." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3316,9 +3447,6 @@ msgstr "Guardar archivo Código G como:" msgid "Save Sliced file as:" msgstr "Guardar el archivo rebanado como:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3396,6 +3524,12 @@ msgstr "Selección de región" msgid "Second" msgstr "Segundo" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Configuración General" @@ -3426,6 +3560,12 @@ msgstr "Imperial" msgid "Units" msgstr "Unidades" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Sincronización del usuario" @@ -3473,6 +3613,9 @@ msgstr "Copia de seguridad automática" msgid "Backup interval" msgstr "Intervalo de copia de seguridad" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Página de inicio y consejos diarios" @@ -3709,6 +3852,9 @@ msgstr "Otro dispositivo" msgid "Input access code" msgstr "Introducir el código de acceso" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Cierre de sesión con éxito." @@ -3724,9 +3870,6 @@ msgstr "Placa de Ingenieria Bambu" msgid "Bamabu High Temperature Plate" msgstr "Placa de alta temperatura Bambu" -msgid "Send print job to" -msgstr "Enviar el trabajo de impresión a" - msgid "Refresh" msgstr "Actualizar" @@ -3736,8 +3879,8 @@ msgstr "Nivelación de la cama" msgid "Flow Calibration" msgstr "Calibración del flujo" -msgid "Send" -msgstr "Enviar" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "envío completo" @@ -3810,12 +3953,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Preparando el trabajo de impresión" msgid "Modifying the device name" msgstr "Modificar el nombre del dispositivo" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Iniciar sesión en la impresora" @@ -3857,12 +4021,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4071,6 +4236,9 @@ msgstr "Final del Código G de filamento " msgid "Printable space" msgstr "Espacio imprimible" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Accesorio" @@ -4095,6 +4263,9 @@ msgstr "Cambiar el código G del filamento" msgid "Pause G-code" msgstr "Código G de pausa" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Capacidad de movimiento" @@ -4139,9 +4310,6 @@ msgstr "Todo" msgid "Set" msgstr "Ajuste" -msgid "Custom G-code" -msgstr "Código G personalizado" - msgid "Click to reset current value and attach to the global value." msgstr "" "Haga clic para restablecer el valor actual y adjuntarlo al valor global." @@ -4614,23 +4782,6 @@ msgstr "Nueva versión de Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4661,6 +4812,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "El guardado de objetos en el 3mf no ha funcionado." @@ -4907,17 +5075,14 @@ msgstr "La llamada de recuperación de la escritura ha fallado" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% está demasiado cerca del área de exclusión, habrá colisiones al imprimir." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% está demasiado cerca del área de exclusión, habrá colisiones al imprimir." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4927,13 +5092,12 @@ msgstr "%1% está demasiado cerca de otros, y pueden producirse colisiones." msgid "%1% is too tall, and collisions will be caused." msgstr "%1% es demasiado alto, y se producirán colisiones." -msgid " is too close to others, there will be collisions when printing.\n" -msgstr " está demasiado cerca de otros, habrá colisiones al imprimir.\n" +msgid " is too close to others, there may be collisions when printing.\n" +msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" -" está demasiado cerca del área de exclusión, habrá colisiones al imprimir.\n" msgid "Prime Tower" msgstr "Torre principal" @@ -5085,23 +5249,84 @@ msgstr "Altura máxima imprimible limitada por el mecanismo de la impresora" msgid "Printer preset names" msgstr "Nombres de ajustes de la impresora" -msgid "Avoid crossing wall when travel" -msgstr "Evitar cruzar el muro en los desplazamientos" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Desvíese y evite atravesar el muro, ya que puede provocar una mancha en la " "superficie" -msgid "Max travel detour distance" -msgstr "Distancia máxima de desvío de viaje" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Distancia máxima de desvío para evitar cruzar el muro. No se desvía si la " -"distancia de desvío es mayor que este valor" msgid "Other layers" msgstr "Otras capas" @@ -5502,25 +5727,25 @@ msgid "Height to rod" msgstr "Altura a la barra" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Altura del cilindro de separación alrededor del extrusor. Se utiliza como " -"entrada de auto-organización para evitar la colisión cuando se imprime " -"objeto por objeto." msgid "Height to lid" msgstr "Altura hasta la tapa" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Radio" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Radio de separación alrededor del extrusor. Se utiliza como entrada de auto-" -"organización para evitar la colisión cuando se imprime objeto por objeto" msgid "Extruder Color" msgstr "Color del extrusor" @@ -5726,6 +5951,9 @@ msgstr "Panal de abeja" msgid "Adaptive Cubic" msgstr "Cúbico Adaptativo" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5867,6 +6095,9 @@ msgstr "" "El material metálico de la boquilla. Esto determina la resistencia a la " "abrasión de la boquilla, y qué tipo de filamento se puede imprimir" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Acero endurecido" @@ -5978,6 +6209,14 @@ msgstr "" "Si la máquina admite el modo silencioso en el que la máquina utiliza una " "menor aceleración para imprimir" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Velocidad máxima X" @@ -6139,6 +6378,14 @@ msgstr "Diámetro de la boquilla" msgid "Diameter of nozzle" msgstr "Diámetro de la boquilla" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Tamaño de la boquilla" @@ -6946,10 +7193,6 @@ msgstr "Malla de corte" msgid " Object:" msgstr "Objeto" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Error en la línea %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Soporte: generar trayectoria en la capa %d" @@ -6981,6 +7224,251 @@ msgstr "Soporte: arreglar huecos en la capa %d" msgid "Support: propagate branches at layer %d" msgstr "Soporte: propagar ramas en la capa %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Borrar lo pintado" + +#~ msgid "Set pen size" +#~ msgstr "Ajustar el tamaño del lápiz" + +#~ msgid "Rotation:" +#~ msgstr "Rotación:" + +#~ msgid "Height:" +#~ msgstr "Altura:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "¡Ha fallado la inicialización [%d]!" + +#~ msgid "Management" +#~ msgstr "Management" + +#~ msgid "Open" +#~ msgstr "Open" + +#~ msgid "Send print job to" +#~ msgstr "Enviar el trabajo de impresión a" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% está demasiado cerca del área de exclusión, habrá colisiones al " +#~ "imprimir." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% está demasiado cerca del área de exclusión, habrá colisiones al " +#~ "imprimir." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr " está demasiado cerca de otros, habrá colisiones al imprimir.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ " está demasiado cerca del área de exclusión, habrá colisiones al " +#~ "imprimir.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Evitar cruzar el muro en los desplazamientos" + +#~ msgid "Max travel detour distance" +#~ msgstr "Distancia máxima de desvío de viaje" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Distancia máxima de desvío para evitar cruzar el muro. No se desvía si la " +#~ "distancia de desvío es mayor que este valor" + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Altura del cilindro de separación alrededor del extrusor. Se utiliza como " +#~ "entrada de auto-organización para evitar la colisión cuando se imprime " +#~ "objeto por objeto." + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Radio de separación alrededor del extrusor. Se utiliza como entrada de " +#~ "auto-organización para evitar la colisión cuando se imprime objeto por " +#~ "objeto" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Error en la línea %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Reducir los triángulos" @@ -7012,9 +7500,6 @@ msgstr "Soporte: propagar ramas en la capa %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + Rueda del ratón" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/fr/BambuStudio_fr.po b/bbl/i18n/fr/BambuStudio_fr.po index 06621f610b..3046db9ae6 100644 --- a/bbl/i18n/fr/BambuStudio_fr.po +++ b/bbl/i18n/fr/BambuStudio_fr.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Prend en charge la peinture" +msgid "Alt + Mouse wheel" +msgstr "Alt + molette de la souris" + msgid "Section view" msgstr "Vue en coupe" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + molette de la souris" + msgid "Pen size" msgstr "Taille du stylo" @@ -37,8 +43,8 @@ msgstr "Bloc prend en charge" msgid "Shift + Left mouse button" msgstr "Maj + bouton gauche de la souris" -msgid "Erase painting" -msgstr "Effacer la peinture" +msgid "Erase" +msgstr "Effacer" msgid "Erase all painting" msgstr "Effacer toute la peinture" @@ -55,12 +61,6 @@ msgstr "Exécuter" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Définir la taille du stylo" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + molette de la souris" - msgid "Tool type" msgstr "Type d'outil" @@ -112,9 +112,6 @@ msgstr "Forme de stylo" msgid "Paint" msgstr "Peindre" -msgid "Erase" -msgstr "Effacer" - msgid "Key 1~9" msgstr "Clé 1 ~ 9" @@ -242,11 +239,11 @@ msgstr "Couper" msgid "Movement:" msgstr "Mouvement:" -msgid "Rotation:" -msgstr "Rotation :" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Hauteur:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Conserver la partie supérieure" @@ -353,10 +350,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -428,6 +425,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Version" @@ -569,6 +569,15 @@ msgstr "Langue" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Sélectionnez un fichier G-code :" @@ -1074,6 +1083,42 @@ msgstr "Impression multicolore" msgid "Line Type" msgstr "Type de ligne" +msgid "More" +msgstr "Suite" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Code G personnalisé" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Pas d'imprimante" @@ -1110,8 +1155,11 @@ msgstr "Déchargement" msgid "Tips" msgstr "astuces" -msgid "AMS Settings" -msgstr "Paramètres AMS" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "Étalonnage de l'AMS..." @@ -1236,6 +1284,9 @@ msgstr "Échec de liaison" msgid "Unknown Failure" msgstr "Erreur inconnue" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "" "Données de fichier d'impression anormales. Veuillez retrancher le fichier." @@ -1414,6 +1465,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "Paramètres AMS" + msgid "Insertion update" msgstr "Insertion de la mise à jour" @@ -1464,6 +1518,19 @@ msgstr "" msgid "File" msgstr "Dossier" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1528,6 +1595,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Succès! G-code exporté vers %1%" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Origine" @@ -1912,6 +1989,12 @@ msgstr "débit" msgid "Tool" msgstr "Outil" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2131,6 +2214,12 @@ msgstr "Barre d'outils de peinture" msgid "Explosion Ratio" msgstr "Taux d'explosion" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Volume total:" @@ -2238,6 +2327,9 @@ msgstr "Projet" msgid "Slice" msgstr "Tranche" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Tout trancher" @@ -2247,6 +2339,12 @@ msgstr "Assiette à trancher" msgid "Print all" msgstr "Tout imprimer" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Envoyer" + msgid "Export sliced file" msgstr "Exporter le fichier découpé" @@ -2272,6 +2370,9 @@ msgstr "Vérifier la mise à jour" msgid "&About %s" msgstr "&À propos de %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2557,18 +2658,34 @@ msgstr "Synchronisation" msgid "Initialize failed (No Device)!" msgstr "Échec de l'initialisation (pas de périphérique) !" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Initialisation..." msgid "Loading..." msgstr "Chargement..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Échec de l'initialisation [%d] !" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Arrêté." @@ -2616,8 +2733,8 @@ msgstr "Télécharger" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Management" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2641,17 +2758,14 @@ msgstr "Loading file list..." msgid "No files" msgstr "No files" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Open" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2753,6 +2867,9 @@ msgstr "sport" msgid "Ludicrous" msgstr "Ridicule" +msgid "Failed to connect to the server" +msgstr "Impossible de se connecter au serveur" + msgid "Status" msgstr "Statut" @@ -2765,15 +2882,9 @@ msgstr "Mise à jour" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Impossible de se connecter au serveur" - msgid "Failed to connect to the printer" msgstr "Échec de la connexion à l'imprimante" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Oui" @@ -2837,9 +2948,6 @@ msgstr "Le logiciel a une nouvelle version." msgid "Goto download page." msgstr "Allez sur la page de téléchargement." -msgid "More" -msgstr "Suite" - msgid "Open Folder." msgstr "Dossier ouvert." @@ -2855,6 +2963,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d L'objet est peint en couleur." msgstr[1] "%1$d L'objet est peint en couleur." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Tranchage terminé" @@ -3006,6 +3126,9 @@ msgstr "Changements de filaments" msgid "Click to edit preset" msgstr "Cliquez pour modifier le préréglage" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Type de lit" @@ -3062,6 +3185,11 @@ msgstr "Projet précédent non enregistré détecté, voulez-vous le restaurer ? msgid "Restore" msgstr "Restaurer" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Chargement du fichier : %s" @@ -3177,6 +3305,9 @@ msgstr "L'objet sélectionné n'a pas pu être divisé." msgid "Another export job is running." msgstr "Une autre tâche d'exportation est en cours d'exécution." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3288,9 +3419,6 @@ msgstr "Enregistrer le fichier G-code sous :" msgid "Save Sliced file as:" msgstr "Enregistrer le fichier découpé sous :" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3366,6 +3494,12 @@ msgstr "Choix de la région" msgid "Second" msgstr "Seconde" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Paramètres généraux" @@ -3396,6 +3530,12 @@ msgstr "Impérial" msgid "Units" msgstr "Unités" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Synchronisation utilisateur" @@ -3443,6 +3583,9 @@ msgstr "Sauvegarde automatique" msgid "Backup interval" msgstr "Intervalle de sauvegarde" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Page d'accueil et conseils quotidiens" @@ -3681,6 +3824,9 @@ msgstr "Autre appareil" msgid "Input access code" msgstr "Saisir le code d'accès" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Déconnexion réussie." @@ -3696,9 +3842,6 @@ msgstr "Plaque d'ingénierie Bamabu" msgid "Bamabu High Temperature Plate" msgstr "Plaque Haute Température Bamabu" -msgid "Send print job to" -msgstr "Envoyer le travail d'impression à" - msgid "Refresh" msgstr "Actualiser" @@ -3708,8 +3851,8 @@ msgstr "Mise à niveau du lit" msgid "Flow Calibration" msgstr "Étalonnage du débit" -msgid "Send" -msgstr "Envoyer" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "envoi terminé" @@ -3783,12 +3926,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Préparation du travail d'impression" msgid "Modifying the device name" msgstr "Modification du nom de l'appareil" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Connectez-vous à l'imprimante" @@ -3831,12 +3995,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4045,6 +4210,9 @@ msgstr "Code G de fin de filament" msgid "Printable space" msgstr "Espace imprimable" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Accessoire" @@ -4069,6 +4237,9 @@ msgstr "Changer le code G du filament" msgid "Pause G-code" msgstr "Mettre le code G en pause" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Capacité de mouvement" @@ -4113,9 +4284,6 @@ msgstr "Tout" msgid "Set" msgstr "Régler" -msgid "Custom G-code" -msgstr "Code G personnalisé" - msgid "Click to reset current value and attach to the global value." msgstr "" "Cliquez pour réinitialiser la valeur actuelle et l'attacher à la valeur " @@ -4593,23 +4761,6 @@ msgstr "Nouvelle version de Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4640,6 +4791,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "L'enregistrement d'objets dans le 3mf a échoué." @@ -4884,19 +5052,14 @@ msgstr "Échec du rappel d'écriture" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% est trop proche de la zone d'exclusion, il y aura des collisions lors de " -"l'impression." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% est trop proche de la zone d'exclusion, il y aura des collisions lors de " -"l'impression." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4907,15 +5070,12 @@ msgstr "" msgid "%1% is too tall, and collisions will be caused." msgstr "%1% est trop grand, cela pourrait provoquer des collisions." -msgid " is too close to others, there will be collisions when printing.\n" +msgid " is too close to others, there may be collisions when printing.\n" msgstr "" -"est trop proche des autres, il y aura des collisions lors de l'impression.\n" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" -"est trop proche de la zone d'exclusion, il y aura des collisions lors de " -"l'impression.\n" msgid "Prime Tower" msgstr "Tour principale" @@ -5070,23 +5230,84 @@ msgstr "Hauteur imprimable maximale limitée par le mécanisme de l'imprimante" msgid "Printer preset names" msgstr "Noms prédéfinis de l'imprimante" -msgid "Avoid crossing wall when travel" -msgstr "Évitez de traverser le mur lorsque vous voyagez" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Faites un détour et évitez de traverser le mur, ce qui pourrait causer des " "taches sur la surface" -msgid "Max travel detour distance" -msgstr "Distance de détour maximale" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Distance de détour maximale pour éviter de traverser le mur. Ne faites pas " -"de détour si la distance de détour est supérieure à cette valeur" msgid "Other layers" msgstr "Autres couches" @@ -5491,25 +5712,25 @@ msgid "Height to rod" msgstr "Hauteur à la tige" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Hauteur du cylindre de dégagement autour de l'extrudeuse. Utilisé comme " -"entrée de l'auto-arrangement pour éviter les collisions lors de l'impression " -"objet par objet" msgid "Height to lid" msgstr "Hauteur au couvercle" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Rayon" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Rayon de dégagement autour de l'extrudeuse. Utilisé comme entrée de l'auto-" -"arrangement pour éviter les collisions lors de l'impression objet par objet" msgid "Extruder Color" msgstr "Couleur de l'extrudeuse" @@ -5716,6 +5937,9 @@ msgstr "Rayon de miel" msgid "Adaptive Cubic" msgstr "Cubique adaptatif" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5861,6 +6085,9 @@ msgstr "" "Le matériau métallique de la buse. Cela détermine la résistance à l'abrasion " "de la buse et le type de filament pouvant être imprimé" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Acier trempé" @@ -5972,6 +6199,14 @@ msgstr "" "Si la machine prend en charge le mode silencieux dans lequel la machine " "utilise une accélération plus faible pour imprimer" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Vitesse maximale X" @@ -6137,6 +6372,14 @@ msgstr "Diamètre de buse" msgid "Diameter of nozzle" msgstr "Diamètre de buse" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Volume de la buse" @@ -6949,10 +7192,6 @@ msgstr "Maillage de tranchage" msgid " Object:" msgstr "Objet:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Erreur à la ligne %1% :\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support : génération du parcours d'impression à la couche %d" @@ -6984,6 +7223,253 @@ msgstr "Support : Correction des trous dans la couche %d" msgid "Support: propagate branches at layer %d" msgstr "Support : propagation des branches à la couche %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Effacer la peinture" + +#~ msgid "Set pen size" +#~ msgstr "Définir la taille du stylo" + +#~ msgid "Rotation:" +#~ msgstr "Rotation :" + +#~ msgid "Height:" +#~ msgstr "Hauteur:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Échec de l'initialisation [%d] !" + +#~ msgid "Management" +#~ msgstr "Management" + +#~ msgid "Open" +#~ msgstr "Open" + +#~ msgid "Send print job to" +#~ msgstr "Envoyer le travail d'impression à" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% est trop proche de la zone d'exclusion, il y aura des collisions lors " +#~ "de l'impression." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% est trop proche de la zone d'exclusion, il y aura des collisions lors " +#~ "de l'impression." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr "" +#~ "est trop proche des autres, il y aura des collisions lors de " +#~ "l'impression.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ "est trop proche de la zone d'exclusion, il y aura des collisions lors de " +#~ "l'impression.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Évitez de traverser le mur lorsque vous voyagez" + +#~ msgid "Max travel detour distance" +#~ msgstr "Distance de détour maximale" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Distance de détour maximale pour éviter de traverser le mur. Ne faites " +#~ "pas de détour si la distance de détour est supérieure à cette valeur" + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Hauteur du cylindre de dégagement autour de l'extrudeuse. Utilisé comme " +#~ "entrée de l'auto-arrangement pour éviter les collisions lors de " +#~ "l'impression objet par objet" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Rayon de dégagement autour de l'extrudeuse. Utilisé comme entrée de " +#~ "l'auto-arrangement pour éviter les collisions lors de l'impression objet " +#~ "par objet" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Erreur à la ligne %1% :\n" + #~ msgid "Reduce Triangles" #~ msgstr "Réduire les triangles" @@ -7015,9 +7501,6 @@ msgstr "Support : propagation des branches à la couche %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + molette de la souris" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/hu/BambuStudio_hu.po b/bbl/i18n/hu/BambuStudio_hu.po index d873325679..5122c008ac 100644 --- a/bbl/i18n/hu/BambuStudio_hu.po +++ b/bbl/i18n/hu/BambuStudio_hu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Támaszok festése" +msgid "Alt + Mouse wheel" +msgstr "Alt + Mouse wheel" + msgid "Section view" msgstr "Keresztmetszet nézet" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + Egérgörgő" + msgid "Pen size" msgstr "Tollméret" @@ -37,8 +43,8 @@ msgstr "Támaszok blokkolása" msgid "Shift + Left mouse button" msgstr "Shift + bal egérgomb" -msgid "Erase painting" -msgstr "Festés törlése" +msgid "Erase" +msgstr "Törlés" msgid "Erase all painting" msgstr "Minden festés törlése" @@ -55,12 +61,6 @@ msgstr "Alkalmaz" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Tollméret beállítása" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + Egérgörgő" - msgid "Tool type" msgstr "Eszköz típusa" @@ -111,9 +111,6 @@ msgstr "Toll formája" msgid "Paint" msgstr "Festés" -msgid "Erase" -msgstr "Törlés" - msgid "Key 1~9" msgstr "Számgombok 1-9" @@ -241,11 +238,11 @@ msgstr "Vágás" msgid "Movement:" msgstr "Mozgatás:" -msgid "Rotation:" -msgstr "Forgatás:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Magasság:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Felső rész megtartása" @@ -353,10 +350,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -428,6 +425,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Verzió" @@ -567,6 +567,15 @@ msgstr "Nyelv" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Válassz egy G-code fájlt:" @@ -1072,6 +1081,42 @@ msgstr "Többszínű nyomtatás" msgid "Line Type" msgstr "Vonaltípus" +msgid "More" +msgstr "Több" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Egyedi G-kód" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Nincs nyomtató" @@ -1108,8 +1153,11 @@ msgstr "Unload" msgid "Tips" msgstr "Tippek" -msgid "AMS Settings" -msgstr "AMS beállítások" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "AMS kalibrálása..." @@ -1236,6 +1284,9 @@ msgstr "Sikertelen párosítás" msgid "Unknown Failure" msgstr "Ismeretlen hiba" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Rendellenes nyomtatási fájladatok. Kérjük, szeleteld újra" @@ -1427,6 +1478,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "AMS beállítások" + msgid "Insertion update" msgstr "Insertion update" @@ -1475,6 +1529,19 @@ msgstr "" msgid "File" msgstr "Fájl" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1539,6 +1606,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "G-kód sikeresen exportálva ide: %1%" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Origó" @@ -1931,6 +2008,12 @@ msgstr "Anyagáramlás" msgid "Tool" msgstr "Tool" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2150,6 +2233,12 @@ msgstr "Festés eszköztár" msgid "Explosion Ratio" msgstr "Robbantási arány" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Total Volume:" @@ -2260,6 +2349,9 @@ msgstr "Projekt" msgid "Slice" msgstr "Szeletelés" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Összes szeletelése" @@ -2269,6 +2361,12 @@ msgstr "Tálca szeletelése" msgid "Print all" msgstr "Összes nyomtatása" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Küldés" + msgid "Export sliced file" msgstr "Szeletelt fájl exportálása" @@ -2294,6 +2392,9 @@ msgstr "Frissítés keresése" msgid "&About %s" msgstr "&%s névjegye" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2584,18 +2685,34 @@ msgstr "Synchronization" msgid "Initialize failed (No Device)!" msgstr "Sikertelen inicializálás (Nincs eszköz)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Initializing..." msgid "Loading..." msgstr "Loading..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Initalization failed [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Megállítva." @@ -2643,8 +2760,8 @@ msgstr "Letöltés" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Management" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2668,17 +2785,14 @@ msgstr "Loading file list..." msgid "No files" msgstr "No files" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Open" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2780,6 +2894,9 @@ msgstr "Sport" msgid "Ludicrous" msgstr "Őrült" +msgid "Failed to connect to the server" +msgstr "Nem sikerült csatlakozni a szerverhez" + msgid "Status" msgstr "Állapot" @@ -2792,15 +2909,9 @@ msgstr "Update" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Nem sikerült csatlakozni a szerverhez" - msgid "Failed to connect to the printer" msgstr "Nem sikerült csatlakozni a nyomtatóhoz" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Igen" @@ -2864,9 +2975,6 @@ msgstr "Új szoftververzió érhető el." msgid "Goto download page." msgstr "Látogass el a letöltési oldalra." -msgid "More" -msgstr "Több" - msgid "Open Folder." msgstr "Mappa megnyitása." @@ -2882,6 +2990,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d objektum színfestést tartalmaz." msgstr[1] "%1$d objektum színfestést tartalmaz." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Szeletelés kész." @@ -3032,6 +3152,9 @@ msgstr "Filamentcserék" msgid "Click to edit preset" msgstr "Kattints a beállítás szerkesztéséhez" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Asztaltípus" @@ -3088,6 +3211,11 @@ msgstr "Korábbi, nem mentett projektet találtunk, vissza szeretnéd állítani msgid "Restore" msgstr "Visszaállítás" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Loading file: %s" @@ -3198,6 +3326,9 @@ msgstr "The selected object couldn't be split." msgid "Another export job is running." msgstr "Egy másik exportálási feladat is fut." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3309,9 +3440,6 @@ msgstr "G-kód fájl mentése mint:" msgid "Save Sliced file as:" msgstr "Szeletelt fájl mentése mint:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3385,6 +3513,12 @@ msgstr "Régió kiválasztása" msgid "Second" msgstr "Másodperc" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Általános beállítások" @@ -3415,6 +3549,12 @@ msgstr "Angolszász" msgid "Units" msgstr "Mértékegység" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Felhasználói szinkronizálás" @@ -3462,6 +3602,9 @@ msgstr "Automatikus biztonsági mentés" msgid "Backup interval" msgstr "Intervallum" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Kezdőoldal és napi tippek" @@ -3693,6 +3836,9 @@ msgstr "Egyéb eszköz" msgid "Input access code" msgstr "Input access code" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Log out successful." @@ -3708,9 +3854,6 @@ msgstr "Bambu Engineering Plate" msgid "Bamabu High Temperature Plate" msgstr "Bambu High Temperature Plate" -msgid "Send print job to" -msgstr "Nyomtatási feladat küldése" - msgid "Refresh" msgstr "Frissítés" @@ -3720,8 +3863,8 @@ msgstr "Asztalszintezés" msgid "Flow Calibration" msgstr "Anyagáramlás kalibráció" -msgid "Send" -msgstr "Küldés" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "küldés befejezve" @@ -3792,12 +3935,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Nyomtatási feladat előkészítése" msgid "Modifying the device name" msgstr "Modifying the device name" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Log in printer" @@ -3837,12 +4001,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4047,6 +4212,9 @@ msgstr "Filament befejező G-kód" msgid "Printable space" msgstr "Nyomtatási terület" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Tartozékok" @@ -4071,6 +4239,9 @@ msgstr "Filament csere G-kód" msgid "Pause G-code" msgstr "Szünet G-kód" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Géplimitek" @@ -4115,9 +4286,6 @@ msgstr "Összes" msgid "Set" msgstr "Beállít" -msgid "Custom G-code" -msgstr "Egyedi G-kód" - msgid "Click to reset current value and attach to the global value." msgstr "Click to reset current value and attach to the global value." @@ -4592,23 +4760,6 @@ msgstr "A Bambu Studio új verziója" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4639,6 +4790,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "Az objektumok mentése a 3mf-be sikertelen volt." @@ -4880,19 +5048,14 @@ msgstr "write callback failed" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " -"ütközések." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " -"ütközések." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4904,16 +5067,12 @@ msgstr "" msgid "%1% is too tall, and collisions will be caused." msgstr "%1% túl magas, a nyomtatás során előfordulhatnak ütközések." -msgid " is too close to others, there will be collisions when printing.\n" +msgid " is too close to others, there may be collisions when printing.\n" msgstr "" -" túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " -"ütközések.\n" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" -" túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " -"ütközések.\n" msgid "Prime Tower" msgstr "Törlő torony" @@ -5064,22 +5223,83 @@ msgstr "" msgid "Printer preset names" msgstr "Nyomtató beállítások neve" -msgid "Avoid crossing wall when travel" -msgstr "Falak elkerülése mozgáskor" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Kerülje a falon keresztül való áthaladást, ami nyomot hagyhat a felületen" -msgid "Max travel detour distance" -msgstr "Max travel detour distance" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Maximum detour distance for avoiding travel across walls. If the distance is " -"larger than this value, there will be no detour." msgid "Other layers" msgstr "Többi réteg" @@ -5472,25 +5692,25 @@ msgid "Height to rod" msgstr "Height to rod" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Height of the clearance cylinder around extruder: used as input for auto-" -"arranging to avoid collisions when printing object by object" msgid "Height to lid" msgstr "Height to lid" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Radius" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Az extruder körüli biztonságos terület sugara. Az automatikus elrendezéshez " -"használatos az ütközések elkerülése érdekében, amikor objektumonként " -"nyomtatsz" msgid "Extruder Color" msgstr "Extruder szín" @@ -5696,6 +5916,9 @@ msgstr "Méhsejt" msgid "Adaptive Cubic" msgstr "Adaptív kocka" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5834,6 +6057,9 @@ msgstr "" "The metallic material of the nozzle: This determines the abrasive resistance " "of the nozzle and what kind of filament can be printed." +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Edzett acél" @@ -5943,6 +6169,14 @@ msgstr "" "Whether the machine supports silent mode in which machine uses lower " "acceleration to print more quietly" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Maximális sebesség X" @@ -6102,6 +6336,14 @@ msgstr "Nozzle diameter" msgid "Diameter of nozzle" msgstr "Fúvóka átmérője" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Nozzle volume" @@ -6907,10 +7149,6 @@ msgstr "Háló szeletelése" msgid " Object:" msgstr " Objektum:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Hiba a következő sorban %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support: generate toolpath at layer %d" @@ -6942,6 +7180,252 @@ msgstr "Support: fix holes at layer %d" msgid "Support: propagate branches at layer %d" msgstr "Support: propagate branches at layer %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Festés törlése" + +#~ msgid "Set pen size" +#~ msgstr "Tollméret beállítása" + +#~ msgid "Rotation:" +#~ msgstr "Forgatás:" + +#~ msgid "Height:" +#~ msgstr "Magasság:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Initalization failed [%d]!" + +#~ msgid "Management" +#~ msgstr "Management" + +#~ msgid "Open" +#~ msgstr "Open" + +#~ msgid "Send print job to" +#~ msgstr "Nyomtatási feladat küldése" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " +#~ "ütközések." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " +#~ "ütközések." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr "" +#~ " túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " +#~ "ütközések.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ " túl közel van a tiltott területhez, a nyomtatás során előfordulhatnak " +#~ "ütközések.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Falak elkerülése mozgáskor" + +#~ msgid "Max travel detour distance" +#~ msgstr "Max travel detour distance" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Maximum detour distance for avoiding travel across walls. If the distance " +#~ "is larger than this value, there will be no detour." + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Height of the clearance cylinder around extruder: used as input for auto-" +#~ "arranging to avoid collisions when printing object by object" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Az extruder körüli biztonságos terület sugara. Az automatikus " +#~ "elrendezéshez használatos az ütközések elkerülése érdekében, amikor " +#~ "objektumonként nyomtatsz" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Hiba a következő sorban %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Háromszögek csökkentése" @@ -6972,9 +7456,6 @@ msgstr "Support: propagate branches at layer %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + Mouse wheel" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/list.txt b/bbl/i18n/list.txt index 9280653e5e..36aebc444a 100644 --- a/bbl/i18n/list.txt +++ b/bbl/i18n/list.txt @@ -25,6 +25,8 @@ src/slic3r/GUI/GUI_ObjectTable.hpp src/slic3r/GUI/GUI_ObjectTableSettings.cpp src/slic3r/GUI/GUI_ObjectTableSettings.hpp src/slic3r/GUI/GUI_Preview.cpp +src/slic3r/GUI/HintNotification.cpp +src/slic3r/GUI/IMSlider.cpp src/slic3r/GUI/Widgets/SideTools.cpp src/slic3r/GUI/Widgets/AMSControl.cpp src/slic3r/GUI/ImGuiWrapper.cpp @@ -86,6 +88,7 @@ src/slic3r/GUI/Search.cpp src/slic3r/GUI/Selection.cpp src/slic3r/GUI/SelectMachine.cpp src/slic3r/GUI/SendSystemInfoDialog.cpp +src/slic3r/GUI/SendToPrinter.cpp src/slic3r/GUI/BindDialog.cpp src/slic3r/GUI/Tab.cpp src/slic3r/GUI/Tab.hpp diff --git a/bbl/i18n/nl/BambuStudio_nl.po b/bbl/i18n/nl/BambuStudio_nl.po index 43eb3731a7..3b89d2798f 100644 --- a/bbl/i18n/nl/BambuStudio_nl.po +++ b/bbl/i18n/nl/BambuStudio_nl.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Onderteuning (Support) tekenen" +msgid "Alt + Mouse wheel" +msgstr "Alt + muiswiel" + msgid "Section view" msgstr "Doorsnede" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + muiswiel" + msgid "Pen size" msgstr "Pen grootte" @@ -37,8 +43,8 @@ msgstr "Support blokkeren" msgid "Shift + Left mouse button" msgstr "Shit + linker muisknop" -msgid "Erase painting" -msgstr "Getekende delen wissen" +msgid "Erase" +msgstr "Wissen" msgid "Erase all painting" msgstr "Alle getekende delen wissen" @@ -55,12 +61,6 @@ msgstr "Uitvoeren" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Selecteer pen formaat" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + muiswiel" - msgid "Tool type" msgstr "Gereedschap type" @@ -112,9 +112,6 @@ msgstr "Pen vorm" msgid "Paint" msgstr "Tekenen" -msgid "Erase" -msgstr "Wissen" - msgid "Key 1~9" msgstr "Toets 1~9" @@ -242,11 +239,11 @@ msgstr "Knippen" msgid "Movement:" msgstr "Beweging:" -msgid "Rotation:" -msgstr "Rotatie:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Hoogte:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Bewaar het bovenstee deel" @@ -353,10 +350,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -427,6 +424,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Versie" @@ -565,6 +565,15 @@ msgstr "Taal" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Selecteer een G-code bestand:" @@ -1079,6 +1088,42 @@ msgstr "Print met meerdere kleuren" msgid "Line Type" msgstr "Lijn type" +msgid "More" +msgstr "Meer" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Aangepaste G-code" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "Offline" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Geen printer" @@ -1115,8 +1160,11 @@ msgstr "Lossen" msgid "Tips" msgstr "Tips" -msgid "AMS Settings" -msgstr "AMS Instellingen" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "AMS kalibreren..." @@ -1243,6 +1291,9 @@ msgstr "Koppeling mislukt" msgid "Unknown Failure" msgstr "Onbekende fout" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Abnormale printbestand. Slice opnieuw" @@ -1423,6 +1474,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "AMS Instellingen" + msgid "Insertion update" msgstr "Update gegevens bij invoeren" @@ -1471,6 +1525,19 @@ msgstr "" msgid "File" msgstr "Bestand" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1536,6 +1603,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Success! G-code geexporteerd naar %1%" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Begin" @@ -1936,6 +2013,12 @@ msgstr "Flow" msgid "Tool" msgstr "Hulpmiddel" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2155,6 +2238,12 @@ msgstr "Kleur instellingen" msgid "Explosion Ratio" msgstr "Vergrotings ratio" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Totaal Volume:" @@ -2261,6 +2350,9 @@ msgstr "Project" msgid "Slice" msgstr "Slice" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Alles slicen" @@ -2270,6 +2362,12 @@ msgstr "Slice printbed" msgid "Print all" msgstr "Print alles" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Versturen" + msgid "Export sliced file" msgstr "Exporteer het \"sliced\" bestand" @@ -2295,6 +2393,9 @@ msgstr "Zoeken naar updates" msgid "&About %s" msgstr "&Over %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2580,18 +2681,34 @@ msgstr "Synchronisatie" msgid "Initialize failed (No Device)!" msgstr "Het initializeren is mislukt (geen apparaat)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Initialiseren..." msgid "Loading..." msgstr "Laden..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Het initialiseren is mislukt [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Gestopt." @@ -2639,8 +2756,8 @@ msgstr "Downloaden" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Management" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2664,17 +2781,14 @@ msgstr "Bestandslijst laden..." msgid "No files" msgstr "Geen bestanden" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Open" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2776,6 +2890,9 @@ msgstr "Sport" msgid "Ludicrous" msgstr "Ludicrous" +msgid "Failed to connect to the server" +msgstr "Verbinding maken met de server is mislukt" + msgid "Status" msgstr "Status" @@ -2788,15 +2905,9 @@ msgstr "Updaten" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Verbinding maken met de server is mislukt" - msgid "Failed to connect to the printer" msgstr "Verbinding maken met de printer is mislukt" -msgid "OK" -msgstr "Offline" - msgid "Yes" msgstr "Ja" @@ -2860,9 +2971,6 @@ msgstr "Er is een update beschikbaar!" msgid "Goto download page." msgstr "Ga naar de download pagina." -msgid "More" -msgstr "Meer" - msgid "Open Folder." msgstr "Open bestandsmap." @@ -2886,6 +2994,18 @@ msgstr[1] "" "%1$d De objecten hebben geschilderde kleuren.@%1$d De objecten hebben " "geschilderde kleuren." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Slice gelukt" @@ -3038,6 +3158,9 @@ msgstr "Filament wisselingen" msgid "Click to edit preset" msgstr "Klik om de instelling te veranderen" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Printbed type" @@ -3095,6 +3218,11 @@ msgstr "" msgid "Restore" msgstr "Herstellen" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Bestand laden: %s" @@ -3210,6 +3338,9 @@ msgstr "Het geselecteerde object kan niet opgesplitst worden." msgid "Another export job is running." msgstr "Er is reeds een export taak actief." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3320,9 +3451,6 @@ msgstr "Bewaar G-code bestand als:" msgid "Save Sliced file as:" msgstr "Bewaar het geslicede bestand als:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3399,6 +3527,12 @@ msgstr "Regio selectie" msgid "Second" msgstr "Seconde" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Algemene instellingen" @@ -3429,6 +3563,12 @@ msgstr "Imperiaal" msgid "Units" msgstr "Eenheden" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Gebruikerssynchronisatie" @@ -3476,6 +3616,9 @@ msgstr "Automatisch backup maken" msgid "Backup interval" msgstr "Backupinterval" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Startpagina en dagelijkse tips" @@ -3715,6 +3858,9 @@ msgstr "Ander apparaat" msgid "Input access code" msgstr "Toegangscode invoeren" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Uitloggen gelukt." @@ -3730,9 +3876,6 @@ msgstr "Bambu Engineering (technische) plate" msgid "Bamabu High Temperature Plate" msgstr "Bambu High Temperature (hoge temperatuur) Plate" -msgid "Send print job to" -msgstr "Stuur de printtaak naar" - msgid "Refresh" msgstr "Vernieuwen" @@ -3742,8 +3885,8 @@ msgstr "Bed leveling" msgid "Flow Calibration" msgstr "Flow calibratie" -msgid "Send" -msgstr "Versturen" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "Versturen gelukt" @@ -3813,12 +3956,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Print opdracht voorbereiden" msgid "Modifying the device name" msgstr "De naam van het apparaat wijzigen" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Inloggen op printer" @@ -3860,12 +4024,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4072,6 +4237,9 @@ msgstr "Filament einde G-code" msgid "Printable space" msgstr "Ruimte waarbinnen geprint kan worden" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Accessoire" @@ -4096,6 +4264,9 @@ msgstr "Filament G-code aanpassen" msgid "Pause G-code" msgstr "Pauzeer G-code" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Bewegingsvermogen" @@ -4140,9 +4311,6 @@ msgstr "Alles" msgid "Set" msgstr "Instellen" -msgid "Custom G-code" -msgstr "Aangepaste G-code" - msgid "Click to reset current value and attach to the global value." msgstr "" "Klik om de huidige waarde terug te zetten en de globale waarde toe te passen." @@ -4621,23 +4789,6 @@ msgstr "Nieuwe versie van Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4668,6 +4819,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "Het opslaan van de objecten naar het 3mf bestand is mislukt." @@ -4913,19 +5081,14 @@ msgstr "callback schrijven is mislukt" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"%1% bevindt zich te dicht bij een uitgesloten gebied, er zullen botsingen " -"optreden tijdens het printen." #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% bevindt zich te dicht bij een uitgesloten gebied, er kunnen botsingen " -"optreden tijdens het printen." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4935,16 +5098,12 @@ msgstr "%1% staat te dicht bij anderen en er kunnen botsingen ontstaan." msgid "%1% is too tall, and collisions will be caused." msgstr "%1% is te hoog en er kunnen botsingen ontstaan." -msgid " is too close to others, there will be collisions when printing.\n" +msgid " is too close to others, there may be collisions when printing.\n" msgstr "" -" bevindt zich te dichtbij andere objecten, er zullen botsingen optreden " -"tijdens het printen.\n" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" +" is too close to exclusion area, there may be collisions when printing.\n" msgstr "" -" komt te dichtbij het uitgesloten gebied, er zullen botsingen optreden " -"tijdens het printen.\n" msgid "Prime Tower" msgstr "Prime toren" @@ -5098,23 +5257,84 @@ msgstr "" msgid "Printer preset names" msgstr "Namen van printer voorinstellingen" -msgid "Avoid crossing wall when travel" -msgstr "Vermijd wanden tijdens het verplaatsen" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Omweg om te voorkomen dat de printkop over wanden verplaatst, dit zou " "namelijk klodders op het oppervlak kunnen veroorzaken" -msgid "Max travel detour distance" -msgstr "Maximale afstand voor printpad omleiding" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Maximale omleidingsafstand om beweging over muren te vermijden. Als de " -"afstand groter is dan deze waarde, is er geen omleiding." msgid "Other layers" msgstr "Andere lagen" @@ -5512,26 +5732,25 @@ msgid "Height to rod" msgstr "Hoogte tot geleider" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Hoogte van de spelingsruimte rond de printkop: wordt gebruikt als invoer " -"voor automatisch rangschikken om botsingen te voorkomen bij object voor " -"object printen." msgid "Height to lid" msgstr "Hoogte tot deksel" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Straal" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Vrije ruimte rond de printkop: deze wordt gebruikt als invoer voor " -"automatisch rangschikken om botsingen te voorkomen bij het afdrukken van " -"object voor object" msgid "Extruder Color" msgstr "Extruder kleur" @@ -5738,6 +5957,9 @@ msgstr "Honinggraad" msgid "Adaptive Cubic" msgstr "Adaptief kubiek" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5882,6 +6104,9 @@ msgstr "" "Het type metaal van de nozzle. Dit bepaalt de slijtvastheid van de nozzle en " "wat voor soort filament kan worden geprint" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Gehard staal" @@ -5993,6 +6218,14 @@ msgstr "" "Dit geeft aan of de machine de stille modus ondersteunt waarin de machine " "een lagere versnelling gebruikt om te printen" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Maximale snelheid voor X" @@ -6153,6 +6386,14 @@ msgstr "Nozzle diameter" msgid "Diameter of nozzle" msgstr "Diameter van de nozzle" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Nozzle volume" @@ -6967,10 +7208,6 @@ msgstr "Slicing mesh" msgid " Object:" msgstr "Object" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Fout op laag %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support: toolpad genereren op laag %d" @@ -7002,6 +7239,253 @@ msgstr "Support: repareer gaten op laag %d" msgid "Support: propagate branches at layer %d" msgstr "Support: verspreid takken op laag %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Getekende delen wissen" + +#~ msgid "Set pen size" +#~ msgstr "Selecteer pen formaat" + +#~ msgid "Rotation:" +#~ msgstr "Rotatie:" + +#~ msgid "Height:" +#~ msgstr "Hoogte:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Het initialiseren is mislukt [%d]!" + +#~ msgid "Management" +#~ msgstr "Management" + +#~ msgid "Open" +#~ msgstr "Open" + +#~ msgid "Send print job to" +#~ msgstr "Stuur de printtaak naar" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "%1% bevindt zich te dicht bij een uitgesloten gebied, er zullen botsingen " +#~ "optreden tijdens het printen." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% bevindt zich te dicht bij een uitgesloten gebied, er kunnen botsingen " +#~ "optreden tijdens het printen." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr "" +#~ " bevindt zich te dichtbij andere objecten, er zullen botsingen optreden " +#~ "tijdens het printen.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "" +#~ " komt te dichtbij het uitgesloten gebied, er zullen botsingen optreden " +#~ "tijdens het printen.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Vermijd wanden tijdens het verplaatsen" + +#~ msgid "Max travel detour distance" +#~ msgstr "Maximale afstand voor printpad omleiding" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Maximale omleidingsafstand om beweging over muren te vermijden. Als de " +#~ "afstand groter is dan deze waarde, is er geen omleiding." + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Hoogte van de spelingsruimte rond de printkop: wordt gebruikt als invoer " +#~ "voor automatisch rangschikken om botsingen te voorkomen bij object voor " +#~ "object printen." + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Vrije ruimte rond de printkop: deze wordt gebruikt als invoer voor " +#~ "automatisch rangschikken om botsingen te voorkomen bij het afdrukken van " +#~ "object voor object" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Fout op laag %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Aantal driehoeken verkleinen (vereenvoudigen)" @@ -7032,9 +7516,6 @@ msgstr "Support: verspreid takken op laag %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + muiswiel" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/sv/BambuStudio_sv.po b/bbl/i18n/sv/BambuStudio_sv.po index 7970d51764..c3c57b9dff 100644 --- a/bbl/i18n/sv/BambuStudio_sv.po +++ b/bbl/i18n/sv/BambuStudio_sv.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Bambu Studio\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -16,9 +16,15 @@ msgstr "" msgid "Supports Painting" msgstr "Färgläggning av Support" +msgid "Alt + Mouse wheel" +msgstr "Alt + mushjul" + msgid "Section view" msgstr "Sektionsvy" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + Mushjul" + msgid "Pen size" msgstr "Penn storlek" @@ -37,8 +43,8 @@ msgstr "Blockera support" msgid "Shift + Left mouse button" msgstr "Shift + Vänster musknapp" -msgid "Erase painting" -msgstr "Radera färgläggning" +msgid "Erase" +msgstr "Radera" msgid "Erase all painting" msgstr "Radera all färgläggning" @@ -55,12 +61,6 @@ msgstr "Utför" msgid "Gap area" msgstr "" -msgid "Set pen size" -msgstr "Välj penn storlek" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + Mushjul" - msgid "Tool type" msgstr "Verktygs typ" @@ -112,9 +112,6 @@ msgstr "Penn form" msgid "Paint" msgstr "Färglägga" -msgid "Erase" -msgstr "Radera" - msgid "Key 1~9" msgstr "Nyckel 1~9" @@ -242,11 +239,11 @@ msgstr "Beskär" msgid "Movement:" msgstr "Rörelse:" -msgid "Rotation:" -msgstr "Rotation:" +msgid "Movement" +msgstr "" -msgid "Height:" -msgstr "Höjd:" +msgid "Height" +msgstr "" msgid "Keep upper part" msgstr "Behåll övre del" @@ -352,10 +349,10 @@ msgstr "" msgid "Block seam" msgstr "" -msgid "Remove selection" +msgid "Seam painting" msgstr "" -msgid "Seam painting" +msgid "Remove selection" msgstr "" msgid "Entering Seam painting" @@ -422,6 +419,9 @@ msgstr "" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "" + msgid "Version" msgstr "Version" @@ -561,6 +561,15 @@ msgstr "Språk" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "" + +msgid "Stop them and continue anyway?" +msgstr "" + +msgid "Ongoing uploads" +msgstr "" + msgid "Select a G-code file:" msgstr "Välj en G-kod fil:" @@ -1064,6 +1073,42 @@ msgstr "Multifärgs Utskrift" msgid "Line Type" msgstr "Linje typ" +msgid "More" +msgstr "Mer" + +msgid "Open Preferences." +msgstr "" + +msgid "Open Documentation in web browser." +msgstr "" + +msgid "Custom G-code" +msgstr "Custom G-kod" + +msgid "Enter Custom G-code used on current layer:" +msgstr "" + +msgid "OK" +msgstr "OK" + +msgid "Add Pause" +msgstr "" + +msgid "Add Custom G-code" +msgstr "" + +msgid "Add Custom Template" +msgstr "" + +msgid "Filament " +msgstr "" + +msgid "Start" +msgstr "" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "Ingen skrivare" @@ -1100,8 +1145,11 @@ msgstr "Mata ut" msgid "Tips" msgstr "Tips" -msgid "AMS Settings" -msgstr "AMS Inställningar" +msgid "Guide" +msgstr "" + +msgid "Retry" +msgstr "" msgid "Calibrating AMS..." msgstr "Kalibrerar AMS..." @@ -1225,6 +1273,9 @@ msgstr "Fel vid sammankoppling" msgid "Unknown Failure" msgstr "Okänt fel" +msgid "Please check the printer network connection." +msgstr "" + msgid "Abnormal print file data. Please slice again" msgstr "Onormal utskrifts fil data. Vänligen bered igen" @@ -1404,6 +1455,9 @@ msgstr "" msgid "Print with the filament mounted on the back of chassis" msgstr "" +msgid "AMS Settings" +msgstr "AMS Inställningar" + msgid "Insertion update" msgstr "Infoga uppdatering" @@ -1451,6 +1505,19 @@ msgstr "" msgid "File" msgstr "Fil" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "" + +msgid "click here to see more info" +msgstr "" + msgid "Please home all axes (click " msgstr "" @@ -1514,6 +1581,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "Success! G-code exporterades till %1%" +msgid "Running post-processing scripts" +msgstr "" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "" + msgid "Origin" msgstr "Ursprung" @@ -1906,6 +1983,12 @@ msgstr "Flöde" msgid "Tool" msgstr "Verktyg" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2125,6 +2208,12 @@ msgstr "Färgläggningsverktyg" msgid "Explosion Ratio" msgstr "Explosions Förhållande" +msgid "Section View" +msgstr "" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "Total Volym:" @@ -2231,6 +2320,9 @@ msgstr "Projekt" msgid "Slice" msgstr "Bered" +msgid "Send and Print" +msgstr "" + msgid "Slice all" msgstr "Bered allt" @@ -2240,6 +2332,12 @@ msgstr "Bered plattan" msgid "Print all" msgstr "Skriv ut allt" +msgid "Export G-code file" +msgstr "" + +msgid "Send" +msgstr "Skicka" + msgid "Export sliced file" msgstr "Exportera beredd fil" @@ -2265,6 +2363,9 @@ msgstr "Sök efter Uppdatering" msgid "&About %s" msgstr "&Om %s" +msgid "Show Tip of the Day" +msgstr "" + msgid "Open Network Test" msgstr "" @@ -2550,18 +2651,34 @@ msgstr "Synkronisering" msgid "Initialize failed (No Device)!" msgstr "Start misslyckad (Ingen Enhet)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "" + msgid "Initializing..." msgstr "Startar..." msgid "Loading..." msgstr "Laddar..." -msgid "Initialize failed (Not supported)!" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "" + +msgid "Initialize failed (Not supported without remote video tunnel)!" msgstr "" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "Start misslyckad [%d]!" +msgid "Initialize failed (%s)!" +msgstr "" + +msgid "Network unreachable" +msgstr "" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "Avbruten." @@ -2609,8 +2726,8 @@ msgstr "Ladda ner" msgid "Download selected files from printer." msgstr "" -msgid "Management" -msgstr "Handhavande" +msgid "Select" +msgstr "" msgid "Batch manage files." msgstr "" @@ -2634,17 +2751,14 @@ msgstr "Laddar fil lista..." msgid "No files" msgstr "Inga filer" -msgid "Choose save directory" -msgstr "" - msgid "Download waiting..." msgstr "" -msgid "Retry" +msgid "Play" msgstr "" -msgid "Open" -msgstr "Öppna" +msgid "Open Folder" +msgstr "" msgid "Download finished" msgstr "" @@ -2746,6 +2860,9 @@ msgstr "Sport" msgid "Ludicrous" msgstr "Galet" +msgid "Failed to connect to the server" +msgstr "Uppkoppling till servern misslyckades" + msgid "Status" msgstr "Status" @@ -2758,15 +2875,9 @@ msgstr "Uppdatera" msgid "HMS" msgstr "HMS" -msgid "Failed to connect to the server" -msgstr "Uppkoppling till servern misslyckades" - msgid "Failed to connect to the printer" msgstr "Uppkoppling till printern misslyckades" -msgid "OK" -msgstr "OK" - msgid "Yes" msgstr "Ja" @@ -2830,9 +2941,6 @@ msgstr "En uppdatering finns tillgänglig." msgid "Goto download page." msgstr "Besök nedladdnings sidan." -msgid "More" -msgstr "Mer" - msgid "Open Folder." msgstr "Öppna Mapp." @@ -2848,6 +2956,18 @@ msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d Objektet har färgläggning.@%1$d Objekten har färgläggning." msgstr[1] "%1$d Objektet har färgläggning.@%1$d Objekten har färgläggning." +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "" + msgid "Slice ok." msgstr "Beredning klar." @@ -2997,6 +3117,9 @@ msgstr "Filament byten" msgid "Click to edit preset" msgstr "Tryck för att redigera inställningar" +msgid "Connection" +msgstr "" + msgid "Bed type" msgstr "Typ av byggplatta" @@ -3053,6 +3176,11 @@ msgstr "Tidigare osparat projekt upptäckt, återställa det?" msgid "Restore" msgstr "Återställ" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "Laddar fil: %s" @@ -3164,6 +3292,9 @@ msgstr "Det valda objektet kan inte delas." msgid "Another export job is running." msgstr "En annan exportering pågår." +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "" @@ -3271,9 +3402,6 @@ msgstr "Spara G-kod som:" msgid "Save Sliced file as:" msgstr "Spara beredningen som:" -msgid "preparing, export 3mf failed!" -msgstr "" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3345,6 +3473,12 @@ msgstr "Val av region" msgid "Second" msgstr "Andra" +msgid "Browse" +msgstr "" + +msgid "Choose Download Directory" +msgstr "" + msgid "General Settings" msgstr "Allmänna inställningar" @@ -3375,6 +3509,12 @@ msgstr "Brittisk standard" msgid "Units" msgstr "Enheter" +msgid "Show \"Tip of the day\" notification after start" +msgstr "" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "" + msgid "User sync" msgstr "Användar synkronisering" @@ -3416,6 +3556,9 @@ msgstr "Auto Säkerhetskopiera" msgid "Backup interval" msgstr "Säkerhetskopierings intervall" +msgid "Downloads" +msgstr "" + msgid "Home page and daily tips" msgstr "Hemsida och dagliga förslag" @@ -3650,6 +3793,9 @@ msgstr "Andra Enheter" msgid "Input access code" msgstr "Inmatning av åtkomstkod" +msgid "Can't find my devices?" +msgstr "" + msgid "Log out successful." msgstr "Utloggning lyckades." @@ -3665,9 +3811,6 @@ msgstr "Bambu Engineering Plate" msgid "Bamabu High Temperature Plate" msgstr "Bambu High Temperature Plate" -msgid "Send print job to" -msgstr "Skicka utskriftsjobb till" - msgid "Refresh" msgstr "Uppdatera" @@ -3677,8 +3820,8 @@ msgstr "Justering av Byggplattan" msgid "Flow Calibration" msgstr "Flödes Kalibrering" -msgid "Send" -msgstr "Skicka" +msgid "Can't connect to the printer" +msgstr "" msgid "send completed" msgstr "Skicka komplett" @@ -3748,12 +3891,33 @@ msgid "" "slot." msgstr "" +msgid "An SD card needs to be inserted before printing." +msgstr "" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "" + +msgid "Please check the following infomation:\n" +msgstr "" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" + msgid "Preparing print job" msgstr "Förbereder utskriftsjobb" msgid "Modifying the device name" msgstr "Ändra enhetens namn" +msgid "Send to Printer" +msgstr "" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "" + msgid "Log in printer" msgstr "Logga in skrivare" @@ -3793,12 +3957,13 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" msgstr "" msgid "" @@ -4001,6 +4166,9 @@ msgstr "Filament stop G-kod" msgid "Printable space" msgstr "Utskriftsbar yta" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "Tillbehör" @@ -4025,6 +4193,9 @@ msgstr "Byta filament G-kod" msgid "Pause G-code" msgstr "Paus G-kod" +msgid "Template Custom G-code" +msgstr "" + msgid "Motion ability" msgstr "Rörelseförmåga" @@ -4073,9 +4244,6 @@ msgstr "Allt" msgid "Set" msgstr "Välj" -msgid "Custom G-code" -msgstr "Custom G-kod" - msgid "Click to reset current value and attach to the global value." msgstr "" "Klicka för att återställa aktuellt värde och koppla till det övergripande " @@ -4543,23 +4711,6 @@ msgstr "Ny version av Bambu Studio" msgid "Don't remind me of this version again" msgstr "" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "" - msgid "Model:" msgstr "" @@ -4590,6 +4741,23 @@ msgstr "" msgid "Upgrading successful" msgstr "" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" + msgid "Saving objects into the 3mf failed." msgstr "Sparande av objektet till 3mf misslyckades." @@ -4830,16 +4998,14 @@ msgstr "skriv återuppringning misslyckad" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." -msgstr "%1% för tätt inpå den isolerade ytan, utskriften kolliderar." +"%1% is too close to exclusion area, there may be collisions when printing." +msgstr "" #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1% för tätt inpå den isolerade ytan, utskriften kolliderar." #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4849,12 +5015,12 @@ msgstr "%1% är för nära andra och kan orsaka kollisioner." msgid "%1% is too tall, and collisions will be caused." msgstr "%1% är för hög, och kollisioner kommer att uppstå." -msgid " is too close to others, there will be collisions when printing.\n" -msgstr " för tätt inpå andra, utskriften kolliderar.\n" +msgid " is too close to others, there may be collisions when printing.\n" +msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" -msgstr " för tätt inpå den isolerade ytan, utskriften kolliderar.\n" +" is too close to exclusion area, there may be collisions when printing.\n" +msgstr "" msgid "Prime Tower" msgstr "Prime Torn" @@ -4995,22 +5161,83 @@ msgstr "Maximala utskriftshöjd begränsas av skrivarens mekanism" msgid "Printer preset names" msgstr "Namn på förinställda skrivare" -msgid "Avoid crossing wall when travel" -msgstr "Undvik väggar vid förflyttning" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "" "Undvik väggar vid förflyttning, för att undvika att filament droppar på ytan" -msgid "Max travel detour distance" -msgstr "Max undvikande förflyttnings avstånd" +msgid "Avoid crossing wall - Max detour length" +msgstr "" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" + +msgid "mm or %" msgstr "" -"Max undvikande förflyttnings avstånd för att undvika väggar. Om avståndet " -"överstiger detta värde, kommer ingen undvikande förflyttning att ske" msgid "Other layers" msgstr "Andra lager" @@ -5395,24 +5622,25 @@ msgid "Height to rod" msgstr "Höjd till axel" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" msgstr "" -"Höjden på fritt cylinder område runt extrudern: Används som input för auto-" -"placering för att undvika kollision när man skriver ut objekt för objekt" msgid "Height to lid" msgstr "Höjd till locket" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "Radie" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" msgstr "" -"Fri radie område runt extrudern: Används som input för auto-placering för " -"att undvika kollision när man skriver ut objekt för objekt" msgid "Extruder Color" msgstr "Extruder Färg" @@ -5615,6 +5843,9 @@ msgstr "Honeycomb" msgid "Adaptive Cubic" msgstr "Adaptiv Cubic" +msgid "Lightning" +msgstr "" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5755,6 +5986,9 @@ msgstr "" "Nozzelns metalliska material avgör munstyckets nötningsbeständighet och " "vilken typ av filament som kan skrivas ut" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "Härdat stål" @@ -5863,6 +6097,14 @@ msgstr "" "Om maskinen stöder tyst läge där maskinen använder lägre acceleration för " "att skriva ut" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "Max hastighet X" @@ -6022,6 +6264,14 @@ msgstr "Nozzel diameter" msgid "Diameter of nozzle" msgstr "Diametern på nozzeln" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "Nozzle volym" @@ -6424,8 +6674,8 @@ msgid "" "interface is Concentric" msgstr "" "Linje mönster för support gränssnittsytan .Standardmönstret för olösligt " -"(material) support gränssnittet är Räta medan standardmönstret för " -"lösligt(material) stödgränssnittet är koncentriskt" +"(material) support gränssnittet är Räta medan standardmönstret för lösligt" +"(material) stödgränssnittet är koncentriskt" msgid "Base pattern spacing" msgstr "Basens mönster mellanrum" @@ -6812,10 +7062,6 @@ msgstr "Bereder mesh" msgid " Object:" msgstr " Objekt:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "Fel i linje %1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "Support: generera verktygsbana vid lager %d" @@ -6847,6 +7093,245 @@ msgstr "Support: åtgärda hål vid lager %d" msgid "Support: propagate branches at layer %d" msgstr "Support: föröka grenar vid lager %d" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did 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\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did 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\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did 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\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" + +#~ msgid "Erase painting" +#~ msgstr "Radera färgläggning" + +#~ msgid "Set pen size" +#~ msgstr "Välj penn storlek" + +#~ msgid "Rotation:" +#~ msgstr "Rotation:" + +#~ msgid "Height:" +#~ msgstr "Höjd:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "Start misslyckad [%d]!" + +#~ msgid "Management" +#~ msgstr "Handhavande" + +#~ msgid "Open" +#~ msgstr "Öppna" + +#~ msgid "Send print job to" +#~ msgstr "Skicka utskriftsjobb till" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "%1% för tätt inpå den isolerade ytan, utskriften kolliderar." + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1% för tätt inpå den isolerade ytan, utskriften kolliderar." + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr " för tätt inpå andra, utskriften kolliderar.\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr " för tätt inpå den isolerade ytan, utskriften kolliderar.\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "Undvik väggar vid förflyttning" + +#~ msgid "Max travel detour distance" +#~ msgstr "Max undvikande förflyttnings avstånd" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "Max undvikande förflyttnings avstånd för att undvika väggar. Om avståndet " +#~ "överstiger detta värde, kommer ingen undvikande förflyttning att ske" + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "" +#~ "Höjden på fritt cylinder område runt extrudern: Används som input för " +#~ "auto-placering för att undvika kollision när man skriver ut objekt för " +#~ "objekt" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "" +#~ "Fri radie område runt extrudern: Används som input för auto-placering för " +#~ "att undvika kollision när man skriver ut objekt för objekt" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "Fel i linje %1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "Reducera Trianglar" @@ -6877,9 +7362,6 @@ msgstr "Support: föröka grenar vid lager %d" #~ msgid "0%" #~ msgstr "0%" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + mushjul" - #~ msgid "" #~ "An object is layed over the boundary of plate.\n" #~ "Please solve the problem by moving it totally inside or outside plate." diff --git a/bbl/i18n/zh_cn/BambuStudio_zh_CN.po b/bbl/i18n/zh_cn/BambuStudio_zh_CN.po index 50072e2712..9c722a4ae9 100644 --- a/bbl/i18n/zh_cn/BambuStudio_zh_CN.po +++ b/bbl/i18n/zh_cn/BambuStudio_zh_CN.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Slic3rPE\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-09-27 16:25+0800\n" -"PO-Revision-Date: 2022-09-05 14:22+0800\n" +"POT-Creation-Date: 2022-10-19 09:33+0800\n" +"PO-Revision-Date: 2022-10-17 22:14+0800\n" "Last-Translator: Jiang Yue \n" "Language-Team: \n" "Language: zh_CN\n" @@ -20,9 +20,15 @@ msgstr "" msgid "Supports Painting" msgstr "支撑绘制" +msgid "Alt + Mouse wheel" +msgstr "Alt + 鼠标滚轮" + msgid "Section view" msgstr "剖面视图" +msgid "Ctrl + Mouse wheel" +msgstr "Ctrl + 鼠标滚轮" + msgid "Pen size" msgstr "画笔尺寸" @@ -41,8 +47,8 @@ msgstr "屏蔽支撑" msgid "Shift + Left mouse button" msgstr "Shift + 鼠标左键" -msgid "Erase painting" -msgstr "擦除绘制" +msgid "Erase" +msgstr "擦除" msgid "Erase all painting" msgstr "擦除所有绘制" @@ -59,12 +65,6 @@ msgstr "执行" msgid "Gap area" msgstr "间隙面积" -msgid "Set pen size" -msgstr "设置画笔大小" - -msgid "Ctrl + Mouse wheel" -msgstr "Ctrl + 鼠标滚轮" - msgid "Tool type" msgstr "工具类型" @@ -114,9 +114,6 @@ msgstr "画笔形状" msgid "Paint" msgstr "绘制" -msgid "Erase" -msgstr "擦除" - msgid "Key 1~9" msgstr "按键 1~9" @@ -244,11 +241,11 @@ msgstr "剪切" msgid "Movement:" msgstr "移动:" -msgid "Rotation:" -msgstr "旋转:" +msgid "Movement" +msgstr "移动" -msgid "Height:" -msgstr "高度:" +msgid "Height" +msgstr "高度" msgid "Keep upper part" msgstr "保留上半部分" @@ -351,12 +348,12 @@ msgstr "添加Z缝" msgid "Block seam" msgstr "屏蔽Z缝" -msgid "Remove selection" -msgstr "移除绘制" - msgid "Seam painting" msgstr "Z缝绘制" +msgid "Remove selection" +msgstr "移除绘制" + msgid "Entering Seam painting" msgstr "" @@ -420,6 +417,9 @@ msgstr "配置文件“%1%”已被加载,但部分数值未被识别。" msgid "V" msgstr "V" +msgid "Internal Version" +msgstr "内部版本" + msgid "Version" msgstr "版本" @@ -552,6 +552,15 @@ msgstr "语言" msgid "*" msgstr "*" +msgid "The uploads are still ongoing" +msgstr "上传任务依然在继续" + +msgid "Stop them and continue anyway?" +msgstr "停止它们并且继续?" + +msgid "Ongoing uploads" +msgstr "正在进行的上传" + msgid "Select a G-code file:" msgstr "选择一个G-code文件:" @@ -819,7 +828,7 @@ msgid "Clone" msgstr "克隆" msgid "Simplify Model" -msgstr "" +msgstr "简化模型" msgid "Center" msgstr "居中" @@ -1019,7 +1028,7 @@ msgid "Auto Brim" msgstr "自动Brim" msgid "Outer wall speed" -msgstr "" +msgstr "外墙速度" msgid "Plate" msgstr "盘" @@ -1039,6 +1048,42 @@ msgstr "多色打印" msgid "Line Type" msgstr "走线类型" +msgid "More" +msgstr "详情" + +msgid "Open Preferences." +msgstr "打开首选项。" + +msgid "Open Documentation in web browser." +msgstr "在web浏览器中打开文档。" + +msgid "Custom G-code" +msgstr "自定义 G-code" + +msgid "Enter Custom G-code used on current layer:" +msgstr "输入当前层上使用的自定义G-code" + +msgid "OK" +msgstr "确认" + +msgid "Add Pause" +msgstr "添加暂停打印" + +msgid "Add Custom G-code" +msgstr "添加自定义G-code" + +msgid "Add Custom Template" +msgstr "添加自定义模板" + +msgid "Filament " +msgstr "耗材丝" + +msgid "Start" +msgstr "首层" + +msgid "G-code" +msgstr "" + msgid "No printer" msgstr "无打印机" @@ -1075,8 +1120,11 @@ msgstr "退料" msgid "Tips" msgstr "提示" -msgid "AMS Settings" -msgstr "AMS 设置" +msgid "Guide" +msgstr "引导" + +msgid "Retry" +msgstr "重试" msgid "Calibrating AMS..." msgstr "正在校准AMS..." @@ -1192,6 +1240,9 @@ msgstr "设备登录失败" msgid "Unknown Failure" msgstr "发生错误" +msgid "Please check the printer network connection." +msgstr "请检查打印机的网络连接。" + msgid "Abnormal print file data. Please slice again" msgstr "打印文件数据异常,请重新切片" @@ -1367,6 +1418,9 @@ msgstr "不启用AMS" msgid "Print with the filament mounted on the back of chassis" msgstr "使用机箱背后挂载的材料打印" +msgid "AMS Settings" +msgstr "AMS 设置" + msgid "Insertion update" msgstr "插入料时更新" @@ -1411,6 +1465,19 @@ msgstr "AMS不会在启动时自动读取耗材丝信息。它会使用上次关 msgid "File" msgstr "文件" +msgid "" +"Failed to download the plug-in. Please check your firewall settings and vpn " +"software, check and retry." +msgstr "插件下载失败。请检查您的防火墙设置和vpn软件,检查后重试。" + +msgid "" +"Failed to install the plug-in. Please check whether it is blocked or deleted " +"by anti-virus software." +msgstr "安装插件失败。请检查是否被杀毒软件屏蔽或删除了。" + +msgid "click here to see more info" +msgstr "点击这里查看更多信息" + msgid "Please home all axes (click " msgstr "请先执行回原点(点击" @@ -1474,6 +1541,16 @@ msgstr "" msgid "Succeed to export G-code to %1%" msgstr "成功导出G-code至 %1%" +msgid "Running post-processing scripts" +msgstr "运行后处理脚本" + +msgid "Copying of the temporary G-code to the output G-code failed" +msgstr "将临时 G 代码复制到输出 G 代码失败" + +#, boost-format +msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" +msgstr "计划上传到 `%1%`。请参阅窗口-> 打印主机上传队列" + msgid "Origin" msgstr "原点" @@ -1853,6 +1930,12 @@ msgstr "流量" msgid "Tool" msgstr "工具" +msgid "Height: " +msgstr "" + +msgid "Width: " +msgstr "" + msgid "Speed: " msgstr "" @@ -2073,6 +2156,12 @@ msgstr "上色工具条" msgid "Explosion Ratio" msgstr "爆炸比例" +msgid "Section View" +msgstr "剖面视图" + +msgid "Assemble Control" +msgstr "" + msgid "Total Volume:" msgstr "总体积:" @@ -2148,13 +2237,13 @@ msgstr "" "查看,如下图所示:" msgid "Invalid input." -msgstr "" +msgstr "非法输入" msgid "New Window" -msgstr "" +msgstr "新窗口" msgid "Open a new window" -msgstr "" +msgstr "打开新窗口" msgid "Application is closing" msgstr "正在关闭应用程序" @@ -2180,6 +2269,9 @@ msgstr "项目" msgid "Slice" msgstr "切片" +msgid "Send and Print" +msgstr "发送并打印" + msgid "Slice all" msgstr "切片所有盘" @@ -2189,6 +2281,12 @@ msgstr "切片单盘" msgid "Print all" msgstr "打印所有盘" +msgid "Export G-code file" +msgstr "导出G-code文件" + +msgid "Send" +msgstr "发送" + msgid "Export sliced file" msgstr "导出切片文件" @@ -2214,8 +2312,11 @@ msgstr "检查新版本" msgid "&About %s" msgstr "关于 %s" +msgid "Show Tip of the Day" +msgstr "展示每日小贴士" + msgid "Open Network Test" -msgstr "" +msgstr "打开网络测试" msgid "Default View" msgstr "默认视图" @@ -2388,10 +2489,10 @@ msgid "Use Orthogonal View" msgstr "使用正交视角" msgid "Show &Labels" -msgstr "" +msgstr "显示名称" msgid "Show object labels in 3D scene" -msgstr "" +msgstr "在3D场景中显示对象名称" msgid "Preferences" msgstr "偏好设置" @@ -2502,18 +2603,34 @@ msgstr "同步" msgid "Initialize failed (No Device)!" msgstr "初始化失败(没有设备)!" +msgid "Initialize failed (No Camera Device)!" +msgstr "初始化失败(没有摄像头)" + msgid "Initializing..." msgstr "正在初始化……" msgid "Loading..." msgstr "正在加载视频……" -msgid "Initialize failed (Not supported)!" -msgstr "" +msgid "Initialize failed (Not supported with LAN-only mode)!" +msgstr "初始化失败(不支持局域网模式的视频连接)" + +msgid "Initialize failed (Not accessible in LAN-only mode)!" +msgstr "初始化失败(局域网描述不可访问)" + +msgid "Initialize failed (Not supported without remote video tunnel)!" +msgstr "初始化失败(不支持远程视频连接)" #, c-format, boost-format -msgid "Initialize failed [%d]!" -msgstr "初始化失败 [%d]!" +msgid "Initialize failed (%s)!" +msgstr "初始化失败(%s)!" + +msgid "Network unreachable" +msgstr "网络不可访问" + +#, c-format, boost-format +msgid "Stopped [%d]!" +msgstr "" msgid "Stopped." msgstr "已经停止。" @@ -2535,43 +2652,43 @@ msgid "All Files" msgstr "所有文件" msgid "Group files by year, recent first." -msgstr "" +msgstr "按年份分组,从最近的开始展示" msgid "Group files by month, recent first." -msgstr "" +msgstr "按月份分组,从最近的开始展示" msgid "Show all files, recent first." -msgstr "" +msgstr "显示所有文件,从最近的开始展示" msgid "Switch to timelapse files." -msgstr "" +msgstr "切换到延时摄影文件列表" msgid "Video" msgstr "录像" msgid "Switch to video files." -msgstr "" +msgstr "切换到视频文件列表" msgid "Delete selected files from printer." -msgstr "" +msgstr "从打印机中输出选中的文件" msgid "Download" msgstr "下载" msgid "Download selected files from printer." -msgstr "" +msgstr "从打印机中下载选择的文件" -msgid "Management" -msgstr "管理" +msgid "Select" +msgstr "选择" msgid "Batch manage files." -msgstr "" +msgstr "批量管理文件" msgid "No printers." msgstr "未选择打印机" msgid "Not supported." -msgstr "" +msgstr "不支持" msgid "Connecting..." msgstr "连接中..." @@ -2586,17 +2703,14 @@ msgstr "加载文件列表..." msgid "No files" msgstr "文件列表为空" -msgid "Choose save directory" -msgstr "选择保存目录" - msgid "Download waiting..." msgstr "正在下载中..." -msgid "Retry" -msgstr "重试" +msgid "Play" +msgstr "播放" -msgid "Open" -msgstr "打开" +msgid "Open Folder" +msgstr "打开目录" msgid "Download finished" msgstr "下载完成" @@ -2698,6 +2812,9 @@ msgstr "运动" msgid "Ludicrous" msgstr "狂暴" +msgid "Failed to connect to the server" +msgstr "无法连接服务器" + msgid "Status" msgstr "设备状态" @@ -2710,15 +2827,9 @@ msgstr "固件更新" msgid "HMS" msgstr "" -msgid "Failed to connect to the server" -msgstr "无法连接服务器" - msgid "Failed to connect to the printer" msgstr "无法连接打印机" -msgid "OK" -msgstr "确认" - msgid "Yes" msgstr "是" @@ -2782,9 +2893,6 @@ msgstr "发现新的软件版本。" msgid "Goto download page." msgstr "前往下载网站。" -msgid "More" -msgstr "详情" - msgid "Open Folder." msgstr "打开目录。" @@ -2798,6 +2906,18 @@ msgid "%1$d Object has color painting." msgid_plural "%1$d Objects have color painting." msgstr[0] "%1$d对象有涂色。" +msgid "ERROR" +msgstr "" + +msgid "CANCELED" +msgstr "" + +msgid "COMPLETED" +msgstr "" + +msgid "Cancel upload" +msgstr "取消上传" + msgid "Slice ok." msgstr "切片完成." @@ -2954,31 +3074,31 @@ msgid "Flushing volumes" msgstr "冲刷体积" msgid "Add one filament" -msgstr "" +msgstr "增加一个材料" msgid "Remove last filament" -msgstr "" +msgstr "删除最后一个材料" msgid "Sync material list from AMS" -msgstr "" +msgstr "同步到 AMS 的材料列表" msgid "Set filaments to use" -msgstr "" +msgstr "配置可选择的材料" msgid "" "No AMS filaments. Please select a printer in 'Device' page to load AMS info." -msgstr "" +msgstr "没有发现AMS材料。请在“设备”页面选择打印机,将加载 AMS 信息" msgid "Sync filaments with AMS" -msgstr "" +msgstr "同步到 AMS 的材料列表" msgid "" "Sync filaments with AMS will drop all current selected filament presets and " "colors. Do you want to continue?" -msgstr "" +msgstr "同步到 AMS 的材料列表将丢弃所有当前配置的材料预设、颜色。是否继续?" msgid "There are no compatible filaments, and sync is not performed." -msgstr "" +msgstr "没有如任何兼容的材料,同步操作未执行。" msgid "Untitled" msgstr "未命名" @@ -3003,6 +3123,11 @@ msgstr "检测到有未保存的项目,是否恢复此项目?" msgid "Restore" msgstr "恢复" +msgid "" +"The bed temperature exceeds filament's vitrification temperature. Please " +"open the front door of printer before printing to avoid nozzle clog." +msgstr "" + #, c-format, boost-format msgid "Loading file: %s" msgstr "加载文件:%s" @@ -3113,6 +3238,9 @@ msgstr "选中的模型不可分裂。" msgid "Another export job is running." msgstr "有其他导出任务正在进行中。" +msgid "Another export job is currently running." +msgstr "" + msgid "Select a new file" msgstr "选择新文件" @@ -3221,9 +3349,6 @@ msgstr "G-code文件另存为:" msgid "Save Sliced file as:" msgstr "切片文件另存为:" -msgid "preparing, export 3mf failed!" -msgstr "正在准备中,导出 3mf 失败!" - msgid "" "Print By Object: \n" "Suggest to use auto-arrange to avoid collisions when printing." @@ -3295,6 +3420,12 @@ msgstr "区域选择" msgid "Second" msgstr "秒" +msgid "Browse" +msgstr "浏览" + +msgid "Choose Download Directory" +msgstr "选择下载文件夹" + msgid "General Settings" msgstr "通用设置" @@ -3325,6 +3456,12 @@ msgstr "英制" msgid "Units" msgstr "单位" +msgid "Show \"Tip of the day\" notification after start" +msgstr "启动后显示“每日小贴士”通知" + +msgid "If enabled, useful hints are displayed at startup." +msgstr "如果启用,将在启动时显示有用的提示。" + msgid "User sync" msgstr "用户同步" @@ -3364,6 +3501,9 @@ msgstr "自动备份" msgid "Backup interval" msgstr "备份间隔时长" +msgid "Downloads" +msgstr "下载" + msgid "Home page and daily tips" msgstr "首页和每日小贴士" @@ -3595,6 +3735,9 @@ msgstr "其他设备" msgid "Input access code" msgstr "输入访问码" +msgid "Can't find my devices?" +msgstr "无法找到我的设备?" + msgid "Log out successful." msgstr "登出成功。" @@ -3610,9 +3753,6 @@ msgstr "工程打印热床" msgid "Bamabu High Temperature Plate" msgstr "高温打印热床" -msgid "Send print job to" -msgstr "发送打印任务至" - msgid "Refresh" msgstr "刷新" @@ -3622,8 +3762,8 @@ msgstr "热床调平" msgid "Flow Calibration" msgstr "流量校准" -msgid "Send" -msgstr "发送" +msgid "Can't connect to the printer" +msgstr "无法连接打印机" msgid "send completed" msgstr "发送完成" @@ -3694,12 +3834,35 @@ msgstr "" "已自动建立 \"耗材丝列表=>AMS槽位\" 的映射关系。 可点击上方具体的耗材丝手动设" "置其所对应的AMS槽位" +msgid "An SD card needs to be inserted before printing." +msgstr "请在发起打印前插入SD卡" + +msgid "An SD card needs to be inserted to recording timelapse" +msgstr "开启延迟摄影功能需要插入SD卡" + +msgid "Please check the following infomation:\n" +msgstr "请确认以下信息后发送打印:\n" + +msgid "" +"The printer type used to generate G-code is not the same type as the " +"currently selected physical printer. It is recommend to re-slice by " +"selecting the same printer type.\n" +msgstr "" +"用于生成G-code的打印机类型与当前选定的物理打印机类型不同,建议选择相同的打印" +"机类型重新切片。\n" + msgid "Preparing print job" msgstr "正在准备打印任务" msgid "Modifying the device name" msgstr "修改打印机名称" +msgid "Send to Printer" +msgstr "发送到打印机" + +msgid "The printer is required to be in the same LAN as Bambu Studio." +msgstr "打印机需要与Bambu Studio在同一个局域网内。" + msgid "Log in printer" msgstr "登录打印机" @@ -3739,13 +3902,14 @@ msgid "Click to reset all settings to the last saved preset." msgstr "" msgid "" -"Prime tower is required by timeplase. Are you sure you want to disable both " -"of them?" -msgstr "延时摄影需要使用擦拭塔,是否同时禁用?" +"Prime tower is required by smooth timeplase. If whthout prime tower, there " +"will be flaws on the model. Are you sure you want to disable prime tower?" +msgstr "" msgid "" -"Prime tower is required by timelapse. Do you want to enable both of them?" -msgstr "延时摄影需要使用擦拭塔,是否同时启用?" +"Prime tower is required by smooth timelapse. If whthout prime tower, there " +"will be flaws on the model. Do you want to enable prime tower?" +msgstr "" msgid "" "When recording timelapse without toolhead, it is recommended to add a " @@ -3931,6 +4095,9 @@ msgstr "耗材丝结束G-code" msgid "Printable space" msgstr "可打印区域" +msgid "Extruder Clearance" +msgstr "" + msgid "Accessory" msgstr "配件" @@ -3955,6 +4122,9 @@ msgstr "耗材丝更换G-code" msgid "Pause G-code" msgstr "暂停 G-code" +msgid "Template Custom G-code" +msgstr "模板自定义G-code" + msgid "Motion ability" msgstr "移动能力" @@ -3998,9 +4168,6 @@ msgstr "所有" msgid "Set" msgstr "设置" -msgid "Custom G-code" -msgstr "自定义 G-code" - msgid "Click to reset current value and attach to the global value." msgstr "点击该图标,恢复到全局的配置数值,并与全局配置同步变化。" @@ -4104,7 +4271,7 @@ msgid "Show all presets (including incompatible)" msgstr "显示所有预设(包括不兼容的)" msgid "Add File" -msgstr "" +msgstr "添加文件" msgid "Set as cover" msgstr "设置为封面" @@ -4459,23 +4626,6 @@ msgstr "新版本的Bambu Studio" msgid "Don't remind me of this version again" msgstr "此版本不再提示" -msgid "" -"Are you sure you want to update? This will take about 10 minutes. Do not " -"turn off the power while the printer is updating." -msgstr "确定要更新吗?更新需要大约10分钟,在此期间请勿关闭电源。" - -msgid "" -"An important update was detected and needs to be run before printing can " -"continue. Do you want to update now? You can also update later from 'Upgrade " -"firmware'." -msgstr "检测到重要更新,需要升级后才可进行打印。你想现在就开始升级吗?你也可以稍后点击‘升级固件’完成升级。" - -msgid "" -"The firmware version is abnormal. Repairing and updating are required before " -"printing. Do you want to update now? You can also update later on printer or " -"update next time starting the studio." -msgstr "当前固件版本异常,需要进行修复升级,否则无法继续打印。你想现在就开始升级吗?你也可以稍后在打印机上升级,或者下一次启动studio再升级。" - msgid "Model:" msgstr "" @@ -4495,7 +4645,7 @@ msgid "Idle" msgstr "" msgid "Latest version" -msgstr "" +msgstr "最新版本" msgid "Upgrading" msgstr "升级中" @@ -4506,6 +4656,27 @@ msgstr "升级失败" msgid "Upgrading successful" msgstr "升级成功" +msgid "" +"Are you sure you want to update? This will take about 10 minutes. Do not " +"turn off the power while the printer is updating." +msgstr "确定要更新吗?更新需要大约10分钟,在此期间请勿关闭电源。" + +msgid "" +"An important update was detected and needs to be run before printing can " +"continue. Do you want to update now? You can also update later from 'Upgrade " +"firmware'." +msgstr "" +"检测到重要更新,需要升级后才可进行打印。你想现在就开始升级吗?你也可以稍后点" +"击‘升级固件’完成升级。" + +msgid "" +"The firmware version is abnormal. Repairing and updating are required before " +"printing. Do you want to update now? You can also update later on printer or " +"update next time starting the studio." +msgstr "" +"当前固件版本异常,需要进行修复升级,否则无法继续打印。你想现在就开始升级吗?" +"你也可以稍后在打印机上升级,或者下一次启动studio再升级。" + msgid "Saving objects into the 3mf failed." msgstr "保存对象到3mf失败。" @@ -4741,16 +4912,14 @@ msgstr "写入回调失败" #, boost-format msgid "" -"%1% is too close to exclusion area, there will be collisions when printing." -msgstr "%1%离不可打印区域太近,会造成打印时出现碰撞。" +"%1% is too close to exclusion area, there may be collisions when printing." +msgstr "" #, boost-format msgid "" "\n" -"%1% is too close to exclusion area, there will be collisions when printing." +"%1% is too close to exclusion area, there may be collisions when printing." msgstr "" -"\n" -"%1%离不可打印区域太近,会造成打印时出现碰撞。" #, boost-format msgid "%1% is too close to others, and collisions may be caused." @@ -4760,12 +4929,12 @@ msgstr "%1%离其它对象太近,可能会发生碰撞。" msgid "%1% is too tall, and collisions will be caused." msgstr "%1%太高,会发生碰撞。" -msgid " is too close to others, there will be collisions when printing.\n" -msgstr "离其它对象太近,会造成打印时出现碰撞。\n" +msgid " is too close to others, there may be collisions when printing.\n" +msgstr "" msgid "" -" is too close to exclusion area, there will be collisions when printing.\n" -msgstr "离不可打印区域太近,会造成打印时出现碰撞。\n" +" is too close to exclusion area, there may be collisions when printing.\n" +msgstr "" msgid "Prime Tower" msgstr "擦拭塔" @@ -4894,19 +5063,84 @@ msgstr "由打印机结构约束的最大可打印高度" msgid "Printer preset names" msgstr "打印机预设名" -msgid "Avoid crossing wall when travel" -msgstr "空驶时避免跨越外墙" +msgid "Hostname, IP or URL" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the hostname, IP address or URL of the printer host instance. Print host " +"behind HAProxy with basic auth enabled can be accessed by putting the user " +"name and password into the URL in the following format: https://username:" +"password@your-octopi-address/" +msgstr "" + +msgid "API Key / Password" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field should contain " +"the API Key or the password required for authentication." +msgstr "" + +msgid "Name of the printer" +msgstr "" + +msgid "HTTPS CA File" +msgstr "" + +msgid "" +"Custom CA certificate file can be specified for HTTPS OctoPrint connections, " +"in crt/pem format. If left blank, the default OS CA certificate repository " +"is used." +msgstr "" + +msgid "User" +msgstr "用户名" + +msgid "Password" +msgstr "密码" + +msgid "Ignore HTTPS certificate revocation checks" +msgstr "" + +msgid "" +"Ignore HTTPS certificate revocation checks in case of missing or offline " +"distribution points. One may want to enable this option for self signed " +"certificates if connection fails." +msgstr "" + +msgid "Names of presets related to the physical printer" +msgstr "" + +msgid "Authorization Type" +msgstr "" + +msgid "API key" +msgstr "" + +msgid "HTTP digest" +msgstr "" + +msgid "Avoid crossing wall" +msgstr "避免跨越外墙" msgid "Detour and avoid to travel across wall which may cause blob on surface" msgstr "空驶时绕过外墙以避免在模型外观面产生斑点" -msgid "Max travel detour distance" -msgstr "最大绕行距离" +msgid "Avoid crossing wall - Max detour length" +msgstr "避免跨越外墙-最大绕行长度" msgid "" "Maximum detour distance for avoiding crossing wall. Don't detour if the " -"detour distance is large than this value" -msgstr "避免跨越外墙时的最大绕行距离。如果绕行距离大于这个阈值,则不再绕行。" +"detour distance is large than this value. Detour length could be specified " +"either as an absolute value or as percentage (for example 50%) of a direct " +"travel path. Zero to disable" +msgstr "" +"避免跨越外墙时的最大绕行距离。当绕行距离比这个数值大时,此次空驶不绕行。绕行" +"距离可表达为绝对值,或者相对直线空驶长度的百分比(例如50%)。0表示禁用" + +msgid "mm or %" +msgstr "mm 或 %" msgid "Other layers" msgstr "其它层" @@ -5240,10 +5474,10 @@ msgid "Monotonic line" msgstr "单调线" msgid "Aligned Rectilinear" -msgstr "" +msgstr "直线排列" msgid "Hilbert Curve" -msgstr "" +msgstr "希尔伯特曲线" msgid "Bottom surface pattern" msgstr "底面图案" @@ -5281,20 +5515,25 @@ msgid "Height to rod" msgstr "到横杆高度" msgid "" -"Height of the clearance cylinder around extruder. Used as input of auto-" -"arrange to avoid collision when print object by object" -msgstr "挤出机的净空高度。作为自动摆放的输入信息,避免逐件打印时发生碰撞。" +"Distance of the nozzle tip to the lower rod. Used as input of auto-arranging " +"to avoid collision when printing by object" +msgstr "" msgid "Height to lid" msgstr "到顶盖高度" +msgid "" +"Distance of the nozzle tip to the lid. Used as input of auto-arranging to " +"avoid collision when printing by object" +msgstr "" + msgid "Radius" msgstr "半径" msgid "" -"Clearance radius around extruder. Used as input of auto-arrange to avoid " -"collision when print object by object" -msgstr "挤出机的净空半径。作为自动摆放的输入信息,避免逐件打印时发生碰撞。" +"Clearance radius around extruder. Used as input of auto-arranging to avoid " +"collision when printing by object" +msgstr "" msgid "Extruder Color" msgstr "挤出机颜色" @@ -5477,6 +5716,9 @@ msgstr "蜂窝" msgid "Adaptive Cubic" msgstr "自适应立方体" +msgid "Lightning" +msgstr "闪电" + msgid "" "Acceleration of top surface infill. Using a lower value may improve top " "surface quality" @@ -5632,6 +5874,9 @@ msgid "" "nozzle, and what kind of filament can be printed" msgstr "喷嘴的金属材料。这将决定喷嘴的耐磨性,以及可打印材料的种类" +msgid "Undefine" +msgstr "" + msgid "Hardened steel" msgstr "硬化钢" @@ -5731,6 +5976,14 @@ msgid "" "acceleration to print" msgstr "机器是否支持使用低加速度打印的静音模式。" +msgid "" +"This G-code will be used as a code for the pause print. User can insert " +"pause G-code in gcode viewer" +msgstr "" + +msgid "This G-code will be used as a custom code" +msgstr "" + msgid "Maximum speed X" msgstr "X最大速度" @@ -5884,6 +6137,14 @@ msgstr "喷嘴直径" msgid "Diameter of nozzle" msgstr "喷嘴直径" +msgid "Host Type" +msgstr "" + +msgid "" +"Slic3r can upload G-code files to a printer host. This field must contain " +"the kind of the host." +msgstr "" + msgid "Nozzle volume" msgstr "喷嘴内腔体积" @@ -6048,7 +6309,7 @@ msgid "Back" msgstr "背面" msgid "Random" -msgstr "" +msgstr "随机" msgid "Skirt distance" msgstr "Skirt距离" @@ -6628,10 +6889,6 @@ msgstr "正在切片网格" msgid " Object:" msgstr " 对象:" -#, boost-format -msgid "Error at line %1%:\n" -msgstr "错误在行%1%:\n" - #, c-format, boost-format msgid "Support: generate toolpath at layer %d" msgstr "支撑:正在生成层%d的走线路径" @@ -6663,6 +6920,315 @@ msgstr "支撑:正在修补层%d的空洞" msgid "Support: propagate branches at layer %d" msgstr "支撑:正在生长层%d的树枝" +#: resources/data/hints.ini: [hint:3D Scene Operations] +msgid "" +"3D Scene Operations\n" +"Did you know how to control view and object/part selection with mouse and " +"touchpanel in the 3D scene?" +msgstr "" +"3D场景操作\n" +"如何在3D场景中使用鼠标和触摸面板进行视角控制和对象/部件选择" + +#: resources/data/hints.ini: [hint:Cut Tool] +msgid "" +"Cut Tool\n" +"Did you know that you can cut a model at any angle and position with the " +"cutting tool?" +msgstr "" +"切割工具\n" +"您知道吗?您可以使用切割工具以任何角度和位置切割模型。" + +#: resources/data/hints.ini: [hint:Fix Model] +msgid "" +"Fix Model\n" +"Did you know that you can fix a corrupted 3D model to avoid a lot of slicing " +"problems?" +msgstr "" +"修复模型\n" +"您知道吗?您可以修复一个损坏的3D模型以避免诸多切片问题。" + +#: resources/data/hints.ini: [hint:Timelapse] +msgid "" +"Timelapse\n" +"Did you know that you can generate a timelapse video during each print?" +msgstr "" +"延时摄影\n" +"您知道吗?您可以每次打印时生成一段延时摄影。" + +#: resources/data/hints.ini: [hint:Auto-Arrange] +msgid "" +"Auto-Arrange\n" +"Did you know that you can auto-arrange all objects in your project?" +msgstr "" +"自动摆盘\n" +"您知道吗?您可以自动排列项目中的所有对象。" + +#: resources/data/hints.ini: [hint:Auto-Orient] +msgid "" +"Auto-Orient\n" +"Did you know that you can rotate objects to an optimal orientation for " +"printing by a simple click?" +msgstr "" +"自动朝向\n" +"您知道吗?您只需单击鼠标,即可将对象旋转到适合的打印方向?" + +#: resources/data/hints.ini: [hint:Lay on Face] +msgid "" +"Lay on Face\n" +"Did you know that you can quickly orient a model so that one of its faces " +"sits on the print bed? Select the \"Place on face\" function or press the " +"F key." +msgstr "" +"放置在面部\n" +"您知道吗?您可以快速指定模型的底面,使其位于打印床上。选择“放置在面部”功能或" +"按F键。" + +#: resources/data/hints.ini: [hint:Object List] +msgid "" +"Object List\n" +"Did you know that you can view all objects/parts in a list and change " +"settings for each object/part?" +msgstr "" +"对象列表\n" +"您知道对象列表吗?您可以在其中的查看所有对象/部件,并更改每个对象/部件的设" +"置。" + +#: resources/data/hints.ini: [hint:Simplify Model] +msgid "" +"Simplify Model\n" +"Did you know that you can reduce the number of triangles in a mesh using the " +"Simplify mesh feature? Right-click the model and select Simplify model. Read " +"more in the documentation." +msgstr "" +"简化模型\n" +"您知道吗?您可以使用“简化模型”功能减少模型的三角形数。在模型上单击鼠标右键," +"然后选择“简化模型”。" + +#: resources/data/hints.ini: [hint:Slicing Parameter Table] +msgid "" +"Slicing Parameter Table\n" +"Did you know that you can view all objects/parts on a table and change " +"settings for each object/part?" +msgstr "" +"参数表格\n" +"您知道吗?您可以参数表格上的所有对象/部件,并更改每个对象/部件的设置。" + +#: resources/data/hints.ini: [hint:Split to Objects/Parts] +msgid "" +"Split to Objects/Parts\n" +"Did you know that you can split a big object into small ones for easy " +"colorizing or printing?" +msgstr "" +"分割成对象/零件\n" +"您知道吗?您可以把一个大对象分割成多个小对象/零件以便着色或打印吗?" + +#: resources/data/hints.ini: [hint:Subtract a Part] +msgid "" +"Subtract a Part\n" +"Did you know that you can subtract one mesh from another using the Negative " +"part modifier? That way you can, for example, create easily resizable holes " +"directly in Bambu Studio. Read more in the documentation." +msgstr "" +"减去部分几何体\n" +"您知道吗?您可以使用负零件从另一个几何体中减去另一个几何体。例如,这样可以直" +"接在Bambu Studio中创建可轻松调整大小的孔。" + +#: resources/data/hints.ini: [hint:STEP] +msgid "" +"STEP\n" +"Did you know that you can improve your print quality by slicing a STEP file " +"instead of an STL?\n" +"Bambu Studio supports slicing STEP files, providing smoother results than a " +"lower resolution STL. Give it a try!" +msgstr "" +"STEP文件\n" +"您知道吗?通过切片STEP文件而不是STL文件可以提高打印质量?\n" +"Bambu Studio支持切片STEP文件,提供比低分辨率STL更平滑的结果。试试看!" + +#: resources/data/hints.ini: [hint:Z seam location] +msgid "" +"Z seam location\n" +"Did 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 "" +"Z接缝位置\n" +"您知道吗?您可以自定义Z接缝的位置,甚至可以将其绘制在打印上,使其位于不太可见" +"的位置?这样可以改善模型的整体外观。试试看!" + +#: resources/data/hints.ini: [hint:Fine-tuning for flow rate] +msgid "" +"Fine-tuning for flow rate\n" +"Did 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 "" +"流量微调\n" +"你知道吗?您可以微调流量,以获得更好看的打印效果吗?根据材料的不同,可以通过" +"进行一些微调来提高打印模型的整体光洁度。" + +#: resources/data/hints.ini: [hint:Split your prints into plates] +msgid "" +"Split your prints into plates\n" +"Did 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 "" +"分盘打印\n" +"您知道吗?您可以把一个有很多零件的模型安排到多个独立的分盘,然后打印出来,这" +"将简化对所有零件的管理。" + +#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer +#: Height] +msgid "" +"Speed up your print with Adaptive Layer Height\n" +"Did you know that you can print a model even faster, by using the Adaptive " +"Layer Height option? Check it out!" +msgstr "" +"自适应层高度加速打印\n" +"您知道吗?您可以使用“自适应层高度”选项可以更快地打印模型。试试看!" + +#: resources/data/hints.ini: [hint:Support painting] +msgid "" +"Support painting\n" +"Did 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 "" +"绘制支撑\n" +"您知道吗?您可以手动绘制添加/屏蔽支撑的位置,此功能使仅将支撑材料放置在实际需" +"要的模型截面上变得容易。" + +#: resources/data/hints.ini: [hint:Different types of supports] +msgid "" +"Different types of supports\n" +"Did 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 "" +"支撑类型\n" +"您知道吗?有多种可选的支撑类型,树状支撑非常适合人物/动物模型,同时可以节耗材" +"并提高打印速度。试试看!" + +#: resources/data/hints.ini: [hint:Printing Silk Filament] +msgid "" +"Printing Silk Filament\n" +"Did 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 "" +"打印丝绸耗材\n" +"你知道丝绸耗材需要特别考虑才能成功打印吗?为了获得最佳效果,通常建议使用较高" +"的温度和较低的速度。" + +#: resources/data/hints.ini: [hint:Brim for better adhesion] +msgid "" +"Brim for better adhesion\n" +"Did you know that when printing models have a small contact interface with " +"the printing surface, it's recommended to use a brim?" +msgstr "" +"使用Brim\n" +"您知道吗?当模型与热床表面的接触面积较小时,建议使用brim以提高打印成功率。" + +#: resources/data/hints.ini: [hint:Set parameters for multiple objects] +msgid "" +"Set parameters for multiple objects\n" +"Did you know that you can set slicing parameters for all selected objects at " +"one time?" +msgstr "" +"为多个对象设置参数\n" +"您知道可以同时为所有选定对象设置切片参数吗?" + +#~ msgid "Show &Wireframe" +#~ msgstr "显示线框" + +#~ msgid "Show wireframes in 3D scene" +#~ msgstr "在3D场景中显示线框" + +#~ msgid "Erase painting" +#~ msgstr "擦除绘制" + +#~ msgid "Set pen size" +#~ msgstr "设置画笔大小" + +#~ msgid "Rotation:" +#~ msgstr "旋转:" + +#~ msgid "Height:" +#~ msgstr "高度:" + +#~ msgid "Initialize failed [%d]!" +#~ msgstr "初始化失败 [%d]!" + +#~ msgid "Management" +#~ msgstr "管理" + +#~ msgid "Choose save directory" +#~ msgstr "选择保存目录" + +#~ msgid "Open" +#~ msgstr "打开" + +#~ msgid "preparing, export 3mf failed!" +#~ msgstr "正在准备中,导出 3mf 失败!" + +#~ msgid "Send print job to" +#~ msgstr "发送打印任务至" + +#~ msgid "" +#~ "Prime tower is required by timeplase. Are you sure you want to disable " +#~ "both of them?" +#~ msgstr "延时摄影需要使用擦拭塔,是否同时禁用?" + +#~ msgid "" +#~ "Prime tower is required by timelapse. Do you want to enable both of them?" +#~ msgstr "延时摄影需要使用擦拭塔,是否同时启用?" + +#~ msgid "" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "%1%离不可打印区域太近,会造成打印时出现碰撞。" + +#~ msgid "" +#~ "\n" +#~ "%1% is too close to exclusion area, there will be collisions when " +#~ "printing." +#~ msgstr "" +#~ "\n" +#~ "%1%离不可打印区域太近,会造成打印时出现碰撞。" + +#~ msgid " is too close to others, there will be collisions when printing.\n" +#~ msgstr "离其它对象太近,会造成打印时出现碰撞。\n" + +#~ msgid "" +#~ " is too close to exclusion area, there will be collisions when printing.\n" +#~ msgstr "离不可打印区域太近,会造成打印时出现碰撞。\n" + +#~ msgid "Avoid crossing wall when travel" +#~ msgstr "空驶时避免跨越外墙" + +#~ msgid "Max travel detour distance" +#~ msgstr "最大绕行距离" + +#~ msgid "" +#~ "Maximum detour distance for avoiding crossing wall. Don't detour if the " +#~ "detour distance is large than this value" +#~ msgstr "" +#~ "避免跨越外墙时的最大绕行距离。如果绕行距离大于这个阈值,则不再绕行。" + +#~ msgid "" +#~ "Height of the clearance cylinder around extruder. Used as input of auto-" +#~ "arrange to avoid collision when print object by object" +#~ msgstr "挤出机的净空高度。作为自动摆放的输入信息,避免逐件打印时发生碰撞。" + +#~ msgid "" +#~ "Clearance radius around extruder. Used as input of auto-arrange to avoid " +#~ "collision when print object by object" +#~ msgstr "挤出机的净空半径。作为自动摆放的输入信息,避免逐件打印时发生碰撞。" + +#~ msgid "Error at line %1%:\n" +#~ msgstr "错误在行%1%:\n" + #~ msgid "Reduce Triangles" #~ msgstr "简化三角形" @@ -6958,9 +7524,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ msgid "Ctrl + Any arrow" #~ msgstr "Ctrl + 方向键" -#~ msgid "Alt + Mouse wheel" -#~ msgstr "Alt + 鼠标滚轮" - #~ msgid "Shift + Mouse wheel" #~ msgstr "Shift + 鼠标滚轮" @@ -7729,9 +8292,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ "as percentage" #~ msgstr "外墙的线宽。如果表示为百分数,则基数为喷嘴直径。" -#~ msgid "mm or %" -#~ msgstr "mm 或 %" - #~ msgid "" #~ "Line width of initial layer. It's relative to nozzle diameter if " #~ "expressed as percentage" @@ -7992,9 +8552,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ msgid "Cancel Calibration" #~ msgstr "取消校准" -#~ msgid "Guide" -#~ msgstr "引导" - #~ msgid "Overhang" #~ msgstr "悬垂" @@ -9181,9 +9738,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ msgid "Masked SLA file exported to %1%" #~ msgstr "已将 贴面过 SLA 文件导出到 %1%" -#~ msgid "Running post-processing scripts" -#~ msgstr "运行后处理脚本" - #~ msgid "G-code file exported to %1%" #~ msgstr "G 代码文件导出到 %1%" @@ -9347,12 +9901,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ msgid "Around object" #~ msgstr "模型周围" -#~ msgid "Copying of the temporary G-code to the output G-code failed" -#~ msgstr "将临时 G 代码复制到输出 G 代码失败" - -#~ msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" -#~ msgstr "计划上传到 `%1%`。请参阅窗口-> 打印主机上传队列" - #~ msgid "Firmware Type" #~ msgstr "固件类型" @@ -9966,15 +10514,6 @@ msgstr "支撑:正在生长层%d的树枝" #~ msgid "For new project all modifications will be reseted" #~ msgstr "对新项目,所有修改都被重置" -#~ msgid "The uploads are still ongoing" -#~ msgstr "上传任务依然在继续" - -#~ msgid "Stop them and continue anyway?" -#~ msgstr "停止它们并且继续?" - -#~ msgid "Ongoing uploads" -#~ msgstr "正在进行的上传" - #~ msgid "It's impossible to print multi-part object(s) with SLA technology." #~ msgstr "无法使用 SLA 技术打印多部分对象。" diff --git a/resources/data/hints.ini b/resources/data/hints.ini new file mode 100644 index 0000000000..12a3cf2df8 --- /dev/null +++ b/resources/data/hints.ini @@ -0,0 +1,160 @@ +# THIS DOCUMENT CONTAINS DATA FOR HINTS NOTIFICATIONS +# +# Each notification is divided by +# [hint:*name of notification*] +# +# Each notification MUST have text var in format: +# text = Headline of hint\nBody of hint. +# Headline is divided by new line (\n) from body. +# Headline is automaticaly printed as Bold. +# Body can contain bold marks: text to be bold (currently rendered as different color, not bold due to font limitations) +# Body can contain hypertext: hypertext text +# Hypertext must be max one per notification and must be closed by +# +# Notification can have documentation link +# +# If notification contains hypertext, it needs to be specified by hypertext_type var. +# each type needs to be supported with one or more additional vars. +# These types are possible: +# +# Settings highlight (like search feature) +# hypertext_type = settings +# hypertext_settings_opt = name_of_settings (hover over settings value and copy last line of hover text) +# hypertext_settings_type = 1 (1 - 5 according to settings tab - to be channged to name of tabs instead of numbers) +# hypertext_settings_category = Infill (name of panel - written on left in settings) +# +# Plater top toolbar highlight +# hypertext_type = plater +# hypertext_plater_item = nameofbutton (internal name of GLToolbar items) +# +# Plater gizmos (left) toolbar highlight +# hypertext_type = gizmo +# hypertext_gizmo_item = name (name of svg icon of gizmo in resources without .svg suffix) +# +# Open preferences (might add item to highlight) +# hypertext_type = preferences +# hypertext_preferences_page = name of the prefernces tab +# hypertext_preferences_item = show_collapse_button (name of variable saved in prusaslicer.ini connected to the setting in preferences) +# +# Open gallery (no aditional var) +# hypertext_type = gallery +# +#Open top menubar item +#hypertext_menubar_menu_name = (Name in english visible as menu name: File, ) +#hypertext_menubar_item_name = (Name of item in english, if there are three dots at the end of name, put name without three dots) +# +# +# Each notification can have disabled and enabled modes and techs - divided by ; and space +# enabled_tags = ... +# disabled_tags = ... +# supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX; +# and all filament types: PLA; PET; ABS; ASA; FLEX; HIPS; EDGE; NGEN; NYLON; PVA; PC; PP; PEI; PEEK; PEKK; POM; PSU; PVDF; SCAFF; +# Tags are case sensitive. +# FFF is affirmative for both one or more extruder printers. +# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) +# Algorithm shows hint only if not in all disabled tags. +# if there are both disabled and preferred, only preferred that are not in disabled are valid. +# +# +# Notifications shows in random order, already shown notifications are saved at cache/hints.cereal (as binary - human non-readable) +# You can affect random ordering by seting weigh +# weight = 5 +# Weight must be larger or equal to 1. Default weight is 1. +# Weight defines probability as weight : sum_of_all_weights. + +[hint:3D Scene Operations] +text = 3D Scene Operations\nDid you know how to control view and object/part selection with mouse and touchpanel in the 3D scene? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/3d-scene-operations + +[hint:Cut Tool] +text = Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/cut-tool + +[hint:Fix Model] +text = Fix Model\nDid you know that you can fix a corrupted 3D model to avoid a lot of slicing problems? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/fix-model + +[hint:Timelapse] +text = Timelapse\nDid you know that you can generate a timelapse video during each print? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/Timelapse + +[hint:Auto-Arrange] +text = Auto-Arrange\nDid you know that you can auto-arrange all objects in your project? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/auto-arranging + +[hint:Auto-Orient] +text = Auto-Orient\nDid you know that you can rotate objects to an optimal orientation for printing by a simple click? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/auto-orientation + +[hint:Lay on Face] +text = Lay on Face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select the \"Place on face\" function or press the F key. +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/lay-on-face + +[hint:Object List] +text = Object List\nDid you know that you can view all objects/parts in a list and change settings for each object/part? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/object-list + +#[hint:Search Functionality] +#text = Search Functionality\nDid you know that you use the Search tool to quickly find a specific Bambu Studio setting? Or use the familiar shortcut Ctrl+F. + +[hint:Simplify Model] +text = Simplify Model\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in the documentation. +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/simplify-model + +[hint:Slicing Parameter Table] +text = Slicing Parameter Table\nDid you know that you can view all objects/parts on a table and change settings for each object/part? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/parameter-table + +[hint:Split to Objects/Parts] +text = Split to Objects/Parts\nDid you know that you can split a big object into small ones for easy colorizing or printing? +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/split-to-objects-parts + +[hint:Subtract a Part] +text = Subtract a Part\nDid you know that you can subtract one mesh from another using the Negative part modifier? That way you can, for example, create easily resizable holes directly in Bambu Studio. Read more in the documentation. +documentation_link = https://wiki.bambulab.com/en/software/bambu-studio/subtract-a-part + +[hint:STEP] +text = STEP\nDid you know that you can improve your print quality by slicing a STEP file instead of an STL?\nBambu Studio supports slicing STEP files, providing smoother results than a lower resolution STL. Give it a try! +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/step + +[hint:Z seam location] +text = 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! +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/Seam + +[hint:Fine-tuning for flow rate] +text = 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. +documentation_link= https://wiki.bambulab.com/en/x1/manual/manual-flow-rate-tuning + +[hint:Split your prints into plates] +text = 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. +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/plates_management + +[hint:Speed up your print with Adaptive Layer Height] +text = 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! +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/adaptive-layer-height + +[hint:Support painting] +text = 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. +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/support-painting + +[hint:Different types of supports] +text = 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! +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/support + +[hint:Printing Silk Filament] +text = 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. +documentation_link= https://wiki.bambulab.com/en/x1/manual/printing-with-silk-filaments + +[hint:Brim for better adhesion] +text = 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? +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/auto-brim + +[hint:Set parameters for multiple objects] +text = Set parameters for multiple objects\nDid you know that you can set slicing parameters for all selected objects at one time? +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/set-parameters-for-selected-objects + +#[hint:] +#text = +#hypertext = +#follow_text = + diff --git a/resources/i18n/de/BambuStudio.mo b/resources/i18n/de/BambuStudio.mo index 36a1918794..283f992714 100644 Binary files a/resources/i18n/de/BambuStudio.mo and b/resources/i18n/de/BambuStudio.mo differ diff --git a/resources/i18n/en/BambuStudio.mo b/resources/i18n/en/BambuStudio.mo index 42e37272cb..8e33975484 100644 Binary files a/resources/i18n/en/BambuStudio.mo and b/resources/i18n/en/BambuStudio.mo differ diff --git a/resources/i18n/es/BambuStudio.mo b/resources/i18n/es/BambuStudio.mo index abe8bad189..f4e0db4225 100644 Binary files a/resources/i18n/es/BambuStudio.mo and b/resources/i18n/es/BambuStudio.mo differ diff --git a/resources/i18n/fr/BambuStudio.mo b/resources/i18n/fr/BambuStudio.mo index fb082b37a0..6d3dd17f35 100644 Binary files a/resources/i18n/fr/BambuStudio.mo and b/resources/i18n/fr/BambuStudio.mo differ diff --git a/resources/i18n/hu/BambuStudio.mo b/resources/i18n/hu/BambuStudio.mo index fe5f19c4fc..db9ac3b516 100644 Binary files a/resources/i18n/hu/BambuStudio.mo and b/resources/i18n/hu/BambuStudio.mo differ diff --git a/resources/i18n/nl/BambuStudio.mo b/resources/i18n/nl/BambuStudio.mo index c33614493c..3903061eaa 100644 Binary files a/resources/i18n/nl/BambuStudio.mo and b/resources/i18n/nl/BambuStudio.mo differ diff --git a/resources/i18n/sv/BambuStudio.mo b/resources/i18n/sv/BambuStudio.mo index 4af60293fd..bc53464c79 100644 Binary files a/resources/i18n/sv/BambuStudio.mo and b/resources/i18n/sv/BambuStudio.mo differ diff --git a/resources/i18n/zh_cn/BambuStudio.mo b/resources/i18n/zh_cn/BambuStudio.mo index 3b8cb27cf7..b015d98c9d 100644 Binary files a/resources/i18n/zh_cn/BambuStudio.mo and b/resources/i18n/zh_cn/BambuStudio.mo differ diff --git a/resources/images/ams_setting_hover.svg b/resources/images/ams_setting_hover.svg new file mode 100644 index 0000000000..25042562ee --- /dev/null +++ b/resources/images/ams_setting_hover.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/ams_setting_normal.svg b/resources/images/ams_setting_normal.svg new file mode 100644 index 0000000000..41836434ca --- /dev/null +++ b/resources/images/ams_setting_normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/ams_setting_press.svg b/resources/images/ams_setting_press.svg new file mode 100644 index 0000000000..9e1144b46e --- /dev/null +++ b/resources/images/ams_setting_press.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/link_wiki_img.svg b/resources/images/link_wiki_img.svg new file mode 100644 index 0000000000..ef913c5db9 --- /dev/null +++ b/resources/images/link_wiki_img.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/monitor_extruder_load.png b/resources/images/monitor_extruder_load.png new file mode 100644 index 0000000000..27f4f3c610 Binary files /dev/null and b/resources/images/monitor_extruder_load.png differ diff --git a/resources/images/monitor_extruder_load.svg b/resources/images/monitor_extruder_load.svg new file mode 100644 index 0000000000..789ddc36c1 --- /dev/null +++ b/resources/images/monitor_extruder_load.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/printer_file.svg b/resources/images/printer_file.svg new file mode 100644 index 0000000000..508e092320 --- /dev/null +++ b/resources/images/printer_file.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/resources/images/printer_host_browser.svg b/resources/images/printer_host_browser.svg new file mode 100644 index 0000000000..f212e0123d --- /dev/null +++ b/resources/images/printer_host_browser.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/printer_host_test.svg b/resources/images/printer_host_test.svg new file mode 100644 index 0000000000..9f75c015a9 --- /dev/null +++ b/resources/images/printer_host_test.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json index 1e9b23d53a..6bd6e9113e 100644 --- a/resources/profiles/BBL.json +++ b/resources/profiles/BBL.json @@ -1,7 +1,7 @@ { "name": "Bambulab", "url": "http://www.bambulab.com/Parameters/vendor/BBL.json", - "version": "01.02.01.02", + "version": "01.03.00.02", "force_update": "0", "description": "the initial version of BBL configurations", "machine_model_list": [ @@ -204,7 +204,7 @@ { "name": "Generic PLA @0.2 nozzle", "sub_path": "filament/Generic PLA @0.2 nozzle.json" - }, + }, { "name": "Generic PLA-CF", "sub_path": "filament/Generic PLA-CF.json" diff --git a/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json b/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json index c44ef83f20..4ebec9633d 100644 --- a/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json +++ b/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json @@ -9,6 +9,7 @@ "256x256", "0x256" ], + "auxiliary_fan": "1", "bed_exclude_area": [ "0x0", "28x0", @@ -76,8 +77,8 @@ "200" ], "machine_max_speed_z": [ - "12", - "12" + "20", + "20" ], "machine_max_jerk_e": [ "2.5", @@ -110,9 +111,10 @@ "0.08" ], "printable_height": "250", - "extruder_clearance_radius": "65", + "extruder_clearance_radius": "32", "extruder_clearance_height_to_rod": "36", "extruder_clearance_height_to_lid": "140", + "nozzle_volume": "118", "nozzle_diameter": [ "0.4" ], @@ -149,6 +151,7 @@ "deretraction_speed": [ "30" ], + "nozzle_type": "hardened_steel", "silent_mode": "0", "single_extruder_multi_material": "1", "change_filament_gcode": "M620 S[next_extruder]A\nM204 S9000\n{if toolchange_count > 1}\nG17\nG2 Z{max_layer_z + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{endif}\nG1 Z{max_layer_z + 3.0} F1200\n\nG1 X70 F12000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\n{if old_filament_temp > 0 && next_extruder < 255}M109 S[old_filament_temp]{endif}\nG1 X90 F3000\nG1 Y250 F4000\nG1 X100 F5000\nG1 X120 F12000\n\nG1 X20 Y50 F12000\nG1 Y-3\n\nT[next_extruder]\n\n; always use highest temperature to flush\nM400\nM109 S[nozzle_temperature_range_high]\n\n{if next_extruder < 255}\nG1 X54 F12000\nG1 Y265\n\nM400\n\nG92 E0\n{if flush_length_1 > 1}\n; FLUSH_START\n{if flush_length_1 > 23.7}\nG1 E23.7 F{old_filament_e_feedrate} ; do not need pulsatile flushing for start part\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{old_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\n{else}\nG1 E{flush_length_1} F{old_filament_e_feedrate}\n{endif}\n; FLUSH_END\nG1 E-[old_retract_length_toolchange] F1800\nG1 E[old_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_2 > 1}\n; FLUSH_START\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_3 > 1}\n; FLUSH_START\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_4 > 1}\n; FLUSH_START\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\n; FLUSH_END\n{endif}\n\nM400\nM109 S[new_filament_temp]\n\nM400\nM106 P1 S255\nM400 S3\nG1 X80 F15000\nG1 X60 F15000\nG1 X80 F15000\nG1 X60 F15000; shake to put down garbage\n\nG92 E0\nG1 E-[new_retract_length_toolchange] F1800\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\nG1 Z[z_after_toolchange] F3000\n{if layer_z <= (initial_layer_print_height + 0.001)}\nM204 S[initial_layer_acceleration]\n{else}\nM204 S[default_acceleration]\n{endif}\n{else}\nG1 X[x_after_toolchange] Y[y_after_toolchange] Z[z_after_toolchange] F12000\n{endif}\nM621 S[next_extruder]A", diff --git a/resources/profiles/BBL/process/fdm_process_bbl_common.json b/resources/profiles/BBL/process/fdm_process_bbl_common.json index e8574725de..a14a39b4a4 100644 --- a/resources/profiles/BBL/process/fdm_process_bbl_common.json +++ b/resources/profiles/BBL/process/fdm_process_bbl_common.json @@ -51,6 +51,7 @@ "overhang_2_4_speed": "50", "overhang_3_4_speed": "30", "overhang_4_4_speed": "10", + "only_one_wall_top": "1", "inner_wall_line_width": "0.45", "inner_wall_speed": "150", "wall_loops": "2", @@ -90,6 +91,7 @@ "detect_thin_wall": "0", "top_surface_pattern": "monotonicline", "top_surface_line_width": "0.42", + "top_surface_acceleration": "2000", "top_surface_speed": "200", "top_shell_layers": "3", "top_shell_thickness": "0.8", diff --git a/resources/profiles/Creality.json b/resources/profiles/Creality.json new file mode 100644 index 0000000000..6ea8e4f1ce --- /dev/null +++ b/resources/profiles/Creality.json @@ -0,0 +1,82 @@ +{ + "name": "Creality", + "version": "01.02.01.03", + "force_update": "0", + "description": "Creality configurations", + "machine_model_list": [ + { + "name": "Creality Ender-3 V2", + "sub_path": "machine/Creality Ender-3 V2.json" + } + ], + "process_list": [ + { + "name": "fdm_process_common", + "sub_path": "process/fdm_process_common.json" + }, + { + "name": "fdm_process_creality_common", + "sub_path": "process/fdm_process_creality_common.json" + }, + { + "name": "0.12mm Fine @Creality", + "sub_path": "process/0.12mm Fine @Creality.json" + }, + { + "name": "0.15mm Optimal @Creality", + "sub_path": "process/0.15mm Optimal @Creality.json" + }, + { + "name": "0.20mm Standard @Creality", + "sub_path": "process/0.20mm Standard @Creality.json" + }, + { + "name": "0.24mm Draft @Creality", + "sub_path": "process/0.24mm Draft @Creality.json" + } + ], + "filament_list": [ + { + "name": "fdm_filament_common", + "sub_path": "filament/fdm_filament_common.json" + }, + { + "name": "fdm_filament_pla", + "sub_path": "filament/fdm_filament_pla.json" + }, + { + "name": "fdm_filament_pet", + "sub_path": "filament/fdm_filament_pet.json" + }, + { + "name": "fdm_filament_abs", + "sub_path": "filament/fdm_filament_abs.json" + }, + { + "name": "Generic PLA @Creality", + "sub_path": "filament/Generic PLA @Creality.json" + }, + { + "name": "Generic PETG @Creality", + "sub_path": "filament/Generic PETG @Creality.json" + }, + { + "name": "Generic ABS @Creality", + "sub_path": "filament/Generic ABS @Creality.json" + } + ], + "machine_list": [ + { + "name": "fdm_machine_common", + "sub_path": "machine/fdm_machine_common.json" + }, + { + "name": "fdm_creality_common", + "sub_path": "machine/fdm_creality_common.json" + }, + { + "name": "Creality Ender-3 V2 0.4 nozzle", + "sub_path": "machine/Creality Ender-3 V2 0.4 nozzle.json" + } + ] +} diff --git a/resources/profiles/Creality/ender3v2.svg b/resources/profiles/Creality/ender3v2.svg new file mode 100644 index 0000000000..7f793c7c01 --- /dev/null +++ b/resources/profiles/Creality/ender3v2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/profiles/Creality/filament/Generic ABS @Creality.json b/resources/profiles/Creality/filament/Generic ABS @Creality.json new file mode 100644 index 0000000000..6848fa4ca3 --- /dev/null +++ b/resources/profiles/Creality/filament/Generic ABS @Creality.json @@ -0,0 +1,18 @@ +{ + "type": "filament", + "filament_id": "GFB99", + "setting_id": "GFSB99_CREALITY_00", + "name": "Generic ABS @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_abs", + "filament_flow_ratio": [ + "0.926" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "compatible_printers": [ + "Creality Ender-3 V2 0.4 nozzle" + ] +} diff --git a/resources/profiles/Creality/filament/Generic PETG @Creality.json b/resources/profiles/Creality/filament/Generic PETG @Creality.json new file mode 100644 index 0000000000..d35d2ab024 --- /dev/null +++ b/resources/profiles/Creality/filament/Generic PETG @Creality.json @@ -0,0 +1,48 @@ +{ + "type": "filament", + "filament_id": "GFG99", + "setting_id": "GFSG99_CREALITY_00", + "name": "Generic PETG @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pet", + "reduce_fan_stop_start_freq": [ + "1" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "fan_cooling_layer_time": [ + "30" + ], + "overhang_fan_speed": [ + "90" + ], + "overhang_fan_threshold": [ + "25%" + ], + "fan_max_speed": [ + "90" + ], + "fan_min_speed": [ + "40" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "8" + ], + "filament_flow_ratio": [ + "0.95" + ], + "filament_max_volumetric_speed": [ + "10" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ], + "compatible_printers": [ + "Creality Ender-3 V2 0.4 nozzle" + ] +} diff --git a/resources/profiles/Creality/filament/Generic PLA @Creality.json b/resources/profiles/Creality/filament/Generic PLA @Creality.json new file mode 100644 index 0000000000..1038c68a4b --- /dev/null +++ b/resources/profiles/Creality/filament/Generic PLA @Creality.json @@ -0,0 +1,21 @@ +{ + "type": "filament", + "filament_id": "GFL99", + "setting_id": "GFSL99_CREALITY_00", + "name": "Generic PLA @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pla", + "filament_flow_ratio": [ + "0.98" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "slow_down_layer_time": [ + "8" + ], + "compatible_printers": [ + "Creality Ender-3 V2 0.4 nozzle" + ] +} diff --git a/resources/profiles/Creality/filament/fdm_filament_abs.json b/resources/profiles/Creality/filament/fdm_filament_abs.json new file mode 100644 index 0000000000..67ce7d013c --- /dev/null +++ b/resources/profiles/Creality/filament/fdm_filament_abs.json @@ -0,0 +1,88 @@ +{ + "type": "filament", + "name": "fdm_filament_abs", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "105" + ], + "eng_plate_temp" : [ + "105" + ], + "hot_plate_temp" : [ + "105" + ], + "textured_plate_temp" : [ + "105" + ], + "cool_plate_temp_initial_layer" : [ + "105" + ], + "eng_plate_temp_initial_layer" : [ + "105" + ], + "hot_plate_temp_initial_layer" : [ + "105" + ], + "textured_plate_temp_initial_layer" : [ + "105" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "30" + ], + "filament_max_volumetric_speed": [ + "28.6" + ], + "filament_type": [ + "ABS" + ], + "filament_density": [ + "1.04" + ], + "filament_cost": [ + "20" + ], + "nozzle_temperature_initial_layer": [ + "260" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "fan_max_speed": [ + "80" + ], + "fan_min_speed": [ + "10" + ], + "overhang_fan_threshold": [ + "25%" + ], + "overhang_fan_speed": [ + "80" + ], + "nozzle_temperature": [ + "260" + ], + "temperature_vitrification": [ + "110" + ], + "nozzle_temperature_range_low": [ + "240" + ], + "nozzle_temperature_range_high": [ + "270" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "3" + ] +} diff --git a/resources/profiles/Creality/filament/fdm_filament_common.json b/resources/profiles/Creality/filament/fdm_filament_common.json new file mode 100644 index 0000000000..22c32f8ca6 --- /dev/null +++ b/resources/profiles/Creality/filament/fdm_filament_common.json @@ -0,0 +1,141 @@ +{ + "type": "filament", + "name": "fdm_filament_common", + "from": "system", + "instantiation": "false", + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "60" + ], + "hot_plate_temp" : [ + "60" + ], + "textured_plate_temp" : [ + "60" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "60" + ], + "hot_plate_temp_initial_layer" : [ + "60" + ], + "textured_plate_temp_initial_layer" : [ + "60" + ], + "overhang_fan_threshold": [ + "95%" + ], + "overhang_fan_speed": [ + "100" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_flow_ratio": [ + "1" + ], + "reduce_fan_stop_start_freq": [ + "0" + ], + "fan_cooling_layer_time": [ + "60" + ], + "filament_cost": [ + "0" + ], + "filament_density": [ + "0" + ], + "filament_deretraction_speed": [ + "nil" + ], + "filament_diameter": [ + "1.75" + ], + "filament_max_volumetric_speed": [ + "0" + ], + "filament_minimal_purge_on_wipe_tower": [ + "15" + ], + "filament_retraction_minimum_travel": [ + "nil" + ], + "filament_retract_before_wipe": [ + "nil" + ], + "filament_retract_when_changing_layer": [ + "nil" + ], + "filament_retraction_length": [ + "nil" + ], + "filament_z_hop": [ + "nil" + ], + "filament_retract_restart_extra": [ + "nil" + ], + "filament_retraction_speed": [ + "nil" + ], + "filament_settings_id": [ + "" + ], + "filament_soluble": [ + "0" + ], + "filament_type": [ + "PLA" + ], + "filament_vendor": [ + "Generic" + ], + "filament_wipe": [ + "nil" + ], + "filament_wipe_distance": [ + "nil" + ], + "bed_type": [ + "Cool Plate" + ], + "nozzle_temperature_initial_layer": [ + "200" + ], + "full_fan_speed_layer": [ + "0" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "35" + ], + "slow_down_min_speed": [ + "10" + ], + "slow_down_layer_time": [ + "8" + ], + "filament_start_gcode": [ + "; Filament gcode\n" + ], + "nozzle_temperature": [ + "200" + ], + "temperature_vitrification": [ + "100" + ] +} diff --git a/resources/profiles/Creality/filament/fdm_filament_pet.json b/resources/profiles/Creality/filament/fdm_filament_pet.json new file mode 100644 index 0000000000..bb2323e9c1 --- /dev/null +++ b/resources/profiles/Creality/filament/fdm_filament_pet.json @@ -0,0 +1,82 @@ +{ + "type": "filament", + "name": "fdm_filament_pet", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "0" + ], + "hot_plate_temp" : [ + "80" + ], + "textured_plate_temp" : [ + "80" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "0" + ], + "hot_plate_temp_initial_layer" : [ + "80" + ], + "textured_plate_temp_initial_layer" : [ + "80" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "close_fan_the_first_x_layers": [ + "3" + ], + "fan_cooling_layer_time": [ + "20" + ], + "filament_max_volumetric_speed": [ + "25" + ], + "filament_type": [ + "PETG" + ], + "filament_density": [ + "1.27" + ], + "filament_cost": [ + "30" + ], + "nozzle_temperature_initial_layer": [ + "255" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "20" + ], + "overhang_fan_speed": [ + "100" + ], + "nozzle_temperature": [ + "255" + ], + "temperature_vitrification": [ + "80" + ], + "nozzle_temperature_range_low": [ + "220" + ], + "nozzle_temperature_range_high": [ + "260" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/Creality/filament/fdm_filament_pla.json b/resources/profiles/Creality/filament/fdm_filament_pla.json new file mode 100644 index 0000000000..6814cff165 --- /dev/null +++ b/resources/profiles/Creality/filament/fdm_filament_pla.json @@ -0,0 +1,94 @@ +{ + "type": "filament", + "name": "fdm_filament_pla", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_common", + "fan_cooling_layer_time": [ + "100" + ], + "filament_max_volumetric_speed": [ + "12" + ], + "filament_type": [ + "PLA" + ], + "filament_density": [ + "1.24" + ], + "filament_cost": [ + "20" + ], + "cool_plate_temp" : [ + "60" + ], + "eng_plate_temp" : [ + "60" + ], + "hot_plate_temp" : [ + "60" + ], + "textured_plate_temp" : [ + "60" + ], + "cool_plate_temp_initial_layer" : [ + "60" + ], + "eng_plate_temp_initial_layer" : [ + "60" + ], + "hot_plate_temp_initial_layer" : [ + "60" + ], + "textured_plate_temp_initial_layer" : [ + "60" + ], + "nozzle_temperature_initial_layer": [ + "220" + ], + "reduce_fan_stop_start_freq": [ + "1" + ], + "slow_down_for_layer_cooling": [ + "1" + ], + "fan_max_speed": [ + "100" + ], + "fan_min_speed": [ + "100" + ], + "overhang_fan_speed": [ + "100" + ], + "overhang_fan_threshold": [ + "50%" + ], + "close_fan_the_first_x_layers": [ + "1" + ], + "nozzle_temperature": [ + "220" + ], + "temperature_vitrification": [ + "60" + ], + "nozzle_temperature_range_low": [ + "190" + ], + "nozzle_temperature_range_high": [ + "230" + ], + "slow_down_min_speed": [ + "20" + ], + "slow_down_layer_time": [ + "4" + ], + "additional_cooling_fan_speed": [ + "70" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/Creality/hotend.stl b/resources/profiles/Creality/hotend.stl new file mode 100644 index 0000000000..4138160ec0 Binary files /dev/null and b/resources/profiles/Creality/hotend.stl differ diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V2 0.4 nozzle.json b/resources/profiles/Creality/machine/Creality Ender-3 V2 0.4 nozzle.json new file mode 100644 index 0000000000..5575d6c83e --- /dev/null +++ b/resources/profiles/Creality/machine/Creality Ender-3 V2 0.4 nozzle.json @@ -0,0 +1,25 @@ +{ + "type": "machine", + "setting_id": "GM_CREALITY_001", + "name": "Creality Ender-3 V2 0.4 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_creality_common", + "printer_model": "Creality Ender-3 V2", + "default_filament_profile": [ + "Generic PLA @Creality" + ], + "default_print_profile": "0.20mm Standard @Creality", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "220x0", + "220x220", + "0x220" + ], + "printable_height": "250", + "nozzle_type": "undefine", + "auxiliary_fan": "0" +} diff --git a/resources/profiles/Creality/machine/Creality Ender-3 V2.json b/resources/profiles/Creality/machine/Creality Ender-3 V2.json new file mode 100644 index 0000000000..90449b5951 --- /dev/null +++ b/resources/profiles/Creality/machine/Creality Ender-3 V2.json @@ -0,0 +1,12 @@ +{ + "type": "machine_model", + "name": "Creality Ender-3 V2", + "model_id": "Creality_Ender_3_V2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "ENDER", + "bed_model": "", + "bed_texture": "ender3v2.svg", + "hotend_model": "hotend.stl", + "default_materials": "Generic PLA @CREALITY;Generic PETG @CREALITY;Generic ABS @CREALITY;" +} diff --git a/resources/profiles/Creality/machine/fdm_creality_common.json b/resources/profiles/Creality/machine/fdm_creality_common.json new file mode 100644 index 0000000000..d2f871b686 --- /dev/null +++ b/resources/profiles/Creality/machine/fdm_creality_common.json @@ -0,0 +1,139 @@ +{ + "type": "machine", + "name": "fdm_creality_common", + "from": "system", + "instantiation": "false", + "inherits": "fdm_machine_common", + "gcode_flavor": "marlin", + "machine_max_acceleration_e": [ + "5000", + "5000" + ], + "machine_max_acceleration_extruding": [ + "500", + "500" + ], + "machine_max_acceleration_retracting": [ + "1000", + "1000" + ], + "machine_max_acceleration_travel": [ + "500", + "500" + ], + "machine_max_acceleration_x": [ + "500", + "500" + ], + "machine_max_acceleration_y": [ + "500", + "500" + ], + "machine_max_acceleration_z": [ + "100", + "100" + ], + "machine_max_speed_e": [ + "60", + "60" + ], + "machine_max_speed_x": [ + "500", + "500" + ], + "machine_max_speed_y": [ + "500", + "500" + ], + "machine_max_speed_z": [ + "10", + "10" + ], + "machine_max_jerk_e": [ + "5", + "5" + ], + "machine_max_jerk_x": [ + "8", + "8" + ], + "machine_max_jerk_y": [ + "8", + "8" + ], + "machine_max_jerk_z": [ + "0.4", + "0.4" + ], + "machine_min_extruding_rate": [ + "0", + "0" + ], + "machine_min_travel_rate": [ + "0", + "0" + ], + "max_layer_height": [ + "0.32" + ], + "min_layer_height": [ + "0.08" + ], + "printable_height": "250", + "extruder_clearance_radius": "47", + "extruder_clearance_height_to_rod": "34", + "extruder_clearance_height_to_lid": "34", + "printer_settings_id": "", + "printer_technology": "FFF", + "printer_variant": "0.4", + "retraction_minimum_travel": [ + "2" + ], + "retract_before_wipe": [ + "70%" + ], + "retract_when_changing_layer": [ + "1" + ], + "retraction_length": [ + "5" + ], + "retract_length_toolchange": [ + "2" + ], + "z_hop": [ + "0" + ], + "retract_restart_extra": [ + "0" + ], + "retract_restart_extra_toolchange": [ + "0" + ], + "retraction_speed": [ + "60" + ], + "deretraction_speed": [ + "40" + ], + "silent_mode": "0", + "single_extruder_multi_material": "1", + "change_filament_gcode": "", + "machine_pause_gcode": "M25 ;pause print", + "wipe": [ + "1" + ], + "default_filament_profile": [ + "Generic PLA @Creality" + ], + "default_print_profile": "0.20mm Standard @Creality", + "bed_exclude_area": [ + "0x0" + ], + "machine_start_gcode": "G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S[bed_temperature_initial_layer] ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[nozzle_temperature_initial_layer] ; set final nozzle temp\nM190 S[bed_temperature_initial_layer] ; wait for bed temp to stabilize\nM109 S[nozzle_temperature_initial_layer] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0", + "machine_end_gcode": "{if max_layer_z < printable_height}G1 Z{min(max_layer_z+2, printable_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\n{if max_layer_z < printable_height-10}G1 Z{min(max_layer_z+70, printable_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < printable_height*0.6}G1 Z{printable_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors", + "layer_change_gcode": "", + "scan_first_layer": "0", + "nozzle_type": "undefine", + "auxiliary_fan": "0" +} diff --git a/resources/profiles/Creality/machine/fdm_machine_common.json b/resources/profiles/Creality/machine/fdm_machine_common.json new file mode 100644 index 0000000000..37891fb1a5 --- /dev/null +++ b/resources/profiles/Creality/machine/fdm_machine_common.json @@ -0,0 +1,116 @@ +{ + "type": "machine", + "name": "fdm_machine_common", + "from": "system", + "instantiation": "false", + "printer_technology": "FFF", + "deretraction_speed": [ + "40" + ], + "extruder_colour": [ + "#FCE94F" + ], + "extruder_offset": [ + "0x0" + ], + "gcode_flavor": "marlin", + "silent_mode": "0", + "machine_max_acceleration_e": [ + "5000" + ], + "machine_max_acceleration_extruding": [ + "10000" + ], + "machine_max_acceleration_retracting": [ + "1000" + ], + "machine_max_acceleration_x": [ + "10000" + ], + "machine_max_acceleration_y": [ + "10000" + ], + "machine_max_acceleration_z": [ + "100" + ], + "machine_max_speed_e": [ + "60" + ], + "machine_max_speed_x": [ + "500" + ], + "machine_max_speed_y": [ + "500" + ], + "machine_max_speed_z": [ + "10" + ], + "machine_max_jerk_e": [ + "5" + ], + "machine_max_jerk_x": [ + "8" + ], + "machine_max_jerk_y": [ + "8" + ], + "machine_max_jerk_z": [ + "0.4" + ], + "machine_min_extruding_rate": [ + "0" + ], + "machine_min_travel_rate": [ + "0" + ], + "max_layer_height": [ + "0.28" + ], + "min_layer_height": [ + "0.08" + ], + "printable_height": "250", + "extruder_clearance_radius": "65", + "extruder_clearance_height_to_rod": "36", + "extruder_clearance_height_to_lid": "140", + "nozzle_diameter": [ + "0.4" + ], + "printer_settings_id": "", + "printer_variant": "0.4", + "retraction_minimum_travel": [ + "2" + ], + "retract_before_wipe": [ + "70%" + ], + "retract_when_changing_layer": [ + "1" + ], + "retraction_length": [ + "5" + ], + "retract_length_toolchange": [ + "1" + ], + "z_hop": [ + "0" + ], + "retract_restart_extra": [ + "0" + ], + "retract_restart_extra_toolchange": [ + "0" + ], + "retraction_speed": [ + "60" + ], + "single_extruder_multi_material": "0", + "change_filament_gcode": "", + "wipe": [ + "1" + ], + "default_print_profile": "0.16mm Optimal @Bambu Lab X1 Carbon 0.4 nozzle", + "machine_start_gcode": "G0 Z20 F9000\nG92 E0; G1 E-10 F1200\nG28\nM970 Q1 A10 B10 C130 K0\nM970 Q1 A10 B131 C250 K1\nM974 Q1 S1 P0\nM970 Q0 A10 B10 C130 H20 K0\nM970 Q0 A10 B131 C250 K1\nM974 Q0 S1 P0\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nG29 ;Home\nG90;\nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nM109 S205;\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder \nG1 X110 Y110 Z2.0 F3000 ;Move Z Axis up", + "machine_end_gcode": "M400 ; wait for buffer to clear\nG92 E0 ; zero the extruder\nG1 E-4.0 F3600; retract \nG91\nG1 Z3;\nM104 S0 ; turn off hotend\nM140 S0 ; turn off bed\nM106 S0 ; turn off fan\nG90 \nG0 X110 Y200 F3600 \nprint_end" +} diff --git a/resources/profiles/Creality/process/0.12mm Fine @Creality.json b/resources/profiles/Creality/process/0.12mm Fine @Creality.json new file mode 100644 index 0000000000..101a18725f --- /dev/null +++ b/resources/profiles/Creality/process/0.12mm Fine @Creality.json @@ -0,0 +1,11 @@ +{ + "type": "process", + "setting_id": "GP_CREALITY_002", + "name": "0.12mm Fine @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_creality_common", + "layer_height": "0.12", + "bottom_shell_layers": "5", + "top_shell_layers": "6" +} diff --git a/resources/profiles/Creality/process/0.15mm Optimal @Creality.json b/resources/profiles/Creality/process/0.15mm Optimal @Creality.json new file mode 100644 index 0000000000..ded44bffcd --- /dev/null +++ b/resources/profiles/Creality/process/0.15mm Optimal @Creality.json @@ -0,0 +1,11 @@ +{ + "type": "process", + "setting_id": "GP_CREALITY_003", + "name": "0.15mm Optimal @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_creality_common", + "bottom_shell_layers": "4", + "top_shell_layers": "5", + "layer_height": "0.15" +} diff --git a/resources/profiles/Creality/process/0.20mm Standard @Creality.json b/resources/profiles/Creality/process/0.20mm Standard @Creality.json new file mode 100644 index 0000000000..6628ff2020 --- /dev/null +++ b/resources/profiles/Creality/process/0.20mm Standard @Creality.json @@ -0,0 +1,11 @@ +{ + "type": "process", + "setting_id": "GP_CREALITY_004", + "name": "0.20mm Standard @Creality", + "from": "system", + "inherits": "fdm_process_creality_common", + "instantiation": "true", + "layer_height": "0.2", + "bottom_shell_layers": "3", + "top_shell_layers": "4" +} diff --git a/resources/profiles/Creality/process/0.24mm Draft @Creality.json b/resources/profiles/Creality/process/0.24mm Draft @Creality.json new file mode 100644 index 0000000000..adbc776fb1 --- /dev/null +++ b/resources/profiles/Creality/process/0.24mm Draft @Creality.json @@ -0,0 +1,12 @@ +{ + "type": "process", + "setting_id": "GP_CREALITY_005", + "name": "0.24mm Draft @Creality", + "from": "system", + "instantiation": "true", + "inherits": "fdm_process_creality_common", + "layer_height": "0.24", + "top_surface_line_width": "0.45", + "bottom_shell_layers": "3", + "top_shell_layers": "4" +} diff --git a/resources/profiles/Creality/process/fdm_process_common.json b/resources/profiles/Creality/process/fdm_process_common.json new file mode 100644 index 0000000000..2f46423f61 --- /dev/null +++ b/resources/profiles/Creality/process/fdm_process_common.json @@ -0,0 +1,71 @@ +{ + "type": "process", + "name": "fdm_process_common", + "from": "system", + "instantiation": "false", + "adaptive_layer_height": "0", + "reduce_crossing_wall": "0", + "bridge_flow": "0.95", + "bridge_speed": "25", + "brim_width": "5", + "compatible_printers": [], + "print_sequence": "by layer", + "default_acceleration": "10000", + "bridge_no_support": "0", + "elefant_foot_compensation": "0.1", + "outer_wall_line_width": "0.4", + "outer_wall_speed": "120", + "line_width": "0.45", + "infill_direction": "45", + "sparse_infill_density": "15%", + "sparse_infill_pattern": "grid", + "initial_layer_line_width": "0.42", + "initial_layer_print_height": "0.2", + "initial_layer_speed": "20", + "gap_infill_speed": "30", + "infill_combination": "0", + "sparse_infill_line_width": "0.45", + "infill_wall_overlap": "25%", + "sparse_infill_speed": "50", + "interface_shells": "0", + "detect_overhang_wall": "0", + "reduce_infill_retraction": "0", + "filename_format": "{input_filename_base}.gcode", + "wall_loops": "2", + "inner_wall_line_width": "0.45", + "inner_wall_speed": "40", + "print_settings_id": "", + "raft_layers": "0", + "seam_position": "nearest", + "skirt_distance": "2", + "skirt_height": "2", + "minimum_sparse_infill_area": "0", + "internal_solid_infill_line_width": "0.45", + "internal_solid_infill_speed": "40", + "spiral_mode": "0", + "standby_temperature_delta": "-5", + "enable_support": "0", + "support_filament": "0", + "support_line_width": "0.42", + "support_interface_filament": "0", + "support_on_build_plate_only": "0", + "support_top_z_distance": "0.15", + "support_interface_loop_pattern": "0", + "support_interface_top_layers": "2", + "support_interface_spacing": "0", + "support_interface_speed": "80", + "support_base_pattern": "rectilinear", + "support_base_pattern_spacing": "2", + "support_speed": "40", + "support_threshold_angle": "40", + "support_object_xy_distance": "0.5", + "tree_support_with_infill": "1", + "detect_thin_wall": "0", + "top_surface_line_width": "0.4", + "top_surface_speed": "30", + "travel_speed": "400", + "enable_prime_tower": "0", + "prime_tower_width": "60", + "xy_hole_compensation": "0", + "xy_contour_compensation": "0" +} diff --git a/resources/profiles/Creality/process/fdm_process_creality_common.json b/resources/profiles/Creality/process/fdm_process_creality_common.json new file mode 100644 index 0000000000..e7bc58b7ee --- /dev/null +++ b/resources/profiles/Creality/process/fdm_process_creality_common.json @@ -0,0 +1,105 @@ +{ + "type": "process", + "name": "fdm_process_creality_common", + "from": "system", + "instantiation": "false", + "inherits": "fdm_process_common", + "adaptive_layer_height": "0", + "reduce_crossing_wall": "0", + "max_travel_detour_distance": "0", + "bottom_surface_pattern": "monotonic", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_flow": "0.95", + "bridge_speed": "25", + "brim_width": "5", + "brim_object_gap": "0.1", + "compatible_printers_condition": "", + "print_sequence": "by layer", + "default_acceleration": "500", + "top_surface_acceleration": "500", + "bridge_no_support": "0", + "draft_shield": "disabled", + "elefant_foot_compensation": "0", + "enable_arc_fitting": "0", + "outer_wall_line_width": "0.4", + "wall_infill_order": "inner wall/outer wall/infill", + "line_width": "0.4", + "infill_direction": "45", + "sparse_infill_density": "15%", + "sparse_infill_pattern": "grid", + "initial_layer_acceleration": "500", + "initial_layer_line_width": "0.5", + "initial_layer_print_height": "0.2", + "infill_combination": "0", + "sparse_infill_line_width": "0.45", + "infill_wall_overlap": "23%", + "interface_shells": "0", + "ironing_flow": "15%", + "ironing_spacing": "0.1", + "ironing_speed": "15", + "ironing_type": "no ironing", + "layer_height": "0.2", + "reduce_infill_retraction": "1", + "filename_format": "{input_filename_base}_{filament_type[0]}_{print_time}.gcode", + "detect_overhang_wall": "1", + "overhang_1_4_speed": "0", + "overhang_2_4_speed": "20", + "overhang_3_4_speed": "15", + "overhang_4_4_speed": "10", + "inner_wall_line_width": "0.45", + "wall_loops": "3", + "print_settings_id": "", + "raft_layers": "0", + "seam_position": "aligned", + "skirt_distance": "2", + "skirt_height": "1", + "skirt_loops": "1", + "minimum_sparse_infill_area": "15", + "internal_solid_infill_line_width": "0.4", + "spiral_mode": "0", + "standby_temperature_delta": "-5", + "enable_support": "0", + "resolution": "0.012", + "support_type": "normal(auto)", + "support_on_build_plate_only": "0", + "support_top_z_distance": "0.2", + "support_filament": "0", + "support_line_width": "0.4", + "support_interface_loop_pattern": "0", + "support_interface_filament": "0", + "support_interface_top_layers": "2", + "support_interface_bottom_layers": "2", + "support_interface_spacing": "0.5", + "support_interface_speed": "80", + "support_base_pattern": "rectilinear", + "support_base_pattern_spacing": "2.5", + "support_speed": "150", + "support_threshold_angle": "30", + "support_object_xy_distance": "0.35", + "tree_support_branch_angle": "30", + "tree_support_wall_count": "0", + "tree_support_with_infill": "0", + "detect_thin_wall": "0", + "top_surface_pattern": "monotonic", + "top_surface_line_width": "0.4", + "top_shell_layers": "3", + "top_shell_thickness": "0.8", + "initial_layer_speed": "15", + "initial_layer_infill_speed": "20", + "outer_wall_speed": "25", + "inner_wall_speed": "40", + "internal_solid_infill_speed": "40", + "top_surface_speed": "30", + "gap_infill_speed": "30", + "sparse_infill_speed": "50", + "travel_speed": "150", + "enable_prime_tower": "0", + "wipe_tower_no_sparse_layers": "0", + "prime_tower_width": "35", + "xy_hole_compensation": "0", + "xy_contour_compensation": "0", + "compatible_printers": [ + "Creality Ender-3 V2 0.4 nozzle" + ] +} diff --git a/resources/profiles/Voron.json b/resources/profiles/Voron.json index 43c0b91751..87f39043ca 100644 --- a/resources/profiles/Voron.json +++ b/resources/profiles/Voron.json @@ -1,6 +1,6 @@ { "name": "Voron", - "version": "01.02.00.04", + "version": "01.02.01.03", "force_update": "0", "description": "Voron configurations", "machine_model_list": [ diff --git a/resources/profiles/Voron/filament/Voron Generic ABS.json b/resources/profiles/Voron/filament/Voron Generic ABS.json index a32e5f62af..fb65c0a59c 100644 --- a/resources/profiles/Voron/filament/Voron Generic ABS.json +++ b/resources/profiles/Voron/filament/Voron Generic ABS.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFB99", - "setting_id": "GFSB99", + "setting_id": "GFSB99_VORON_00", "name": "Voron Generic ABS", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic ASA.json b/resources/profiles/Voron/filament/Voron Generic ASA.json index d0875ab367..1283f53401 100644 --- a/resources/profiles/Voron/filament/Voron Generic ASA.json +++ b/resources/profiles/Voron/filament/Voron Generic ASA.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFB98", - "setting_id": "GFSB98", + "setting_id": "GFSB98_VORON_00", "name": "Voron Generic ASA", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PA-CF.json b/resources/profiles/Voron/filament/Voron Generic PA-CF.json index bae64a95a7..5353ad9adb 100644 --- a/resources/profiles/Voron/filament/Voron Generic PA-CF.json +++ b/resources/profiles/Voron/filament/Voron Generic PA-CF.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFN98", - "setting_id": "GFSN99", + "setting_id": "GFSN98_VORON_00", "name": "Voron Generic PA-CF", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PA.json b/resources/profiles/Voron/filament/Voron Generic PA.json index 51bddeab23..2796576ab7 100644 --- a/resources/profiles/Voron/filament/Voron Generic PA.json +++ b/resources/profiles/Voron/filament/Voron Generic PA.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFN99", - "setting_id": "GFSN98", + "setting_id": "GFSN99_VORON_00", "name": "Voron Generic PA", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PC.json b/resources/profiles/Voron/filament/Voron Generic PC.json index 798d28c2f3..23890ca49d 100644 --- a/resources/profiles/Voron/filament/Voron Generic PC.json +++ b/resources/profiles/Voron/filament/Voron Generic PC.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFC99", - "setting_id": "GFSC99", + "setting_id": "GFSC99_VORON_00", "name": "Voron Generic PC", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PETG.json b/resources/profiles/Voron/filament/Voron Generic PETG.json index 06f5492e38..145840d650 100644 --- a/resources/profiles/Voron/filament/Voron Generic PETG.json +++ b/resources/profiles/Voron/filament/Voron Generic PETG.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFG99", - "setting_id": "GFSG99", + "setting_id": "GFSG99_VORON_00", "name": "Voron Generic PETG", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PLA-CF.json b/resources/profiles/Voron/filament/Voron Generic PLA-CF.json index be4304d051..46ec3f9710 100644 --- a/resources/profiles/Voron/filament/Voron Generic PLA-CF.json +++ b/resources/profiles/Voron/filament/Voron Generic PLA-CF.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFL98", - "setting_id": "GFSL98", + "setting_id": "GFSL98_VORON_00", "name": "Voron Generic PLA-CF", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PLA.json b/resources/profiles/Voron/filament/Voron Generic PLA.json index 9655890e69..85839be793 100644 --- a/resources/profiles/Voron/filament/Voron Generic PLA.json +++ b/resources/profiles/Voron/filament/Voron Generic PLA.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFL99", - "setting_id": "GFSL99", + "setting_id": "GFSL99_VORON_00", "name": "Voron Generic PLA", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic PVA.json b/resources/profiles/Voron/filament/Voron Generic PVA.json index 5ed8fd05ca..b8a7d826a1 100644 --- a/resources/profiles/Voron/filament/Voron Generic PVA.json +++ b/resources/profiles/Voron/filament/Voron Generic PVA.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFS99", - "setting_id": "GFSS99", + "setting_id": "GFSS99_VORON_00", "name": "Voron Generic PVA", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/Voron Generic TPU.json b/resources/profiles/Voron/filament/Voron Generic TPU.json index 2d96fd0065..53ce822f2b 100644 --- a/resources/profiles/Voron/filament/Voron Generic TPU.json +++ b/resources/profiles/Voron/filament/Voron Generic TPU.json @@ -1,7 +1,7 @@ { "type": "filament", "filament_id": "GFU99", - "setting_id": "GFSR99", + "setting_id": "GFSU99_VORON_00", "name": "Voron Generic TPU", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/filament/fdm_filament_abs.json b/resources/profiles/Voron/filament/fdm_filament_abs.json index 1aaa31323a..67ce7d013c 100644 --- a/resources/profiles/Voron/filament/fdm_filament_abs.json +++ b/resources/profiles/Voron/filament/fdm_filament_abs.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "105" ], + "textured_plate_temp" : [ + "105" + ], "cool_plate_temp_initial_layer" : [ "105" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "105" ], + "textured_plate_temp_initial_layer" : [ + "105" + ], "slow_down_for_layer_cooling": [ "1" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_asa.json b/resources/profiles/Voron/filament/fdm_filament_asa.json index c8de040b7f..fb1a3e5309 100644 --- a/resources/profiles/Voron/filament/fdm_filament_asa.json +++ b/resources/profiles/Voron/filament/fdm_filament_asa.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "105" ], + "textured_plate_temp" : [ + "105" + ], "cool_plate_temp_initial_layer" : [ "105" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "105" ], + "textured_plate_temp_initial_layer" : [ + "105" + ], "slow_down_for_layer_cooling": [ "1" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_common.json b/resources/profiles/Voron/filament/fdm_filament_common.json index 5212d445a5..22c32f8ca6 100644 --- a/resources/profiles/Voron/filament/fdm_filament_common.json +++ b/resources/profiles/Voron/filament/fdm_filament_common.json @@ -12,6 +12,9 @@ "hot_plate_temp" : [ "60" ], + "textured_plate_temp" : [ + "60" + ], "cool_plate_temp_initial_layer" : [ "60" ], @@ -21,6 +24,9 @@ "hot_plate_temp_initial_layer" : [ "60" ], + "textured_plate_temp_initial_layer" : [ + "60" + ], "overhang_fan_threshold": [ "95%" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_pa.json b/resources/profiles/Voron/filament/fdm_filament_pa.json index 67c8ff050f..ce1b3d653c 100644 --- a/resources/profiles/Voron/filament/fdm_filament_pa.json +++ b/resources/profiles/Voron/filament/fdm_filament_pa.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "100" ], + "textured_plate_temp" : [ + "100" + ], "cool_plate_temp_initial_layer" : [ "0" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "100" ], + "textured_plate_temp_initial_layer" : [ + "100" + ], "slow_down_for_layer_cooling": [ "1" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_pc.json b/resources/profiles/Voron/filament/fdm_filament_pc.json index 9174d53530..dd46925585 100644 --- a/resources/profiles/Voron/filament/fdm_filament_pc.json +++ b/resources/profiles/Voron/filament/fdm_filament_pc.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "110" ], + "textured_plate_temp" : [ + "110" + ], "cool_plate_temp_initial_layer" : [ "0" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "110" ], + "textured_plate_temp_initial_layer" : [ + "110" + ], "slow_down_for_layer_cooling": [ "1" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_pet.json b/resources/profiles/Voron/filament/fdm_filament_pet.json index 2f98be665f..bb2323e9c1 100644 --- a/resources/profiles/Voron/filament/fdm_filament_pet.json +++ b/resources/profiles/Voron/filament/fdm_filament_pet.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "80" ], + "textured_plate_temp" : [ + "80" + ], "cool_plate_temp_initial_layer" : [ "60" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "80" ], + "textured_plate_temp_initial_layer" : [ + "80" + ], "slow_down_for_layer_cooling": [ "1" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_pla.json b/resources/profiles/Voron/filament/fdm_filament_pla.json index ded388585d..6814cff165 100644 --- a/resources/profiles/Voron/filament/fdm_filament_pla.json +++ b/resources/profiles/Voron/filament/fdm_filament_pla.json @@ -28,6 +28,9 @@ "hot_plate_temp" : [ "60" ], + "textured_plate_temp" : [ + "60" + ], "cool_plate_temp_initial_layer" : [ "60" ], @@ -37,6 +40,9 @@ "hot_plate_temp_initial_layer" : [ "60" ], + "textured_plate_temp_initial_layer" : [ + "60" + ], "nozzle_temperature_initial_layer": [ "220" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_pva.json b/resources/profiles/Voron/filament/fdm_filament_pva.json index 0d3e26c134..6b6576a444 100644 --- a/resources/profiles/Voron/filament/fdm_filament_pva.json +++ b/resources/profiles/Voron/filament/fdm_filament_pva.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "45" ], + "textured_plate_temp" : [ + "45" + ], "cool_plate_temp_initial_layer" : [ "35" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "45" ], + "textured_plate_temp_initial_layer" : [ + "45" + ], "fan_cooling_layer_time": [ "100" ], diff --git a/resources/profiles/Voron/filament/fdm_filament_tpu.json b/resources/profiles/Voron/filament/fdm_filament_tpu.json index d5cc57fbcc..d00b7dbcab 100644 --- a/resources/profiles/Voron/filament/fdm_filament_tpu.json +++ b/resources/profiles/Voron/filament/fdm_filament_tpu.json @@ -13,6 +13,9 @@ "hot_plate_temp" : [ "35" ], + "textured_plate_temp" : [ + "35" + ], "cool_plate_temp_initial_layer" : [ "30" ], @@ -22,6 +25,9 @@ "hot_plate_temp_initial_layer" : [ "35" ], + "textured_plate_temp_initial_layer" : [ + "35" + ], "fan_cooling_layer_time": [ "100" ], diff --git a/resources/profiles/Voron/hotend.stl b/resources/profiles/Voron/hotend.stl new file mode 100644 index 0000000000..4138160ec0 Binary files /dev/null and b/resources/profiles/Voron/hotend.stl differ diff --git a/resources/profiles/Voron/machine/Voron 0.1 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron 0.1 0.4 nozzle.json index 70fb3a7d09..ce9ce8bc02 100644 --- a/resources/profiles/Voron/machine/Voron 0.1 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron 0.1 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-001", + "setting_id": "GM_VORON_001", "name": "Voron 0.1 0.4 nozzle", "from": "system", "instantiation": "true", @@ -17,6 +17,6 @@ "0x120" ], "printable_height": "120", - "nozzle_type": "hardened_steel", + "nozzle_type": "undefine", "auxiliary_fan": "0" } diff --git a/resources/profiles/Voron/machine/Voron 0.1.json b/resources/profiles/Voron/machine/Voron 0.1.json index 345bc7b3e8..160ee037bb 100644 --- a/resources/profiles/Voron/machine/Voron 0.1.json +++ b/resources/profiles/Voron/machine/Voron 0.1.json @@ -7,6 +7,6 @@ "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_v0_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron 2.4 250 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron 2.4 250 0.4 nozzle.json index d8ffc905f8..e77d2fc571 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 250 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron 2.4 250 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-002", + "setting_id": "GM_VORON_002", "name": "Voron 2.4 250 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron 2.4 250.json b/resources/profiles/Voron/machine/Voron 2.4 250.json index bbbc9a1292..26e8f241d4 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 250.json +++ b/resources/profiles/Voron/machine/Voron 2.4 250.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron 2.4 250", - "model_id": "Voron2-250", + "model_id": "Voron2_250", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron 2.4 300 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron 2.4 300 0.4 nozzle.json index e3bf89ef42..9c7a4b2709 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 300 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron 2.4 300 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-003", + "setting_id": "GM_VORON_003", "name": "Voron 2.4 300 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron 2.4 300.json b/resources/profiles/Voron/machine/Voron 2.4 300.json index da803292c5..b32bd22a9f 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 300.json +++ b/resources/profiles/Voron/machine/Voron 2.4 300.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron 2.4 300", - "model_id": "Voron2-300", + "model_id": "Voron2_300", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron 2.4 350 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron 2.4 350 0.4 nozzle.json index c4e6bbe8f3..11c95366c9 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 350 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron 2.4 350 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-004", + "setting_id": "GM_VORON_004", "name": "Voron 2.4 350 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron 2.4 350.json b/resources/profiles/Voron/machine/Voron 2.4 350.json index 02bbcf85a5..0e165df533 100644 --- a/resources/profiles/Voron/machine/Voron 2.4 350.json +++ b/resources/profiles/Voron/machine/Voron 2.4 350.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron 2.4 350", - "model_id": "Voron2-350", + "model_id": "Voron2_350", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron Trident 250 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron Trident 250 0.4 nozzle.json index b890212809..eccee2b426 100644 --- a/resources/profiles/Voron/machine/Voron Trident 250 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron Trident 250 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-005", + "setting_id": "GM_VORON_005", "name": "Voron Trident 250 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron Trident 250.json b/resources/profiles/Voron/machine/Voron Trident 250.json index e0aee7b30e..424229cc9e 100644 --- a/resources/profiles/Voron/machine/Voron Trident 250.json +++ b/resources/profiles/Voron/machine/Voron Trident 250.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron Trident 250", - "model_id": "Voron2Trident-250", + "model_id": "Voron2_Trident_250", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron Trident 300 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron Trident 300 0.4 nozzle.json index 91bab9be1a..027d1e1def 100644 --- a/resources/profiles/Voron/machine/Voron Trident 300 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron Trident 300 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-006", + "setting_id": "GM_VORON_006", "name": "Voron Trident 300 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron Trident 300.json b/resources/profiles/Voron/machine/Voron Trident 300.json index b6120c70f9..68aac7a999 100644 --- a/resources/profiles/Voron/machine/Voron Trident 300.json +++ b/resources/profiles/Voron/machine/Voron Trident 300.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron Trident 300", - "model_id": "Voron2Trident-300", + "model_id": "Voron2_Trident_300", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/Voron Trident 350 0.4 nozzle.json b/resources/profiles/Voron/machine/Voron Trident 350 0.4 nozzle.json index 43ca844eba..955b3e70cf 100644 --- a/resources/profiles/Voron/machine/Voron Trident 350 0.4 nozzle.json +++ b/resources/profiles/Voron/machine/Voron Trident 350 0.4 nozzle.json @@ -1,6 +1,6 @@ { "type": "machine", - "setting_id": "VORON-007", + "setting_id": "GM_VORON_007", "name": "Voron Trident 350 0.4 nozzle", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/machine/Voron Trident 350.json b/resources/profiles/Voron/machine/Voron Trident 350.json index 4cc8c0097f..51b9e14c2d 100644 --- a/resources/profiles/Voron/machine/Voron Trident 350.json +++ b/resources/profiles/Voron/machine/Voron Trident 350.json @@ -1,12 +1,12 @@ { "type": "machine_model", "name": "Voron Trident 350", - "model_id": "Voron2Trident-350", + "model_id": "Voron2_Trident_350", "nozzle_diameter": "0.4", "machine_tech": "FFF", "family": "VoronDesign", "bed_model": "", "bed_texture": "voron_logo.png", - "hotend_model": "bbl-3dp-hotend.stl", + "hotend_model": "hotend.stl", "default_materials": "Voron Generic ABS;Voron Generic PLA;Voron Generic PLA-CF;Voron Generic PETG;Voron Generic TPU;Voron Generic ASA;Voron Generic PC;Voron Generic PVA;Voron Generic PA;Voron Generic PA-CF" } diff --git a/resources/profiles/Voron/machine/fdm_klipper_common.json b/resources/profiles/Voron/machine/fdm_klipper_common.json index a554b495f5..44ec1123c8 100644 --- a/resources/profiles/Voron/machine/fdm_klipper_common.json +++ b/resources/profiles/Voron/machine/fdm_klipper_common.json @@ -136,6 +136,6 @@ "layer_change_gcode": "", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n", "scan_first_layer": "0", - "nozzle_type": "hardened_steel", + "nozzle_type": "undefine", "auxiliary_fan": "0" -} \ No newline at end of file +} diff --git a/resources/profiles/Voron/process/0.08mm Extra Fine @Voron.json b/resources/profiles/Voron/process/0.08mm Extra Fine @Voron.json index aea877827c..a60c84cc13 100644 --- a/resources/profiles/Voron/process/0.08mm Extra Fine @Voron.json +++ b/resources/profiles/Voron/process/0.08mm Extra Fine @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP001", + "setting_id": "GP_VORON_001", "name": "0.08mm Extra Fine @Voron", "from": "system", "instantiation": "true", @@ -8,4 +8,4 @@ "layer_height": "0.08", "bottom_shell_layers": "7", "top_shell_layers": "9" -} \ No newline at end of file +} diff --git a/resources/profiles/Voron/process/0.12mm Fine @Voron.json b/resources/profiles/Voron/process/0.12mm Fine @Voron.json index 8e47c2fbd0..bc96205135 100644 --- a/resources/profiles/Voron/process/0.12mm Fine @Voron.json +++ b/resources/profiles/Voron/process/0.12mm Fine @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP002", + "setting_id": "GP_VORON_002", "name": "0.12mm Fine @Voron", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/process/0.15mm Optimal @Voron.json b/resources/profiles/Voron/process/0.15mm Optimal @Voron.json index 2b3fb0b0ac..5f576f1620 100644 --- a/resources/profiles/Voron/process/0.15mm Optimal @Voron.json +++ b/resources/profiles/Voron/process/0.15mm Optimal @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP003", + "setting_id": "GP_VORON_003", "name": "0.15mm Optimal @Voron", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/process/0.20mm Standard @Voron.json b/resources/profiles/Voron/process/0.20mm Standard @Voron.json index 4464fd09a2..fe74a231f9 100644 --- a/resources/profiles/Voron/process/0.20mm Standard @Voron.json +++ b/resources/profiles/Voron/process/0.20mm Standard @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP004", + "setting_id": "GP_VORON_004", "name": "0.20mm Standard @Voron", "from": "system", "inherits": "fdm_process_voron_common", diff --git a/resources/profiles/Voron/process/0.24mm Draft @Voron.json b/resources/profiles/Voron/process/0.24mm Draft @Voron.json index 23de2afb04..b2ce2ce049 100644 --- a/resources/profiles/Voron/process/0.24mm Draft @Voron.json +++ b/resources/profiles/Voron/process/0.24mm Draft @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP005", + "setting_id": "GP_VORON_005", "name": "0.24mm Draft @Voron", "from": "system", "instantiation": "true", diff --git a/resources/profiles/Voron/process/0.28mm Extra Draft @Voron.json b/resources/profiles/Voron/process/0.28mm Extra Draft @Voron.json index ff85fb5cc1..8e0153982c 100644 --- a/resources/profiles/Voron/process/0.28mm Extra Draft @Voron.json +++ b/resources/profiles/Voron/process/0.28mm Extra Draft @Voron.json @@ -1,6 +1,6 @@ { "type": "process", - "setting_id": "GP006", + "setting_id": "GP_VORON_006", "name": "0.28mm Extra Draft @Voron", "from": "system", "instantiation": "true", diff --git a/resources/shaders/gouraud_130.fs b/resources/shaders/gouraud_130.fs new file mode 100644 index 0000000000..9ab12676ec --- /dev/null +++ b/resources/shaders/gouraud_130.fs @@ -0,0 +1,124 @@ +#version 130 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +//BBS: add grey and orange +//const vec3 GREY = vec3(0.9, 0.9, 0.9); +const vec3 ORANGE = vec3(0.8, 0.4, 0.0); +const float EPSILON = 0.0001; + +struct PrintVolumeDetection +{ + // 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid + int type; + // type = 0 (rectangle): + // x = min.x, y = min.y, z = max.x, w = max.y + // type = 1 (circle): + // x = center.x, y = center.y, z = radius + vec4 xy_data; + // x = min z, y = max z + vec2 z_data; +}; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +//BBS: add wireframe logic +varying vec3 barycentric_coordinates; +float edgeFactor(float lineWidth) { + vec3 d = fwidth(barycentric_coordinates); + vec3 a3 = smoothstep(vec3(0.0), d * lineWidth, barycentric_coordinates); + return min(min(a3.x, a3.y), a3.z); +} + +vec3 wireframe(vec3 fill, vec3 stroke, float lineWidth) { + return mix(stroke, fill, edgeFactor(lineWidth)); +} + +vec3 getWireframeColor(vec3 fill) { + float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b; + return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988); +} + +uniform vec4 uniform_color; +uniform SlopeDetection slope; + +//BBS: add outline_color +uniform bool is_outline; +uniform bool show_wireframe; + +uniform bool offset_depth_buffer; + +#ifdef ENABLE_ENVIRONMENT_MAP + uniform sampler2D environment_tex; + uniform bool use_environment_tex; +#endif // ENABLE_ENVIRONMENT_MAP + +varying vec3 clipping_planes_dots; + +// x = diffuse, y = specular; +varying vec2 intensity; + +uniform PrintVolumeDetection print_volume; + +varying vec4 model_pos; +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + vec3 color = uniform_color.rgb; + float alpha = uniform_color.a; + + if (slope.actived && world_normal_z < slope.normal_z - EPSILON) { + //color = vec3(0.7, 0.7, 1.0); + color = ORANGE; + alpha = 1.0; + } + + // if the fragment is outside the print volume -> use darker color + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + // rectangle + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color; + } + else if (print_volume.type == 1) { + // circle + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color; + } + + //BBS: add outline_color + if (is_outline) + gl_FragColor = uniform_color; +#ifdef ENABLE_ENVIRONMENT_MAP + else if (use_environment_tex) + gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha); +#endif + else { + //gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha); + if (show_wireframe) { + vec3 wireframeColor = show_wireframe ? getWireframeColor(color) : color; + vec3 triangleColor = wireframe(color, wireframeColor, 1.0); + gl_FragColor = vec4(vec3(intensity.y) + triangleColor * intensity.x, alpha); + } + else { + gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha); + } + } + // In the support painting gizmo and the seam painting gizmo are painted triangles rendered over the already + // rendered object. To resolved z-fighting between previously rendered object and painted triangles, values + // inside the depth buffer are offset by small epsilon for painted triangles inside those gizmos. + gl_FragDepth = gl_FragCoord.z - (offset_depth_buffer ? EPSILON : 0.0); +} \ No newline at end of file diff --git a/resources/shaders/gouraud_130.vs b/resources/shaders/gouraud_130.vs new file mode 100644 index 0000000000..9d46b5c55c --- /dev/null +++ b/resources/shaders/gouraud_130.vs @@ -0,0 +1,79 @@ +#version 130 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform mat4 volume_world_matrix; +uniform SlopeDetection slope; + +// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane. +uniform vec2 z_range; +// Clipping plane - general orientation. Used by the SLA gizmo. +uniform vec4 clipping_plane; + +// x = diffuse, y = specular; +varying vec2 intensity; + +varying vec3 clipping_planes_dots; + +varying vec4 model_pos; +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +varying vec3 barycentric_coordinates; + +void main() +{ + // First transform the normal into camera space and normalize the result. + eye_normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + model_pos = gl_Vertex; + // Point in homogenous coordinates. + world_pos = volume_world_matrix * gl_Vertex; + + // z component of normal vector in world coordinate used for slope shading + world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0; + + gl_Position = ftransform(); + // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); + + //compute the Barycentric Coordinates + int vertexMod3 = gl_VertexID % 3; + barycentric_coordinates = vec3(float(vertexMod3 == 0), float(vertexMod3 == 1), float(vertexMod3 == 2)); +} diff --git a/resources/web/guide/21/21.js b/resources/web/guide/21/21.js index 5a37c4c32e..fbc32ced38 100644 --- a/resources/web/guide/21/21.js +++ b/resources/web/guide/21/21.js @@ -133,7 +133,7 @@ function HandleModelList( pVal ) let nSelect=AlreadySelect.length; if(nSelect==0) { - $("input[nozzel='0.4']").prop("checked", true); + $("input[nozzel='0.4'][vendor='BBL']").prop("checked", true); } TranslatePage(); diff --git a/resources/web/guide/24/24.js b/resources/web/guide/24/24.js index 43dffda130..c1b7d33915 100644 --- a/resources/web/guide/24/24.js +++ b/resources/web/guide/24/24.js @@ -133,7 +133,7 @@ function HandleModelList( pVal ) let nSelect=AlreadySelect.length; if(nSelect==0) { - $("input[nozzel='0.4']").prop("checked", true); + $("input[nozzel='0.4'][vendor='BBL']").prop("checked", true); } TranslatePage(); diff --git a/resources/web/image/printer/Creality Ender-3 V2_cover.png b/resources/web/image/printer/Creality Ender-3 V2_cover.png new file mode 100644 index 0000000000..8e76847884 Binary files /dev/null and b/resources/web/image/printer/Creality Ender-3 V2_cover.png differ diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index ff916eaf83..0298585704 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -170,10 +170,10 @@ void AppConfig::set_defaults() set_bool("reverse_mouse_wheel_zoom", false); #endif -#ifdef SUPPORT_SHOW_HINTS +//#ifdef SUPPORT_SHOW_HINTS if (get("show_hints").empty()) set_bool("show_hints", true); -#endif +//#endif #ifdef _WIN32 @@ -273,6 +273,10 @@ void AppConfig::set_defaults() set("precise_control", "none/mouse left"); } + if (get("download_path").empty()) { + set("download_path", ""); + } + if (get("mouse_wheel").empty()) { set("mouse_wheel", "0"); } @@ -284,7 +288,7 @@ void AppConfig::set_defaults() if (get("backup_interval").empty()) { set("backup_interval", "10"); } - + if (get("curr_bed_type").empty()) { set("curr_bed_type", "0"); } @@ -299,10 +303,6 @@ void AppConfig::set_defaults() // } // #endif - if (get("uniform_scale").empty()) { - set("uniform_scale", "1"); - } - // Remove legacy window positions/sizes erase("app", "main_frame_maximized"); erase("app", "main_frame_pos"); diff --git a/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp b/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp index 494b7b0b67..c8a84c4011 100644 --- a/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp +++ b/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp @@ -21,7 +21,7 @@ DistributedBeadingStrategy::DistributedBeadingStrategy(const coord_t optimal_wid name = "DistributedBeadingStrategy"; } -DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const +DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(const coord_t thickness, const coord_t bead_count) const { Beading ret; @@ -40,18 +40,24 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) weights[bead_idx] = getWeight(bead_idx); - const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f); + const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f); + coord_t accumulated_width = 0; for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) { - const float weight_fraction = weights[bead_idx] / total_weight; + const float weight_fraction = weights[bead_idx] / total_weight; const coord_t splitup_left_over_weight = to_be_divided * weight_fraction; - const coord_t width = optimal_width + splitup_left_over_weight; + const coord_t width = (bead_idx == bead_count - 1) ? thickness - accumulated_width : optimal_width + splitup_left_over_weight; + + // Be aware that toolpath_locations is computed by dividing the width by 2, so toolpath_locations + // could be off by 1 because of rounding errors. if (bead_idx == 0) ret.toolpath_locations.emplace_back(width / 2); else ret.toolpath_locations.emplace_back(ret.toolpath_locations.back() + (ret.bead_widths.back() + width) / 2); ret.bead_widths.emplace_back(width); + accumulated_width += width; } ret.left_over = 0; + assert((accumulated_width + ret.left_over) == thickness); } else if (bead_count == 2) { const coord_t outer_width = thickness / 2; ret.bead_widths.emplace_back(outer_width); @@ -68,6 +74,13 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t ret.left_over = thickness; } + assert(([&ret = std::as_const(ret), thickness]() -> bool { + coord_t total_bead_width = 0; + for (const coord_t &bead_width : ret.bead_widths) + total_bead_width += bead_width; + return (total_bead_width + ret.left_over) == thickness; + }())); + return ret; } diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 56d98ec5af..04dede5469 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -17,6 +17,7 @@ #include "Utils.hpp" #include "SVG.hpp" #include "Geometry/VoronoiVisualUtils.hpp" +#include "Geometry/VoronoiUtilsCgal.hpp" #include "../EdgeGrid.hpp" #define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance). @@ -43,6 +44,71 @@ template<> struct segment_traits namespace Slic3r::Arachne { +#ifdef ARACHNE_DEBUG +static void export_graph_to_svg(const std::string &path, + SkeletalTrapezoidationGraph &graph, + const Polygons &polys, + const std::vector> &edge_junctions = {}, + const bool beat_count = true, + const bool transition_middles = true, + const bool transition_ends = true) +{ + const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"}; + coordf_t stroke_width = scale_(0.03); + BoundingBox bbox = get_extents(polys); + for (const auto &node : graph.nodes) + bbox.merge(node.p); + + bbox.offset(scale_(1.)); + + ::Slic3r::SVG svg(path.c_str(), bbox); + for (const auto &line : to_lines(polys)) + svg.draw(line, "gray", stroke_width); + + for (const auto &edge : graph.edges) + svg.draw(Line(edge.from->p, edge.to->p), (edge.data.centralIsSet() && edge.data.isCentral()) ? "blue" : "cyan", stroke_width); + + for (const auto &line_junction : edge_junctions) + for (const auto &extrusion_junction : *line_junction) + svg.draw(extrusion_junction.p, "orange", coord_t(stroke_width * 2.)); + + if (beat_count) { + for (const auto &node : graph.nodes) { + svg.draw(node.p, "red", coord_t(stroke_width * 1.6)); + svg.draw_text(node.p, std::to_string(node.data.bead_count).c_str(), "black", 1); + } + } + + if (transition_middles) { + for (auto &edge : graph.edges) { + if (std::shared_ptr> transitions = edge.data.getTransitions(); transitions) { + for (auto &transition : *transitions) { + Line edge_line = Line(edge.to->p, edge.from->p); + double edge_length = edge_line.length(); + Point pt = edge_line.a + (edge_line.vector().cast() * (double(transition.pos) / edge_length)).cast(); + svg.draw(pt, "magenta", coord_t(stroke_width * 1.5)); + svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1); + } + } + } + } + + if (transition_ends) { + for (auto &edge : graph.edges) { + if (std::shared_ptr> transitions = edge.data.getTransitionEnds(); transitions) { + for (auto &transition : *transitions) { + Line edge_line = Line(edge.to->p, edge.from->p); + double edge_length = edge_line.length(); + Point pt = edge_line.a + (edge_line.vector().cast() * (double(transition.pos) / edge_length)).cast(); + svg.draw(pt, transition.is_lower_end ? "green" : "lime", coord_t(stroke_width * 1.5)); + svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1); + } + } + } + } +} +#endif + SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_type& vd_node, Point p) { auto he_node_it = vd_node_to_he_node.find(&vd_node); @@ -285,7 +351,6 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ } } - bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector& segments) { if (cell.incident_edge()->is_infinite()) @@ -386,7 +451,23 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead constructFromPolygons(polys); } -bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector &segments) { +static bool has_finite_edge_with_non_finite_vertex(const Geometry::VoronoiDiagram &voronoi_diagram) +{ + for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) { + if (edge.is_finite()) { + assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr); + if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) || + !VoronoiUtils::is_finite(*edge.vertex1())) + return true; + } + } + return false; +} + +static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector &segments) { + if (has_finite_edge_with_non_finite_vertex(voronoi_diagram)) + return true; + for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) { if (!cell.incident_edge()) continue; // There is no spoon @@ -405,7 +486,8 @@ bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagr VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr; VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge(); do { - if (edge->is_infinite()) continue; + if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1())) + continue; Vec2i64 v0 = VoronoiUtils::p(edge->vertex0()); Vec2i64 v1 = VoronoiUtils::p(edge->vertex1()); @@ -432,8 +514,71 @@ bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagr return false; } +static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph) +{ + for (const auto &edge : graph.edges) + if (edge.twin == nullptr) + return true; + return false; +} + +inline static std::unordered_map try_to_fix_degenerated_voronoi_diagram_by_rotation( + Geometry::VoronoiDiagram &voronoi_diagram, + const Polygons &polys, + Polygons &polys_rotated, + std::vector &segments, + const double fix_angle) +{ + std::unordered_map vertex_mapping; + for (Polygon &poly : polys_rotated) + poly.rotate(fix_angle); + + assert(polys_rotated.size() == polys.size()); + for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { + assert(polys_rotated[poly_idx].size() == polys[poly_idx].size()); + for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) + vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]}); + } + + segments.clear(); + for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++) + for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++) + segments.emplace_back(&polys_rotated, poly_idx, point_idx); + + voronoi_diagram.clear(); + construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); + +#ifdef ARACHNE_DEBUG_VORONOI + { + static int iRun = 0; + dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); + } +#endif + + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); + + return vertex_mapping; +} + +inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, + const double fix_angle, + const std::unordered_map &vertex_mapping) +{ + for (STHalfEdgeNode &node : graph.nodes) { + // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. + if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end()) + node.p = node_it->second; + else + node.p.rotate(-fix_angle); + } +} + void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) { +#ifdef ARACHNE_DEBUG + this->outline = polys; +#endif + // Check self intersections. assert([&polys]() -> bool { EdgeGrid::Grid grid; @@ -450,39 +595,57 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); point_idx++) segments.emplace_back(&polys, poly_idx, point_idx); +#ifdef ARACHNE_DEBUG + { + static int iRun = 0; + BoundingBox bbox = get_extents(polys); + SVG svg(debug_out_path("arachne_voronoi-input-%d.svg", iRun++).c_str(), bbox); + svg.draw_outline(polys, "black", scaled(0.03f)); + } +#endif + Geometry::VoronoiDiagram voronoi_diagram; construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); - // Try to detect cases when some Voronoi vertex is missing. - // When any Voronoi vertex is missing, rotate input polygon and try again. - const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments); - const double fix_angle = PI / 6; +#ifdef ARACHNE_DEBUG_VORONOI + { + static int iRun = 0; + dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); + } +#endif + + // Try to detect cases when some Voronoi vertex is missing and when + // the Voronoi diagram is not planar. + // When any Voronoi vertex is missing, or the Voronoi diagram is not + // planar, rotate the input polygon and try again. + const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments); + // Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446. + const bool is_voronoi_diagram_planar = Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram); + const double fix_angle = PI / 6; + std::unordered_map vertex_mapping; + // polys_copy is referenced through items stored in the std::vector segments. Polygons polys_copy = polys; - if (has_missing_voronoi_vertex) { - BOOST_LOG_TRIVIAL(debug) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth."; - for (Polygon &poly : polys_copy) - poly.rotate(fix_angle); + if (has_missing_voronoi_vertex || !is_voronoi_diagram_planar) { + if (has_missing_voronoi_vertex) + BOOST_LOG_TRIVIAL(warning) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth."; + else if (!is_voronoi_diagram_planar) + BOOST_LOG_TRIVIAL(warning) << "Detected non-planar Voronoi diagram, input polygons will be rotated back and forth."; - assert(polys_copy.size() == polys.size()); - for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { - assert(polys_copy[poly_idx].size() == polys[poly_idx].size()); - for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) - vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]}); - } + vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); - segments.clear(); - for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++) - for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++) - segments.emplace_back(&polys_copy, poly_idx, point_idx); - - voronoi_diagram.clear(); - construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram)); if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input."; + else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram)) + BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input."; } + bool degenerated_voronoi_diagram = has_missing_voronoi_vertex || !is_voronoi_diagram_planar; + +process_voronoi_diagram: + assert(this->graph.edges.empty() && this->graph.nodes.empty() && this->vd_edge_to_he_edge.empty() && this->vd_node_to_he_node.empty()); for (vd_t::cell_type cell : voronoi_diagram.cells()) { if (!cell.incident_edge()) continue; // There is no spoon @@ -538,16 +701,43 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) prev_edge->to->data.distance_to_boundary = 0; } - if (has_missing_voronoi_vertex) { - for (node_t &node : graph.nodes) { - // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. - if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end()) - node.p = node_it->second; - else - node.p.rotate(-fix_angle); - } + // For some input polygons, as in GH issues #8474 and #8514 resulting Voronoi diagram is degenerated because it is not planar. + // When this degenerated Voronoi diagram is processed, the resulting half-edge structure contains some edges that don't have + // a twin edge. Based on this, we created a fast mechanism that detects those causes and tries to recompute the Voronoi + // diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram. + if (!degenerated_voronoi_diagram && has_missing_twin_edge(this->graph)) { + BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth."; + degenerated_voronoi_diagram = true; + vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle); + + assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); + if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) + BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex after the rotation of input."; + + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); + + this->graph.edges.clear(); + this->graph.nodes.clear(); + this->vd_edge_to_he_edge.clear(); + this->vd_node_to_he_node.clear(); + + goto process_voronoi_diagram; } + if (degenerated_voronoi_diagram) { + assert(!has_missing_twin_edge(this->graph)); + + if (has_missing_twin_edge(this->graph)) + BOOST_LOG_TRIVIAL(error) << "Detected degenerated Voronoi diagram even after the rotation of input."; + } + + if (degenerated_voronoi_diagram) + rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping); + +#ifdef ARACHNE_DEBUG + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); +#endif + separatePointyQuadEndNodes(); graph.collapseSmallEdges(); @@ -594,45 +784,62 @@ void SkeletalTrapezoidation::separatePointyQuadEndNodes() // vvvvvvvvvvvvvvvvvvvvv // -#if 0 -static void export_graph_to_svg(const std::string &path, const SkeletalTrapezoidationGraph &graph, const Polygons &polys) -{ - const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"}; - coordf_t stroke_width = scale_(0.05); - BoundingBox bbox; - for (const auto &node : graph.nodes) - bbox.merge(node.p); - - bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(path.c_str(), bbox); - for (const auto &line : to_lines(polys)) - svg.draw(line, "red", stroke_width); - - for (const auto &edge : graph.edges) - svg.draw(Line(edge.from->p, edge.to->p), "cyan", scale_(0.01)); -} -#endif - void SkeletalTrapezoidation::generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges) { +#ifdef ARACHNE_DEBUG + static int iRun = 0; +#endif + p_generated_toolpaths = &generated_toolpaths; updateIsCentral(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline); +#endif + filterCentral(central_filter_dist); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline); +#endif + if (filter_outermost_central_edges) filterOuterCentral(); updateBeadCount(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline); +#endif + filterNoncentralRegions(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateTransitioningRibs(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateExtraRibs(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateSegments(); + +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline); +#endif + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } void SkeletalTrapezoidation::updateIsCentral() @@ -844,11 +1051,24 @@ void SkeletalTrapezoidation::generateTransitioningRibs() filterTransitionMids(); +#ifdef ARACHNE_DEBUG + static int iRun = 0; + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline); +#endif + ptr_vector_t> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted generateAllTransitionEnds(edge_transition_ends); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline); +#endif + applyTransitions(edge_transition_ends); // Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr. + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } @@ -1568,17 +1788,38 @@ void SkeletalTrapezoidation::generateSegments() } } } - + +#ifdef ARACHNE_DEBUG + static int iRun = 0; + export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + propagateBeadingsUpward(upward_quad_mids, node_beadings); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + propagateBeadingsDownward(upward_quad_mids, node_beadings); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + ptr_vector_t edge_junctions; // junctions ordered high R to low R generateJunctions(node_beadings, edge_junctions); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions); +#endif + connectJunctions(edge_junctions); - + generateLocalMaximaSingleBeads(); + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } SkeletalTrapezoidation::edge_t* SkeletalTrapezoidation::getQuadMaxRedgeTo(edge_t* quad_start_edge) @@ -1811,7 +2052,10 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t& for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--) { coord_t bead_R = beading->toolpath_locations[junction_idx]; - if (bead_R <= start_R) + // toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors. + // In GH issue #8472, these roundings errors caused missing the middle extrusion. + // Adding small epsilon should help resolve those cases. + if (bead_R <= start_R + 1) { // Junction coinciding with start node is used in this function call break; } diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp index 51b24bbcdf..819b71367b 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp @@ -18,6 +18,10 @@ #include "SkeletalTrapezoidationJoint.hpp" #include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp" #include "SkeletalTrapezoidationGraph.hpp" +#include "../Geometry/Voronoi.hpp" + +//#define ARACHNE_DEBUG +//#define ARACHNE_DEBUG_VORONOI namespace Slic3r::Arachne { @@ -122,6 +126,10 @@ public: */ void generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges = false); +#ifdef ARACHNE_DEBUG + Polygons outline; +#endif + protected: /*! * Auxiliary for referencing one transition along an edge which may contain multiple transitions diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index d40b6c4882..13bc901729 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -24,18 +24,19 @@ namespace Slic3r::Arachne { WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x, - const size_t inset_count, const coord_t wall_0_inset, const WallToolPathsParams ¶ms) + const size_t inset_count, const coord_t wall_0_inset, const coordf_t layer_height, const WallToolPathsParams ¶ms) : outline(outline) , bead_width_0(bead_width_0) , bead_width_x(bead_width_x) , inset_count(inset_count) , wall_0_inset(wall_0_inset) + , layer_height(layer_height) , print_thin_walls(Slic3r::Arachne::fill_outline_gaps) , min_feature_size(scaled(params.min_feature_size)) , min_bead_width(scaled(params.min_bead_width)) , small_area_length(static_cast(bead_width_0) / 2.) - , toolpaths_generated(false) , wall_transition_filter_deviation(scaled(params.wall_transition_filter_deviation)) + , toolpaths_generated(false) , m_params(params) { } @@ -312,60 +313,46 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re }; auto new_end = thiss.end(); - if(remove_holes) - { - for(auto it = thiss.begin(); it < new_end; it++) - { - // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector - if(fabs(ClipperLib::Area(to_path(*it))) < min_area_size) - { - new_end--; + if (remove_holes) { + for (auto it = thiss.begin(); it < new_end;) { + // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector. + if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) { + --new_end; *it = std::move(*new_end); - it--; // wind back the iterator such that the polygon just swaped in is checked next + continue; // Don't increment the iterator such that the polygon just swapped in is checked next. } + ++it; } - } - else - { + } else { // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes std::vector small_holes; - for(auto it = thiss.begin(); it < new_end; it++) { - double area = ClipperLib::Area(to_path(*it)); - if (fabs(area) < min_area_size) - { - if(area >= 0) - { - new_end--; - if(it < new_end) { + for (auto it = thiss.begin(); it < new_end;) { + if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) { + if (area >= 0) { + --new_end; + if (it < new_end) { std::swap(*new_end, *it); - it--; - } - else - { // Don't self-swap the last Path + continue; + } else { // Don't self-swap the last Path break; } - } - else - { + } else { small_holes.push_back(*it); } } + ++it; } // Removes small holes that have their first point inside one of the removed outlines // Iterating in reverse ensures that unprocessed small holes won't be moved const auto removed_outlines_start = new_end; - for(auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++) - { - for(auto outline_it = removed_outlines_start; outline_it < thiss.end() ; outline_it++) - { - if(Polygon(*outline_it).contains(*hole_it->begin())) { + for (auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++) + for (auto outline_it = removed_outlines_start; outline_it < thiss.end(); outline_it++) + if (Polygon(*outline_it).contains(*hole_it->begin())) { new_end--; *hole_it = std::move(*new_end); break; } - } - } } thiss.resize(new_end-thiss.begin()); } @@ -471,7 +458,7 @@ const std::vector &WallToolPaths::generate() // The functions above could produce intersecting polygons that could cause a crash inside Arachne. // Applying Clipper union should be enough to get rid of this issue. // Clipper union also fixed an issue in Arachne that in post-processing Voronoi diagram, some edges - // didn't have twin edges (this probably isn't an issue in Boost Voronoi generator). + // didn't have twin edges. (a non-planar Voronoi diagram probably caused this). prepared_outline = union_(prepared_outline); if (area(prepared_outline) <= 0) { @@ -479,9 +466,14 @@ const std::vector &WallToolPaths::generate() return toolpaths; } + const float external_perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale(bead_width_0), float(this->layer_height)); + const float perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale(bead_width_x), float(this->layer_height)); + const coord_t wall_transition_length = scaled(this->m_params.wall_transition_length); - const double wall_split_middle_threshold = this->m_params.wall_split_middle_threshold; // For an uneven nr. of lines: When to split the middle wall into two. - const double wall_add_middle_threshold = this->m_params.wall_add_middle_threshold; // For an even nr. of lines: When to add a new middle in between the innermost two walls. + + const double wall_split_middle_threshold = std::clamp(2. * unscaled(this->min_bead_width) / external_perimeter_extrusion_width - 1., 0.01, 0.99); // For an uneven nr. of lines: When to split the middle wall into two. + const double wall_add_middle_threshold = std::clamp(unscaled(this->min_bead_width) / perimeter_extrusion_width, 0.01, 0.99); // For an even nr. of lines: When to add a new middle in between the innermost two walls. + const int wall_distribution_count = this->m_params.wall_distribution_count; const size_t max_bead_count = (inset_count < std::numeric_limits::max() / 2) ? 2 * inset_count : std::numeric_limits::max(); const auto beading_strat = BeadingStrategyFactory::makeStrategy @@ -609,6 +601,14 @@ void WallToolPaths::stitchToolPaths(std::vector &toolpaths, { continue; } + + // PolylineStitcher, in some cases, produced closed extrusion (polygons), + // but the endpoints differ by a small distance. So we reconnect them. + // FIXME Lukas H.: Investigate more deeply why it is happening. + if (wall_polygon.junctions.front().p != wall_polygon.junctions.back().p && + (wall_polygon.junctions.back().p - wall_polygon.junctions.front().p).cast().norm() < stitch_distance) { + wall_polygon.junctions.emplace_back(wall_polygon.junctions.front()); + } wall_polygon.is_closed = true; wall_lines.emplace_back(std::move(wall_polygon)); // add stitched polygons to result } diff --git a/src/libslic3r/Arachne/WallToolPaths.hpp b/src/libslic3r/Arachne/WallToolPaths.hpp index 2f9879f0c0..6bb1153191 100644 --- a/src/libslic3r/Arachne/WallToolPaths.hpp +++ b/src/libslic3r/Arachne/WallToolPaths.hpp @@ -29,8 +29,6 @@ public: float wall_transition_angle; float wall_transition_filter_deviation; int wall_distribution_count; - float wall_add_middle_threshold; - float wall_split_middle_threshold; }; class WallToolPaths @@ -44,7 +42,7 @@ public: * \param inset_count The maximum number of parallel extrusion lines that make up the wall * \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls. */ - WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, const WallToolPathsParams ¶ms); + WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, coordf_t layer_height, const WallToolPathsParams ¶ms); /*! * Generates the Toolpaths @@ -123,14 +121,15 @@ private: coord_t bead_width_x; // toolpaths; //x(); const double y = node->y(); + assert(std::isfinite(x) && std::isfinite(y)); assert(x <= double(std::numeric_limits::max()) && x >= std::numeric_limits::lowest()); assert(y <= double(std::numeric_limits::max()) && y >= std::numeric_limits::lowest()); return {int64_t(x + 0.5 - (x < 0)), int64_t(y + 0.5 - (y < 0))}; // Round to the nearest integer coordinates. diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp index e736f98bcf..aa46936437 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp @@ -35,6 +35,11 @@ public: * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. */ static std::vector discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); + + static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) + { + return std::isfinite(vertex.x()) && std::isfinite(vertex.y()); + } }; } // namespace Slic3r::Arachne diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6203a0725b..66874674a9 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -151,6 +151,8 @@ set(lisbslic3r_sources Geometry/Voronoi.hpp Geometry/VoronoiOffset.cpp Geometry/VoronoiOffset.hpp + Geometry/VoronoiUtilsCgal.cpp + Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiVisualUtils.hpp Int128.hpp InternalBridgeDetector.cpp diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 56791cd1be..38f48b67d5 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -11,6 +11,7 @@ #include "FillBase.hpp" #include "FillRectilinear.hpp" +#include "FillLightning.hpp" #include "FillConcentricInternal.hpp" #include "FillConcentric.hpp" @@ -108,6 +109,9 @@ struct SurfaceFill { Surface surface; ExPolygons expolygons; SurfaceFillParams params; + // BBS + std::vector region_id_group; + ExPolygons no_overlap_expolygons; }; // BBS: used to judge whether the internal solid infill area is narrow @@ -209,8 +213,18 @@ std::vector group_fills(const Layer &layer) fill.region_id = region_id; fill.surface = surface; fill.expolygons.emplace_back(std::move(fill.surface.expolygon)); - } else - fill.expolygons.emplace_back(surface.expolygon); + //BBS + fill.region_id_group.push_back(region_id); + fill.no_overlap_expolygons = layerm.fill_no_overlap_expolygons; + } else { + fill.expolygons.emplace_back(surface.expolygon); + //BBS + auto t = find(fill.region_id_group.begin(), fill.region_id_group.end(), region_id); + if (t == fill.region_id_group.end()) { + fill.region_id_group.push_back(region_id); + fill.no_overlap_expolygons = union_ex(fill.no_overlap_expolygons, layerm.fill_no_overlap_expolygons); + } + } } } } @@ -336,6 +350,8 @@ std::vector group_fills(const Layer &layer) surface_fills.back().region_id = surface_fills[i].region_id; surface_fills.back().surface.surface_type = stInternalSolid; surface_fills.back().surface.thickness = surface_fills[i].surface.thickness; + surface_fills.back().region_id_group = surface_fills[i].region_id_group; + surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons; for (size_t j = 0; j < narrow_expolygons_index.size(); j++) { // BBS: move the narrow expolygons to new surface_fills.back(); surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expolygons_index[j]])); @@ -373,7 +389,7 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer -void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) +void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) { for (LayerRegion *layerm : m_regions) layerm->fills.clear(); @@ -413,7 +429,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: assert(fill_concentric != nullptr); fill_concentric->print_config = &this->object()->print()->config(); fill_concentric->print_object_config = &this->object()->config(); - } + } else if (surface_fill.params.pattern == ipLightning) + dynamic_cast(f.get())->generator = lightning_generator; // calculate flow spacing for infill pattern generation bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; @@ -441,6 +458,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.anchor_length_max = surface_fill.params.anchor_length_max; params.resolution = resolution; params.use_arachne = surface_fill.params.pattern == ipConcentric; + params.layer_height = m_regions[surface_fill.region_id]->layer()->height; // BBS params.flow = surface_fill.params.flow; diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 74a3f630e1..9487e463b8 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -65,6 +65,8 @@ struct FillParams // For Concentric infill, to switch between Classic and Arachne. bool use_arachne{ false }; + // Layer height for Concentric infill with Arachne. + coordf_t layer_height { 0.f }; // BBS Flow flow; diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index fe9b79e09b..3bcfc23b46 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -83,15 +83,13 @@ void FillConcentric::_fill_surface_single(const FillParams& params, double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end()); Arachne::WallToolPathsParams input_params; input_params.min_bead_width = 0.85 * min_nozzle_diameter; - input_params.min_feature_size = 0.1; + input_params.min_feature_size = 0.25 * min_nozzle_diameter; input_params.wall_transition_length = 1.0 * min_nozzle_diameter; input_params.wall_transition_angle = 10; input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter; input_params.wall_distribution_count = 1; - input_params.wall_add_middle_threshold = 0.75; - input_params.wall_split_middle_threshold = 0.5; - Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, input_params); + Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params); std::vector loops = wallToolPaths.getToolPaths(); std::vector all_extrusions; diff --git a/src/libslic3r/Fill/FillConcentricInternal.cpp b/src/libslic3r/Fill/FillConcentricInternal.cpp index 33878578c0..8b3e90930f 100644 --- a/src/libslic3r/Fill/FillConcentricInternal.cpp +++ b/src/libslic3r/Fill/FillConcentricInternal.cpp @@ -27,15 +27,13 @@ void FillConcentricInternal::fill_surface_extrusion(const Surface* surface, cons double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end()); Arachne::WallToolPathsParams input_params; input_params.min_bead_width = 0.85 * min_nozzle_diameter; - input_params.min_feature_size = 0.1; + input_params.min_feature_size = 0.25 * min_nozzle_diameter; input_params.wall_transition_length = 0.4; input_params.wall_transition_angle = 10; input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter; input_params.wall_distribution_count = 1; - input_params.wall_add_middle_threshold = 0.75; - input_params.wall_split_middle_threshold = 0.5; - Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, input_params); + Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params); std::vector loops = wallToolPaths.getToolPaths(); std::vector all_extrusions; diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp index 447fd8057b..b50694e95b 100644 --- a/src/libslic3r/Fill/FillLightning.cpp +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -1,20 +1,25 @@ #include "../Print.hpp" +#include "../ShortestPath.hpp" #include "FillLightning.hpp" #include "Lightning/Generator.hpp" -#include "../Surface.hpp" - -#include -#include -#include -#include namespace Slic3r::FillLightning { -Polylines Filler::fill_surface(const Surface *surface, const FillParams ¶ms) +void Filler::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) { - const Layer &layer = generator->getTreesForLayer(this->layer_id); - return layer.convertToLines(to_polygons(surface->expolygon), generator->infilll_extrusion_width()); + const Layer &layer = generator->getTreesForLayer(this->layer_id); + Polylines fill_lines = layer.convertToLines(to_polygons(expolygon), scaled(0.5 * this->spacing - this->overlap)); + + if (params.dont_connect() || fill_lines.size() <= 1) { + append(polylines_out, chain_polylines(std::move(fill_lines))); + } else + connect_infill(std::move(fill_lines), expolygon, polylines_out, this->spacing, params); } void GeneratorDeleter::operator()(Generator *p) { diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp index b4e7e35f11..9c36e01577 100644 --- a/src/libslic3r/Fill/FillLightning.hpp +++ b/src/libslic3r/Fill/FillLightning.hpp @@ -3,6 +3,13 @@ #include "FillBase.hpp" +/* +* A few modifications based on dba1179(2022.06.10) from Prusa, mainly in Generator.hpp and .cpp: +* 1. delete the second parameter(a throw back function) of Generator(), since I didnt find corresponding throw back function in BBS code +* 2. those codes that call the functions above +* 3. add codes of generating lightning in TreeSupport.cpp +*/ + namespace Slic3r { class PrintObject; @@ -24,8 +31,13 @@ public: Generator *generator { nullptr }; protected: Fill* clone() const override { return new Filler(*this); } - // Perform the fill. - Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + + void _fill_surface_single(const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) override; + // Let the G-code export reoder the infill lines. bool no_sort() const override { return false; } }; diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 91c7efc089..0d7f5940fc 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -407,13 +407,15 @@ public: // for the infill pattern, don't cut the corners. // default miterLimt = 3 //double miterLimit = 10.; - assert(aoffset1 < 0); + // FIXME: Resolve properly the cases when it is constructed with aoffset1 = 0 and aoffset2 = 0, + // that is used in sample_grid_pattern() for Lightning infill. + //assert(aoffset1 < 0); assert(aoffset2 <= 0); - assert(aoffset2 == 0 || aoffset2 < aoffset1); + // assert(aoffset2 == 0 || aoffset2 < aoffset1); // bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; - polygons_outer = offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); + polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); if (aoffset2 < 0) polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); // Filter out contours with zero area or small area, contours with 2 points only. @@ -3053,14 +3055,18 @@ Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams return polylines_out; } -Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing) +// Lightning infill assumes that the distance between any two sampled points is always +// at least equal to the value of spacing. To meet this assumption, we need to use +// BoundingBox for whole layers instead of bounding box just around processing ExPolygon. +// Using just BoundingBox around processing ExPolygon could produce two points closer +// than spacing (in cases where two ExPolygon are closer than spacing). +Points sample_grid_pattern(const ExPolygon& expolygon, coord_t spacing, const BoundingBox& global_bounding_box) { ExPolygonWithOffset poly_with_offset(expolygon, 0, 0, 0); - BoundingBox bounding_box = poly_with_offset.bounding_box_src(); std::vector segs = slice_region_by_vertical_lines( poly_with_offset, - (bounding_box.max.x() - bounding_box.min.x() + spacing - 1) / spacing, - bounding_box.min.x(), + (global_bounding_box.max.x() - global_bounding_box.min.x() + spacing - 1) / spacing, + global_bounding_box.min.x(), spacing); Points out; @@ -3076,17 +3082,17 @@ Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing) return out; } -Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing) +Points sample_grid_pattern(const ExPolygons& expolygons, coord_t spacing, const BoundingBox& global_bounding_box) { Points out; - for (const ExPolygon &expoly : expolygons) - append(out, sample_grid_pattern(expoly, spacing)); + for (const ExPolygon& expoly : expolygons) + append(out, sample_grid_pattern(expoly, spacing, global_bounding_box)); return out; } -Points sample_grid_pattern(const Polygons &polygons, coord_t spacing) +Points sample_grid_pattern(const Polygons& polygons, coord_t spacing, const BoundingBox& global_bounding_box) { - return sample_grid_pattern(union_ex(polygons), spacing); + return sample_grid_pattern(union_ex(polygons), spacing, global_bounding_box); } void FillMonotonicLineWGapFill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out) diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp index e85781e9a9..cb9d74845a 100644 --- a/src/libslic3r/Fill/FillRectilinear.hpp +++ b/src/libslic3r/Fill/FillRectilinear.hpp @@ -132,9 +132,9 @@ private: void fill_surface_by_lines(const Surface* surface, const FillParams& params, Polylines& polylines_out); }; -Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing); -Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing); -Points sample_grid_pattern(const Polygons &polygons, coord_t spacing); +Points sample_grid_pattern(const ExPolygon& expolygon, coord_t spacing, const BoundingBox& global_bounding_box); +Points sample_grid_pattern(const ExPolygons& expolygons, coord_t spacing, const BoundingBox& global_bounding_box); +Points sample_grid_pattern(const Polygons& polygons, coord_t spacing, const BoundingBox& global_bounding_box); } // namespace Slic3r diff --git a/src/libslic3r/Fill/Lightning/DistanceField.cpp b/src/libslic3r/Fill/Lightning/DistanceField.cpp index 6df1d043ca..7c7eae448f 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.cpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.cpp @@ -5,48 +5,99 @@ #include "../FillRectilinear.hpp" #include "../../ClipperUtils.hpp" +#include + +#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT +#include "../../SVG.hpp" +#endif + namespace Slic3r::FillLightning { constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient. -DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : - m_cell_size(radius / radius_per_cell_size), - m_supporting_radius(radius) +#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT +void export_distance_field_to_svg(const std::string &path, const Polygons &outline, const Polygons &overhang, const std::list &unsupported_points, const Points &points = {}) { - m_supporting_radius2 = double(radius) * double(radius); + coordf_t stroke_width = scaled(0.01); + BoundingBox bbox = get_extents(outline); + + bbox.offset(SCALED_EPSILON); + SVG svg(path, bbox); + svg.draw_outline(outline, "green", stroke_width); + svg.draw_outline(overhang, "blue", stroke_width); + + for (const DistanceField::UnsupportedCell &cell : unsupported_points) + svg.draw(cell.loc, "cyan", coord_t(stroke_width)); + + for (const Point &pt : points) + svg.draw(pt, "red", coord_t(stroke_width)); +} +#endif + +DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang) : + m_cell_size(radius / radius_per_cell_size), + m_supporting_radius(radius), + m_unsupported_points_bbox(current_outlines_bbox) +{ + m_supporting_radius2 = Slic3r::sqr(int64_t(radius)); // Sample source polygons with a regular grid sampling pattern. - for (const ExPolygon &expoly : union_ex(current_outline)) { - for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) { - // Find a squared distance to the source expolygon boundary. - double d2 = std::numeric_limits::max(); - for (size_t icontour = 0; icontour <= expoly.holes.size(); ++ icontour) { - const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; - if (contour.size() > 2) { - Point prev = contour.points.back(); - for (const Point &p2 : contour.points) { - d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); - prev = p2; + const BoundingBox overhang_bbox = get_extents(current_overhang); + for (const ExPolygon &expoly : union_ex(current_overhang)) { + const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox); + const size_t unsupported_points_prev_size = m_unsupported_points.size(); + m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size()); + + tbb::parallel_for(tbb::blocked_range(0, sampled_points.size()), [&self = *this, &expoly = std::as_const(expoly), &sampled_points = std::as_const(sampled_points), &unsupported_points_prev_size = std::as_const(unsupported_points_prev_size)](const tbb::blocked_range &range) -> void { + for (size_t sp_idx = range.begin(); sp_idx < range.end(); ++sp_idx) { + const Point &sp = sampled_points[sp_idx]; + // Find a squared distance to the source expolygon boundary. + double d2 = std::numeric_limits::max(); + for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) { + const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; + if (contour.size() > 2) { + Point prev = contour.points.back(); + for (const Point &p2 : contour.points) { + d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2)); + prev = p2; + } } } + self.m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))}; + assert(self.m_unsupported_points_bbox.contains(sp)); } - m_unsupported_points.emplace_back(p, sqrt(d2)); - } + }); // end of parallel_for } - m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) { + std::stable_sort(m_unsupported_points.begin(), m_unsupported_points.end(), [&radius](const UnsupportedCell &a, const UnsupportedCell &b) { constexpr coord_t prime_for_hash = 191; return std::abs(b.dist_to_boundary - a.dist_to_boundary) > radius ? a.dist_to_boundary < b.dist_to_boundary : (PointHash{}(a.loc) % prime_for_hash) < (PointHash{}(b.loc) % prime_for_hash); }); - for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) { - UnsupportedCell& cell = *it; - m_unsupported_points_grid.emplace(Point{ cell.loc.x() / m_cell_size, cell.loc.y() / m_cell_size }, it); + + m_unsupported_points_erased.resize(m_unsupported_points.size()); + std::fill(m_unsupported_points_erased.begin(), m_unsupported_points_erased.end(), false); + + m_unsupported_points_grid.initialize(m_unsupported_points, [&self = std::as_const(*this)](const Point &p) -> Point { return self.to_grid_point(p); }); + + // Because the distance between two points is at least one axis equal to m_cell_size, every cell + // in m_unsupported_points_grid contains exactly one point. + assert(m_unsupported_points.size() == m_unsupported_points_grid.size()); + +#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT + { + static int iRun = 0; + export_distance_field_to_svg(debug_out_path("FillLightning-DistanceField-%d.svg", iRun++), current_outline, current_overhang, m_unsupported_points); } +#endif } void DistanceField::update(const Point& to_node, const Point& added_leaf) { + std::ofstream out1("z:/misc/lightning.txt", std::ios::app); + out1 << m_unsupported_points.size() << std::endl; + out1.close(); + Vec2d v = (added_leaf - to_node).cast(); auto l2 = v.squaredNorm(); Vec2d extent = Vec2d(-v.y(), v.x()) * m_supporting_radius / sqrt(l2); @@ -60,17 +111,24 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) grid.merge(to_node + iextent); grid.merge(added_leaf - iextent); grid.merge(added_leaf + iextent); - grid.min /= m_cell_size; - grid.max /= m_cell_size; + + // Clip grid by m_unsupported_points_bbox. Mainly to ensure that grid.min is a non-negative value. + grid.min.x() = std::max(grid.min.x(), m_unsupported_points_bbox.min.x()); + grid.min.y() = std::max(grid.min.y(), m_unsupported_points_bbox.min.y()); + grid.max.x() = std::min(grid.max.x(), m_unsupported_points_bbox.max.x()); + grid.max.y() = std::min(grid.max.y(), m_unsupported_points_bbox.max.y()); + + grid.min = this->to_grid_point(grid.min); + grid.max = this->to_grid_point(grid.max); } + Point grid_addr; Point grid_loc; - for (coord_t row = grid.min.y(); row <= grid.max.y(); ++ row) { - grid_loc.y() = row * m_cell_size; - for (coord_t col = grid.min.x(); col <= grid.max.y(); ++ col) { - grid_loc.x() = col * m_cell_size; + for (grid_addr.y() = grid.min.y(); grid_addr.y() <= grid.max.y(); ++grid_addr.y()) { + for (grid_addr.x() = grid.min.x(); grid_addr.x() <= grid.max.x(); ++grid_addr.x()) { + grid_loc = this->from_grid_point(grid_addr); // Test inside a circle at the new leaf. - if ((grid_loc - added_leaf).cast().squaredNorm() > m_supporting_radius2) { + if ((grid_loc - added_leaf).cast().squaredNorm() > m_supporting_radius2) { // Not inside a circle at the end of the new leaf. // Test inside a rotated rectangle. Vec2d vx = (grid_loc - to_node).cast(); @@ -84,10 +142,29 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) } // Inside a circle at the end of the new leaf, or inside a rotated rectangle. // Remove unsupported leafs at this grid location. - if (auto it = m_unsupported_points_grid.find(grid_loc); it != m_unsupported_points_grid.end()) { - std::list::iterator& list_it = it->second; - UnsupportedCell& cell = *list_it; - if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { + if (const size_t cell_idx = m_unsupported_points_grid.find_cell_idx(grid_addr); cell_idx != std::numeric_limits::max()) { + const UnsupportedCell &cell = m_unsupported_points[cell_idx]; + if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { + m_unsupported_points_erased[cell_idx] = true; + m_unsupported_points_grid.mark_erased(grid_addr); + } + } + } + } +} + +#if 0 +void DistanceField::update(const Point &to_node, const Point &added_leaf) +{ + const Point supporting_radius_point(m_supporting_radius, m_supporting_radius); + const BoundingBox grid(this->to_grid_point(added_leaf - supporting_radius_point), this->to_grid_point(added_leaf + supporting_radius_point)); + + for (coord_t grid_y = grid.min.y(); grid_y <= grid.max.y(); ++grid_y) { + for (coord_t grid_x = grid.min.x(); grid_x <= grid.max.x(); ++grid_x) { + if (auto it = m_unsupported_points_grid.find({grid_x, grid_y}); it != m_unsupported_points_grid.end()) { + std::list::iterator &list_it = it->second; + UnsupportedCell &cell = *list_it; + if ((cell.loc - added_leaf).cast().squaredNorm() <= m_supporting_radius2) { m_unsupported_points.erase(list_it); m_unsupported_points_grid.erase(it); } @@ -95,5 +172,6 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf) } } } +#endif } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/DistanceField.hpp b/src/libslic3r/Fill/Lightning/DistanceField.hpp index 7fdce434ce..007ac235e5 100644 --- a/src/libslic3r/Fill/Lightning/DistanceField.hpp +++ b/src/libslic3r/Fill/Lightning/DistanceField.hpp @@ -4,9 +4,12 @@ #ifndef LIGHTNING_DISTANCE_FIELD_H #define LIGHTNING_DISTANCE_FIELD_H +#include "../../BoundingBox.hpp" #include "../../Point.hpp" #include "../../Polygon.hpp" +//#define LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT + namespace Slic3r::FillLightning { @@ -29,7 +32,7 @@ public: * \param current_overhang The overhang that needs to be supported on this * layer. */ - DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); + DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang); /*! * Gets the next unsupported location to be supported by a new branch. @@ -37,11 +40,17 @@ public: * \return ``true`` if successful, or ``false`` if there are no more points * to consider. */ - bool tryGetNextPoint(Point* p) const { - if (m_unsupported_points.empty()) - return false; - *p = m_unsupported_points.front().loc; - return true; + bool tryGetNextPoint(Point *out_unsupported_location, size_t *out_unsupported_cell_idx, const size_t start_idx = 0) const + { + for (size_t point_idx = start_idx; point_idx < m_unsupported_points.size(); ++point_idx) { + if (!m_unsupported_points_erased[point_idx]) { + *out_unsupported_cell_idx = point_idx; + *out_unsupported_location = m_unsupported_points[point_idx].loc; + return true; + } + } + + return false; } /*! @@ -69,14 +78,13 @@ protected: * branch of a tree. */ coord_t m_supporting_radius; - double m_supporting_radius2; + int64_t m_supporting_radius2; /*! * Represents a small discrete area of infill that needs to be supported. */ struct UnsupportedCell { - UnsupportedCell(Point loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} // The position of the center of this cell. Point loc; // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. @@ -86,13 +94,114 @@ protected: /*! * Cells which still need to be supported at some point. */ - std::list m_unsupported_points; + std::vector m_unsupported_points; + std::vector m_unsupported_points_erased; + + /*! + * BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers. + */ + const BoundingBox m_unsupported_points_bbox; /*! * Links the unsupported points to a grid point, so that we can quickly look * up the cell belonging to a certain position in the grid. */ - std::unordered_map::iterator, PointHash> m_unsupported_points_grid; + + class UnsupportedPointsGrid + { + public: + UnsupportedPointsGrid() = default; + void initialize(const std::vector &unsupported_points, const std::function &map_cell_to_grid) + { + if (unsupported_points.empty()) + return; + + BoundingBox unsupported_points_bbox; + for (const UnsupportedCell &cell : unsupported_points) + unsupported_points_bbox.merge(cell.loc); + + m_size = unsupported_points.size(); + m_grid_range = BoundingBox(map_cell_to_grid(unsupported_points_bbox.min), map_cell_to_grid(unsupported_points_bbox.max)); + m_grid_size = m_grid_range.size() + Point::Ones(); + + m_data.assign(m_grid_size.y() * m_grid_size.x(), std::numeric_limits::max()); + m_data_erased.assign(m_grid_size.y() * m_grid_size.x(), true); + + for (size_t cell_idx = 0; cell_idx < unsupported_points.size(); ++cell_idx) { + const size_t flat_idx = map_to_flat_array(map_cell_to_grid(unsupported_points[cell_idx].loc)); + assert(m_data[flat_idx] == std::numeric_limits::max()); + m_data[flat_idx] = cell_idx; + m_data_erased[flat_idx] = false; + } + } + + size_t size() const { return m_size; } + + size_t find_cell_idx(const Point &grid_addr) + { + if (!m_grid_range.contains(grid_addr)) + return std::numeric_limits::max(); + + if (const size_t flat_idx = map_to_flat_array(grid_addr); !m_data_erased[flat_idx]) { + assert(m_data[flat_idx] != std::numeric_limits::max()); + return m_data[flat_idx]; + } + + return std::numeric_limits::max(); + } + + void mark_erased(const Point &grid_addr) + { + assert(m_grid_range.contains(grid_addr)); + if (!m_grid_range.contains(grid_addr)) + return; + + const size_t flat_idx = map_to_flat_array(grid_addr); + assert(!m_data_erased[flat_idx] && m_data[flat_idx] != std::numeric_limits::max()); + assert(m_size != 0); + + m_data_erased[flat_idx] = true; + --m_size; + } + + private: + size_t m_size = 0; + + BoundingBox m_grid_range; + Point m_grid_size; + + std::vector m_data; + std::vector m_data_erased; + + inline size_t map_to_flat_array(const Point &loc) const + { + const Point offset_loc = loc - m_grid_range.min; + const size_t flat_idx = m_grid_size.x() * offset_loc.y() + offset_loc.x(); + assert(offset_loc.x() >= 0 && offset_loc.y() >= 0); + assert(flat_idx < size_t(m_grid_size.y() * m_grid_size.x())); + return flat_idx; + } + }; + + UnsupportedPointsGrid m_unsupported_points_grid; + + /*! + * Maps the point to the grid coordinates. + */ + Point to_grid_point(const Point &point) const { + return (point - m_unsupported_points_bbox.min) / m_cell_size; + } + + /*! + * Maps the point to the grid coordinates. + */ + Point from_grid_point(const Point &point) const { + return point * m_cell_size + m_unsupported_points_bbox.min; + } + +#ifdef LIGHTNING_DISTANCE_FIELD_DEBUG_OUTPUT + friend void export_distance_field_to_svg(const std::string &path, const Polygons &outline, const Polygons &overhang, const std::list &unsupported_points, const Points &points); +#endif }; } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/Generator.cpp b/src/libslic3r/Fill/Lightning/Generator.cpp index eeb53ae47c..5cdf7f37f4 100644 --- a/src/libslic3r/Fill/Lightning/Generator.cpp +++ b/src/libslic3r/Fill/Lightning/Generator.cpp @@ -7,7 +7,8 @@ #include "../../ClipperUtils.hpp" #include "../../Layer.hpp" #include "../../Print.hpp" -#include "../../Surface.hpp" + +#include "ExPolygon.hpp" /* Possible future tasks/optimizations,etc.: * - Improve connecting heuristic to favor connecting to shorter trees @@ -23,6 +24,43 @@ * - Move more complex computations from Generator constructor to elsewhere. */ +namespace Slic3r +{ +static std::string get_svg_filename(std::string layer_nr_or_z, std::string tag = "bbl_ts") +{ + static bool rand_init = false; + + if (!rand_init) { + srand(time(NULL)); + rand_init = true; + } + + int rand_num = rand() % 1000000; + //makedir("./SVG"); + std::string prefix = "./SVG/"; + std::string suffix = ".svg"; + return prefix + tag + "_" + layer_nr_or_z /*+ "_" + std::to_string(rand_num)*/ + suffix; +} + +Slic3r::SVG draw_two_overhangs_to_svg(size_t ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2) +{ + //if (overhangs1.empty() && overhangs2.empty()) + // return ; + BoundingBox bbox1 = get_extents(overhangs1); + BoundingBox bbox2 = get_extents(overhangs2); + bbox1.merge(bbox2); + + Slic3r::SVG svg(get_svg_filename(std::to_string(ts_layer), "two_overhangs_generator"), bbox1); + //if (!svg.is_opened()) return; + + svg.draw(union_ex(overhangs1), "blue"); + svg.draw(union_ex(overhangs2), "red"); + + return svg; +} +} + + namespace Slic3r::FillLightning { Generator::Generator(const PrintObject &print_object) @@ -32,13 +70,46 @@ Generator::Generator(const PrintObject &print_object) const PrintRegionConfig ®ion_config = print_object.shared_regions()->all_regions.front()->config(); const std::vector &nozzle_diameters = print_config.nozzle_diameter.values; double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); -// const int sparse_infill_filament = region_config.sparse_infill_filament.value; +// const int infill_extruder = region_config.infill_extruder.value; const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. - const double layer_thickness = object_config.layer_height; + const double layer_thickness = scaled(object_config.layer_height.value); + + //m_infill_extrusion_width = scaled(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width : region_config.infill_extrusion_width); + //m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / coord_t(region_config.fill_density.value); + m_infill_extrusion_width = scaled(region_config.sparse_infill_line_width.value); + m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / region_config.sparse_infill_density; + + const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees + const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees + const double lightning_infill_straightening_angle = M_PI / 4; // 45 degrees + m_wall_supporting_radius = coord_t(layer_thickness * std::tan(lightning_infill_overhang_angle)); + m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle)); + m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle)); + + generateInitialInternalOverhangs(print_object); + generateTrees(print_object); +} + +Generator::Generator(PrintObject* m_object, std::vector& contours, std::vector& overhangs, float density) +{ + const PrintConfig &print_config = m_object->print()->config(); + const PrintObjectConfig &object_config = m_object->config(); + const PrintRegionConfig ®ion_config = m_object->shared_regions()->all_regions.front()->config(); + const std::vector &nozzle_diameters = print_config.nozzle_diameter.values; + double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); +// const int infill_extruder = region_config.infill_extruder.value; + const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); + // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. + const double layer_thickness = scaled(object_config.layer_height.value); m_infill_extrusion_width = scaled(region_config.sparse_infill_line_width.value); - m_supporting_radius = scaled(m_infill_extrusion_width * 0.001 / region_config.sparse_infill_density); + //m_supporting_radius: against to the density of lightning, failures may happen if set to high density + //higher density lightning makes support harder, more time-consuming on computing and printing, but more reliable on supporting overhangs + //lower density lightning performs opposite + //TODO: decide whether enable density controller in advanced options or not + density = std::max(0.15f, density); + m_supporting_radius = coord_t(m_infill_extrusion_width) / density; const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees @@ -47,26 +118,33 @@ Generator::Generator(const PrintObject &print_object) m_prune_length = layer_thickness * std::tan(lightning_infill_prune_angle); m_straightening_max_distance = layer_thickness * std::tan(lightning_infill_straightening_angle); - generateInitialInternalOverhangs(print_object); - generateTrees(print_object); + m_overhang_per_layer = overhangs; + + generateTreesforSupport(contours); + + //for (size_t i = 0; i < overhangs.size(); i++) + //{ + // auto svg = draw_two_overhangs_to_svg(i, to_expolygons(contours[i]), to_expolygons(overhangs[i])); + // for (auto& root : m_lightning_layers[i].tree_roots) + // root->draw_tree(svg); + //} } void Generator::generateInitialInternalOverhangs(const PrintObject &print_object) { m_overhang_per_layer.resize(print_object.layers().size()); - const float infill_wall_offset = - m_infill_extrusion_width; Polygons infill_area_above; //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. - for (int layer_nr = print_object.layers().size() - 1; layer_nr >= 0; layer_nr--) { + for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; --layer_nr) { Polygons infill_area_here; for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const Surface& surface : layerm->fill_surfaces.surfaces) - if (surface.surface_type == stInternal) - append(infill_area_here, offset(surface.expolygon, infill_wall_offset)); + if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) + append(infill_area_here, to_polygons(surface.expolygon)); //Remove the part of the infill area that is already supported by the walls. - Polygons overhang = diff(offset(infill_area_here, -m_wall_supporting_radius), infill_area_above); + Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above); m_overhang_per_layer[layer_nr] = overhang; infill_area_above = std::move(infill_area_here); @@ -82,16 +160,16 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const void Generator::generateTrees(const PrintObject &print_object) { m_lightning_layers.resize(print_object.layers().size()); - const coord_t infill_wall_offset = - m_infill_extrusion_width; - + bboxs.resize(print_object.layers().size()); std::vector infill_outlines(print_object.layers().size(), Polygons()); // For-each layer from top to bottom: - for (int layer_id = print_object.layers().size() - 1; layer_id >= 0; layer_id--) + for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) { for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const Surface &surface : layerm->fill_surfaces.surfaces) - if (surface.surface_type == stInternal) - append(infill_outlines[layer_id], offset(surface.expolygon, infill_wall_offset)); + if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) + append(infill_outlines[layer_id], to_polygons(surface.expolygon)); + } // For various operations its beneficial to quickly locate nearby features on the polygon: const size_t top_layer_id = print_object.layers().size() - 1; @@ -99,23 +177,77 @@ void Generator::generateTrees(const PrintObject &print_object) outlines_locator.create(infill_outlines[top_layer_id], locator_cell_size); // For-each layer from top to bottom: - for (int layer_id = top_layer_id; layer_id >= 0; layer_id--) - { - Layer& current_lightning_layer = m_lightning_layers[layer_id]; - Polygons& current_outlines = infill_outlines[layer_id]; + for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) { + Layer ¤t_lightning_layer = m_lightning_layers[layer_id]; + const Polygons ¤t_outlines = infill_outlines[layer_id]; + const BoundingBox ¤t_outlines_bbox = get_extents(current_outlines); + + bboxs[layer_id] = get_extents(current_outlines); // register all trees propagated from the previous layer as to-be-reconnected std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; - current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); - current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); // Initialize trees for next lower layer from the current one. if (layer_id == 0) return; - const Polygons& below_outlines = infill_outlines[layer_id - 1]; - outlines_locator.set_bbox(get_extents(below_outlines).inflated(SCALED_EPSILON)); + const Polygons &below_outlines = infill_outlines[layer_id - 1]; + BoundingBox below_outlines_bbox = get_extents(below_outlines).inflated(SCALED_EPSILON); + if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined) + below_outlines_bbox.merge(outlines_locator_bbox); + + if (!current_lightning_layer.tree_roots.empty()) + below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON)); + + outlines_locator.set_bbox(below_outlines_bbox); + outlines_locator.create(below_outlines, locator_cell_size); + + std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; + for (auto& tree : current_lightning_layer.tree_roots) + tree->propagateToNextLayer(lower_trees, below_outlines, outlines_locator, m_prune_length, m_straightening_max_distance, locator_cell_size / 2); + } +} + +void Generator::generateTreesforSupport(std::vector& contours) +{ + m_lightning_layers.resize(contours.size()); + bboxs.resize(contours.size()); + + // For various operations its beneficial to quickly locate nearby features on the polygon: + const size_t top_layer_id = contours.size() - 1; + EdgeGrid::Grid outlines_locator(get_extents(contours[top_layer_id]).inflated(SCALED_EPSILON)); + outlines_locator.create(contours[top_layer_id], locator_cell_size); + + // For-each layer from top to bottom: + for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) { + Layer& current_lightning_layer = m_lightning_layers[layer_id]; + const Polygons& current_outlines = contours[layer_id]; + const BoundingBox& current_outlines_bbox = get_extents(current_outlines); + + bboxs[layer_id] = get_extents(current_outlines); + + // register all trees propagated from the previous layer as to-be-reconnected + std::vector to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; + + current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius); + + // Initialize trees for next lower layer from the current one. + if (layer_id == 0) + return; + + const Polygons& below_outlines = contours[layer_id - 1]; + BoundingBox below_outlines_bbox = get_extents(below_outlines).inflated(SCALED_EPSILON); + if (const BoundingBox& outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined) + below_outlines_bbox.merge(outlines_locator_bbox); + + if (!current_lightning_layer.tree_roots.empty()) + below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON)); + + outlines_locator.set_bbox(below_outlines_bbox); outlines_locator.create(below_outlines, locator_cell_size); std::vector& lower_trees = m_lightning_layers[layer_id - 1].tree_roots; diff --git a/src/libslic3r/Fill/Lightning/Generator.hpp b/src/libslic3r/Fill/Lightning/Generator.hpp index c44ecfe7a2..0b25fd513c 100644 --- a/src/libslic3r/Fill/Lightning/Generator.hpp +++ b/src/libslic3r/Fill/Lightning/Generator.hpp @@ -43,9 +43,8 @@ public: * This generator will pre-compute things in preparation of generating * Lightning Infill for the infill areas in that mesh. The infill areas must * already be calculated at this point. - * \param mesh The mesh to generate infill for. */ - Generator(const PrintObject &print_object); + explicit Generator(const PrintObject &print_object); /*! * Get a tree of paths generated for a certain layer of the mesh. @@ -58,8 +57,12 @@ public: */ const Layer& getTreesForLayer(const size_t& layer_id) const; + std::vector& Overhangs() { return m_overhang_per_layer; } + float infilll_extrusion_width() const { return m_infill_extrusion_width; } + Generator(PrintObject* m_object, std::vector& contours, std::vector& overhangs, float density = 0.15); + protected: /*! * Calculate the overhangs above the infill areas that need to be supported @@ -75,6 +78,7 @@ protected: * Calculate the tree structure of all layers. */ void generateTrees(const PrintObject &print_object); + void generateTreesforSupport(std::vector& contours); float m_infill_extrusion_width; @@ -124,6 +128,8 @@ protected: * This is generated by \ref generateTrees. */ std::vector m_lightning_layers; + + std::vector bboxs; }; } // namespace FillLightning diff --git a/src/libslic3r/Fill/Lightning/Layer.cpp b/src/libslic3r/Fill/Lightning/Layer.cpp index 1b3936e340..5aa217d8f2 100644 --- a/src/libslic3r/Fill/Lightning/Layer.cpp +++ b/src/libslic3r/Fill/Lightning/Layer.cpp @@ -3,12 +3,16 @@ #include "Layer.hpp" //The class we're implementing. -#include // advance - #include "DistanceField.hpp" #include "TreeNode.hpp" +#include "../../ClipperUtils.hpp" #include "../../Geometry.hpp" +#include "Utils.hpp" + +#include +#include +#include namespace Slic3r::FillLightning { @@ -23,10 +27,15 @@ Point GroundingLocation::p() const return tree_node ? tree_node->getLocation() : *boundary_location; } -void Layer::fillLocator(SparseNodeGrid &tree_node_locator) +inline static Point to_grid_point(const Point &point, const BoundingBox &bbox) { - std::function add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) { - tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node)); + return (point - bbox.min) / locator_cell_size; +} + +void Layer::fillLocator(SparseNodeGrid &tree_node_locator, const BoundingBox& current_outlines_bbox) +{ + std::function add_node_to_locator_func = [&tree_node_locator, ¤t_outlines_bbox](const NodeSPtr &node) { + tree_node_locator.insert(std::make_pair(to_grid_point(node->getLocation(), current_outlines_bbox), node)); }; for (auto& tree : tree_roots) tree->visitNodes(add_node_to_locator_func); @@ -36,38 +45,47 @@ void Layer::generateNewTrees ( const Polygons& current_overhang, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outlines_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius ) { - DistanceField distance_field(supporting_radius, current_outlines, current_overhang); + DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang); SparseNodeGrid tree_node_locator; - fillLocator(tree_node_locator); + fillLocator(tree_node_locator, current_outlines_bbox); // Until no more points need to be added to support all: // Determine next point from tree/outline areas via distance-field - Point unsupported_location; - while (distance_field.tryGetNextPoint(&unsupported_location)) { + size_t unsupported_cell_idx = 0; + Point unsupported_location; + while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) { GroundingLocation grounding_loc = getBestGroundingLocation( - unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); + unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); NodeSPtr new_parent; NodeSPtr new_child; this->attach(unsupported_location, grounding_loc, new_child, new_parent); - tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_child->getLocation(), current_outlines_bbox), new_child)); if (new_parent) - tree_node_locator.insert(std::make_pair(Point(new_parent->getLocation().x() / locator_cell_size, new_parent->getLocation().y() / locator_cell_size), new_parent)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_parent->getLocation(), current_outlines_bbox), new_parent)); // update distance field distance_field.update(grounding_loc.p(), unsupported_location); } + +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT + { + static int iRun = 0; + export_to_svg(debug_out_path("FillLightning-TreeNodes-%d.svg", iRun++), current_outlines, this->tree_roots); + } +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ } -static bool polygonCollidesWithLineSegment(const Point from, const Point to, const EdgeGrid::Grid &loc_to_line) +static bool polygonCollidesWithLineSegment(const Point &from, const Point &to, const EdgeGrid::Grid &loc_to_line) { struct Visitor { - explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} + explicit Visitor(const EdgeGrid::Grid &grid, const Line &line) : grid(grid), line(line) {} bool operator()(coord_t iy, coord_t ix) { // Called with a row and colum of the grid cell, which is intersected by a line. @@ -87,7 +105,7 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con const EdgeGrid::Grid& grid; Line line; bool intersect = false; - } visitor(loc_to_line); + } visitor(loc_to_line, {from, to}); loc_to_line.visit_cells_intersecting_line(from, to, visitor); return visitor.intersect; @@ -97,6 +115,7 @@ GroundingLocation Layer::getBestGroundingLocation ( const Point& unsupported_location, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius, @@ -112,9 +131,10 @@ GroundingLocation Layer::getBestGroundingLocation if (contour.size() > 2) { Point prev = contour.points.back(); for (const Point &p2 : contour.points) { - if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) { + Point closest_point; + if (double d = line_alg::distance_to_squared(Line{prev, p2}, unsupported_location, &closest_point); d < d2) { d2 = d; - node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast(); + node_location = closest_point; } prev = p2; } @@ -123,30 +143,52 @@ GroundingLocation Layer::getBestGroundingLocation const auto within_dist = coord_t((node_location - unsupported_location).cast().norm()); - NodeSPtr sub_tree{ nullptr }; - coord_t current_dist = getWeightedDistance(node_location, unsupported_location); + NodeSPtr sub_tree{nullptr}; + coord_t current_dist = getWeightedDistance(node_location, unsupported_location); if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. const coord_t search_radius = std::min(current_dist, within_dist); BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size)); - region.min /= locator_cell_size; - region.max /= locator_cell_size; - Point grid_addr; - for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y()) - for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) { - auto it_range = tree_node_locator.equal_range(grid_addr); - for (auto it = it_range.first; it != it_range.second; ++ it) { - auto candidate_sub_tree = it->second.lock(); - if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && - !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && - !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { - const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); - if (candidate_dist < current_dist) { - current_dist = candidate_dist; - sub_tree = candidate_sub_tree; + region.min = to_grid_point(region.min, current_outlines_bbox); + region.max = to_grid_point(region.max, current_outlines_bbox); + + Point current_dist_grid_addr{std::numeric_limits::lowest(), std::numeric_limits::lowest()}; + std::mutex current_dist_mutex; + tbb::parallel_for(tbb::blocked_range2d(region.min.y(), region.max.y(), region.min.x(), region.max.x()), [¤t_dist, current_dist_copy = current_dist, ¤t_dist_mutex, &sub_tree, ¤t_dist_grid_addr, &exclude_tree = std::as_const(exclude_tree), &outline_locator = std::as_const(outline_locator), &supporting_radius = std::as_const(supporting_radius), &tree_node_locator = std::as_const(tree_node_locator), &unsupported_location = std::as_const(unsupported_location)](const tbb::blocked_range2d &range) -> void { + for (coord_t grid_addr_y = range.rows().begin(); grid_addr_y < range.rows().end(); ++grid_addr_y) + for (coord_t grid_addr_x = range.cols().begin(); grid_addr_x < range.cols().end(); ++grid_addr_x) { + const Point local_grid_addr{grid_addr_x, grid_addr_y}; + NodeSPtr local_sub_tree{nullptr}; + coord_t local_current_dist = current_dist_copy; + const auto it_range = tree_node_locator.equal_range(local_grid_addr); + for (auto it = it_range.first; it != it_range.second; ++it) { + const NodeSPtr candidate_sub_tree = it->second.lock(); + if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) && + !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) && + !polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) { + if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < local_current_dist) { + local_current_dist = candidate_dist; + local_sub_tree = candidate_sub_tree; + } + } + } + // To always get the same result in a parallel version as in a non-parallel version, + // we need to preserve that for the same current_dist, we select the same sub_tree + // as in the non-parallel version. For this purpose, inside the variable + // current_dist_grid_addr is stored from with 2D grid position assigned sub_tree comes. + // And when there are two sub_tree with the same current_dist, one which will be found + // the first in the non-parallel version is selected. + { + std::lock_guard lock(current_dist_mutex); + if (local_current_dist < current_dist || + (local_current_dist == current_dist && (grid_addr_y < current_dist_grid_addr.y() || + (grid_addr_y == current_dist_grid_addr.y() && grid_addr_x < current_dist_grid_addr.x())))) { + current_dist = local_current_dist; + sub_tree = local_sub_tree; + current_dist_grid_addr = local_grid_addr; } } } - } + }); // end of parallel_for } return ! sub_tree ? @@ -176,6 +218,7 @@ void Layer::reconnectRoots ( std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, const coord_t supporting_radius, const coord_t wall_supporting_radius @@ -184,10 +227,10 @@ void Layer::reconnectRoots constexpr coord_t tree_connecting_ignore_offset = 100; SparseNodeGrid tree_node_locator; - fillLocator(tree_node_locator); + fillLocator(tree_node_locator, current_outlines_bbox); const coord_t within_max_dist = outline_locator.resolution() * 2; - for (auto root_ptr : to_be_reconnected_tree_roots) + for (const auto &root_ptr : to_be_reconnected_tree_roots) { auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr); @@ -203,7 +246,7 @@ void Layer::reconnectRoots root_ptr->addChild(new_root); new_root->reroot(); - tree_node_locator.insert(std::make_pair(Point(new_root->getLocation().x() / locator_cell_size, new_root->getLocation().y() / locator_cell_size), new_root)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root)); *old_root_it = std::move(new_root); // replace old root with new root continue; @@ -217,6 +260,7 @@ void Layer::reconnectRoots ( root_ptr->getLocation(), current_outlines, + current_outlines_bbox, outline_locator, supporting_radius, tree_connecting_ignore_width, @@ -233,7 +277,7 @@ void Layer::reconnectRoots attach_ptr->reroot(); new_root->addChild(attach_ptr); - tree_node_locator.insert(std::make_pair(new_root->getLocation(), new_root)); + tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root)); *old_root_it = std::move(new_root); // replace old root with new root } @@ -256,15 +300,26 @@ void Layer::reconnectRoots } } -/* - * Implementation assumes moving inside, but moving outside should just as well be possible. +#if 0 +/*! + * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance. + * Given a \p distance more than zero, the point will end up inside, and conversely outside. + * When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned. + * When the point is in/outside by less than \p distance, \p from is moved to the correct place. + * Implementation assumes moving inside, but moving outside should just as well be possible. + * + * \param polygons The polygons onto which to move the point + * \param from[in,out] The point to move. + * \param distance The distance by which to move the point. + * \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon. + * \return The index to the polygon onto which we have moved the point. */ static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2) { - Point ret = from; - int64_t bestDist2 = std::numeric_limits::max(); - unsigned int bestPoly = static_cast(-1); - bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary + Point ret = from; + int64_t bestDist2 = std::numeric_limits::max(); + auto bestPoly = static_cast(-1); + bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) { const Polygon &poly = polygons[poly_idx]; @@ -333,7 +388,7 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan else { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . projected_p_beyond_prev_segment = false; - Point x = a + ab * dot_prod / ab_length2; + Point x = (a.cast() + ab.cast() * dot_prod / ab_length2).cast(); int64_t dist2 = (p - x).cast().squaredNorm(); if (dist2 < bestDist2) @@ -373,38 +428,18 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan } return static_cast(-1); } +#endif -// Returns 'added someting'. -Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const +Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_overlap) const { if (tree_roots.empty()) return {}; - Polygons result_lines; - for (const auto& tree : tree_roots) { - // If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon. - // (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.) - constexpr coord_t epsilon = 5; - Point should_be_inside = tree->getLocation(); - moveInside(limit_to_outline, should_be_inside, epsilon, epsilon * epsilon); - if (inside(limit_to_outline, should_be_inside)) - tree->convertToPolylines(result_lines, line_width); - } + Polylines result_lines; + for (const auto &tree : tree_roots) + tree->convertToPolylines(result_lines, line_overlap); - // TODO: allow for polylines! - Polylines split_lines; - for (Polygon &line : result_lines) { - if (line.size() <= 1) - continue; - Point last = line[0]; - for (size_t point_idx = 1; point_idx < line.size(); point_idx++) { - Point here = line[point_idx]; - split_lines.push_back({ last, here }); - last = here; - } - } - - return split_lines; + return intersection_pl(result_lines, limit_to_outline); } } // namespace Slic3r::Lightning diff --git a/src/libslic3r/Fill/Lightning/Layer.hpp b/src/libslic3r/Fill/Lightning/Layer.hpp index 853c634f27..c0b91ad1f8 100644 --- a/src/libslic3r/Fill/Lightning/Layer.hpp +++ b/src/libslic3r/Fill/Lightning/Layer.hpp @@ -41,9 +41,10 @@ public: ( const Polygons& current_overhang, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius + coord_t supporting_radius, + coord_t wall_supporting_radius ); /*! Determine & connect to connection point in tree/outline. @@ -53,9 +54,10 @@ public: ( const Point& unsupported_location, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius, + coord_t supporting_radius, + coord_t wall_supporting_radius, const SparseNodeGrid& tree_node_locator, const NodeSPtr& exclude_tree = nullptr ); @@ -71,16 +73,17 @@ public: ( std::vector& to_be_reconnected_tree_roots, const Polygons& current_outlines, + const BoundingBox& current_outlines_bbox, const EdgeGrid::Grid& outline_locator, - const coord_t supporting_radius, - const coord_t wall_supporting_radius + coord_t supporting_radius, + coord_t wall_supporting_radius ); - Polylines convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; + Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_overlap) const; coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); - void fillLocator(SparseNodeGrid& tree_node_locator); + void fillLocator(SparseNodeGrid& tree_node_locator, const BoundingBox& current_outlines_bbox); }; } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.cpp b/src/libslic3r/Fill/Lightning/TreeNode.cpp index 8ab11bd6d4..982d47b10e 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.cpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.cpp @@ -4,7 +4,6 @@ #include "TreeNode.hpp" #include "../../Geometry.hpp" -#include "../../ClipperUtils.hpp" namespace Slic3r::FillLightning { @@ -107,7 +106,7 @@ NodeSPtr Node::deepCopy() const return local_root; } -void Node::reroot(NodeSPtr new_parent /*= nullptr*/) +void Node::reroot(const NodeSPtr &new_parent) { if (! m_is_root) { auto old_parent = m_parent.lock(); @@ -142,7 +141,7 @@ NodeSPtr Node::closestNode(const Point& loc) return result; } -bool inside(const Polygons &polygons, const Point p) +bool inside(const Polygons &polygons, const Point &p) { int poly_count_inside = 0; for (const Polygon &poly : polygons) { @@ -181,7 +180,11 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG } visitor { outline_locator, a.cast(), b.cast() }; outline_locator.visit_cells_intersecting_line(a, b, visitor); - return visitor.d2min < within_max_dist * within_max_dist; + if (visitor.d2min < double(within_max_dist) * double(within_max_dist)) { + result = Point(visitor.intersection_pt); + return true; + } + return false; } bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector& rerooted_parts) @@ -226,14 +229,14 @@ bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locat void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) { - straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist); + straighten(magnitude, m_p, 0, int64_t(max_remove_colinear_dist) * int64_t(max_remove_colinear_dist)); } Node::RectilinearJunction Node::straighten( const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, - const coord_t max_remove_colinear_dist2) + const int64_t max_remove_colinear_dist2) { constexpr coord_t junction_magnitude_factor_numerator = 3; constexpr coord_t junction_magnitude_factor_denominator = 4; @@ -245,13 +248,13 @@ Node::RectilinearJunction Node::straighten( auto child_dist = coord_t((m_p - child_p->m_p).cast().norm()); RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2); coord_t total_dist_to_junction_below = junction_below.total_recti_dist; - Point a = junction_above; - Point b = junction_below.junction_loc; + const Point& a = junction_above; + Point b = junction_below.junction_loc; if (a != b) // should always be true! { Point ab = b - a; - Point destination = a + ab * accumulated_dist / std::max(coord_t(1), total_dist_to_junction_below); - if ((destination - m_p).cast().squaredNorm() <= magnitude * magnitude) + Point destination = (a.cast() + ab.cast() * int64_t(accumulated_dist) / std::max(int64_t(1), int64_t(total_dist_to_junction_below))).cast(); + if ((destination - m_p).cast().squaredNorm() <= int64_t(magnitude) * int64_t(magnitude)) m_p = destination; else m_p += ((destination - m_p).cast().normalized() * magnitude).cast(); @@ -262,7 +265,7 @@ Node::RectilinearJunction Node::straighten( child_p = m_children.front(); //recursive call to straighten might have removed the child const NodeSPtr& parent_node = m_parent.lock(); if (parent_node && - (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && + (child_p->m_p - parent_node->m_p).cast().squaredNorm() < max_remove_colinear_dist2 && Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) { child_p->m_parent = m_parent; for (auto& sibling : parent_node->m_children) @@ -344,16 +347,16 @@ coord_t Node::prune(const coord_t& pruning_distance) return max_distance_pruned; } -void Node::convertToPolylines(Polygons& output, const coord_t line_width) const +void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) const { - Polygons result; - output.emplace_back(); + Polylines result; + result.emplace_back(); convertToPolylines(0, result); - removeJunctionOverlap(result, line_width); + removeJunctionOverlap(result, line_overlap); append(output, std::move(result)); } -void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const +void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const { if (m_children.empty()) { output[long_line_idx].points.push_back(m_p); @@ -373,11 +376,12 @@ void Node::convertToPolylines(size_t long_line_idx, Polygons& output) const } } -void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_width) const +void Node::removeJunctionOverlap(Polylines &result_lines, const coord_t line_overlap) const { - const coord_t reduction = line_width / 2; // TODO make configurable? - for (auto poly_it = result_lines.begin(); poly_it != result_lines.end(); ) { - Polygon &polyline = *poly_it; + const coord_t reduction = line_overlap; + size_t res_line_idx = 0; + while (res_line_idx < result_lines.size()) { + Polyline &polyline = result_lines[res_line_idx]; if (polyline.size() <= 1) { polyline = std::move(result_lines.back()); result_lines.pop_back(); @@ -386,8 +390,8 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt coord_t to_be_reduced = reduction; Point a = polyline.back(); - for (int point_idx = polyline.size() - 2; point_idx >= 0; point_idx--) { - const Point b = polyline[point_idx]; + for (int point_idx = int(polyline.size()) - 2; point_idx >= 0; point_idx--) { + const Point b = polyline.points[point_idx]; const Point ab = b - a; const auto ab_len = coord_t(ab.cast().norm()); if (ab_len >= to_be_reduced) { @@ -404,8 +408,33 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt polyline = std::move(result_lines.back()); result_lines.pop_back(); } else - ++ poly_it; + ++ res_line_idx; } } +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT +void export_to_svg(const NodeSPtr &root_node, SVG &svg) +{ + for (const NodeSPtr &children : root_node->m_children) { + svg.draw(Line(root_node->getLocation(), children->getLocation()), "red"); + export_to_svg(children, svg); + } +} + +void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes) { + BoundingBox bbox = get_extents(contour); + + bbox.offset(SCALED_EPSILON); + SVG svg(path, bbox); + svg.draw_outline(contour, "blue"); + + for (const NodeSPtr &root_node: root_nodes) { + for (const NodeSPtr &children: root_node->m_children) { + svg.draw(Line(root_node->getLocation(), children->getLocation()), "red"); + export_to_svg(children, svg); + } + } +} +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ + } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Fill/Lightning/TreeNode.hpp b/src/libslic3r/Fill/Lightning/TreeNode.hpp index bc2e76dc2f..8e0c029a95 100644 --- a/src/libslic3r/Fill/Lightning/TreeNode.hpp +++ b/src/libslic3r/Fill/Lightning/TreeNode.hpp @@ -11,6 +11,9 @@ #include "../../EdgeGrid.hpp" #include "../../Polygon.hpp" +#include "SVG.hpp" + +//#define LIGHTNING_TREE_NODE_DEBUG_OUTPUT namespace Slic3r::FillLightning { @@ -43,7 +46,7 @@ public: { struct EnableMakeShared : public Node { - EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {} + explicit EnableMakeShared(Arg&&...arg) : Node(std::forward(arg)...) {} }; return std::make_shared(std::forward(arg)...); } @@ -99,9 +102,9 @@ public: std::vector& next_trees, const Polygons& next_outlines, const EdgeGrid::Grid& outline_locator, - const coord_t prune_distance, - const coord_t smooth_magnitude, - const coord_t max_remove_colinear_dist + coord_t prune_distance, + coord_t smooth_magnitude, + coord_t max_remove_colinear_dist ) const; /*! @@ -156,7 +159,7 @@ public: * This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf. * \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree. */ - void reroot(NodeSPtr new_parent = nullptr); + void reroot(const NodeSPtr &new_parent = nullptr); /*! * Retrieves the closest node to the specified location. @@ -176,16 +179,16 @@ public: */ bool hasOffspring(const NodeSPtr& to_be_checked) const; -protected: Node() = delete; // Don't allow empty contruction +protected: /*! * Construct a new node, either for insertion in a tree or as root. * \param p The physical location in the 2D layer that this node represents. * Connecting other nodes to this node indicates that a line segment should * be drawn between those two physical positions. */ - Node(const Point& p, const std::optional& last_grounding_location = std::nullopt); + explicit Node(const Point& p, const std::optional& last_grounding_location = std::nullopt); /*! * Copy this node and its entire sub-tree. @@ -211,7 +214,7 @@ protected: * \param magnitude The maximum allowed distance to move the node. * \param max_remove_colinear_dist Maximum distance of the (compound) line-segment from which a co-linear point may be removed. */ - void straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist); + void straighten(coord_t magnitude, coord_t max_remove_colinear_dist); /*! Recursive part of \ref straighten(.) * \param junction_above The last seen junction with multiple children above @@ -219,7 +222,7 @@ protected: * \param max_remove_colinear_dist2 Maximum distance _squared_ of the (compound) line-segment from which a co-linear point may be removed. * \return the total distance along the tree from the last junction above to the first next junction below and the location of the next junction below */ - RectilinearJunction straighten(const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, const coord_t max_remove_colinear_dist2); + RectilinearJunction straighten(coord_t magnitude, const Point& junction_above, coord_t accumulated_dist, int64_t max_remove_colinear_dist2); /*! Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. * \return The distance that has been pruned. If less than \p distance, then the whole tree was puned away. @@ -236,7 +239,7 @@ public: * * \param output all branches in this tree connected into polylines */ - void convertToPolylines(Polygons& output, const coord_t line_width) const; + void convertToPolylines(Polylines &output, coord_t line_overlap) const; /*! If this was ever a direct child of the root, it'll have a previous grounding location. * @@ -244,6 +247,8 @@ public: */ const std::optional& getLastGroundingLocation() const { return m_last_grounding_location; } + void draw_tree(SVG& svg) { for (auto& child : m_children) { svg.draw(Line(m_p, child->getLocation()), "yellow"); child->draw_tree(svg); } } + protected: /*! * Convert the tree into polylines @@ -255,9 +260,9 @@ protected: * \param long_line a reference to a polyline in \p output which to continue building on in the recursion * \param output all branches in this tree connected into polylines */ - void convertToPolylines(size_t long_line_idx, Polygons& output) const; + void convertToPolylines(size_t long_line_idx, Polylines &output) const; - void removeJunctionOverlap(Polygons& polylines, const coord_t line_width) const; + void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const; bool m_is_root; Point m_p; @@ -265,10 +270,40 @@ protected: std::vector m_children; std::optional m_last_grounding_location; // &tree_roots); + +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT + friend void export_to_svg(const NodeSPtr &root_node, Slic3r::SVG &svg); + friend void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes); +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ }; -bool inside(const Polygons &polygons, const Point p); -bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist); +bool inside(const Polygons &polygons, const Point &p); +bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist); + +inline BoundingBox get_extents(const NodeSPtr &root_node) +{ + BoundingBox bbox; + for (const NodeSPtr &children : root_node->m_children) + bbox.merge(get_extents(children)); + bbox.merge(root_node->getLocation()); + return bbox; +} + +inline BoundingBox get_extents(const std::vector &tree_roots) +{ + BoundingBox bbox; + for (const NodeSPtr &root_node : tree_roots) + bbox.merge(get_extents(root_node)); + return bbox; +} + +#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT +void export_to_svg(const NodeSPtr &root_node, SVG &svg); +void export_to_svg(const std::string &path, const Polygons &contour, const std::vector &root_nodes); +#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */ } // namespace Slic3r::FillLightning diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f74545e982..4fd3096549 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -3189,7 +3189,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv //BBS std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") : code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") : - //code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : + code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : code.type == CustomGCode::ToolChange ? "tool_change" : code.extra; code_tree.put(".gcode" , gcode ); } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index ca50d3ffb1..1e0b6687d9 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -1336,7 +1336,7 @@ bool load_amf(const char *path, DynamicPrintConfig *config, ConfigSubstitutionCo //BBS std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") : code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") : - //code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : + code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : code.type == CustomGCode::ToolChange ? "tool_change" : code.extra; code_tree.put(".gcode" , gcode ); } diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index b5225f6721..3a7d35b72d 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -175,6 +175,8 @@ static constexpr const char* BUILD_TAG = "build"; static constexpr const char* ITEM_TAG = "item"; static constexpr const char* METADATA_TAG = "metadata"; static constexpr const char* FILAMENT_TAG = "filament"; +static constexpr const char* SLICE_WARNING_TAG = "warning"; +static constexpr const char* WARNING_MSG_TAG = "msg"; static constexpr const char *FILAMENT_ID_TAG = "id"; static constexpr const char* FILAMENT_TYPE_TAG = "type"; static constexpr const char *FILAMENT_COLOR_TAG = "color"; @@ -454,12 +456,28 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) for (auto it = ps.volumes_per_extruder.begin(); it != ps.volumes_per_extruder.end(); it++) { double volume = it->second; auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, it->first); + FilamentInfo info; - info.id = it->first; - info.used_m = used_filament_m; - info.used_g = used_filament_g; + info.id = it->first; + if (ps.flush_per_filament.find(it->first) != ps.flush_per_filament.end()) { + volume = ps.flush_per_filament.at(it->first); + auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(volume, it->first); + info.used_m = used_filament_m + flushed_filament_m; + info.used_g = used_filament_g + flushed_filament_g; + } else { + info.used_m = used_filament_m; + info.used_g = used_filament_g; + } slice_filaments_info.push_back(info); } + + /* only for test + GCodeProcessorResult::SliceWarning sw; + sw.msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT; + sw.level = 1; + result->warnings.push_back(sw); + */ + warnings = result->warnings; } @@ -843,6 +861,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _handle_start_config_filament(const char** attributes, unsigned int num_attributes); bool _handle_end_config_filament(); + bool _handle_start_config_warning(const char** attributes, unsigned int num_attributes); + bool _handle_end_config_warning(); + //BBS: add plater config parse functions bool _handle_start_config_plater(const char** attributes, unsigned int num_attributes); bool _handle_end_config_plater(); @@ -1510,6 +1531,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) plate_data_list[it->first-1]->gcode_weight = it->second->gcode_weight; plate_data_list[it->first-1]->toolpath_outside = it->second->toolpath_outside; plate_data_list[it->first-1]->slice_filaments_info = it->second->slice_filaments_info; + plate_data_list[it->first-1]->warnings = it->second->warnings; plate_data_list[it->first-1]->thumbnail_file = (m_load_restore || it->second->thumbnail_file.empty()) ? it->second->thumbnail_file : m_backup_path + "/" + it->second->thumbnail_file; plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file; plate_data_list[it->first-1]->pattern_bbox_file = (m_load_restore || it->second->pattern_bbox_file.empty()) ? it->second->pattern_bbox_file : m_backup_path + "/" + it->second->pattern_bbox_file; @@ -2355,6 +2377,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) res = _handle_start_config_plater_instance(attributes, num_attributes); else if (::strcmp(FILAMENT_TAG, name) == 0) res = _handle_start_config_filament(attributes, num_attributes); + else if (::strcmp(SLICE_WARNING_TAG, name) == 0) + res = _handle_start_config_warning(attributes, num_attributes); else if (::strcmp(ASSEMBLE_TAG, name) == 0) res = _handle_start_assemble(attributes, num_attributes); else if (::strcmp(ASSEMBLE_ITEM_TAG, name) == 0) @@ -3205,6 +3229,30 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } + bool _BBS_3MF_Importer::_handle_start_config_warning(const char** attributes, unsigned int num_attributes) + { + if (m_curr_plater) { + std::string msg = bbs_get_attribute_value_string(attributes, num_attributes, WARNING_MSG_TAG); + std::string lvl_str = bbs_get_attribute_value_string(attributes, num_attributes, "level"); + GCodeProcessorResult::SliceWarning sw; + sw.msg = msg; + try { + sw.level = atoi(lvl_str.c_str()); + } + catch(...) { + }; + + m_curr_plater->warnings.push_back(sw); + } + return true; + } + + bool _BBS_3MF_Importer::_handle_end_config_warning() + { + // do nothing + return true; + } + bool _BBS_3MF_Importer::_handle_start_config_plater(const char** attributes, unsigned int num_attributes) { if (!m_parsing_slice_info) { @@ -3827,6 +3875,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool m_split_model { false }; // save object per file with Production Extention bool m_save_gcode { false }; // whether to save gcode for normal save bool m_skip_model { false }; // skip model when exporting .gcode.3mf + bool m_skip_auxiliary { false }; // skip normal axuiliary files public: //BBS: add plate data related logic @@ -3910,6 +3959,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) m_split_model = store_params.strategy & SaveStrategy::SplitModel; m_save_gcode = store_params.strategy & SaveStrategy::WithGcode; m_skip_model = store_params.strategy & SaveStrategy::SkipModel; + m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary; boost::system::error_code ec; std::string filename = std::string(store_params.path); @@ -5000,7 +5050,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) // if (volume == nullptr) // continue; - bool is_left_handed = volume->is_left_handed(); + //BBS: as we stored matrix seperately, not multiplied into vertex + //we don't need to consider this left hand case specially + //bool is_left_handed = volume->is_left_handed(); + bool is_left_handed = false; //VolumeToOffsetsMap::iterator volume_it = volumes_objectID.find(volume); //assert(volume_it != volumes_objectID.end()); @@ -5637,6 +5690,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) << FILAMENT_USED_M_TAG << "=\"" << it->used_m << "\" " << FILAMENT_USED_G_TAG << "=\"" << it->used_g << "\" />\n"; } + + for (auto it = plate_data->warnings.begin(); it != plate_data->warnings.end(); it++) { + stream << " <" << SLICE_WARNING_TAG << " " << "msg=\"" << it->msg << "\" " << "level=\"" << std::to_string(it->level) << "\" />\n"; + } stream << " \n"; } } @@ -5737,7 +5794,7 @@ bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_ar //BBS std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") : code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") : - //code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : + code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") : code.type == CustomGCode::ToolChange ? "tool_change" : code.extra; code_tree.put(".gcode" , gcode ); } @@ -5826,7 +5883,7 @@ bool _BBS_3MF_Exporter::_add_auxiliary_dir_to_archive(mz_zip_archive &archive, c } continue; } - if (boost::filesystem::is_regular_file(dir_entry.path())) + if (boost::filesystem::is_regular_file(dir_entry.path()) && !m_skip_auxiliary) { src_file = dir_entry.path().string(); dst_in_3mf = dir_entry.path().string(); diff --git a/src/libslic3r/Format/bbs_3mf.hpp b/src/libslic3r/Format/bbs_3mf.hpp index d7a90fba7f..07d55f8976 100644 --- a/src/libslic3r/Format/bbs_3mf.hpp +++ b/src/libslic3r/Format/bbs_3mf.hpp @@ -70,6 +70,8 @@ struct PlateData bool is_sliced_valid = false; bool toolpath_outside {false}; + std::vector warnings; + std::string get_gcode_prediction_str() { return gcode_prediction; } @@ -93,6 +95,7 @@ enum class SaveStrategy SkipStatic = 1 << 6, SkipModel = 1 << 7, WithSliceInfo = 1 << 8, + SkipAuxiliary = 1 << 9, SplitModel = 0x1000 | ProductionExt, Encrypted = SecureContentExt | SplitModel, diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 052bfbb21a..2f0a99129b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -335,8 +335,8 @@ bool GCode::gcode_label_objects = true; float new_retract_length = full_config.retraction_length.get_at(new_extruder_id); float old_retract_length_toolchange = gcode_writer.extruder() != nullptr ? full_config.retract_length_toolchange.get_at(previous_extruder_id) : 0; float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_extruder_id); - int old_filament_temp = gcode_writer.extruder() != nullptr ? full_config.nozzle_temperature.get_at(previous_extruder_id) : 210; - int new_filament_temp = full_config.nozzle_temperature.get_at(new_extruder_id); + int old_filament_temp = gcode_writer.extruder() != nullptr ? (gcodegen.on_first_layer()? full_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : full_config.nozzle_temperature.get_at(previous_extruder_id)) : 210; + int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_extruder_id) : full_config.nozzle_temperature.get_at(new_extruder_id); Vec3d nozzle_pos = gcode_writer.get_position(); float purge_volume = tcr.purge_volume < EPSILON ? 0 : std::max(tcr.purge_volume, g_min_purge_volume); @@ -866,7 +866,7 @@ namespace DoExport { //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value); //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause G-code")), config.machine_pause_gcode.value); - //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value); if (ret.size() < MAX_TAGS_COUNT) { for (const std::string& value : config.filament_start_gcode.values) { check(_(L("Filament start G-code")), value); @@ -1457,6 +1457,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); + //BBS: emit printing accelerate if has non-zero value + if (m_config.default_acceleration.value > 0) { + float acceleration = m_config.default_acceleration.value; + file.write(m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5))); + } + // Disable fan. if (print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) { file.write(m_writer.set_fan(0)); @@ -2224,11 +2230,11 @@ namespace ProcessLayer // Extruder switches are processed by LayerTools, they should be filtered out. assert(custom_gcode->type != CustomGCode::ToolChange); - CustomGCode::Type gcode_type = custom_gcode->type; + CustomGCode::Type gcode_type = custom_gcode->type; bool color_change = gcode_type == CustomGCode::ColorChange; - bool tool_change = gcode_type == CustomGCode::ToolChange; + bool tool_change = gcode_type == CustomGCode::ToolChange; // Tool Change is applied as Color Change for a single extruder printer only. - assert(! tool_change || single_filament_print); + assert(!tool_change || single_filament_print); std::string pause_print_msg; int m600_extruder_before_layer = -1; @@ -2236,13 +2242,13 @@ namespace ProcessLayer m600_extruder_before_layer = custom_gcode->extruder - 1; else if (gcode_type == CustomGCode::PausePrint) pause_print_msg = custom_gcode->extra; - //BBS: inserting color gcode and template_custom_gcode is removed + //BBS: inserting color gcode is removed #if 0 // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count if (color_change || tool_change) { assert(m600_extruder_before_layer >= 0); - // Color Change or Tool Change as Color Change. + // Color Change or Tool Change as Color Change. // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n"; @@ -2264,20 +2270,19 @@ namespace ProcessLayer // see GH issue #6362 gcodegen.writer().unretract(); } - } - else { + } + else { #endif - if (gcode_type == CustomGCode::PausePrint) // Pause print - { + if (gcode_type == CustomGCode::PausePrint) // Pause print + { // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; //! FIXME_in_fw show message during print pause - //if (!pause_print_msg.empty()) - // gcode += "M117 " + pause_print_msg + "\n"; + //if (!pause_print_msg.empty()) + // gcode += "M117 " + pause_print_msg + "\n"; gcode += gcodegen.placeholder_parser_process("machine_pause_gcode", config.machine_pause_gcode, current_extruder_id) + "\n"; } -#if 0 - else { + else { // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; if (gcode_type == CustomGCode::Template) // Template Custom Gcode @@ -2287,9 +2292,9 @@ namespace ProcessLayer } gcode += "\n"; +#if 0 } #endif - } return gcode; @@ -2581,7 +2586,7 @@ GCode::LayerResult GCode::process_layer( for (const LayerToPrint &layer_to_print : layers) { if (layer_to_print.support_layer != nullptr) { const SupportLayer &support_layer = *layer_to_print.support_layer; - const PrintObject &object = *support_layer.object(); + const PrintObject& object = *layer_to_print.original_object; if (! support_layer.support_fills.entities.empty()) { ExtrusionRole role = support_layer.support_fills.role(); bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition; @@ -2666,7 +2671,7 @@ GCode::LayerResult GCode::process_layer( // BBS if (layer_to_print.tree_support_layer != nullptr) { const TreeSupportLayer& tree_support_layer = *layer_to_print.tree_support_layer; - const PrintObject& object = *tree_support_layer.object(); + const PrintObject& object = *layer_to_print.original_object; if (!tree_support_layer.support_fills.entities.empty()) { ExtrusionRole role = tree_support_layer.support_fills.role(); bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition; @@ -2810,7 +2815,7 @@ GCode::LayerResult GCode::process_layer( } printing_extruders.clear(); if (is_anything_overridden) { - entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size()); + entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, layer_to_print.original_object, correct_extruder_id, layer_to_print.object()->instances().size()); if (entity_overrides == nullptr) { printing_extruders.emplace_back(correct_extruder_id); } else { @@ -2996,8 +3001,8 @@ GCode::LayerResult GCode::process_layer( // BBS WipingExtrusions& wiping_extrusions = const_cast(layer_tools).wiping_extrusions(); - bool support_overridden = wiping_extrusions.is_support_overridden(layer.object()); - bool support_intf_overridden = wiping_extrusions.is_support_interface_overridden(layer.object()); + bool support_overridden = wiping_extrusions.is_support_overridden(layer_to_print.original_object); + bool support_intf_overridden = wiping_extrusions.is_support_interface_overridden(layer_to_print.original_object); ExtrusionRole support_extrusion_role = instance_to_print.object_by_extruder.support_extrusion_role; bool is_overridden = support_extrusion_role == erSupportMaterialInterface ? support_intf_overridden : support_overridden; @@ -3909,12 +3914,20 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) if (role == erSupportMaterial || role == erSupportTransition) { const SupportLayer* support_layer = dynamic_cast(m_layer); + //FIXME support_layer->support_islands.contains should use some search structure! if (support_layer != NULL && support_layer->support_islands.contains(travel)) // skip retraction if this is a travel move inside a support material island //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material // at the end of the extrusion path! return false; + + //reduce the retractions in lightning infills for tree support + const TreeSupportLayer* ts_layer = dynamic_cast(m_layer); + if (ts_layer != NULL) + for (auto& area : ts_layer->base_areas) + if(area.contains(travel)) + return false; } //BBS: need retract when long moving to print perimeter to avoid dropping of material @@ -4013,7 +4026,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // BBS float new_retract_length = m_config.retraction_length.get_at(extruder_id); float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(extruder_id); - int new_filament_temp = m_config.nozzle_temperature.get_at(extruder_id); + int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(extruder_id): m_config.nozzle_temperature.get_at(extruder_id); Vec3d nozzle_pos = m_writer.get_position(); float old_retract_length, old_retract_length_toolchange, wipe_volume; int old_filament_temp, old_filament_e_feedrate; @@ -4029,7 +4042,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) int previous_extruder_id = m_writer.extruder()->id(); old_retract_length = m_config.retraction_length.get_at(previous_extruder_id); old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id); - old_filament_temp = m_config.nozzle_temperature.get_at(previous_extruder_id); + old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id); wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id]; old_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(previous_extruder_id) / filament_area); old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 6eb33b83ba..ebba1dda27 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1167,12 +1167,14 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & travel_intersection_count = 0; } - const ConfigOptionFloat &opt_max_detour = gcodegen.config().max_travel_detour_distance; + const ConfigOptionFloatOrPercent &opt_max_detour = gcodegen.config().max_travel_detour_distance; bool max_detour_length_exceeded = false; if (opt_max_detour.value > 0) { double direct_length = travel.length(); double detour = result_pl.length() - direct_length; - double max_detour_length = scale_(opt_max_detour.value); + double max_detour_length = opt_max_detour.percent ? + direct_length * 0.01 * opt_max_detour.value : + scale_(opt_max_detour.value); if (detour > max_detour_length) { result_pl = {start, end}; max_detour_length_exceeded = true; diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 530293c909..a7faa7c502 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -811,6 +811,8 @@ std::string CoolingBuffer::apply_layer_cooldown( //BBS: force to write a fan speed command again if (m_current_fan_speed != -1) new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed); + if (m_additional_fan_speed != -1) + new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed); } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) { // Just remove this comment. diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6f7dc21cdd..88b65d7b82 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -36,6 +36,7 @@ static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f; static const size_t MIN_EXTRUDERS_COUNT = 5; static const float DEFAULT_FILAMENT_DIAMETER = 1.75f; static const float DEFAULT_FILAMENT_DENSITY = 1.245f; +static const int DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE = 0; static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero(); namespace Slic3r { @@ -776,6 +777,7 @@ void GCodeProcessorResult::reset() { filament_diameters = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_densities = std::vector(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); custom_gcode_per_print_z = std::vector(); + warnings.clear(); //BBS: add mutex for protection of gcode result unlock(); @@ -877,6 +879,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_colors.resize(extruders_count); m_result.filament_diameters.resize(extruders_count); m_result.filament_densities.resize(extruders_count); + m_result.filament_vitrification_temperature.resize(extruders_count); m_extruder_temps.resize(extruders_count); for (size_t i = 0; i < extruders_count; ++ i) { @@ -884,6 +887,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_colors[i] = static_cast(i); m_result.filament_diameters[i] = static_cast(config.filament_diameter.get_at(i)); m_result.filament_densities[i] = static_cast(config.filament_density.get_at(i)); + m_result.filament_vitrification_temperature[i] = static_cast(config.temperature_vitrification.get_at(i)); } if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) { @@ -985,6 +989,20 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_result.filament_densities.emplace_back(DEFAULT_FILAMENT_DENSITY); } } + //BBS + const ConfigOptionInts* filament_vitrification_temperature = config.option("temperature_vitrification"); + if (filament_vitrification_temperature != nullptr) { + m_result.filament_vitrification_temperature.clear(); + m_result.filament_vitrification_temperature.resize(filament_vitrification_temperature->values.size()); + for (size_t i = 0; i < filament_vitrification_temperature->values.size(); ++i) { + m_result.filament_vitrification_temperature[i] = static_cast(filament_vitrification_temperature->values[i]); + } + } + if (m_result.filament_vitrification_temperature.size() < m_result.extruders_count) { + for (size_t i = m_result.filament_vitrification_temperature.size(); i < m_result.extruders_count; ++i) { + m_result.filament_vitrification_temperature.emplace_back(DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); + } + } const ConfigOptionPoints* extruder_offset = config.option("extruder_offset"); const ConfigOptionBool* single_extruder_multi_material = config.option("single_extruder_multi_material"); @@ -1198,6 +1216,7 @@ void GCodeProcessor::reset() for (size_t i = 0; i < MIN_EXTRUDERS_COUNT; ++i) { m_extruder_temps[i] = 0.0f; } + m_highest_bed_temp = 0; m_extruded_last_z = 0.0f; m_zero_layer_height = 0.0f; @@ -1363,6 +1382,8 @@ void GCodeProcessor::finalize(bool post_process) #if ENABLE_GCODE_VIEWER_STATISTICS m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS + //BBS: update slice warning + update_slice_warnings(); } float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const @@ -1652,6 +1673,16 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool default: break; } break; + case '4': + switch (cmd[3]) { + case '0': { process_M140(line); break; } // Set bed temperature + default: break; + } + case '9': + switch (cmd[3]) { + case '0': { process_M190(line); break; } // Wait bed temperature + default: break; + } default: break; } @@ -3427,6 +3458,21 @@ void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line) process_T(cmd.substr(pos)); } +void GCodeProcessor::process_M140(const GCodeReader::GCodeLine& line) +{ + float new_temp; + if (line.has_value('S', new_temp)) + m_highest_bed_temp = m_highest_bed_temp < (int)new_temp ? (int)new_temp : m_highest_bed_temp; +} + +void GCodeProcessor::process_M190(const GCodeReader::GCodeLine& line) +{ + float new_temp; + if (line.has_value('S', new_temp)) + m_highest_bed_temp = m_highest_bed_temp < (int)new_temp ? (int)new_temp : m_highest_bed_temp; +} + + void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) { // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration @@ -3848,6 +3894,15 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id) return m_time_processor.extruder_unloaded ? 0.0f : m_time_processor.filament_unload_times; } +//BBS +int GCodeProcessor::get_filament_vitrification_temperature(size_t extrude_id) +{ + if (extrude_id < m_result.filament_vitrification_temperature.size()) + return m_result.filament_vitrification_temperature[extrude_id]; + else + return 0; +} + void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) { for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { @@ -3909,5 +3964,36 @@ void GCodeProcessor::update_estimated_times_stats() m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role; } +//BBS: ugly code... +void GCodeProcessor::update_slice_warnings() +{ + m_result.warnings.clear(); + + auto get_used_extruders = [this]() { + std::vector used_extruders; + used_extruders.reserve(m_used_filaments.volumes_per_extruder.size()); + for (auto item : m_used_filaments.volumes_per_extruder) { + used_extruders.push_back(item.first); + } + return used_extruders; + }; + + auto used_extruders = get_used_extruders(); + assert(!used_extruders.empty()); + if (m_highest_bed_temp != 0) { + for (size_t i = 0; i < used_extruders.size(); i++) { + int temperature = get_filament_vitrification_temperature(used_extruders[i]); + if (temperature != 0 && m_highest_bed_temp > temperature) { + GCodeProcessorResult::SliceWarning warning; + warning.level = 1; + warning.msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT; + m_result.warnings.emplace_back(std::move(warning)); + } + } + } + + m_result.warnings.shrink_to_fit(); +} + } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 686c861606..05dab19b97 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -16,6 +16,9 @@ namespace Slic3r { +// slice warnings enum strings +#define BED_TEMP_TOO_HIGH_THAN_FILAMENT "bed_temperature_too_high_than_filament" + enum class EMoveType : unsigned char { Noop, @@ -128,6 +131,12 @@ namespace Slic3r { } }; + struct SliceWarning { + int level; // 0: normal tips, 1: warning; 2: error + std::string msg; // enum string + std::vector params; // extra msg info + }; + std::string filename; unsigned int id; std::vector moves; @@ -144,8 +153,11 @@ namespace Slic3r { std::vector extruder_colors; std::vector filament_diameters; std::vector filament_densities; + std::vector filament_vitrification_temperature; PrintEstimatedStatistics print_statistics; std::vector custom_gcode_per_print_z; + //BBS + std::vector warnings; #if ENABLE_GCODE_VIEWER_STATISTICS int64_t time{ 0 }; @@ -171,6 +183,7 @@ namespace Slic3r { filament_densities = other.filament_densities; print_statistics = other.print_statistics; custom_gcode_per_print_z = other.custom_gcode_per_print_z; + warnings = other.warnings; #if ENABLE_GCODE_VIEWER_STATISTICS time = other.time; #endif @@ -593,6 +606,7 @@ namespace Slic3r { unsigned char m_last_extruder_id; ExtruderColors m_extruder_colors; ExtruderTemps m_extruder_temps; + int m_highest_bed_temp; float m_extruded_last_z; float m_first_layer_height; // mm float m_zero_layer_height; // mm @@ -759,6 +773,12 @@ namespace Slic3r { // Set tool (MakerWare) void process_M135(const GCodeReader::GCodeLine& line); + //BBS: Set bed temperature + void process_M140(const GCodeReader::GCodeLine& line); + + //BBS: wait bed temperature + void process_M190(const GCodeReader::GCodeLine& line); + // Set max printing acceleration void process_M201(const GCodeReader::GCodeLine& line); @@ -811,6 +831,7 @@ namespace Slic3r { void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value); float get_filament_load_time(size_t extruder_id); float get_filament_unload_time(size_t extruder_id); + int get_filament_vitrification_temperature(size_t extrude_id); void process_custom_gcode_time(CustomGCode::Type code); void process_filaments(CustomGCode::Type code); @@ -819,6 +840,8 @@ namespace Slic3r { void simulate_st_synchronize(float additional_time = 0.0f); void update_estimated_times_stats(); + //BBS: + void update_slice_warnings(); }; } /* namespace Slic3r */ diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 24c1d7911e..d2e05ccfc8 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -864,11 +864,11 @@ const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const } // This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual) -void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies) +void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies) { something_overridden = true; - auto entity_map_it = (entity_map.emplace(entity, ExtruderPerCopy())).first; // (add and) return iterator + auto entity_map_it = (entity_map.emplace(std::make_tuple(entity, object), ExtruderPerCopy())).first; // (add and) return iterator ExtruderPerCopy& copies_vector = entity_map_it->second; copies_vector.resize(num_of_copies, -1); @@ -958,12 +958,20 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it // BBS - if (print.config().filament_is_support.get_at(old_extruder)) + if (print.config().filament_is_support.get_at(old_extruder) || print.config().filament_is_support.get_at(new_extruder)) return std::max(0.f, volume_to_wipe); // Support filament cannot be used to print support, infill, wipe_tower, etc. // we will sort objects so that dedicated for wiping are at the beginning: ConstPrintObjectPtrs object_list = print.objects().vector(); - std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().flush_into_objects; }); + // BBS: fix the exception caused by not fixed order between different objects + std::sort(object_list.begin(), object_list.end(), [object_list](const PrintObject* a, const PrintObject* b) { + if (a->config().flush_into_objects != b->config().flush_into_objects) { + return a->config().flush_into_objects.getBool(); + } + else { + return a->id() < b->id(); + } + }); // We will now iterate through // - first the dedicated objects to mark perimeters or infills (depending on infill_first) @@ -1011,9 +1019,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if (!lt.is_extruder_order(lt.wall_filament(region), new_extruder)) continue; - if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) + if ((!is_entity_overridden(fill, object, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder - set_extruder_override(fill, copy, new_extruder, num_of_copies); + set_extruder_override(fill, object, copy, new_extruder, num_of_copies); if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f) // More material was purged already than asked for. return 0.f; @@ -1026,8 +1034,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int { for (const ExtrusionEntity* ee : layerm->perimeters.entities) { auto* fill = dynamic_cast(ee); - if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) { - set_extruder_override(fill, copy, new_extruder, num_of_copies); + if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, object, copy) && fill->total_volume() > min_infill_volume) { + set_extruder_override(fill, object, copy, new_extruder, num_of_copies); if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f) // More material was purged already than asked for. return 0.f; @@ -1117,7 +1125,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) auto* fill = dynamic_cast(ee); if (!is_overriddable(*fill, print.config(), *object, region) - || is_entity_overridden(fill, copy) ) + || is_entity_overridden(fill, object, copy) ) continue; // This infill could have been overridden but was not - unless we do something, it could be @@ -1130,7 +1138,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) //|| object->config().flush_into_objects // in this case the perimeter is overridden, so we can override by the last one safely || lt.is_extruder_order(lt.wall_filament(region), last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints || ! lt.has_extruder(lt.sparse_infill_filament(region)))) // we have to force override - this could violate infill_first (FIXME) - set_extruder_override(fill, copy, (is_infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); + set_extruder_override(fill, object, copy, (is_infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); else { // In this case we can (and should) leave it to be printed normally. // Force overriding would mean it gets printed before its perimeter. @@ -1140,8 +1148,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // Now the same for perimeters - see comments above for explanation: for (const ExtrusionEntity* ee : layerm->perimeters.entities) { // iterate through all perimeter Collections auto* fill = dynamic_cast(ee); - if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy)) - set_extruder_override(fill, copy, (is_infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); + if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, object, copy)) + set_extruder_override(fill, object, copy, (is_infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); } } } @@ -1154,10 +1162,10 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // so -1 was used as "print as usual"). // The resulting vector therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden, // its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero). -const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) +const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, const PrintObject* object, int correct_extruder_id, size_t num_of_copies) { ExtruderPerCopy *overrides = nullptr; - auto entity_map_it = entity_map.find(entity); + auto entity_map_it = entity_map.find(std::make_tuple(entity, object)); if (entity_map_it != entity_map.end()) { overrides = &entity_map_it->second; overrides->resize(num_of_copies, -1); diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 34fdbf49c2..53c6603c63 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -31,7 +31,7 @@ public: typedef boost::container::small_vector ExtruderPerCopy; // This is called from GCode::process_layer - see implementation for further comments: - const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); + const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, const PrintObject* object, int correct_extruder_id, size_t num_of_copies); int get_support_extruder_overrides(const PrintObject* object); int get_support_interface_extruder_overrides(const PrintObject* object); @@ -71,18 +71,18 @@ private: int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const; // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual) - void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies); + void set_extruder_override(const ExtrusionEntity* entity, const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies); // BBS void set_support_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies); void set_support_interface_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies); // Returns true in case that entity is not printed with its usual extruder for a given copy: - bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { - auto it = entity_map.find(entity); + bool is_entity_overridden(const ExtrusionEntity* entity, const PrintObject *object, size_t copy_id) const { + auto it = entity_map.find(std::make_tuple(entity, object)); return it == entity_map.end() ? false : it->second[copy_id] != -1; } - std::map entity_map; // to keep track of who prints what + std::map, ExtruderPerCopy> entity_map; // to keep track of who prints what // BBS std::map support_map; std::map support_intf_map; diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp new file mode 100644 index 0000000000..062a3b3979 --- /dev/null +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +#include "libslic3r/Geometry/Voronoi.hpp" +#include "libslic3r/Arachne/utils/VoronoiUtils.hpp" + +#include "VoronoiUtilsCgal.hpp" + +using VD = Slic3r::Geometry::VoronoiDiagram; + +namespace Slic3r::Geometry { + +using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2; +using CGAL_Segment = CGAL::Arr_segment_traits_2::Curve_2; + +inline static CGAL_Point to_cgal_point(const VD::vertex_type &pt) { return {pt.x(), pt.y()}; } + +// FIXME Lukas H.: Also includes parabolic segments. +bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_diagram) +{ + assert(std::all_of(voronoi_diagram.edges().cbegin(), voronoi_diagram.edges().cend(), + [](const VD::edge_type &edge) { return edge.color() == 0; })); + + std::vector segments; + segments.reserve(voronoi_diagram.num_edges()); + + for (const VD::edge_type &edge : voronoi_diagram.edges()) { + if (edge.color() != 0) + continue; + + if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) { + segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1())); + edge.color(1); + assert(edge.twin() != nullptr); + edge.twin()->color(1); + } + } + + for (const VD::edge_type &edge : voronoi_diagram.edges()) + edge.color(0); + + std::vector intersections_pt; + CGAL::compute_intersection_points(segments.begin(), segments.end(), std::back_inserter(intersections_pt)); + return intersections_pt.empty(); +} + +static bool check_if_three_vectors_are_ccw(const CGAL_Point &common_pt, const CGAL_Point &pt_1, const CGAL_Point &pt_2, const CGAL_Point &test_pt) { + CGAL::Orientation orientation = CGAL::orientation(common_pt, pt_1, pt_2); + if (orientation == CGAL::Orientation::COLLINEAR) { + // The first two edges are collinear, so the third edge must be on the right side on the first of them. + return CGAL::orientation(common_pt, pt_1, test_pt) == CGAL::Orientation::RIGHT_TURN; + } else if (orientation == CGAL::Orientation::LEFT_TURN) { + // CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is bellow PI. + // So we need to check if test_pt isn't between them. + CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt); + CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt); + return (orientation1 != CGAL::Orientation::LEFT_TURN || orientation2 != CGAL::Orientation::RIGHT_TURN); + } else { + assert(orientation == CGAL::Orientation::RIGHT_TURN); + // CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is upper PI. + // So we need to check if test_pt is between them. + CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt); + CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt); + return (orientation1 == CGAL::Orientation::RIGHT_TURN || orientation2 == CGAL::Orientation::LEFT_TURN); + } +} + +bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram) +{ + for (const VD::vertex_type &vertex : voronoi_diagram.vertices()) { + std::vector edges; + const VD::edge_type *edge = vertex.incident_edge(); + + do { + // FIXME Lukas H.: Also process parabolic segments. + if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1())) + edges.emplace_back(edge); + + edge = edge->rot_next(); + } while (edge != vertex.incident_edge()); + + // Checking for CCW make sense for three and more edges. + if (edges.size() > 2) { + for (auto edge_it = edges.begin() ; edge_it != edges.end(); ++edge_it) { + const Geometry::VoronoiDiagram::edge_type *prev_edge = edge_it == edges.begin() ? edges.back() : *std::prev(edge_it); + const Geometry::VoronoiDiagram::edge_type *curr_edge = *edge_it; + const Geometry::VoronoiDiagram::edge_type *next_edge = std::next(edge_it) == edges.end() ? edges.front() : *std::next(edge_it); + + if (!check_if_three_vectors_are_ccw(to_cgal_point(*prev_edge->vertex0()), to_cgal_point(*prev_edge->vertex1()), + to_cgal_point(*curr_edge->vertex1()), to_cgal_point(*next_edge->vertex1()))) + return false; + } + } + } + + return true; +} + + +} // namespace Slic3r::Geometry \ No newline at end of file diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp new file mode 100644 index 0000000000..897891bd93 --- /dev/null +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.hpp @@ -0,0 +1,21 @@ +#ifndef slic3r_VoronoiUtilsCgal_hpp_ +#define slic3r_VoronoiUtilsCgal_hpp_ + +#include "Voronoi.hpp" + +namespace Slic3r::Geometry { +class VoronoiDiagram; + +class VoronoiUtilsCgal +{ +public: + // Check if the Voronoi diagram is planar using CGAL sweeping edge algorithm for enumerating all intersections between lines. + static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram); + + // Check if the Voronoi diagram is planar using verification that all neighboring edges are ordered CCW for each vertex. + static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram); + +}; +} // namespace Slic3r::Geometry + +#endif // slic3r_VoronoiUtilsCgal_hpp_ diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 8e01f599b5..0c2193330e 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -19,6 +19,10 @@ namespace FillAdaptive { struct Octree; }; +namespace FillLightning { + class Generator; +}; + class LayerRegion { public: @@ -166,7 +170,7 @@ public: void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr); } - void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree); + void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr); void make_ironing(); void export_region_slices_to_svg(const char *path) const; @@ -245,7 +249,7 @@ public: ExPolygons floor_areas; ExPolygons base_areas; - enum AreaType { + enum AreaType { BaseType=0, RoofType=1, FloorType=2, diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index e9cf2eed91..4ab0be7d76 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -118,7 +118,8 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly const bool has_infill = this->region().config().sparse_infill_density.value > 0.; //BBS auto nozzle_diameter = this->region().nozzle_dmr_avg(this->layer()->object()->print()->config()); - const float margin = std::min(float(scale_(EXTERNAL_INFILL_MARGIN)), float(scale_(nozzle_diameter * EXTERNAL_INFILL_MARGIN / 0.4))); + const float margin = float(scale_(EXTERNAL_INFILL_MARGIN)); + const float bridge_margin = std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4))); // BBS const PrintObjectConfig& object_config = this->layer()->object()->config(); @@ -224,7 +225,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly break; } // Grown by 3mm. - Polygons polys = offset(bridges[i].expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS); + Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS); if (idx_island == -1) { BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!"; } else { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2451cca7ce..152d91fce3 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -755,12 +755,14 @@ std::string Model::get_backup_path() { if (backup_path.empty()) { + auto pid = get_current_pid(); boost::filesystem::path parent_path(temporary_dir()); std::time_t t = std::time(0); std::tm* now_time = std::localtime(&t); std::stringstream buf; buf << "/bamboo_model/"; buf << std::put_time(now_time, "%a_%b_%d/%H_%M_%S#"); + buf << pid << "#"; buf << this->id().id; backup_path = parent_path.string() + buf.str(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 4c61bacd31..3b1a3145f3 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -222,29 +222,47 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { // get non 100% overhang paths by intersecting this loop with the grown lower slices Polylines remain_polines; - for (auto it = lower_polygons_series->begin(); - it != lower_polygons_series->end(); it++) - { - Polylines inside_polines = (it == lower_polygons_series->begin()) ? - intersection_pl({ polygon }, it->second) : - intersection_pl(remain_polines, it->second); + if (perimeter_generator.config->enable_overhang_speed) { + for (auto it = lower_polygons_series->begin(); + it != lower_polygons_series->end(); it++) + { + + Polylines inside_polines = (it == lower_polygons_series->begin()) ? + intersection_pl({ polygon }, it->second) : + intersection_pl(remain_polines, it->second); + extrusion_paths_append( + paths, + std::move(inside_polines), + it->first, + int(0), + role, + extrusion_mm3_per_mm, + extrusion_width, + (float)perimeter_generator.layer_height); + + remain_polines = (it == lower_polygons_series->begin()) ? + diff_pl({ polygon }, it->second) : + diff_pl(remain_polines, it->second); + + if (remain_polines.size() == 0) + break; + } + } else { + auto it = lower_polygons_series->end(); + it--; + Polylines inside_polines = intersection_pl({ polygon }, it->second); extrusion_paths_append( paths, std::move(inside_polines), - it->first, + int(0), int(0), role, extrusion_mm3_per_mm, extrusion_width, (float)perimeter_generator.layer_height); - remain_polines = (it == lower_polygons_series->begin())? - diff_pl({ polygon }, it->second) : - diff_pl(remain_polines, it->second); - - if (remain_polines.size() == 0) - break; + remain_polines = diff_pl({ polygon }, it->second); } // get 100% overhang paths by checking what parts of this loop fall diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 6e987fd41b..c2373caa5c 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -940,31 +940,31 @@ namespace client auto error_line = std::string(first, first_pos) + std::string(last, 0, last_pos); // Position of the it_error from the start of its line. auto error_pos = (it_error - it_begin) - first_pos; - msg += Slic3r::format(_(L("Error at line %1%:\n")), std::to_string(line_nr)); - //if (! info.tag.empty() && info.tag.front() == '*') { - // // The gat contains an explanatory string. - // msg += ": "; - // msg += info.tag.substr(1); - //} else { - // auto it = tag_to_error_message.find(info.tag); - // if (it == tag_to_error_message.end()) { - // // A generic error report based on the nonterminal or terminal symbol name. - // msg += ". Expecting tag "; - // msg += info.tag; - // } else { - // // Use the human readable error message. - // msg += ". "; - // msg += it->second; - // } - //} - //msg += '\n'; + msg += "Parsing error at line " + std::to_string(line_nr); + if (! info.tag.empty() && info.tag.front() == '*') { + // The gat contains an explanatory string. + msg += ": "; + msg += info.tag.substr(1); + } else { + auto it = tag_to_error_message.find(info.tag); + if (it == tag_to_error_message.end()) { + // A generic error report based on the nonterminal or terminal symbol name. + msg += ". Expecting tag "; + msg += info.tag; + } else { + // Use the human readable error message. + msg += ". "; + msg += it->second; + } + } + msg += '\n'; // This hack removes all non-UTF8 characters from the source line, so that the upstream wxWidgets conversions // from UTF8 to UTF16 don't bail out. msg += boost::nowide::narrow(boost::nowide::widen(error_line)); msg += '\n'; - //for (size_t i = 0; i < error_pos; ++ i) - // msg += ' '; - //msg += "^\n"; + for (size_t i = 0; i < error_pos; ++ i) + msg += ' '; + msg += "^\n"; } }; diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 010692eff7..4ac4e1b69d 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -180,6 +180,11 @@ inline bool operator<(const Point &l, const Point &r) return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); } +inline Point operator* (const Point& l, const double& r) +{ + return { coord_t(l.x() * r), coord_t(l.y() * r) }; +} + inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON)) { Point d = (p2 - p1).cwiseAbs(); diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 78b064e24f..df28742360 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -422,7 +422,7 @@ bool Polyline::split_fitting_result_before_index(const size_t index, Point& new_ if (data.back().is_arc_move() && data.back().end_point_index > index) { if (!data.back().arc_data.clip_end(this->points[index])) //BBS: failed to clip arc, then return to be linear move - data.back().path_type == EMovePathType::Linear_move; + data.back().path_type = EMovePathType::Linear_move; else //BBS: succeed to clip arc, then update and return the new end point new_endpoint = data.back().arc_data.end_point; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 487c44be84..a07163fade 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -2,6 +2,7 @@ #include "Exception.hpp" #include "Preset.hpp" +#include "PresetBundle.hpp" #include "AppConfig.hpp" #ifdef _MSC_VER @@ -643,6 +644,44 @@ std::string Preset::get_filament_type(std::string &display_filament_type) return config.get_filament_type(display_filament_type); } +std::string Preset::get_printer_type(PresetBundle *preset_bundle) +{ + if (preset_bundle) { + auto config = &preset_bundle->printers.get_edited_preset().config; + std::string vendor_name; + for (auto vendor_profile : preset_bundle->vendors) { + for (auto vendor_model : vendor_profile.second.models) + if (vendor_model.name == config->opt_string("printer_model")) + { + vendor_name = vendor_profile.first; + return vendor_model.model_id; + } + } + } + return ""; +} + + +bool Preset::is_bbl_vendor_preset(PresetBundle *preset_bundle) +{ + bool is_bbl_vendor_preset = true; + if (preset_bundle) { + auto config = &preset_bundle->printers.get_edited_preset().config; + std::string vendor_name; + for (auto vendor_profile : preset_bundle->vendors) { + for (auto vendor_model : vendor_profile.second.models) + if (vendor_model.name == config->opt_string("printer_model")) + { + vendor_name = vendor_profile.first; + break; + } + } + if (!vendor_name.empty()) + is_bbl_vendor_preset = vendor_name.compare("BBL") == 0 ? true : false; + } + return is_bbl_vendor_preset; +} + static std::vector s_Preset_print_options { "layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness", @@ -728,7 +767,7 @@ static std::vector s_Preset_printer_options { "default_print_profile", "inherits", "silent_mode", // BBS - "scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", + "scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode", "nozzle_type", "nozzle_diameter", "auxiliary_fan", "nozzle_volume", //SoftFever "host_type", "print_host", "printhost_apikey", diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index f0495f739a..7578bb2393 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -296,6 +296,9 @@ public: // special for upport G and Support W std::string get_filament_type(std::string &display_filament_type); + std::string get_printer_type(PresetBundle *preset_bundle); + + bool is_bbl_vendor_preset(PresetBundle *preset_bundle); static const std::vector& print_options(); static const std::vector& filament_options(); diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index e1da46f20d..c48cfc7426 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -179,8 +179,13 @@ void PresetBundle::setup_directories() boost::filesystem::path subdir = path; subdir.make_preferred(); if (! boost::filesystem::is_directory(subdir) && - ! boost::filesystem::create_directory(subdir)) - throw Slic3r::RuntimeError(std::string("Unable to create directory ") + subdir.string()); + ! boost::filesystem::create_directory(subdir)) { + if (boost::filesystem::is_directory(subdir)) { + BOOST_LOG_TRIVIAL(warning) << boost::format("creating directory %1% failed, maybe created by other instance, go on!")%subdir.string(); + } + else + throw Slic3r::RuntimeError(std::string("Unable to create directory ") + subdir.string()); + } } } @@ -247,6 +252,10 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward else { dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/default"; } + fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + if (!fs::exists(user_folder)) + fs::create_directory(user_folder); + fs::path folder(dir_user_presets); if (!fs::exists(folder)) fs::create_directory(folder); @@ -659,7 +668,7 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector continue; } new_config.apply(std::move(config)); - + Preset &preset = collection->load_preset(collection->path_from_name(name), name, std::move(new_config), false); preset.is_external = true; preset.version = *version; @@ -695,6 +704,11 @@ void PresetBundle::save_user_presets(AppConfig& config, std::vector const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/"+ config.get("preset_folder"); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, save to %1%")%dir_user_presets; + + fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + if (!fs::exists(user_folder)) + fs::create_directory(user_folder); + fs::path folder(dir_user_presets); if (!fs::exists(folder)) fs::create_directory(folder); @@ -712,6 +726,11 @@ void PresetBundle::update_user_presets_directory(const std::string preset_folder const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/"+ preset_folder; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, update directory to %1%")%dir_user_presets; + + fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + if (!fs::exists(user_folder)) + fs::create_directory(user_folder); + fs::path folder(dir_user_presets); if (!fs::exists(folder)) fs::create_directory(folder); @@ -726,6 +745,10 @@ void PresetBundle::remove_user_presets_directory(const std::string preset_folder { const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + preset_folder; + if (preset_folder.empty()) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": preset_folder is empty, no need to remove directory : %1%") % dir_user_presets; + return; + } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, delete directory : %1%") % dir_user_presets; fs::path folder(dir_user_presets); if (fs::exists(folder)) { @@ -838,7 +861,7 @@ void PresetBundle::remove_users_preset(AppConfig& config) } else { printers.select_preset_by_name(printer_selected_preset_name, false); } - + std::string selected_print_name = prints.get_selected_preset().name; bool need_reset_print_preset = false; // remove preset if user_id is not current user @@ -1114,14 +1137,33 @@ void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset:: void PresetBundle::load_installed_filaments(AppConfig &config) { - if (! config.has_section(AppConfig::SECTION_FILAMENTS) - || config.get_section(AppConfig::SECTION_FILAMENTS).empty()) { + //if (! config.has_section(AppConfig::SECTION_FILAMENTS) + // || config.get_section(AppConfig::SECTION_FILAMENTS).empty()) { // Compatibility with the PrusaSlicer 2.1.1 and older, where the filament profiles were not installable yet. // Find all filament profiles, which are compatible with installed printers, and act as if these filament profiles // were installed. std::unordered_set compatible_filaments; for (const Preset &printer : printers) if (printer.is_visible && printer.printer_technology() == ptFFF && printer.vendor && (!printer.vendor->models.empty())) { + bool add_default_materials = true; + if (config.has_section(AppConfig::SECTION_FILAMENTS)) + { + const std::map& installed_filament = config.get_section(AppConfig::SECTION_FILAMENTS); + for (auto filament_iter : installed_filament) + { + Preset* filament = filaments.find_preset(filament_iter.first, false, true); + if (filament && is_compatible_with_printer(PresetWithVendorProfile(*filament, filament->vendor), PresetWithVendorProfile(printer, printer.vendor))) + { + //already has compatible filament + add_default_materials = false; + break; + } + } + } + + if (!add_default_materials) + continue; + for (auto default_filament: printer.vendor->models[0].default_materials) { Preset* filament = filaments.find_preset(default_filament, false, true); @@ -1136,7 +1178,7 @@ void PresetBundle::load_installed_filaments(AppConfig &config) // and mark these filaments as installed, therefore this code will not be executed at the next start of the application. for (const auto &filament: compatible_filaments) config.set(AppConfig::SECTION_FILAMENTS, filament->name, "true"); - } + //} for (auto &preset : filaments) preset.set_visible_from_appconfig(config); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d7c6d1002f..be3ac78096 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1738,13 +1738,13 @@ void Print::finalize_first_layer_convex_hull() // Wipe tower support. bool Print::has_wipe_tower() const { - if (enable_timelapse_print()) - return true; + if (m_config.enable_prime_tower.value == true) { + if (enable_timelapse_print()) + return true; - return - ! m_config.spiral_mode.value && - m_config.enable_prime_tower.value && - m_config.filament_diameter.values.size() > 1; + return !m_config.spiral_mode.value && m_config.filament_diameter.values.size() > 1; + } + return false; } const WipeTowerData& Print::wipe_tower_data(size_t filaments_cnt) const diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 084763db3a..761422e7bf 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -61,6 +61,12 @@ namespace FillAdaptive { using OctreePtr = std::unique_ptr; }; +namespace FillLightning { + class Generator; + struct GeneratorDeleter; + using GeneratorPtr = std::unique_ptr; +}; // namespace FillLightning + // Print step IDs for keeping track of the print state. // The Print steps are applied in this order. enum PrintStep { @@ -466,6 +472,7 @@ private: void combine_infill(); void _generate_support_material(); std::pair prepare_adaptive_infill_data(); + FillLightning::GeneratorPtr prepare_lightning_infill_data(); // BBS SupportNecessaryType is_support_necessary(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 42b529000b..2b581ae03e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -180,7 +180,10 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SlicingMode) static t_config_enum_values s_keys_map_SupportMaterialPattern { { "rectilinear", smpRectilinear }, { "rectilinear-grid", smpRectilinearGrid }, - { "honeycomb", smpHoneycomb } + { "honeycomb", smpHoneycomb }, +#if HAS_LIGHTNING_INFILL + { "lightning", smpLightning }, +#endif }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern) @@ -282,6 +285,7 @@ static const t_config_enum_values s_keys_map_BedType = { CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType) static t_config_enum_values s_keys_map_NozzleType { + { "undefine", int(NozzleType::ntUndefine) }, { "hardened_steel", int(NozzleType::ntHardenedSteel) }, { "stainless_steel",int(NozzleType::ntStainlessSteel) }, { "brass", int(NozzleType::ntBrass) } @@ -371,9 +375,9 @@ void PrintConfigDef::init_common_params() def = this->add("print_host", coString); def->label = L("Hostname, IP or URL"); def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " - "the hostname, IP address or URL of the printer host instance. " - "Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL " - "in the following format: https://username:password@your-octopi-address/"); + "the hostname, IP address or URL of the printer host instance. " + "Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL " + "in the following format: https://username:password@your-octopi-address/"); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); @@ -381,11 +385,11 @@ void PrintConfigDef::init_common_params() def = this->add("printhost_apikey", coString); def->label = L("API Key / Password"); def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain " - "the API Key or the password required for authentication."); + "the API Key or the password required for authentication."); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_port", coString); def->label = L("Printer"); def->tooltip = L("Name of the printer"); @@ -393,27 +397,27 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_cafile", coString); def->label = L("HTTPS CA File"); def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " - "If left blank, the default OS CA certificate repository is used."); + "If left blank, the default OS CA certificate repository is used."); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + // Options used by physical printers - + def = this->add("printhost_user", coString); def->label = L("User"); -// def->tooltip = L(""); + // def->tooltip = L(""); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_password", coString); def->label = L("Password"); -// def->tooltip = L(""); + // def->tooltip = L(""); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); @@ -422,11 +426,11 @@ void PrintConfigDef::init_common_params() def = this->add("printhost_ssl_ignore_revoke", coBool); def->label = L("Ignore HTTPS certificate revocation checks"); def->tooltip = L("Ignore HTTPS certificate revocation checks in case of missing or offline distribution points. " - "One may want to enable this option for self signed certificates if connection fails."); + "One may want to enable this option for self signed certificates if connection fails."); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("preset_names", coStrings); def->label = L("Printer preset names"); def->tooltip = L("Names of presets related to the physical printer"); @@ -435,7 +439,7 @@ void PrintConfigDef::init_common_params() def = this->add("printhost_authorization_type", coEnum); def->label = L("Authorization Type"); -// def->tooltip = L(""); + // def->tooltip = L(""); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("key"); def->enum_values.push_back("user"); @@ -460,22 +464,23 @@ void PrintConfigDef::init_fff_params() const int max_temp = 1500; def = this->add("reduce_crossing_wall", coBool); - def->label = L("Avoid crossing wall when travel"); + def->label = L("Avoid crossing wall"); def->category = L("Quality"); def->tooltip = L("Detour and avoid to travel across wall which may cause blob on surface"); - def->mode = comDevelop; + def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); - def = this->add("max_travel_detour_distance", coFloat); - def->label = L("Max travel detour distance"); + def = this->add("max_travel_detour_distance", coFloatOrPercent); + def->label = L("Avoid crossing wall - Max detour length"); def->category = L("Quality"); def->tooltip = L("Maximum detour distance for avoiding crossing wall. " - "Don't detour if the detour distance is large than this value"); - def->sidetext = L("mm"); + "Don't detour if the detour distance is large than this value. " + "Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. Zero to disable"); + def->sidetext = L("mm or %"); def->min = 0; def->max_literal = 1000; - def->mode = comDevelop; - def->set_default_value(new ConfigOptionFloat(0.)); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); // BBS def = this->add("cool_plate_temp", coInts); @@ -590,7 +595,7 @@ void PrintConfigDef::init_fff_params() "than bottom shell thickness, the bottom shell layers will be increased"); def->full_label = L("Bottom shell layers"); def->min = 0; - def->set_default_value(new ConfigOptionInt(2)); + def->set_default_value(new ConfigOptionInt(3)); def = this->add("bottom_shell_thickness", coFloat); def->label = L("Bottom shell thickness"); @@ -672,7 +677,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Only one wall on top surfaces"); def->category = L("Quality"); def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern"); - def->set_default_value(new ConfigOptionBool(true)); + def->set_default_value(new ConfigOptionBool(false)); def = this->add("only_one_wall_first_layer", coBool); def->label = L("Only one wall on first layer"); @@ -736,7 +741,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); + def->set_default_value(new ConfigOptionFloat(25)); def = this->add("brim_width", coFloat); def->label = L("Brim width"); @@ -846,7 +851,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5000)); + def->set_default_value(new ConfigOptionFloat(500.0)); def = this->add("default_filament_profile", coStrings); def->label = L("Default filament profile"); @@ -893,7 +898,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(20)); + def->set_default_value(new ConfigOptionFloat(10)); def = this->add("machine_end_gcode", coString); def->label = L("End G-code"); @@ -902,7 +907,7 @@ void PrintConfigDef::init_fff_params() def->full_width = true; def->height = 12; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("M104 S0\n")); + def->set_default_value(new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n")); def = this->add("filament_end_gcode", coStrings); def->label = L("End G-code"); @@ -962,7 +967,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(120)); + def->set_default_value(new ConfigOptionFloat(60)); def = this->add("wall_infill_order", coEnum); def->label = L("Order of inner wall/outer wall/infil"); @@ -997,29 +1002,29 @@ void PrintConfigDef::init_fff_params() def = this->add("extruder_clearance_height_to_rod", coFloat); def->label = L("Height to rod"); - def->tooltip = L("Height of the clearance cylinder around extruder. " - "Used as input of auto-arrange to avoid collision when print object by object"); + def->tooltip = L("Distance of the nozzle tip to the lower rod. " + "Used as input of auto-arranging to avoid collision when printing by object"); def->sidetext = L("mm"); def->min = 0; - def->mode = comDevelop; + def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(40)); // BBS def = this->add("extruder_clearance_height_to_lid", coFloat); def->label = L("Height to lid"); - def->tooltip = L("Height of the clearance cylinder around extruder. " - "Used as input of auto-arrange to avoid collision when print object by object"); + def->tooltip = L("Distance of the nozzle tip to the lid. " + "Used as input of auto-arranging to avoid collision when printing by object"); def->sidetext = L("mm"); def->min = 0; - def->mode = comDevelop; + def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(120)); def = this->add("extruder_clearance_radius", coFloat); def->label = L("Radius"); - def->tooltip = L("Clearance radius around extruder. Used as input of auto-arrange to avoid collision when print object by object"); + def->tooltip = L("Clearance radius around extruder. Used as input of auto-arranging to avoid collision when printing by object"); def->sidetext = L("mm"); def->min = 0; - def->mode = comDevelop; + def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(40)); def = this->add("extruder_colour", coStrings); @@ -1257,7 +1262,7 @@ void PrintConfigDef::init_fff_params() //def->enum_values.push_back("octagramspiral"); //def->enum_values.push_back("supportcubic"); #if HAS_LIGHTNING_INFILL - //def->enum_values.push_back("lightning"); + def->enum_values.push_back("lightning"); #endif // HAS_LIGHTNING_INFILL def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Rectilinear")); @@ -1276,7 +1281,7 @@ void PrintConfigDef::init_fff_params() //def->enum_labels.push_back(L("Octagram Spiral")); //def->enum_labels.push_back(L("Support Cubic")); #if HAS_LIGHTNING_INFILL - //def->enum_labels.push_back(L("Lightning")); + def->enum_labels.push_back(L("Lightning")); #endif // HAS_LIGHTNING_INFILL def->set_default_value(new ConfigOptionEnum(ipCubic)); @@ -1310,7 +1315,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(2000)); + def->set_default_value(new ConfigOptionFloat(500)); def = this->add("initial_layer_acceleration", coFloat); def->label = L("Initial layer"); @@ -1502,20 +1507,22 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The metallic material of nozzle. This determines the abrasive resistance of nozzle, and " "what kind of filament can be printed"); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("undefine"); def->enum_values.push_back("hardened_steel"); def->enum_values.push_back("stainless_steel"); def->enum_values.push_back("brass"); + def->enum_labels.push_back(L("Undefine")); def->enum_labels.push_back(L("Hardened steel")); def->enum_labels.push_back(L("Stainless steel")); def->enum_labels.push_back(L("Brass")); - def->mode = comSimple; - def->set_default_value(new ConfigOptionEnum(ntHardenedSteel)); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionEnum(ntUndefine)); def = this->add("auxiliary_fan", coBool); def->label = L("Auxiliary part cooling fan"); def->tooltip = L("Enable this option if machine has auxiliary part cooling fan"); - def->mode = comSimple; - def->set_default_value(new ConfigOptionBool(true)); + def->mode = comDevelop; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("gcode_flavor", coEnum); def->label = L("G-code flavor"); @@ -1533,7 +1540,7 @@ void PrintConfigDef::init_fff_params() //def->enum_values.push_back("machinekit"); //def->enum_values.push_back("smoothie"); //def->enum_values.push_back("no-extrusion"); - def->enum_labels.push_back("Marlin"); + def->enum_labels.push_back("Marlin(legacy)"); //def->enum_labels.push_back("RepRap/Sprinter"); //def->enum_labels.push_back("RepRapFirmware"); //def->enum_labels.push_back("Repetier"); @@ -1592,7 +1599,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(150)); + def->set_default_value(new ConfigOptionFloat(100)); def = this->add("inherits", coString); //def->label = L("Inherits profile"); @@ -1685,7 +1692,16 @@ void PrintConfigDef::init_fff_params() def = this->add("machine_pause_gcode", coString); def->label = L("Pause G-code"); - //def->tooltip = L("This G-code will be used as a code for the pause print"); + def->tooltip = L("This G-code will be used as a code for the pause print. User can insert pause G-code in gcode viewer"); + def->multiline = true; + def->full_width = true; + def->height = 12; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("template_custom_gcode", coString); + def->label = L("Custom G-code"); + def->tooltip = L("This G-code will be used as a custom code"); def->multiline = true; def->full_width = true; def->height = 12; @@ -1701,10 +1717,10 @@ void PrintConfigDef::init_fff_params() }; std::vector axes { // name, max_feedrate, max_acceleration, max_jerk - { "x", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } }, - { "y", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } }, + { "x", { 500., 200. }, { 1000., 1000. }, { 10. , 10. } }, + { "y", { 500., 200. }, { 1000., 1000. }, { 10. , 10. } }, { "z", { 12., 12. }, { 500., 200. }, { 0.2, 0.4 } }, - { "e", { 120., 120. }, { 10000., 5000. }, { 2.5, 2.5 } } + { "e", { 120., 120. }, { 5000., 5000. }, { 2.5, 2.5 } } }; for (const AxisDefault &axis : axes) { std::string axis_upper = boost::to_upper_copy(axis.name); @@ -1795,7 +1811,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->readonly = false; - def->mode = comDevelop; + def->mode = comSimple; def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); @@ -1807,7 +1823,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s²"); def->min = 0; def->readonly = false; - def->mode = comDevelop; + def->mode = comSimple; def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); // M204 T... [mm/sec^2] @@ -1937,7 +1953,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm³"); def->mode = comDevelop; def->readonly = true; - def->set_default_value(new ConfigOptionFloat { 118 }); + def->set_default_value(new ConfigOptionFloat { 0.0 }); def = this->add("reduce_infill_retraction", coBool); def->label = L("Reduce infill retraction"); @@ -1945,7 +1961,7 @@ void PrintConfigDef::init_fff_params() "This can reduce times of retraction for complex model and save printing time, but make slicing and " "G-code generating slower"); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(true)); + def->set_default_value(new ConfigOptionBool(false)); def = this->add("ooze_prevention", coBool); def->label = L("Enable"); @@ -2251,7 +2267,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm²"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(5)); + def->set_default_value(new ConfigOptionFloat(15)); def = this->add("solid_infill_filament", coInt); //def->label = L("Solid infill"); @@ -2327,7 +2343,7 @@ void PrintConfigDef::init_fff_params() def->full_width = true; def->height = 12; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("G28\nG1 Z10 F100\n")); + def->set_default_value(new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n")); def = this->add("filament_start_gcode", coStrings); def->label = L("Start G-code"); @@ -2532,7 +2548,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(0)); + def->set_default_value(new ConfigOptionFloat(0.5)); //BBS def = this->add("support_bottom_interface_spacing", coFloat); @@ -2561,9 +2577,15 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("rectilinear"); def->enum_values.push_back("rectilinear-grid"); def->enum_values.push_back("honeycomb"); +#if HAS_LIGHTNING_INFILL + def->enum_values.push_back("lightning"); +#endif def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Rectilinear grid")); def->enum_labels.push_back(L("Honeycomb")); +#if HAS_LIGHTNING_INFILL + def->enum_labels.push_back(L("Lightning")); +#endif def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(smpRectilinear)); @@ -2765,7 +2787,7 @@ void PrintConfigDef::init_fff_params() "than top shell thickness, the top shell layers will be increased"); def->full_label = L("Top solid layers"); def->min = 0; - def->set_default_value(new ConfigOptionInt(2)); + def->set_default_value(new ConfigOptionInt(4)); def = this->add("top_shell_thickness", coFloat); def->label = L("Top shell thickness"); @@ -2784,7 +2806,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm/s"); def->min = 1; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(200)); + def->set_default_value(new ConfigOptionFloat(120)); def = this->add("travel_speed_z", coFloat); //def->label = L("Z travel"); @@ -3869,10 +3891,7 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments) ConfigOptionEnum* timelapse_opt = this->option>("timelapse_type"); bool is_smooth_timelapse = timelapse_opt != nullptr && timelapse_opt->value == TimelapseType::tlSmooth; - if (is_smooth_timelapse) { - ept_opt->value = true; - } - else if (used_filaments == 1 || ps_opt->value == PrintSequence::ByObject) { + if (!is_smooth_timelapse && (used_filaments == 1 || ps_opt->value == PrintSequence::ByObject)) { ept_opt->value = false; } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 474aa8bdc2..eef0a9efea 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -50,7 +50,7 @@ enum AuthorizationType { atKeyPassword, atUserPassword }; -#define HAS_LIGHTNING_INFILL 0 +#define HAS_LIGHTNING_INFILL 1 enum InfillPattern : int { ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb, @@ -96,6 +96,9 @@ enum class SlicingMode enum SupportMaterialPattern { smpRectilinear, smpRectilinearGrid, smpHoneycomb, +#if HAS_LIGHTNING_INFILL + smpLightning, +#endif // HAS_LIGHTNING_INFILL }; enum SupportMaterialStyle { @@ -178,7 +181,8 @@ enum BedType { // BBS enum NozzleType { - ntHardenedSteel = 0, + ntUndefine = 0, + ntHardenedSteel, ntStainlessSteel, ntBrass, ntCount @@ -794,6 +798,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, travel_speed_z)) ((ConfigOptionBool, silent_mode)) ((ConfigOptionString, machine_pause_gcode)) + ((ConfigOptionString, template_custom_gcode)) //BBS ((ConfigOptionEnum, nozzle_type)) ((ConfigOptionBool, auxiliary_fan)) @@ -807,7 +812,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( //BBS ((ConfigOptionInts, additional_cooling_fan_speed)) ((ConfigOptionBool, reduce_crossing_wall)) - ((ConfigOptionFloat, max_travel_detour_distance)) + ((ConfigOptionFloatOrPercent, max_travel_detour_distance)) ((ConfigOptionPoints, printable_area)) //BBS: add bed_exclude_area ((ConfigOptionPoints, bed_exclude_area)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fc6579775f..60009e1469 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -14,6 +14,7 @@ #include "TriangleMeshSlicer.hpp" #include "Utils.hpp" #include "Fill/FillAdaptive.hpp" +#include "Fill/FillLightning.hpp" #include "Format/STL.hpp" #include "InternalBridgeDetector.hpp" #include "TreeSupport.hpp" @@ -367,14 +368,16 @@ void PrintObject::infill() m_print->set_status(35, L("Generating infill toolpath")); auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + auto lightning_generator = this->prepare_lightning_infill_data(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), - [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get()); } } ); @@ -534,6 +537,18 @@ std::pair PrintObject::prepare support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr()); } +FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() +{ + bool has_lightning_infill = false; + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) + if (const PrintRegionConfig& config = this->printing_region(region_id).config(); config.sparse_infill_density > 0 && config.sparse_infill_pattern == ipLightning) { + has_lightning_infill = true; + break; + } + + return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr(); +} + void PrintObject::clear_layers() { if (!m_shared_object) { @@ -759,6 +774,17 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "top_surface_line_width" || opt_key == "initial_layer_line_width") { steps.emplace_back(posInfill); + } else if (opt_key == "sparse_infill_pattern") { + steps.emplace_back(posInfill); +#if HAS_LIGHTNING_INFILL + const auto *old_fill_pattern = old_config.option>(opt_key); + const auto *new_fill_pattern = new_config.option>(opt_key); + assert(old_fill_pattern && new_fill_pattern); + // We need to recalculate infill surfaces when infill_only_where_needed is enabled, and we are switching from + // the Lightning infill to another infill or vice versa. + if (PrintObject::infill_only_where_needed && (new_fill_pattern->value == ipLightning || old_fill_pattern->value == ipLightning)) + steps.emplace_back(posPrepareInfill); +#endif } else if (opt_key == "sparse_infill_density") { // One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes), // normal infill and 100% (solid) infill. @@ -781,6 +807,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "fuzzy_skin_thickness" || opt_key == "fuzzy_skin_point_distance" || opt_key == "detect_overhang_wall" + //BBS + || opt_key == "enable_overhang_speed" || opt_key == "detect_thin_wall") { steps.emplace_back(posPerimeters); steps.emplace_back(posSupportMaterial); @@ -796,8 +824,6 @@ bool PrintObject::invalidate_state_by_config_options( opt_key == "seam_position" || opt_key == "support_speed" || opt_key == "support_interface_speed" - //BBS - || opt_key == "enable_overhang_speed" || opt_key == "overhang_1_4_speed" || opt_key == "overhang_2_4_speed" || opt_key == "overhang_3_4_speed" diff --git a/src/libslic3r/ProjectTask.hpp b/src/libslic3r/ProjectTask.hpp index 8454acb025..fbcdb91187 100644 --- a/src/libslic3r/ProjectTask.hpp +++ b/src/libslic3r/ProjectTask.hpp @@ -38,6 +38,7 @@ struct FilamentInfo int id; // filament id = extruder id, start with 0. std::string type; std::string color; + std::string filament_id; float used_m; float used_g; int tray_id; // start with 0 diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 13282b90d4..4afa6098fe 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1513,7 +1513,7 @@ static inline Polygons detect_overhangs( const coordf_t max_bridge_length = scale_(object_config.max_bridge_length.value); const bool bridge_no_support = object_config.bridge_no_support.value; - if (layer_id == 0) + if (layer_id == 0) { // Don't fill in the holes. The user may apply a higher raft_expansion if one wants a better 1st layer adhesion. overhang_polygons = to_polygons(layer.lslices); diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 70ffdd5414..5c7fd6e20e 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -11,6 +11,8 @@ #include "I18N.hpp" #include +#include "Fill/FillBase.hpp" + #define _L(s) Slic3r::I18N::translate(s) @@ -283,6 +285,49 @@ static void draw_layer_mst svg.draw(lines, "blue", coord_t(scale_(0.05))); svg.draw_outline(outline, "yellow"); } + +static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2) +{ + if (overhangs1.empty() && overhangs2.empty()) + return; + BoundingBox bbox1 = get_extents(overhangs1); + BoundingBox bbox2 = get_extents(overhangs2); + bbox1.merge(bbox2); + + SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "two_overhangs"), bbox1); + if (!svg.is_opened()) return; + + svg.draw(union_ex(overhangs1), "blue"); + svg.draw(union_ex(overhangs2), "red"); +} + +static void draw_polylines(TreeSupportLayer* ts_layer, Polylines& polylines) +{ + if (polylines.empty()) + return; + BoundingBox bbox = get_extents(polylines); + + SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "lightnings"), bbox); + if (!svg.is_opened()) return; + + int id = 0; + for (Polyline& pline : polylines) + { + int i1, i2; + for (size_t i = 0; i < pline.size() - 1; i++) + { + i1 = i; + i2 = i + 1; + svg.draw(Line(pline.points[i1], pline.points[i2]), "blue"); + svg.draw(pline.points[i1], "red"); + id++; + svg.draw_text(pline.points[i1], std::to_string(id).c_str(), "black", 1); + } + svg.draw(pline.points[i2], "red"); + id++; + svg.draw_text(pline.points[i2], std::to_string(id).c_str(), "black", 1); + } +} #endif // Move point from inside polygon if distance>0, outside if distance<0. @@ -641,7 +686,11 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers; SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; - m_support_params.base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : + m_support_params.base_fill_pattern = +#if HAS_LIGHTNING_INFILL + support_pattern == smpLightning ? ipLightning : +#endif + support_pattern == smpHoneycomb ? ipHoneycomb : m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); @@ -659,7 +708,7 @@ void TreeSupport::detect_object_overhangs() if (m_object->tree_support_layer_count() >= m_object->layer_count()) return; - // Create Tree Support Layers + // Clear and create Tree Support Layers m_object->clear_tree_support_layers(); m_object->clear_tree_support_preview_cache(); @@ -672,11 +721,11 @@ void TreeSupport::detect_object_overhangs() const coordf_t extrusion_width = config.line_width.value; const coordf_t extrusion_width_scaled = scale_(extrusion_width); const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); - const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value; + const bool bridge_no_support = max_bridge_length > 0; const bool support_critical_regions_only = config.support_critical_regions_only.value; const int enforce_support_layers = config.enforce_support_layers.value; - const double area_thresh_well_supported = SQ(scale_(6)); // min: 6x6=36mm^2 - const double length_thresh_well_supported = scale_(6); // min: 6mm + const double area_thresh_well_supported = SQ(scale_(6)); + const double length_thresh_well_supported = scale_(6); static const double sharp_tail_max_support_height = 8.f; // a region is considered well supported if the number of layers below it exceeds this threshold const int thresh_layers_below = 10 / config.layer_height; @@ -773,20 +822,19 @@ void TreeSupport::detect_object_overhangs() region2clusterInd.emplace(®ion, regionClusters.size() - 1); } }; - + // main part of sharptail detections has_sharp_tail = false; if (std::set{stTreeAuto, stHybridAuto, stTree}.count(stype))// == stTreeAuto || stype == stHybridAuto || stype == stTree) { double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value+1) * M_PI / 180.; - ExPolygons regions_well_supported; // regions on buildplate or well supported - std::map region_layers_below; // regions and the number of layers below - ExPolygons lower_overhang_dilated; // for small overhang - - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) - { + ExPolygons regions_well_supported; + std::map region_layers_below; + ExPolygons lower_overhang_dilated; + + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++){ if (m_object->print()->canceled()) break; - + if (!is_auto && layer_nr > enforce_support_layers) continue; @@ -824,7 +872,7 @@ void TreeSupport::detect_object_overhangs() // normal overhang ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); - // overhang_areas = std::move(offset2_ex(overhang_areas, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled)); + overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), [extrusion_width_scaled](ExPolygon &area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), overhang_areas.end()); @@ -912,7 +960,7 @@ void TreeSupport::detect_object_overhangs() // 2.4 if the area grows fast than threshold, it get connected to other part or // it has a sharp slop and will be auto supported. - ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails); + ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails); if (!offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { is_sharp_tail = false; break; @@ -1084,7 +1132,6 @@ void TreeSupport::detect_object_overhangs() // if (erode1.empty() && !inter_with_others.empty()) // blockers[layer_nr].push_back(p_overhang->contour); } - } } @@ -1107,12 +1154,13 @@ void TreeSupport::detect_object_overhangs() for (auto &area : ts_layer->overhang_areas) { ts_layer->overhang_types.emplace(&area, TreeSupportLayer::Detected); } - + // enforcers if (layer_nr < enforcers.size()) { Polygons& enforcer = enforcers[layer_nr]; // coconut: enforcer can't do offset2_ex, otherwise faces with angle near 90 degrees can't have enforcers, which // is not good. For example: tails of animals needs extra support except the lowest tip. //enforcer = std::move(offset2_ex(enforcer, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled)); + enforcer = offset(enforcer, 0.1 * extrusion_width_scaled); for (const Polygon& poly : enforcer) { ts_layer->overhang_areas.emplace_back(poly); ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), TreeSupportLayer::Enforced); @@ -1507,7 +1555,7 @@ void TreeSupport::generate_toolpaths() ExtrusionRole role; Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() : (m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ? support_transition_flow(m_object) : support_flow); - if (with_infill && layer_id > 0) { + if (with_infill && layer_id > 0 && m_support_params.base_fill_pattern != ipLightning) { if (m_support_params.base_fill_pattern == ipRectilinear) { role = erSupportMaterial;// layer_id% num_layers_to_change_infill_direction == 0 ? erSupportTransition : erSupportMaterial; filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);// obj_is_vertical* M_PI_2;// (obj_is_vertical + int(layer_id / num_layers_to_change_infill_direction))* M_PI_2; @@ -1541,6 +1589,67 @@ void TreeSupport::generate_toolpaths() } } } + if (with_infill && m_support_params.base_fill_pattern == ipLightning) + { + double print_z = ts_layer->print_z; + if (printZ_to_lightninglayer.find(print_z) == printZ_to_lightninglayer.end()) + continue; + //TODO: + //1.the second parameter of convertToLines seems to decide how long the lightning should be trimmed from its root, so that the root wont overlap/detach the support contour. + // whether current value works correctly remained to be tested + //2.related to previous one, that lightning roots need to be trimed more when support has multiple walls + //3.function connect_infill() and variable 'params' helps create connection pattern along contours between two lightning roots, + // strengthen lightnings while it may make support harder. decide to enable it or not. if yes, proper values for params are remained to be tested + auto& lightning_layer = generator->getTreesForLayer(printZ_to_lightninglayer[print_z]); + + Flow flow = (layer_id == 0 && m_raft_layers == 0) ? + m_object->print()->brim_flow() : + (m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ? + support_transition_flow(m_object) : + support_flow); + ExPolygons areas = offset_ex(ts_layer->base_areas, -flow.scaled_spacing()); + + for (auto& area : areas) + { + Polylines polylines = lightning_layer.convertToLines(to_polygons(area), 0); + for (auto itr = polylines.begin(); itr != polylines.end();) + { + if (itr->length() < scale_(1.0)) + itr = polylines.erase(itr); + else + itr++; + } + Polylines opt_polylines; +#if 1 + //this wont create connection patterns along contours + append(opt_polylines, chain_polylines(std::move(polylines))); +#else + //this will create connection patterns along contours + FillParams params; + params.anchor_length = float(Fill::infill_anchor * 0.01 * flow.spacing()); + params.anchor_length_max = Fill::infill_anchor_max; + params.anchor_length = std::min(params.anchor_length, params.anchor_length_max); + Fill::connect_infill(std::move(polylines), area, opt_polylines, flow.spacing(), params); +#endif + extrusion_entities_append_paths(ts_layer->support_fills.entities, opt_polylines, erSupportMaterial, + float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); + +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + std::string prefix = "./SVG/"; + std::string suffix = ".svg"; + std::string name = prefix + "trees_polyline" + "_" + std::to_string(ts_layer->print_z) /*+ "_" + std::to_string(rand_num)*/ + suffix; + BoundingBox bbox = get_extents(ts_layer->base_areas); + SVG svg(name, bbox); + + svg.draw(ts_layer->base_areas, "blue"); + svg.draw(generator->Overhangs()[printZ_to_lightninglayer[print_z]], "red"); + for (auto& line : opt_polylines) + { + svg.draw(line, "yellow"); + } +#endif + } + } // sort extrusions to reduce travel, also make sure walls go before infills chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities); @@ -1761,30 +1870,30 @@ void TreeSupport::generate_support_areas() if (!tree_support_enable) return; - std::vector> contact_nodes(m_object->layers().size()); //Generate empty layers to store the points in. + std::vector> contact_nodes(m_object->layers().size()); profiler.stage_start(STAGE_total); // Generate overhang areas profiler.stage_start(STAGE_DETECT_OVERHANGS); m_object->print()->set_status(55, _L("Support: detect overhangs")); - detect_object_overhangs(); + detect_object_overhangs(); // Entry of step#1; profiler.stage_finish(STAGE_DETECT_OVERHANGS); // Generate contact points of tree support profiler.stage_start(STAGE_GENERATE_CONTACT_NODES); m_object->print()->set_status(56, _L("Support: generate contact points")); - generate_contact_points(contact_nodes); + generate_contact_points(contact_nodes); // Entry of step#2; profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES); //Drop nodes to lower layers. profiler.stage_start(STAGE_DROP_DOWN_NODES); m_object->print()->set_status(60, _L("Support: propagate branches")); - drop_nodes(contact_nodes); + drop_nodes(contact_nodes); // Entry of step#3; profiler.stage_finish(STAGE_DROP_DOWN_NODES); // Adjust support layer heights - adjust_layer_heights(contact_nodes); + adjust_layer_heights(contact_nodes); // Entry of step#4; //Generate support areas. profiler.stage_start(STAGE_DRAW_CIRCLES); @@ -1866,7 +1975,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no const int CIRCLE_RESOLUTION = SQUARE_SUPPORT ? 4 : 100; // The number of vertices in each circle. - for (unsigned int i = 0; i < CIRCLE_RESOLUTION; i++) + for (int i = 0; i < CIRCLE_RESOLUTION; i++) { double angle; if (SQUARE_SUPPORT) @@ -1895,6 +2004,15 @@ void TreeSupport::draw_circles(const std::vector>& contact_no const coordf_t line_width = config.support_line_width; const coordf_t line_width_scaled = scale_(line_width); + const bool with_lightning_infill = config.tree_support_with_infill.value && config.support_base_pattern.value == smpLightning; + coordf_t support_extrusion_width = config.support_line_width.value > 0 ? config.support_line_width : config.line_width; + const size_t wall_count = config.tree_support_wall_count.value; + + const PrintObjectConfig& object_config = m_object->config(); + auto m_support_material_flow = support_material_flow(m_object, float(m_slicing_params.layer_height)); + coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing(); + coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); + // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map tbb::parallel_for( tbb::blocked_range(0, m_object->layer_count()), @@ -1924,6 +2042,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; ExPolygons& floor_areas = ts_layer->floor_areas; + BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. ts_layer->lslices.reserve(contact_nodes[layer_nr].size()); for (const Node* p_node : contact_nodes[layer_nr]) @@ -2060,100 +2179,168 @@ void TreeSupport::draw_circles(const std::vector>& contact_no }); #if 1 - // move the holes to contour so they can be well supported - if (!has_infill) { - // check if poly's contour intersects with expoly's contour - auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point &pt_on_poly, Point &pt_on_expoly, Point &pt_far_on_poly, float dist_thresh = 0.01) { - float min_dist = std::numeric_limits::max(); - float max_dist = 0; - for (auto from : poly.points) { - for (int i = 0; i < expoly.num_contours(); i++) { - const Point *candidate = expoly.contour_or_hole(i).closest_point(from); - double dist2 = vsize2_with_unscale(*candidate - from); - if (dist2 < min_dist) { - min_dist = dist2; - pt_on_poly = from; - pt_on_expoly = *candidate; - } - if (dist2 > max_dist) { - max_dist = dist2; - pt_far_on_poly = from; - } - if (dist2 < dist_thresh) { return true; } + if (with_lightning_infill) + { + const bool global_lightning_infill = true; + + std::vector contours; + std::vector overhangs; + for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) { + if (print->canceled()) break; + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); + assert(ts_layer != nullptr); + + // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355) + if (curr_layer_nodes.empty()) continue; + if (ts_layer->height < EPSILON) continue; + if (ts_layer->area_groups.empty()) continue; + + ExPolygons& base_areas = ts_layer->base_areas; + + int layer_nr_lower = layer_nr - 1; + for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { + if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; } + TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers); + ExPolygons& base_areas_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->base_areas; + + ExPolygons overhang; + + if (layer_nr_lower == 0) + continue; + + if (global_lightning_infill) + { + //search overhangs globally + overhang = std::move(diff_ex(offset_ex(base_areas_lower, -2.0 * scale_(support_extrusion_width)), base_areas)); + } + else + { + //search overhangs only on floating islands + for (auto& base_area : base_areas) + for (auto& hole : base_area.holes) + { + Polygon rev_hole = hole; + rev_hole.make_counter_clockwise(); + ExPolygons ex_hole = to_expolygons(ExPolygon(rev_hole)); + for (auto& other_area : base_areas) + //if (&other_area != &base_area) + ex_hole = std::move(diff_ex(ex_hole, other_area)); + overhang = std::move(union_ex(overhang, ex_hole)); + } + overhang = std::move(intersection_ex(overhang, offset_ex(base_areas_lower, -0.5 * scale_(support_extrusion_width)))); + } + + overhangs.emplace_back(to_polygons(overhang)); + contours.emplace_back(to_polygons(base_areas_lower)); //cant guarantee for 100% success probability, infill fails sometimes + printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; + +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + draw_two_overhangs_to_svg(m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back())); +#endif } - return false; - }; - // polygon pointer: depth, direction, farPoint - std::map> holePropagationInfos; - for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { - if (print->canceled()) break; - m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); + generator = std::make_unique(m_object, contours, overhangs, support_density); + } - const std::vector &curr_layer_nodes = contact_nodes[layer_nr]; - TreeSupportLayer * ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); - assert(ts_layer != nullptr); + else if (!has_infill) { + // move the holes to contour so they can be well supported - // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355) - if (curr_layer_nodes.empty()) continue; - if (ts_layer->height < EPSILON) continue; - if (ts_layer->area_groups.empty()) continue; - - int layer_nr_lower = layer_nr - 1; - for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { - if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; - } - auto &area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups; - - for (const auto &area_group : ts_layer->area_groups) { - if (area_group.second != TreeSupportLayer::BaseType) continue; - const auto area = area_group.first; - for (const auto &hole : area->holes) { - // auto hole_bbox = get_extents(hole).polygon(); - for (auto &area_group_lower : area_groups_lower) { - if (area_group.second != TreeSupportLayer::BaseType) continue; - auto &base_area_lower = *area_group_lower.first; - Point pt_on_poly, pt_on_expoly, pt_far_on_poly; - // if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour - if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) { - Polygon hole_lower = hole; - Point direction = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2); - hole_lower.translate(direction); - // note to expand a hole, we need to do negative offset - auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, ClipperLib::JoinType::jtSquare); - if (!hole_expanded.empty()) { - base_area_lower.holes.push_back(std::move(hole_expanded[0])); - holePropagationInfos.insert({&base_area_lower.holes.back(), {25, direction, pt_far_on_poly}}); + // check if poly's contour intersects with expoly's contour + auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point& pt_on_poly, Point& pt_on_expoly, Point& pt_far_on_poly, float dist_thresh = 0.01) { + float min_dist = std::numeric_limits::max(); + float max_dist = 0; + for (auto from : poly.points) { + for (int i = 0; i < expoly.num_contours(); i++) { + const Point* candidate = expoly.contour_or_hole(i).closest_point(from); + double dist2 = vsize2_with_unscale(*candidate - from); + if (dist2 < min_dist) { + min_dist = dist2; + pt_on_poly = from; + pt_on_expoly = *candidate; } - break; - } else if (holePropagationInfos.find(&hole) != holePropagationInfos.end() && std::get<0>(holePropagationInfos[&hole]) > 0 && - base_area_lower.contour.contains(std::get<2>(holePropagationInfos[&hole]))) { - Polygon hole_lower = hole; - auto && direction = std::get<1>(holePropagationInfos[&hole]); - hole_lower.translate(direction); - // note to shrink a hole, we need to do positive offset - auto hole_expanded = offset(hole_lower, line_width_scaled / 2, ClipperLib::JoinType::jtSquare); - Point farPoint = std::get<2>(holePropagationInfos[&hole]) + direction * 2; - if (!hole_expanded.empty()) { - base_area_lower.holes.push_back(std::move(hole_expanded[0])); - holePropagationInfos.insert({&base_area_lower.holes.back(), {std::get<0>(holePropagationInfos[&hole]) - 1, direction, farPoint}}); + if (dist2 > max_dist) { + max_dist = dist2; + pt_far_on_poly = from; } - break; + if (dist2 < dist_thresh) { return true; } } } - { + return false; + }; + + // polygon pointer: depth, direction, farPoint + std::map> holePropagationInfos; + for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { + if (print->canceled()) break; + m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); + + const std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); + assert(ts_layer != nullptr); + + // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355) + if (curr_layer_nodes.empty()) continue; + if (ts_layer->height < EPSILON) continue; + if (ts_layer->area_groups.empty()) continue; + + int layer_nr_lower = layer_nr - 1; + for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { + if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; + } + if (layer_nr_lower < 0) continue; + auto& area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups; + + for (const auto& area_group : ts_layer->area_groups) { + if (area_group.second != TreeSupportLayer::BaseType) continue; + const auto area = area_group.first; + for (const auto& hole : area->holes) { + // auto hole_bbox = get_extents(hole).polygon(); + for (auto& area_group_lower : area_groups_lower) { + if (area_group.second != TreeSupportLayer::BaseType) continue; + auto& base_area_lower = *area_group_lower.first; + Point pt_on_poly, pt_on_expoly, pt_far_on_poly; + // if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour + if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) { + Polygon hole_lower = hole; + Point direction = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2); + hole_lower.translate(direction); + // note to expand a hole, we need to do negative offset + auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, ClipperLib::JoinType::jtSquare); + if (!hole_expanded.empty()) { + base_area_lower.holes.push_back(std::move(hole_expanded[0])); + holePropagationInfos.insert({ &base_area_lower.holes.back(), {25, direction, pt_far_on_poly} }); + } + break; + } + else if (holePropagationInfos.find(&hole) != holePropagationInfos.end() && std::get<0>(holePropagationInfos[&hole]) > 0 && + base_area_lower.contour.contains(std::get<2>(holePropagationInfos[&hole]))) { + Polygon hole_lower = hole; + auto&& direction = std::get<1>(holePropagationInfos[&hole]); + hole_lower.translate(direction); + // note to shrink a hole, we need to do positive offset + auto hole_expanded = offset(hole_lower, line_width_scaled / 2, ClipperLib::JoinType::jtSquare); + Point farPoint = std::get<2>(holePropagationInfos[&hole]) + direction * 2; + if (!hole_expanded.empty()) { + base_area_lower.holes.push_back(std::move(hole_expanded[0])); + holePropagationInfos.insert({ &base_area_lower.holes.back(), {std::get<0>(holePropagationInfos[&hole]) - 1, direction, farPoint} }); + } + break; + } + } + { // if roof1 interface is inside a hole, need to expand the interface - for (auto &roof1 : ts_layer->roof_1st_layer) { + for (auto& roof1 : ts_layer->roof_1st_layer) { //if (hole.contains(roof1.contour.points.front()) && hole.contains(roof1.contour.bounding_box().center())) - bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point &pt) { return hole.contains(pt); }); + bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point& pt) { return hole.contains(pt); }); if (is_inside_hole) { Polygon hole_reoriented = hole; if (roof1.contour.is_counter_clockwise()) hole_reoriented.make_counter_clockwise(); else if (roof1.contour.is_clockwise()) hole_reoriented.make_clockwise(); - auto tmp = union_({roof1.contour}, {hole_reoriented}); + auto tmp = union_({ roof1.contour }, { hole_reoriented }); if (!tmp.empty()) roof1.contour = tmp[0]; // make sure 1) roof1 and object 2) roof1 and roof, won't intersect @@ -2163,38 +2350,38 @@ void TreeSupport::draw_circles(const std::vector>& contact_no tmp1 = diff_ex(tmp1, ts_layer->roof_areas); if (!tmp1.empty()) { roof1.contour = std::move(tmp1[0].contour); - roof1.holes = std::move(tmp1[0].holes); - } + roof1.holes = std::move(tmp1[0].holes); + } break; + } } } + } } } } - } - } #endif #ifdef SUPPORT_TREE_DEBUG_TO_SVG - for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { - TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); - ExPolygons& base_areas = ts_layer->base_areas; - ExPolygons& roof_areas = ts_layer->roof_areas; - ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; - ExPolygons& floor_areas = ts_layer->floor_areas; - if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); - draw_contours_and_nodes_to_svg(-1, base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"}); - } + for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { + TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); + ExPolygons& base_areas = ts_layer->base_areas; + ExPolygons& roof_areas = ts_layer->roof_areas; + ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; + ExPolygons& floor_areas = ts_layer->floor_areas; + if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; + char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); + draw_contours_and_nodes_to_svg(-1, base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), { "base", "roof", "roof1st" }); + } #endif - TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers(); - auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; }); - ts_layers.erase(iter, ts_layers.end()); - for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) { - ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr; - ts_layers[layer_nr]->lower_layer = layer_nr > 0 ? ts_layers[layer_nr - 1] : nullptr; - } + TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers(); + auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; }); + ts_layers.erase(iter, ts_layers.end()); + for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) { + ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr; + ts_layers[layer_nr]->lower_layer = layer_nr > 0 ? ts_layers[layer_nr - 1] : nullptr; + } } void TreeSupport::drop_nodes(std::vector>& contact_nodes) @@ -2268,16 +2455,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) continue; m_object->print()->set_status(60, (boost::format(_L("Support: propagate branches at layer %d")) % layer_nr).str()); - for (Node* p_node : layer_contact_nodes) - { - if (p_node->type == ePolygon) { - const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position); - Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, to_buildplate, p_node, - m_object->get_layer(layer_nr - 1)->print_z, m_object->get_layer(layer_nr - 1)->height); - contact_nodes[layer_nr - 1].emplace_back(next_node); - } - } - Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr)); //std::unordered_map& mst_line_x_layer_contour_cache = m_mst_line_x_layer_contour_caches[layer_nr]; std::unordered_map mst_line_x_layer_contour_cache; @@ -2320,6 +2497,10 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } if (node.type == ePolygon) { // polygon node do not merge or move + const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position); + Node *next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, to_buildplate, p_node, + m_object->get_layer(layer_nr - 1)->print_z, m_object->get_layer(layer_nr - 1)->height); + contact_nodes[layer_nr - 1].emplace_back(next_node); continue; } /* Find which part this node is located in and group the nodes in @@ -2409,7 +2590,8 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) size_t new_support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour->support_roof_layers_below) - 1; const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr - 1), next_position); - Node* next_node = new Node(next_position, new_distance_to_top, node.skin_direction, new_support_roof_layers_below, to_buildplate, p_node,p_node->print_z,p_node->height); + Node * next_node = new Node(next_position, new_distance_to_top, node.skin_direction, new_support_roof_layers_below, to_buildplate, p_node, + m_object->get_layer(layer_nr - 1)->print_z, p_node->height); next_node->movement = next_position - node.position; contact_nodes[layer_nr - 1].push_back(next_node); @@ -2457,21 +2639,20 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) { unsupported_branch_leaves.push_front({ layer_nr, p_node }); } - /*else { + else { Node* pn = p_node; - for (int i = 0; i < bottom_interface_layers && pn; i++, pn = pn->parent) - pn->support_floor_layers_above = bottom_interface_layers - i; + for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) + pn->support_floor_layers_above = bottom_interface_layers - i + 1; // +1 so the parent node has support_floor_layers_above=2 to_delete.insert(p_node); - }*/ + } continue; } - // if the link between parent and current is cut by contours, delete this branch + // if the link between parent and current is cut by contours, mark current as bottom contact node if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false) { - //unsupported_branch_leaves.push_front({ layer_nr, p_node }); Node* pn = p_node->parent; - for (int i = 0; i < bottom_interface_layers && pn; i++, pn = pn->parent) - pn->support_floor_layers_above = bottom_interface_layers - i; + for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent) + pn->support_floor_layers_above = bottom_interface_layers - i + 1; to_delete.insert(p_node); continue; } diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index 2b562d52e7..610908ac1b 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -10,6 +10,7 @@ #include "tbb/concurrent_unordered_map.h" #include "Flow.hpp" #include "PrintConfig.hpp" +#include "Fill/Lightning/Generator.hpp" #ifndef SQ #define SQ(x) ((x)*(x)) @@ -350,6 +351,9 @@ public: int avg_node_per_layer = 0; float nodes_angle = 0; bool has_sharp_tail; + + std::unique_ptr generator; + std::unordered_map printZ_to_lightninglayer; private: /*! * \brief Generator for model collision, avoidance and internal guide volumes diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 6182dfe641..6b124d92f9 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -399,7 +399,7 @@ struct SlabLines { std::vector at_slice; // Projections of triangle set boundary lines into layer below (for projection from the top) // or into layer above (for projection from the bottom). - // In both cases the intersection liens are CCW oriented. + // In both cases the intersection lines are CCW oriented. std::vector between_slices; }; @@ -790,7 +790,8 @@ inline std::pair slice_slabs_make_lines( } slice_facet_with_slabs(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top); } - if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) { + // BBS: add vertical faces option + if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Vertical || fo == FaceOrientation::Degenerate)) { Vec3i neighbors = face_neighbors[face_idx]; // Reset neighborship of this triangle in case the other triangle is oriented backwards from this one. for (int i = 0; i < 3; ++ i) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 019a5f8770..3a5e539bc4 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -70,12 +70,8 @@ static constexpr double SUPPORT_RESOLUTION = 0.05; static constexpr double INSET_OVERLAP_TOLERANCE = 0.4; // 3mm ring around the top / bottom / bridging areas. //FIXME This is quite a lot. -// BBS: 3mm is too large and will cause overflow when printing object which likes shell. -// We decided to reduce this value according to superslicer. -// The right way is that area should not be enlarged. But should find arched point at last layer, expecially for -// bridge area. -//static constexpr double EXTERNAL_INFILL_MARGIN = 3; -static constexpr double EXTERNAL_INFILL_MARGIN = 1; +static constexpr double EXTERNAL_INFILL_MARGIN = 3; +static constexpr double BRIDGE_INFILL_MARGIN = 1; //FIXME Better to use an inline function with an explicit return type. //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); } #define scale_(val) ((val) / SCALING_FACTOR) diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in index dc843d7233..15f5803db5 100644 --- a/src/libslic3r/libslic3r_version.h.in +++ b/src/libslic3r/libslic3r_version.h.in @@ -7,5 +7,6 @@ #define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@" //#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@" #define BBL_RELEASE_TO_PUBLIC @BBL_RELEASE_TO_PUBLIC@ +#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@ #endif /* __SLIC3R_VERSION_H */ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 645bad5538..cfa3193952 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -205,6 +205,8 @@ set(SLIC3R_GUI_SOURCES GUI/ParamsDialog.hpp GUI/ParamsPanel.cpp GUI/ParamsPanel.hpp + GUI/PrintHostDialogs.cpp + GUI/PrintHostDialogs.hpp GUI/AmsWidgets.cpp GUI/AmsWidgets.hpp GUI/MediaFilePanel.cpp @@ -215,6 +217,8 @@ set(SLIC3R_GUI_SOURCES GUI/MonitorBasePanel.h GUI/UpgradePanel.cpp GUI/UpgradePanel.hpp + GUI/HintNotification.hpp + GUI/HintNotification.cpp GUI/HMSPanel.hpp GUI/HMSPanel.cpp GUI/MonitorPage.cpp @@ -303,10 +307,14 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/ProgressIndicator.hpp GUI/Jobs/PrintJob.hpp GUI/Jobs/PrintJob.cpp + GUI/Jobs/SendJob.hpp + GUI/Jobs/SendJob.cpp GUI/Jobs/BindJob.hpp GUI/Jobs/BindJob.cpp GUI/Jobs/NotificationProgressIndicator.hpp GUI/Jobs/NotificationProgressIndicator.cpp + GUI/PhysicalPrinterDialog.hpp + GUI/PhysicalPrinterDialog.cpp GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.cpp GUI/BBLStatusBar.hpp @@ -348,10 +356,14 @@ set(SLIC3R_GUI_SOURCES GUI/PublishDialog.hpp GUI/RecenterDialog.cpp GUI/RecenterDialog.hpp + GUI/BonjourDialog.cpp + GUI/BonjourDialog.hpp GUI/BindDialog.cpp GUI/BindDialog.hpp GUI/SelectMachine.hpp GUI/SelectMachine.cpp + GUI/SendToPrinter.hpp + GUI/SendToPrinter.cpp GUI/AmsMappingPopup.hpp GUI/AmsMappingPopup.cpp GUI/ReleaseNote.hpp @@ -389,6 +401,10 @@ set(SLIC3R_GUI_SOURCES Utils/minilzo_extension.cpp Utils/ColorSpaceConvert.hpp Utils/ColorSpaceConvert.cpp + Utils/OctoPrint.hpp + Utils/OctoPrint.cpp + Utils/PrintHost.hpp + Utils/PrintHost.cpp Utils/NetworkAgent.cpp Utils/NetworkAgent.hpp Utils/OctoPrint.cpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 935677ee15..31f34e263b 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -251,7 +251,9 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig m_extended_bounding_box = this->calc_extended_bounding_box(false); //BBS: add part plate logic -/*#if 0 + + //BBS add default bed +#if 1 ExPolygon poly{ Polygon::new_scale(printable_area) }; #else ExPolygon poly; @@ -265,7 +267,7 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig const BoundingBox& bed_bbox = poly.contour.bounding_box(); calc_gridlines(poly, bed_bbox); - m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];*/ + m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0]; if (with_reset) { this->release_VBOs(); @@ -375,7 +377,7 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box(bool consider_model_offset) cons return out; } -/*void Bed3D::calc_triangles(const ExPolygon& poly) +void Bed3D::calc_triangles(const ExPolygon& poly) { if (! m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) BOOST_LOG_TRIVIAL(error) << "Unable to create bed triangles"; @@ -406,7 +408,7 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n"; -}*/ +} // Try to match the print bed shape with the shape of an active profile. If such a match exists, // return the print bed model. @@ -611,6 +613,36 @@ void Bed3D::update_model_offset() const const_cast(m_extended_bounding_box) = calc_extended_bounding_box(); } +GeometryBuffer Bed3D::update_bed_triangles() const +{ + GeometryBuffer new_triangles; + Vec3d shift = m_extended_bounding_box.center(); + shift(2) = -0.03; + Vec3d* model_offset_ptr = const_cast(&m_model_offset); + *model_offset_ptr = shift; + //BBS: TODO: hack for default bed + BoundingBoxf3 build_volume; + + if (!m_build_volume.valid()) return new_triangles; + + (*model_offset_ptr)(0) = m_build_volume.bounding_volume2d().min.x(); + (*model_offset_ptr)(1) = m_build_volume.bounding_volume2d().min.y(); + (*model_offset_ptr)(2) = -0.41 + GROUND_Z; + + std::vector new_bed_shape; + for (auto point: m_bed_shape) { + Vec2d new_point(point.x() + model_offset_ptr->x(), point.y() + model_offset_ptr->y()); + new_bed_shape.push_back(new_point); + } + ExPolygon poly{ Polygon::new_scale(new_bed_shape) }; + if (!new_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) { + ; + } + // update extended bounding box + const_cast(m_extended_bounding_box) = calc_extended_bounding_box(); + return new_triangles; +} + void Bed3D::render_model() const { if (m_model_filename.empty()) @@ -654,9 +686,11 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const void Bed3D::render_default(bool bottom) const { - /*const_cast(&m_texture)->reset(); + bool picking = false; + const_cast(&m_texture)->reset(); unsigned int triangles_vcount = m_triangles.get_vertices_count(); + GeometryBuffer default_triangles = update_bed_triangles(); if (triangles_vcount > 0) { bool has_model = !m_model.get_filename().empty(); @@ -671,7 +705,7 @@ void Bed3D::render_default(bool bottom) const glsafe(::glDepthMask(GL_FALSE)); glsafe(::glColor4fv(picking ? PICKING_MODEL_COLOR.data() : DEFAULT_MODEL_COLOR.data())); glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); - glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); + glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)default_triangles.get_vertices_data())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDepthMask(GL_TRUE)); } @@ -683,14 +717,14 @@ void Bed3D::render_default(bool bottom) const glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f)); else glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f)); - glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); + glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); } glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glDisable(GL_BLEND)); - }*/ + } } void Bed3D::release_VBOs() diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 205e9b0320..984c4f7de8 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_3DBed_hpp_ #define slic3r_3DBed_hpp_ -//#include "GLTexture.hpp" +#include "GLTexture.hpp" #include "3DScene.hpp" #include "GLModel.hpp" @@ -90,10 +90,10 @@ private: // Print volume bounding box exteded with axes and model. BoundingBoxf3 m_extended_bounding_box; // Slightly expanded print bed polygon, for collision detection. - //Polygon m_polygon; - //GeometryBuffer m_triangles; - //GeometryBuffer m_gridlines; - //GLTexture m_texture; + Polygon m_polygon; + GeometryBuffer m_triangles; + GeometryBuffer m_gridlines; + GLTexture m_texture; // temporary texture shown until the main texture has still no levels compressed //GLTexture m_temp_texture; GLModel m_model; @@ -148,6 +148,8 @@ private: void calc_triangles(const ExPolygon& poly); void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); void update_model_offset() const; + //BBS: with offset + GeometryBuffer update_bed_triangles() const; static std::tuple detect_type(const Pointfs& shape); void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 8f48579a45..a6ea563e11 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -869,6 +869,10 @@ void GLVolume::render(bool with_outline) const std::array body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red shader->set_uniform("uniform_color", body_color); + //if (GUI::wxGetApp().plater()->is_show_wireframe()) + // shader->set_uniform("show_wireframe", true); + //else + // shader->set_uniform("show_wireframe", false); shader->set_uniform("is_outline", true); glsafe(::glPopMatrix()); glsafe(::glPushMatrix()); diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index fa99b15f78..2a09ae1805 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -244,7 +244,11 @@ AboutDialog::AboutDialog() // version { vesizer->Add(0, FromDIP(165), 1, wxEXPAND, FromDIP(5)); +#if BBL_INTERNAL_TESTING + auto version_string = _L("Internal Version") + " " + std::string(SLIC3R_VERSION); +#else auto version_string = _L("Version") + " " + std::string(SLIC3R_VERSION); +#endif wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize); wxFont version_font = GetFont(); #ifdef __WXMSW__ diff --git a/src/slic3r/GUI/Auxiliary.cpp b/src/slic3r/GUI/Auxiliary.cpp index e7756fd350..6b2bca8079 100644 --- a/src/slic3r/GUI/Auxiliary.cpp +++ b/src/slic3r/GUI/Auxiliary.cpp @@ -139,6 +139,8 @@ void AuFile::enter_rename_mode() { m_input_name->Show(); m_text_name->Hide(); + auto name = m_file_name.SubString(0, (m_file_name.Find(".") - 1)); + m_input_name->GetTextCtrl()->SetLabelText(name); Layout(); } diff --git a/src/slic3r/GUI/Calibration.cpp b/src/slic3r/GUI/Calibration.cpp index 8b10e1198a..6d775f66cf 100644 --- a/src/slic3r/GUI/Calibration.cpp +++ b/src/slic3r/GUI/Calibration.cpp @@ -158,7 +158,7 @@ void CalibrationDialog::on_dpi_changed(const wxRect &suggested_rect) {} void CalibrationDialog::update_cali(MachineObject *obj) { if (!obj) return; - if (obj->is_in_calibration() || obj->is_calibration_done()) { + if (obj->is_calibration_running() || obj->is_calibration_done()) { if (obj->is_calibration_done()) { m_calibration_btn->Enable(); m_calibration_btn->SetLabel(_L("Completed")); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 1e09706ce2..3f7e40b957 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -560,9 +560,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : { "ironing_flow", "ironing_spacing", "ironing_speed" }) toggle_field(el, has_ironing); - bool have_sequential_printing = (config->opt_enum("print_sequence") == PrintSequence::ByObject); - for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) - toggle_field(el, have_sequential_printing); + // bool have_sequential_printing = (config->opt_enum("print_sequence") == PrintSequence::ByObject); + // for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) + // toggle_field(el, have_sequential_printing); bool have_ooze_prevention = config->opt_bool("ooze_prevention"); toggle_field("standby_temperature_delta", have_ooze_prevention); diff --git a/src/slic3r/GUI/ConfirmHintDialog.cpp b/src/slic3r/GUI/ConfirmHintDialog.cpp index c930e313b6..c64aea79f7 100644 --- a/src/slic3r/GUI/ConfirmHintDialog.cpp +++ b/src/slic3r/GUI/ConfirmHintDialog.cpp @@ -9,7 +9,7 @@ namespace Slic3r { namespace GUI { wxDEFINE_EVENT(EVT_CONFIRM_HINT, wxCommandEvent); -ConfirmHintDialog::ConfirmHintDialog(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) +ConfirmHintDialog::ConfirmHintDialog(wxWindow* parent, wxWindowID id, const wxString& title, enum ButtonStyle btn_style, const wxPoint& pos, const wxSize& size, long style) : DPIDialog(parent, id, title, pos, size, style) { std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); @@ -46,6 +46,11 @@ ConfirmHintDialog::ConfirmHintDialog(wxWindow* parent, wxWindowID id, const wxSt button_sizer->AddSpacer(FromDIP(20)); button_sizer->Add(m_button_close); + if (btn_style == CONFIRM_AND_CANCEL) + m_button_close->Show(); + else + m_button_close->Hide(); + main_sizer->Add(m_line_top, 0, wxEXPAND, 0); main_sizer->AddSpacer(wxSize(FromDIP(475), FromDIP(100)).y); main_sizer->Add(button_sizer, 0, wxBOTTOM | wxRIGHT | wxEXPAND, FromDIP(25)); @@ -99,8 +104,13 @@ void ConfirmHintDialog::render(wxDC& dc) { auto text_size = dc.GetTextExtent(count_txt); if (text_size.x + pos_firm_up_hint.x + FromDIP(25) < wxSize(FromDIP(475), FromDIP(100)).x) { - if (firm_up_hint[i] == ' ' || firm_up_hint[i] == '\n') + if (firm_up_hint[i] == ' ') { new_line_pos = i; + } else if (firm_up_hint[i] == '\n') { + fisrt_line = firm_up_hint.SubString(0, i); + remaining_line = firm_up_hint.SubString(i + 1, firm_up_hint.length()); + break; + } } else { if (!is_ch) { @@ -109,7 +119,7 @@ void ConfirmHintDialog::render(wxDC& dc) { break; } else { - fisrt_line = firm_up_hint.SubString(0, i); + fisrt_line = firm_up_hint.SubString(0, i - 1); remaining_line = firm_up_hint.SubString(i, firm_up_hint.length()); break; } @@ -118,7 +128,6 @@ void ConfirmHintDialog::render(wxDC& dc) { } dc.DrawText(fisrt_line, pos_firm_up_hint); - count_txt = ""; new_line_pos = 0; for (int i = 0; i < remaining_line.length(); i++) { @@ -162,11 +171,19 @@ void ConfirmHintDialog::on_button_close(wxCommandEvent& event) { this->Close(); } +bool ConfirmHintDialog::Show(bool show) +{ + if (show) { CentreOnParent(); } + return DPIDialog::Show(show); +} + void ConfirmHintDialog::on_dpi_changed(const wxRect& suggested_rect) { m_button_confirm->SetMinSize(wxSize(-1, FromDIP(24))); m_button_confirm->SetCornerRadius(FromDIP(12)); - m_button_close->SetMinSize(wxSize(-1, FromDIP(24))); - m_button_close->SetCornerRadius(FromDIP(12)); + if (m_button_close->IsShown()) { + m_button_close->SetMinSize(wxSize(-1, FromDIP(24))); + m_button_close->SetCornerRadius(FromDIP(12)); + } Layout(); } diff --git a/src/slic3r/GUI/ConfirmHintDialog.hpp b/src/slic3r/GUI/ConfirmHintDialog.hpp index 657ce995d5..e265077e07 100644 --- a/src/slic3r/GUI/ConfirmHintDialog.hpp +++ b/src/slic3r/GUI/ConfirmHintDialog.hpp @@ -27,9 +27,16 @@ private: void on_dpi_changed(const wxRect& suggested_rect) override; public: + enum ButtonStyle { + ONLY_CONFIRM = 0, + CONFIRM_AND_CANCEL = 1, + MAX_STYLE_NUM = 2 + }; + ConfirmHintDialog(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, + enum ButtonStyle btn_style = CONFIRM_AND_CANCEL, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxCLOSE_BOX | wxCAPTION); @@ -38,6 +45,8 @@ public: void SetHint(const wxString &hint); + bool Show(bool show) override; + ~ConfirmHintDialog(); }; }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 555968832e..a5753202ec 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -358,6 +358,7 @@ MachineObject::MachineObject(NetworkAgent* agent, std::string name, std::string mc_print_sub_stage = 0; mc_left_time = 0; home_flag = -1; + hw_switch_state = 0; printing_speed_lvl = PrintingSpeedLevel::SPEED_LEVEL_INVALID; } @@ -1046,6 +1047,18 @@ bool MachineObject::is_axis_at_home(std::string axis) } } +bool MachineObject::is_filament_at_extruder() +{ + if (hw_switch_state == 1) + return true; + else if (hw_switch_state == 0) + return false; + else { + //default + return true; + } +} + wxString MachineObject::get_curr_stage() { if (stage_list_info.empty()) { @@ -1359,6 +1372,19 @@ int MachineObject::command_ams_select_tray(std::string tray_id) return this->publish_gcode(gcode_cmd); } +int MachineObject::command_ams_control(std::string action) +{ + //valid actions + if (action == "resume" || action == "reset" || action == "pause") { + json j; + j["print"]["command"] = "ams_control"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["param"] = action; + return this->publish_json(j.dump()); + } + return -1; +} + int MachineObject::command_set_chamber_light(LIGHT_EFFECT effect, int on_time, int off_time, int loops, int interval) { @@ -1726,6 +1752,12 @@ bool MachineObject::is_function_supported(PrinterFunction func) return DeviceManager::is_function_supported(printer_type, func_name); } +bool MachineObject::is_support_print_with_timelapse() +{ + //TODO version check, set true by default + return true; +} + int MachineObject::publish_json(std::string json_str, int qos) { if (is_lan_mode_printer()) { @@ -1820,6 +1852,9 @@ int MachineObject::parse_json(std::string payload) if (jj.contains("home_flag")) { home_flag = jj["home_flag"].get(); } + if (jj.contains("hw_switch_state")) { + hw_switch_state = jj["hw_switch_state"].get(); + } if (jj.contains("mc_remaining_time")) { if (jj["mc_remaining_time"].is_string()) diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 455a340af1..cf4573fce4 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -466,6 +466,7 @@ public: int mc_left_time; /* left time in seconds */ int last_mc_print_stage; int home_flag; + int hw_switch_state; bool is_system_printing(); int print_error; @@ -476,6 +477,8 @@ public: bool is_axis_at_home(std::string axis); + bool is_filament_at_extruder(); + wxString get_curr_stage(); // return curr stage index of stage list int get_curr_stage_idx(); @@ -561,6 +564,7 @@ public: int command_ams_filament_settings(int ams_id, int tray_id, std::string setting_id, std::string tray_color, std::string tray_type, int nozzle_temp_min, int nozzle_temp_max); int command_ams_select_tray(std::string tray_id); int command_ams_refresh_rfid(std::string tray_id); + int command_ams_control(std::string action); int command_set_chamber_light(LIGHT_EFFECT effect, int on_time = 500, int off_time = 500, int loops = 1, int interval = 1000); int command_set_work_light(LIGHT_EFFECT effect, int on_time = 500, int off_time = 500, int loops = 1, int interval = 1000); @@ -605,6 +609,7 @@ public: bool is_online() { return m_is_online; } bool is_info_ready(); bool is_function_supported(PrinterFunction func); + bool is_support_print_with_timelapse(); /* Msg for display MsgFn */ diff --git a/src/slic3r/GUI/DownloadProgressDialog.cpp b/src/slic3r/GUI/DownloadProgressDialog.cpp index 1f2fbe7ecb..c5f018befd 100644 --- a/src/slic3r/GUI/DownloadProgressDialog.cpp +++ b/src/slic3r/GUI/DownloadProgressDialog.cpp @@ -31,6 +31,13 @@ namespace GUI { DownloadProgressDialog::DownloadProgressDialog(wxString title) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { + wxString download_failed_url = wxT("https://wiki.bambulab.com/e/en/software/bambu-studio/failed-to-get-network-plugin"); + wxString install_failed_url = wxT("https://wiki.bambulab.com/e/en/software/bambu-studio/failed-to-get-network-plugin"); + + wxString download_failed_msg = _L("Failed to download the plug-in. Please check your firewall settings and vpn software, check and retry."); + wxString install_failed_msg = _L("Failed to install the plug-in. Please check whether it is blocked or deleted by anti-virus software."); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); @@ -39,14 +46,68 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title) auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1)); m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); - m_status_bar = std::make_shared(this); + + + m_simplebook_status = new wxSimplebook(this); + m_simplebook_status->SetSize(wxSize(FromDIP(400), FromDIP(70))); + m_simplebook_status->SetMinSize(wxSize(FromDIP(400), FromDIP(70))); + m_simplebook_status->SetMaxSize(wxSize(FromDIP(400), FromDIP(70))); + + //mode normal + m_status_bar = std::make_shared(m_simplebook_status); m_panel_download = m_status_bar->get_panel(); - m_panel_download->SetSize(wxSize(FromDIP(340), -1)); - m_panel_download->SetMinSize(wxSize(FromDIP(340), -1)); - m_panel_download->SetMaxSize(wxSize(FromDIP(340), -1)); - m_sizer_main->Add(m_panel_download, 0, wxALL, FromDIP(20)); + m_panel_download->SetSize(wxSize(FromDIP(400), FromDIP(70))); + m_panel_download->SetMinSize(wxSize(FromDIP(400), FromDIP(70))); + m_panel_download->SetMaxSize(wxSize(FromDIP(400), FromDIP(70))); + + + //mode Download Failed + auto m_panel_download_failed = new wxPanel(m_simplebook_status, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + + wxBoxSizer* sizer_download_failed = new wxBoxSizer(wxVERTICAL); + + auto m_statictext_download_failed = new wxStaticText(m_panel_download_failed, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_statictext_download_failed->SetLabel(format_text(m_statictext_download_failed, download_failed_msg, FromDIP(360))); + m_statictext_download_failed->Wrap(FromDIP(360)); + + sizer_download_failed->Add(m_statictext_download_failed, 0, wxALIGN_CENTER | wxALL, 5); + + auto m_download_hyperlink = new wxHyperlinkCtrl(m_panel_download_failed, wxID_ANY, _L("click here to see more info"), download_failed_url, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + sizer_download_failed->Add(m_download_hyperlink, 0, wxALIGN_CENTER | wxALL, 5); + + + m_panel_download_failed->SetSizer(sizer_download_failed); + m_panel_download_failed->Layout(); + sizer_download_failed->Fit(m_panel_download_failed); + + + //mode Installed failed + auto m_panel_install_failed = new wxPanel(m_simplebook_status, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + + wxBoxSizer* sizer_install_failed = new wxBoxSizer(wxVERTICAL); + + auto m_statictext_install_failed = new wxStaticText(m_panel_install_failed, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_statictext_install_failed->SetLabel(format_text(m_statictext_install_failed, install_failed_msg,FromDIP(360))); + m_statictext_install_failed->Wrap(FromDIP(360)); + + sizer_install_failed->Add(m_statictext_install_failed, 0, wxALIGN_CENTER | wxALL, 5); + + auto m_install_hyperlink = new wxHyperlinkCtrl(m_panel_install_failed, wxID_ANY, _L("click here to see more info"), install_failed_url, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + sizer_install_failed->Add(m_install_hyperlink, 0, wxALIGN_CENTER | wxALL, 5); + + + m_panel_download_failed->SetSizer(sizer_install_failed); + m_panel_download_failed->Layout(); + sizer_install_failed->Fit(m_panel_install_failed); + + m_sizer_main->Add(m_simplebook_status, 0, wxALL, FromDIP(20)); m_sizer_main->Add(0, 0, 1, wxBOTTOM, 10); + + m_simplebook_status->AddPage(m_status_bar->get_panel(), wxEmptyString, true); + m_simplebook_status->AddPage(m_panel_download_failed, wxEmptyString, false); + m_simplebook_status->AddPage(m_panel_install_failed, wxEmptyString, false); + SetSizer(m_sizer_main); Layout(); Fit(); @@ -55,9 +116,31 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title) Bind(wxEVT_CLOSE_WINDOW, &DownloadProgressDialog::on_close, this); } +wxString DownloadProgressDialog::format_text(wxStaticText* st, wxString str, int warp) +{ + if (wxGetApp().app_config->get("language") != "zh_CN") { return str; } + + wxString out_txt = str; + wxString count_txt = ""; + int new_line_pos = 0; + + for (int i = 0; i < str.length(); i++) { + auto text_size = st->GetTextExtent(count_txt); + if (text_size.x < warp) { + count_txt += str[i]; + } + else { + out_txt.insert(i - 1, '\n'); + count_txt = ""; + } + } + return out_txt; +} + bool DownloadProgressDialog::Show(bool show) { if (show) { + m_simplebook_status->SetSelection(0); m_upgrade_job = std::make_shared(m_status_bar); m_upgrade_job->set_event_handle(this); m_status_bar->set_progress(0); @@ -71,9 +154,23 @@ bool DownloadProgressDialog::Show(bool show) ); }); - Bind(EVT_UPGRADE_NETWORK_FAILED, [this](wxCommandEvent& evt) { + //download failed + Bind(EVT_DOWNLOAD_NETWORK_FAILED, [this](wxCommandEvent& evt) { m_status_bar->change_button_label(_L("Close")); m_status_bar->set_progress(0); + this->m_simplebook_status->SetSelection(1); + m_status_bar->set_cancel_callback_fina( + [this]() { + this->Close(); + } + ); + }); + + //install failed + Bind(EVT_INSTALL_NETWORK_FAILED, [this](wxCommandEvent& evt) { + m_status_bar->change_button_label(_L("Close")); + m_status_bar->set_progress(0); + this->m_simplebook_status->SetSelection(2); m_status_bar->set_cancel_callback_fina( [this]() { this->Close(); diff --git a/src/slic3r/GUI/DownloadProgressDialog.hpp b/src/slic3r/GUI/DownloadProgressDialog.hpp index b35d206d11..0f5504cc1f 100644 --- a/src/slic3r/GUI/DownloadProgressDialog.hpp +++ b/src/slic3r/GUI/DownloadProgressDialog.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "Widgets/Button.hpp" #include "BBLStatusBar.hpp" #include "BBLStatusBarSend.hpp" @@ -37,11 +38,14 @@ protected: public: DownloadProgressDialog(wxString title); + wxString format_text(wxStaticText* st, wxString str, int warp); ~DownloadProgressDialog(); void on_dpi_changed(const wxRect &suggested_rect) override; void update_release_note(std::string release_note, std::string version); + wxSimplebook* m_simplebook_status{nullptr}; + std::shared_ptr m_status_bar; std::shared_ptr m_upgrade_job { nullptr }; wxPanel * m_panel_download; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index e2ffd44520..1a07cb1472 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1235,7 +1235,9 @@ void Choice::set_value(const boost::any& value, bool change_event) // BBS case coEnums: { int val = boost::any_cast(value); - if (m_opt_id.compare("host_type") == 0 && val != 0 && + + // Support ThirdPartyPrinter + if (m_opt_id.compare("host_type") == 0 && val != 0 && m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType val--; if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern") @@ -1317,15 +1319,15 @@ boost::any& Choice::get_value() // BBS if (m_opt.type == coEnum || m_opt.type == coEnums) { - if (m_opt_id.compare("host_type") == 0 && m_opt.enum_values.size() > field->GetCount()) { - // for case, when PrusaLink isn't used as a HostType - m_value = field->GetSelection()+1; - } - else if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern") { + if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "sparse_infill_pattern") { const std::string& key = m_opt.enum_values[field->GetSelection()]; m_value = int(ConfigOptionEnum::get_enum_values().at(key)); } - else + // Support ThirdPartyPrinter + else if (m_opt_id.compare("host_type") == 0 && m_opt.enum_values.size() > field->GetCount()) { + // for case, when PrusaLink isn't used as a HostType + m_value = field->GetSelection() + 1; + } else m_value = field->GetSelection(); } else if (m_opt.gui_type == ConfigOptionDef::GUIType::f_enum_open || m_opt.gui_type == ConfigOptionDef::GUIType::i_enum_open) { diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b8adcc9910..42beb65326 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -334,6 +334,13 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he static const ImU32 text_value_clr = IM_COL32(144, 144, 144, 255); static const ImU32 window_bg_clr = IM_COL32(255, 255, 255, 255); + auto it = std::find_if(moves.begin(), moves.end(), [&curr_line_id](auto move) { + return move.gcode_id == curr_line_id; + }); + if (it == moves.end()) { + return; + } + ImGuiWrapper& imgui = *wxGetApp().imgui(); //BBS: GUI refactor: add canvas size from parameters imgui.set_next_window_pos(0.5f * static_cast(canvas_width), static_cast(canvas_height), ImGuiCond_Always, 0.5f, 1.0f); @@ -352,19 +359,101 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he std::string x = ImGui::ColorMarkerStart + std::string("X: ") + ImGui::ColorMarkerEnd; std::string y = ImGui::ColorMarkerStart + std::string("Y: ") + ImGui::ColorMarkerEnd; std::string z = ImGui::ColorMarkerStart + std::string("Z: ") + ImGui::ColorMarkerEnd; + std::string height = ImGui::ColorMarkerStart + _u8L("Height: ") + ImGui::ColorMarkerEnd; + std::string width = ImGui::ColorMarkerStart + _u8L("Width: ") + ImGui::ColorMarkerEnd; std::string speed = ImGui::ColorMarkerStart + _u8L("Speed: ") + ImGui::ColorMarkerEnd; std::string flow = ImGui::ColorMarkerStart + _u8L("Flow: ") + ImGui::ColorMarkerEnd; + const float item_size = imgui.calc_text_size("X: 000.000 ").x; + const float item_spacing = imgui.get_item_spacing().x; + const float window_padding = ImGui::GetStyle().WindowPadding.x; - std::ostringstream buffer; char buf[1024]; - if (view_type == EViewType::Feedrate) { - auto it = std::find_if(moves.begin(), moves.end(), [&curr_line_id](auto move) { - if (move.gcode_id == curr_line_id) - return true; - else - return false; - }); - if (it != moves.end()) { + switch (view_type){ + case EViewType::Height: { + sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + sprintf(buf, "%s%.3f", z.c_str(), position.z()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.2f", height.c_str(), it->height); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + break; + } + case EViewType::Width: { + sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + sprintf(buf, "%s%.3f", z.c_str(), position.z()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.2f", width.c_str(), it->width); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + break; + } + case EViewType::Feedrate: { + sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + sprintf(buf, "%s%.3f", z.c_str(), position.z()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.2f", speed.c_str(), it->feedrate); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + break; + } + case EViewType::VolumetricRate: { + sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + sprintf(buf, "%s%.3f", z.c_str(), position.z()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + ImGui::SameLine(window_padding + item_size + item_spacing); + sprintf(buf, "%s%.2f", flow.c_str(), it->volumetric_rate()); + ImGui::PushItemWidth(item_size); + imgui.text(buf); + + break; + } + default: sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); imgui.text(buf); @@ -372,54 +461,16 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); imgui.text(buf); + ImGui::SameLine(); sprintf(buf, "%s%.3f", z.c_str(), position.z()); imgui.text(buf); - - ImGui::SameLine(); - sprintf(buf, "%s%.f", speed.c_str(), it->feedrate); - imgui.text(buf); - } } - else if (view_type == EViewType::VolumetricRate) { - auto it = std::find_if(moves.begin(), moves.end(), [&curr_line_id](auto move) { - if (move.gcode_id == curr_line_id) - return true; - else - return false; - }); - if (it != moves.end()) { - sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); - imgui.text(buf); - ImGui::SameLine(); - sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); - imgui.text(buf); - - sprintf(buf, "%s%.3f", z.c_str(), position.z()); - imgui.text(buf); - - ImGui::SameLine(); - sprintf(buf, "%s%.f", flow.c_str(), it->volumetric_rate()); - imgui.text(buf); - } - } - else { - sprintf(buf, "%s%.3f", x.c_str(), position.x() - plate->get_origin().x()); - imgui.text(buf); - - ImGui::SameLine(); - sprintf(buf, "%s%.3f", y.c_str(), position.y() - plate->get_origin().y()); - imgui.text(buf); - - ImGui::SameLine(); - sprintf(buf, "%s%.3f", z.c_str() , position.z()); - imgui.text(buf); - } // force extra frame to automatically update window size - float width = ImGui::GetWindowWidth(); + float window_width = ImGui::GetWindowWidth(); //size_t length = strlen(buf); - if (width != last_window_width /*|| length != last_text_length*/) { - last_window_width = width; + if (window_width != last_window_width /*|| length != last_text_length*/) { + last_window_width = window_width; //last_text_length = length; #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT imgui.set_requires_extra_frame(); @@ -3085,8 +3136,8 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); const PrintConfig& config = print.config(); - if (print.enable_timelapse_print() - || (extruders_count > 1 && config.enable_prime_tower && (config.print_sequence == PrintSequence::ByLayer))) { + if (config.enable_prime_tower && + (print.enable_timelapse_print() || (extruders_count > 1 && (config.print_sequence == PrintSequence::ByLayer)))) { const float depth = print.wipe_tower_data(extruders_count).depth; const float brim_width = print.wipe_tower_data(extruders_count).brim_width; @@ -4703,7 +4754,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv append_option_item(item,offsets); //BBS display filament change times - if (m_print_statistics.total_filamentchanges > 0) { + if (m_print_statistics.total_filamentchanges > 0 && ( total_flushed_filament_m > 0 || total_flushed_filament_g > 0)) { std::string flushed_filament_title_str = _u8L("Flushed filament"); std::string flushed_filament_str = _u8L("Filament"); std::string total_flushed_filament_str = _u8L("Total"); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fdc78d2226..e932b867b0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1935,7 +1935,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re auto timelapse_type = dconfig.option>("timelapse_type"); bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false; - if (timelapse_enabled || (filaments_count > 1 && wt && co != nullptr && co->value != PrintSequence::ByObject)) { + if ((timelapse_enabled && wt) || (filaments_count > 1 && wt && co != nullptr && co->value != PrintSequence::ByObject)) { for (int plate_id = 0; plate_id < n_plates; plate_id++) { DynamicPrintConfig& proj_cfg = wxGetApp().preset_bundle->project_config; float x = dynamic_cast(proj_cfg.option("wipe_tower_x"))->get_at(plate_id); @@ -1978,6 +1978,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_selection.volumes_changed(map_glvolume_old_to_new); m_gizmos.update_data(); + m_gizmos.update_assemble_view_data(); m_gizmos.refresh_on_off_state(); // Update the toolbar @@ -2409,6 +2410,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { //case WXK_BACK: case WXK_DELETE: { post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; } + // BBS +#ifdef __APPLE__ + case WXK_BACK: { post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; } +#endif case WXK_ESCAPE: { deselect_all(); break; } //case WXK_F5: { // if ((wxGetApp().is_editor() && !wxGetApp().plater()->model().objects.empty()) || @@ -2661,6 +2666,12 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) #if !BBL_RELEASE_TO_PUBLIC wxGetApp().plater()->toggle_render_statistic_dialog(); m_dirty = true; +#endif + } + else if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_RETURN) { +#if !BBL_RELEASE_TO_PUBLIC + wxGetApp().plater()->toggle_show_wireframe(); + m_dirty = true; #endif } else if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) { @@ -2694,7 +2705,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; - else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { + else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { translationProcessor.process(evt); //switch (keyCode) @@ -2774,7 +2785,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; - else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { + else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { // auto do_rotate = [this](double angle_z_rad) { // m_selection.start_dragging(); // m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); @@ -3351,6 +3362,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) //BBS do not limit rotate in assemble view camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target); //camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, rotate_target); + auto clp_dist = m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position(); + m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true); } else { #ifdef SUPPORT_FEEE_CAMERA @@ -5720,7 +5733,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with else m_volumes.set_z_range(-FLT_MAX, FLT_MAX); - m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + if (m_canvas_type == CanvasAssembleView) { + m_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data()); + } + else { + m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + } //BBS: remove sinking logic //m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); @@ -5761,6 +5779,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with shader->start_using(); } } + break; } case GLVolumeCollection::ERenderType::Transparent: @@ -5774,6 +5793,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with return true; } }, with_outline); + if (m_canvas_type == CanvasAssembleView) { + const GLGizmosManager& gm = get_gizmos_manager(); + shader->stop_using(); + gm.render_painter_assemble_view(); + shader->start_using(); + } break; } } @@ -5959,7 +5984,7 @@ void GLCanvas3D::_render_overlays() _check_and_update_toolbar_icon_scale(); - _render_explosion_control(); + _render_assemble_control(); _render_assemble_info(); // main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed @@ -6167,6 +6192,8 @@ void GLCanvas3D::_render_gizmos_overlay() const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment #endif /* __WXMSW__ */ + if (m_canvas_type == CanvasAssembleView) + return; m_gizmos.render_overlay(); @@ -6664,7 +6691,7 @@ void GLCanvas3D::_render_paint_toolbar() const } //BBS -void GLCanvas3D::_render_explosion_control() const +void GLCanvas3D::_render_assemble_control() const { if (m_canvas_type != ECanvasType::CanvasAssembleView) { GLVolume::explosion_ratio = m_explosion_ratio = 1.0; @@ -6679,26 +6706,45 @@ void GLCanvas3D::_render_explosion_control() const auto canvas_h = float(get_canvas_size().get_height()); const float text_padding = 7.0f; - ImVec2 text_size = imgui->calc_text_size(_L("Explosion Ratio")); - const float slider_width = 130.0f; + const float text_size_x = std::max(imgui->calc_text_size(_L("Explosion Ratio")).x, imgui->calc_text_size(_L("Section View")).x); + const float slider_width = 75.0f; const float value_size = imgui->calc_text_size("3.00").x + text_padding * 2; const float item_spacing = imgui->get_item_spacing().x; ImVec2 window_padding = ImGui::GetStyle().WindowPadding; - ImVec2 window_size = ImVec2(text_size.x + slider_width + value_size + item_spacing * 2 + window_padding.x * 2, window_padding.y * 2 + text_size.y); - // 13.0f is bottom margin - imgui->set_next_window_pos(canvas_w * 0.5 - window_size.x * 0.5, canvas_h - window_size.y - 13.0f, ImGuiCond_Always, 0.0f, 0.0f); - imgui->begin(_L("Explosion Ratio"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + imgui->set_next_window_pos(canvas_w / 2, canvas_h - 13.0f * get_scale(), ImGuiCond_Always, 0.5f, 1.0f); + imgui->begin(_L("Assemble Control"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); ImGui::AlignTextToFramePadding(); - imgui->text(_L("Explosion Ratio")); - ImGui::SameLine(window_padding.x + text_size.x + item_spacing); - ImGui::PushItemWidth(slider_width); - bool slider_changed = imgui->bbl_slider_float_style("##ratio_slider", &m_explosion_ratio, 1.0f, 3.0f, "%1.2f"); - ImGui::SameLine(window_padding.x + text_size.x + slider_width + item_spacing * 2); - ImGui::PushItemWidth(value_size); - bool input_changed = ImGui::BBLDragFloat("##ratio_input", &m_explosion_ratio, 0.1f, 1.0f, 3.0f, "%1.2f"); + { + imgui->text(_L("Section View")); + + ImGui::SameLine(window_padding.x + text_size_x + item_spacing); + ImGui::PushItemWidth(slider_width); + static float clp_dist = 0.f; + bool view_slider_changed = imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true); + + ImGui::SameLine(window_padding.x + text_size_x + slider_width + item_spacing * 2); + ImGui::PushItemWidth(value_size); + bool view_input_changed = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); + + if (view_slider_changed || view_input_changed) + m_gizmos.m_assemble_view_data->model_objects_clipper()->set_position(clp_dist, true); + } + + { + ImGui::SameLine(window_padding.x + text_size_x + slider_width + item_spacing * 6 + value_size); + imgui->text(_L("Explosion Ratio")); + + ImGui::SameLine(window_padding.x + 2 * text_size_x + slider_width + item_spacing * 7 + value_size); + ImGui::PushItemWidth(slider_width); + bool explosion_slider_changed = imgui->bbl_slider_float_style("##ratio_slider", &m_explosion_ratio, 1.0f, 3.0f, "%1.2f"); + + ImGui::SameLine(window_padding.x + 2 * text_size_x + 2 * slider_width + item_spacing * 8 + value_size); + ImGui::PushItemWidth(value_size); + bool explosion_input_changed = ImGui::BBLDragFloat("##ratio_input", &m_explosion_ratio, 0.1f, 1.0f, 3.0f, "%1.2f"); + } imgui->end(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 933f698c1c..452cd0add2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -978,7 +978,7 @@ private: // BBS //void _render_view_toolbar() const; void _render_paint_toolbar() const; - void _render_explosion_control() const; + void _render_assemble_control() const; void _render_assemble_info() const; #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target() const; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 1e7bc76f1f..74668fb20b 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -45,11 +45,20 @@ std::pair GLShadersManager::init() // used to render extrusion and travel paths as lines in gcode preview valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); // used to render objects in 3d editor - valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" } + if (0) { + valid &= append_shader("gouraud", { "gouraud_130.vs", "gouraud_130.fs" } +#if ENABLE_ENVIRONMENT_MAP + , { "ENABLE_ENVIRONMENT_MAP"sv } +#endif // ENABLE_ENVIRONMENT_MAP + ); + } + else { + valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" } #if ENABLE_ENVIRONMENT_MAP , { "ENABLE_ENVIRONMENT_MAP"sv } #endif // ENABLE_ENVIRONMENT_MAP ); + } // used to render variable layers heights in 3d editor valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); // used to render highlight contour around selected triangles inside the multi-material gizmo diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index c5583c8233..24e84ef535 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -25,8 +25,10 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_ALL, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_PLATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_EXPORT_GCODE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_GCODE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_UPLOAD_GCODE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 9fd8ff0724..3c96d9f860 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -25,8 +25,10 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_PLATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_GCODE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_GCODE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_UPLOAD_GCODE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bb27eabadd..e6695207b7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -280,6 +280,15 @@ public: memDc.SetTextForeground(wxColor(134, 134, 134)); memDc.DrawLabel(m_constant_text.version, version_rect, wxALIGN_LEFT | wxALIGN_BOTTOM); +#if BBL_INTERNAL_TESTING + wxSize text_rect = memDc.GetTextExtent("Internal Version"); + int start_x = (title_rect.GetLeft() + version_rect.GetRight()) / 2 - text_rect.GetWidth(); + int start_y = version_rect.GetBottom() + 10; + wxRect internal_sign_rect(wxPoint(start_x, start_y), wxSize(text_rect)); + memDc.SetFont(m_constant_text.title_font); + memDc.DrawLabel("Internal Version", internal_sign_rect, wxALIGN_TOP | wxALIGN_LEFT); +#endif + // load bitmap for logo BitmapCache bmp_cache; int logo_margin = FromDIP(72 * m_scale); @@ -545,7 +554,11 @@ private: title = wxGetApp().is_editor() ? SLIC3R_APP_FULL_NAME : GCODEVIEWER_APP_NAME; // dynamically get the version to display +#if BBL_INTERNAL_TESTING + version = _L("Internal Version") + " " + std::string(SLIC3R_VERSION); +#else version = _L("Version") + " " + std::string(SLIC3R_VERSION); +#endif // credits infornation credits = title; @@ -1052,9 +1065,9 @@ void GUI_App::post_init() }*/ // BBS: to be checked -#if SUPPORT_SHOW_HINTS +#if 1 // show "Did you know" notification - if (app_config->get("show_hints") == "1" && ! is_gcode_viewer()) + if (app_config->get("show_hints") == "true" && ! is_gcode_viewer()) plater_->get_notification_manager()->push_hint_notification(true); #endif @@ -1176,13 +1189,14 @@ GUI_App::GUI_App() { //app config initializes early becasuse it is used in instance checking in BambuStudio.cpp this->init_app_config(); + this->init_download_path(); reset_to_active(); } void GUI_App::shutdown() { - BOOST_LOG_TRIVIAL(info) << "shutdown"; + BOOST_LOG_TRIVIAL(info) << "GUI_App::shutdown enter"; if (m_removable_drive_manager) { removable_drive_manager()->shutdown(); @@ -1202,6 +1216,7 @@ void GUI_App::shutdown() delete m_agent; m_agent = nullptr; } + BOOST_LOG_TRIVIAL(info) << "GUI_App::shutdown exit"; } @@ -1608,6 +1623,8 @@ void GUI_App::init_networking_callbacks() return; } GUI::wxGetApp().CallAfter([this] { + if (m_is_closing) + return; BOOST_LOG_TRIVIAL(trace) << "static: server connected"; m_agent->set_user_selected_machine(m_agent->get_user_selected_machine()); }); @@ -1618,6 +1635,8 @@ void GUI_App::init_networking_callbacks() return; } GUI::wxGetApp().CallAfter([this, dev_id] { + if (m_is_closing) + return; /* request_pushing */ MachineObject* obj = m_device_manager->get_my_machine(dev_id); if (obj) { @@ -1677,6 +1696,8 @@ void GUI_App::init_networking_callbacks() return; } CallAfter([this, dev_id, msg] { + if (m_is_closing) + return; MachineObject* obj = this->m_device_manager->get_user_machine(dev_id); if (obj) { obj->is_ams_need_update = false; @@ -1696,6 +1717,8 @@ void GUI_App::init_networking_callbacks() return; } CallAfter([this, dev_id, msg] { + if (m_is_closing) + return; MachineObject* obj = m_device_manager->get_my_machine(dev_id); if (!obj) { obj = m_device_manager->get_local_machine(dev_id); @@ -1766,6 +1789,26 @@ static boost::optional parse_semver_from_ini(std::string path) return Semver::parse(body); } +void GUI_App::init_download_path() +{ + std::string down_path = app_config->get("download_path"); + + if (down_path.empty()) { + std::string user_down_path = wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads).ToUTF8().data(); + app_config->set("download_path", user_down_path); + } + else { + fs::path dp(down_path); + if (!fs::exists(dp)) { + + if (!fs::create_directory(dp)) { + std::string user_down_path = wxStandardPaths::Get().GetUserDir(wxStandardPaths::Dir_Downloads).ToUTF8().data(); + app_config->set("download_path", user_down_path); + } + } + } +} + void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. @@ -1899,7 +1942,8 @@ bool GUI_App::on_init_inner() std::time_t t = std::time(0); std::tm* now_time = std::localtime(&t); std::stringstream buf; - buf << std::put_time(now_time, "debug_%a_%b_%d_%H_%M_%S.log"); + buf << std::put_time(now_time, "debug_%a_%b_%d_%H_%M_%S_"); + buf << get_current_pid() << ".log"; std::string log_filename = buf.str(); #if !BBL_RELEASE_TO_PUBLIC set_log_path_and_level(log_filename, 5); @@ -1929,6 +1973,7 @@ bool GUI_App::on_init_inner() #endif wxGetApp().Bind(wxEVT_QUERY_END_SESSION, [this](auto & e) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< "received wxEVT_QUERY_END_SESSION"; if (mainframe) { wxCloseEvent e2(wxEVT_CLOSE_WINDOW); e2.SetCanVeto(true); @@ -2097,8 +2142,9 @@ bool GUI_App::on_init_inner() if (!skip_this_version || evt.GetInt() != 0) { UpdateVersionDialog dialog(this->mainframe); - //dialog.update_version_info(extmsg, version_info.version_str); - dialog.update_version_info(version_info.description); + wxString extmsg = wxString::FromUTF8(version_info.description); + dialog.update_version_info(extmsg, version_info.version_str); + //dialog.update_version_info(version_info.description); if (evt.GetInt() != 0) { dialog.m_remind_choice->Hide(); } @@ -2719,6 +2765,7 @@ void GUI_App::check_printer_presets() void GUI_App::recreate_GUI(const wxString& msg_name) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "recreate_GUI enter"; m_is_recreating_gui = true; mainframe->shutdown(); @@ -2766,6 +2813,8 @@ void GUI_App::recreate_GUI(const wxString& msg_name) // }); m_is_recreating_gui = false; + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "recreate_GUI exit"; } void GUI_App::system_info() @@ -2925,6 +2974,7 @@ void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_max const std::string name = into_u8(window->GetName()); window->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent &event) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": received wxEVT_CLOSE_WINDOW, trigger save for window_mainframe"; window_pos_save(window, "mainframe"); event.Skip(); }); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 72a38d633b..561c609db7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -305,7 +305,8 @@ public: wxGLContext* init_glcontext(wxGLCanvas& canvas); bool init_opengl(); - static unsigned get_colour_approx_luma(const wxColour &colour); + void init_download_path(); + static unsigned get_colour_approx_luma(const wxColour& colour); static bool dark_mode(); const wxColour get_label_default_clr_system(); const wxColour get_label_default_clr_modified(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 80971189bb..fffe2a805c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -181,8 +181,7 @@ ObjectList::ObjectList(wxWindow* parent) : entries[index++].Set(wxACCEL_CTRL, (int)'A', wxID_SELECTALL); entries[index++].Set(wxACCEL_CTRL, (int)'Z', wxID_UNDO); entries[index++].Set(wxACCEL_CTRL, (int)'Y', wxID_REDO); - entries[index++].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE); - //entries[index++].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); + entries[index++].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); //entries[index++].Set(wxACCEL_NORMAL, int('+'), wxID_ADD); //entries[index++].Set(wxACCEL_NORMAL, WXK_NUMPAD_ADD, wxID_ADD); //entries[index++].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE); @@ -5041,6 +5040,15 @@ void ObjectList::apply_object_instance_transfrom_to_all_volumes(ModelObject *mod const Geometry::Transformation &instance_transformation = model_object->instances[0]->get_transformation(); Vec3d original_instance_center = instance_transformation.get_offset(); + // apply the instance_transform(except offset) to assemble_transform + Geometry::Transformation instance_transformation_copy = instance_transformation; + instance_transformation_copy.set_offset(Vec3d(0, 0, 0)); // remove the effect of offset + const Transform3d & instance_inverse_matrix = instance_transformation_copy.get_matrix().inverse(); + const Transform3d & assemble_matrix = model_object->instances[0]->get_assemble_transformation().get_matrix(); + Transform3d new_assemble_transform = assemble_matrix * instance_inverse_matrix; + model_object->instances[0]->set_assemble_from_transform(new_assemble_transform); + + // apply the instance_transform to volumn const Transform3d &transformation_matrix = instance_transformation.get_matrix(); for (ModelVolume *volume : model_object->volumes) { const Transform3d &volume_matrix = volume->get_matrix(); diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index a5463a50c2..a034741a1e 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -2681,6 +2681,7 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi SetSize(wxSize(-1, FromDIP(450))); SetMinSize(wxSize(-1, FromDIP(450))); SetMaxSize(wxSize(-1, FromDIP(450))); + //m_search_line = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); @@ -3228,7 +3229,7 @@ void ObjectTablePanel::resetAllValuesInSideWindow(int row, bool is_object, Model // ObjectTableDialog // ---------------------------------------------------------------------------- ObjectTableDialog::ObjectTableDialog(wxWindow* parent, Plater* platerObj, Model *modelObj, wxSize maxSize) - : GUI::DPIDialog(parent, wxID_ANY, _L("Object/Part Setting"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + : GUI::DPIDialog(parent, wxID_ANY, _L("Object/Part Setting"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER) , m_model(modelObj), m_plater(platerObj) { @@ -3369,12 +3370,27 @@ void ObjectTableDialog::OnText(wxKeyEvent &evt) void ObjectTableDialog::OnSize(wxSizeEvent& event) { - wxSize new_size = event.GetSize(); + /* wxSize new_size = event.GetSize(); if ((new_size.GetWidth() > g_dialog_max_width) || (new_size.GetHeight() > g_dialog_max_height)) { - int width = (new_size.GetWidth() > g_dialog_max_width) ? new_size.GetWidth() : g_dialog_max_width; + int width = (new_size.GetWidth() > g_dialog_max_width) ? new_size.GetWidth() : g_dialog_max_width; int height = (new_size.GetHeight() > g_dialog_max_height) ? new_size.GetHeight() : g_dialog_max_height; this->SetMaxSize(wxSize(width, height)); + }*/ + + if (event.GetSize().y <= FromDIP(450)) { + SetMaxSize(wxSize(GetSize().x, -1)); + SetMinSize(wxSize(GetSize().x, -1)); + SetSize(wxSize(GetSize().x, -1)); + return; } + SetMaxSize(wxSize(GetSize().x, -1)); + SetMinSize(wxSize(GetSize().x, -1)); + SetSize(wxSize(GetSize().x, -1)); + + m_obj_panel->SetSize(wxSize(-1, GetSize().y)); + m_obj_panel->SetMinSize(wxSize(-1, GetSize().y)); + m_obj_panel->SetMaxSize(wxSize(-1, GetSize().y)); + event.Skip(); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 26b9f5104c..d8f8f2f891 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -506,7 +506,8 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent &event) IMSlider *m_layers_slider = m_canvas->get_gcode_viewer().get_layers_slider(); IMSlider *m_moves_slider = m_canvas->get_gcode_viewer().get_moves_slider(); if (key == 'L') { - m_layers_slider->switch_one_layer_mode(); + if(!m_layers_slider->switch_one_layer_mode()) + event.Skip(); m_canvas->set_as_dirty(); } /*else if (key == WXK_SHIFT) @@ -767,7 +768,7 @@ bool AssembleView::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrint m_canvas->set_process(process); m_canvas->set_type(GLCanvas3D::ECanvasType::CanvasAssembleView); m_canvas->set_config(config); - m_canvas->enable_gizmos(false); + m_canvas->enable_gizmos(true); m_canvas->enable_selection(true); m_canvas->enable_main_toolbar(false); m_canvas->enable_labels(false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index 7e41be0ddf..d92717ed5d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -438,7 +438,7 @@ void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_l // Rotation input box ImGui::PushItemWidth(caption_size); - m_imgui->text(_L("Rotation:")); + m_imgui->text(_L("Rotation") + " "); ImGui::SameLine(caption_size + 1 * space_size); ImGui::PushItemWidth(unit_size); ImGui::BBLInputDouble("##cut_rotation_x", &rotation[0], 0.0f, 0.0f, "%.2f"); @@ -471,7 +471,7 @@ void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_l double movement = m_movement; ImGui::PushItemWidth(caption_size); ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Movement:")); + m_imgui->text(_L("Movement") + " "); ImGui::SameLine(caption_size + 1 * space_size); ImGui::PushItemWidth(3 * unit_size + 2 * space_size); ImGui::BBLInputDouble("##cut_movement", &movement, 0.0f, 0.0f, "%.2f"); @@ -497,7 +497,7 @@ void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_l double height = m_height; ImGui::PushItemWidth(caption_size); ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Height:")); + m_imgui->text(_L("Height") + " "); ImGui::SameLine(caption_size + 1 * space_size); ImGui::PushItemWidth(3 * unit_size + 2 * space_size); ImGui::BBLInputDouble("##cut_height", &height, 0.0f, 0.0f, "%.2f"); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 5aa5f2c6b9..3164d3cc6b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -79,23 +79,25 @@ bool GLGizmoFdmSupports::on_init() // BBS m_shortcut_key = WXK_CONTROL_L; - m_desc["clipping_of_view"] = _L("Section view") + ": "; - m_desc["cursor_size"] = _L("Pen size") + ": "; - m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["clipping_of_view_caption"] = _L("Alt + Mouse wheel"); + m_desc["clipping_of_view"] = _L("Section view"); + m_desc["cursor_size_caption"] = _L("Ctrl + Mouse wheel"); + m_desc["cursor_size"] = _L("Pen size"); + m_desc["enforce_caption"] = _L("Left mouse button"); m_desc["enforce"] = _L("Enforce supports"); - m_desc["block_caption"] = _L("Right mouse button") + ": "; + m_desc["block_caption"] = _L("Right mouse button"); m_desc["block"] = _L("Block supports"); - m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; - m_desc["remove"] = _L("Erase painting"); + m_desc["remove_caption"] = _L("Shift + Left mouse button"); + m_desc["remove"] = _L("Erase"); m_desc["remove_all"] = _L("Erase all painting"); - m_desc["highlight_by_angle"] = _L("Highlight overhang areas") + ": "; + m_desc["highlight_by_angle"] = _L("Highlight overhang areas"); m_desc["gap_fill"] = _L("Gap fill"); m_desc["perform"] = _L("Perform"); + m_desc["gap_area_caption"] = _L("Ctrl + Mouse wheel"); m_desc["gap_area"] = _L("Gap area"); - m_desc["brush_size"] = _L("Set pen size"); - m_desc["brush_size_caption"] = _L("Ctrl + Mouse wheel") + ": "; m_desc["tool_type"] = _L("Tool type"); - m_desc["smart_fill_angle"] = _L("Smart fill angle") + ": "; + m_desc["smart_fill_angle_caption"] = _L("Ctrl + Mouse wheel"); + m_desc["smart_fill_angle"] = _L("Smart fill angle"); memset(&m_print_instance, sizeof(m_print_instance), 0); return true; @@ -224,7 +226,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l float caption_max = 0.f; float total_text_max = 0.f; - for (const auto &t : std::array{"enforce", "block", "remove", "brush_size"}) { + for (const auto &t : std::array{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) { caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); } @@ -461,6 +463,8 @@ void GLGizmoFdmSupports::show_tooltip_information(float caption_max, float x, fl ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + caption_max += m_imgui->calc_text_size(": ").x + 15.f; + float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); @@ -475,7 +479,24 @@ void GLGizmoFdmSupports::show_tooltip_information(float caption_max, float x, fl m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - for (const auto &t : std::array{"enforce", "block", "remove", "brush_size"}) draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + std::vector tip_items; + switch (m_tool_type) { + case ToolType::BRUSH: + tip_items = {"enforce", "block", "remove", "cursor_size", "clipping_of_view"}; + break; + case ToolType::BUCKET_FILL: + break; + case ToolType::SMART_FILL: + tip_items = {"enforce", "block", "remove", "smart_fill_angle", "clipping_of_view"}; + break; + case ToolType::GAP_FILL: + tip_items = {"gap_area"}; + break; + default: + break; + } + for (const auto &t : tip_items) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); + ImGui::EndTooltip(); } ImGui::PopStyleVar(1); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index ed717f0cdb..461b5b813e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -97,18 +97,20 @@ bool GLGizmoMmuSegmentation::on_init() // BBS m_shortcut_key = WXK_CONTROL_N; - m_desc["clipping_of_view"] = _L("Section view") + ": "; - m_desc["cursor_size"] = _L("Pen size") + ": "; + m_desc["clipping_of_view_caption"] = _L("Alt + Mouse wheel"); + m_desc["clipping_of_view"] = _L("Section view"); + m_desc["cursor_size_caption"] = _L("Ctrl + Mouse wheel"); + m_desc["cursor_size"] = _L("Pen size"); m_desc["cursor_type"] = _L("Pen shape"); - // BBS - m_desc["paint_caption"] = _L("Left mouse button") + ": "; + m_desc["paint_caption"] = _L("Left mouse button"); m_desc["paint"] = _L("Paint"); - m_desc["erase_caption"] = _L("Right mouse button") + ": "; + m_desc["erase_caption"] = _L("Shift + Left mouse button"); m_desc["erase"] = _L("Erase"); - m_desc["shortcut_key_caption"] = _L("Key 1~9") + ": "; + m_desc["shortcut_key_caption"] = _L("Key 1~9"); m_desc["shortcut_key"] = _L("Choose filament"); m_desc["edge_detection"] = _L("Edge detection"); + m_desc["gap_area_caption"] = _L("Ctrl + Mouse wheel"); m_desc["gap_area"] = _L("Gap area"); m_desc["perform"] = _L("Perform"); @@ -123,12 +125,10 @@ bool GLGizmoMmuSegmentation::on_init() m_desc["tool_smart_fill"] = _L("Smart fill"); m_desc["tool_bucket_fill"] = _L("Bucket fill"); + m_desc["smart_fill_angle_caption"] = _L("Ctrl + Mouse wheel"); m_desc["smart_fill_angle"] = _L("Smart fill angle"); - m_desc["brush_size"] = _L("Set pen size"); - m_desc["brush_size_caption"] = _L("Ctrl + Mouse wheel") + ": "; - - // BBS + m_desc["height_range_caption"] = _L("Ctrl + Mouse wheel"); m_desc["height_range"] = _L("Height range"); init_extruders_data(); @@ -297,6 +297,8 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + caption_max += m_imgui->calc_text_size(": ").x + 15.f; + float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); @@ -310,7 +312,24 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - for (const auto &t : std::array{"paint", "erase", "brush_size"}) draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + std::vector tip_items; + switch (m_tool_type) { + case ToolType::BRUSH: + tip_items = {"paint", "erase", "cursor_size", "clipping_of_view"}; + break; + case ToolType::BUCKET_FILL: + tip_items = {"paint", "erase", "smart_fill_angle", "clipping_of_view"}; + break; + case ToolType::SMART_FILL: + // TODO: + break; + case ToolType::GAP_FILL: + tip_items = {"gap_area"}; + break; + default: + break; + } + for (const auto &t : tip_items) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } ImGui::PopStyleVar(1); @@ -347,7 +366,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott float caption_max = 0.f; float total_text_max = 0.f; - for (const auto &t : std::array{"paint", "erase", "brush_size"}) { + for (const auto &t : std::array{"paint", "erase", "cursor_size", "smart_fill_angle", "height_range", "clipping_of_view"}) { caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); } @@ -537,7 +556,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott if (m_detect_geometry_edge) { ImGui::AlignTextToFramePadding(); - m_imgui->text(m_desc["smart_fill_angle"] + ":"); + m_imgui->text(m_desc["smart_fill_angle"]); std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Face angle threshold," "placed after the number with no whitespace in between."); ImGui::SameLine(sliders_left_width); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index 3d5254f639..de032fc84f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -90,7 +90,7 @@ protected: void on_set_state() override; EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType(m_selected_extruder_idx + 1); } - EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType::NONE; } + EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType(-1); } void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index cf1ad0e4d3..549972119c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -495,21 +495,22 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (action == SLAGizmoEventType::MouseWheelUp || action == SLAGizmoEventType::MouseWheelDown) { if (control_down) { + //BBS + if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::HEIGHT_RANGE) { + m_cursor_height = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_cursor_height - this->get_cursor_height_step(), this->get_cursor_height_min()) : + std::min(m_cursor_height + this->get_cursor_height_step(), this->get_cursor_height_max()); + m_parent.set_as_dirty(); + return true; + } + if (m_tool_type == ToolType::BRUSH && (m_cursor_type == TriangleSelector::CursorType::SPHERE || m_cursor_type == TriangleSelector::CursorType::CIRCLE)) { m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_cursor_radius - this->get_cursor_radius_step(), this->get_cursor_radius_min()) : std::min(m_cursor_radius + this->get_cursor_radius_step(), this->get_cursor_radius_max()); m_parent.set_as_dirty(); return true; } - double pos = m_c->object_clipper()->get_position(); - pos = action == SLAGizmoEventType::MouseWheelDown - ? std::max(0., pos - 0.01) - : std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); - return true; - } - else if (alt_down) { - if (m_tool_type == ToolType::SMART_FILL) { + + if (m_tool_type == ToolType::BUCKET_FILL || m_tool_type == ToolType::SMART_FILL) { m_smart_fill_angle = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_smart_fill_angle - SmartFillAngleStep, SmartFillAngleMin) : std::min(m_smart_fill_angle + SmartFillAngleStep, SmartFillAngleMax); m_parent.set_as_dirty(); @@ -527,7 +528,22 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return true; } - return false; + if (m_tool_type == ToolType::GAP_FILL) { + TriangleSelectorPatch::gap_area = action == SLAGizmoEventType::MouseWheelDown ? + std::max(TriangleSelectorPatch::gap_area - TriangleSelectorPatch::GapAreaStep, TriangleSelectorPatch::GapAreaMin) : + std::min(TriangleSelectorPatch::gap_area + TriangleSelectorPatch::GapAreaStep, TriangleSelectorPatch::GapAreaMax); + m_parent.set_as_dirty(); + return true; + } + } + else if (alt_down) { + // BBS + double pos = m_c->object_clipper()->get_position(); + pos = action == SLAGizmoEventType::MouseWheelDown + ? std::max(0., pos - 0.01) + : std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); + return true; } } @@ -544,6 +560,16 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return false; EnforcerBlockerType new_state = EnforcerBlockerType::NONE; + // BBS + if (action == SLAGizmoEventType::Dragging) { + if (m_button_down == Button::Right && this->get_right_button_state_type() == EnforcerBlockerType(-1)) + return false; + } + else { + if (action == SLAGizmoEventType::RightDown && this->get_right_button_state_type() == EnforcerBlockerType(-1)) + return false; + } + if (! shift_down) { if (action == SLAGizmoEventType::Dragging) new_state = m_button_down == Button::Left ? this->get_left_button_state_type() : this->get_right_button_state_type(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index caef5b73e3..a0791bb887 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -142,6 +142,7 @@ public: constexpr static float GapAreaMin = 0.f; constexpr static float GapAreaMax = 5.f; + constexpr static float GapAreaStep = 0.2f; // BBS: fix me static float gap_area; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 6b69b0ba69..6885838835 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -120,16 +120,6 @@ void GLGizmoScale3D::on_start_dragging() void GLGizmoScale3D::on_update(const UpdateData& data) { - bool uniform_scale = false; - AppConfig* config = wxGetApp().app_config; - if (config) - uniform_scale = config->get("uniform_scale") == "1" ? true : false; - - if (uniform_scale) { - do_scale_uniform(data); - return; - } - if ((m_hover_id == 0) || (m_hover_id == 1)) do_scale_along_axis(X, data); else if ((m_hover_id == 2) || (m_hover_id == 3)) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index a1bdd80dd4..0e96fec842 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -29,16 +29,18 @@ bool GLGizmoSeam::on_init() { m_shortcut_key = WXK_CONTROL_P; - m_desc["clipping_of_view"] = _L("Section view") + ": "; + m_desc["clipping_of_view_caption"] = _L("Alt + Mouse wheel"); + m_desc["clipping_of_view"] = _L("Section view"); m_desc["reset_direction"] = _L("Reset direction"); - m_desc["cursor_size"] = _L("Brush size") + ": "; - m_desc["cursor_type"] = _L("Brush shape") + ": "; - m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["cursor_size_caption"] = _L("Ctrl + Mouse wheel"); + m_desc["cursor_size"] = _L("Brush size"); + m_desc["cursor_type"] = _L("Brush shape"); + m_desc["enforce_caption"] = _L("Left mouse button"); m_desc["enforce"] = _L("Enforce seam"); - m_desc["block_caption"] = _L("Right mouse button") + ": "; + m_desc["block_caption"] = _L("Right mouse button"); m_desc["block"] = _L("Block seam"); - m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; - m_desc["remove"] = _L("Remove selection"); + m_desc["remove_caption"] = _L("Shift + Left mouse button"); + m_desc["remove"] = _L("Erase"); m_desc["remove_all"] = _L("Erase all painting"); m_desc["circle"] = _L("Circle"); m_desc["sphere"] = _L("Sphere"); @@ -125,6 +127,8 @@ void GLGizmoSeam::show_tooltip_information(float caption_max, float x, float y) ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + caption_max += m_imgui->calc_text_size(": ").x + 35.f; + float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); @@ -138,7 +142,7 @@ void GLGizmoSeam::show_tooltip_information(float caption_max, float x, float y) m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - for (const auto &t : std::array{"enforce", "block", "remove"}) draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + for (const auto &t : std::array{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } ImGui::PopStyleVar(1); @@ -187,7 +191,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) float caption_max = 0.f; float total_text_max = 0.f; - for (const auto &t : std::array{"enforce", "block", "remove"}) { + for (const auto &t : std::array{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) { caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index def666e5b0..83a832ca8b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -141,35 +141,6 @@ std::string GLGizmoSimplify::on_get_name() const return _u8L("Simplify"); } -void GLGizmoSimplify::push_simplify_style() -{ - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 10.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowTitleAlign, ImVec2(0.05f, 0.50f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(50 / 255.0f, 58 / 255.0f, 61 / 255.0f, 1.00f)); // 1 - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGuiWrapper::COL_WINDOW_BG); // 2 - ImGui::PushStyleColor(ImGuiCol_TitleBg, ImGuiWrapper::COL_TITLE_BG); // 3 - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImGuiWrapper::COL_TITLE_BG); // 4 - ImGui::PushStyleColor(ImGuiCol_Separator, ImGuiWrapper::COL_SEPARATOR); // 5 - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); // 6 - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); // 7 - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); // 8 - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 9 - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10 - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11 - ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); // 12 - ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, ImGuiWrapper::COL_GREEN_LIGHT); - ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); - -} -void GLGizmoSimplify::pop_simplify_style() -{ - ImGui::PopStyleColor(14); - ImGui::PopStyleVar(5); -} - void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { create_gui_cfg(); @@ -260,7 +231,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi float slider_width = m_imgui->scaled(5.0f); - push_simplify_style(); + m_imgui->push_common_window_style(m_parent.get_scale()); int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; m_imgui->begin(on_get_name(), flag); @@ -412,7 +383,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::PopStyleColor(1); m_imgui->end(); - pop_simplify_style(); + m_imgui->pop_common_window_style(); if (start_process) process(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 2a53fd8e72..39093e14d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -48,8 +48,6 @@ private: void process(); void stop_worker_thread_request(); void worker_finished(); - void push_simplify_style(); - void pop_simplify_style(); void create_gui_cfg(); void request_rerender(bool force = false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index b7583977fd..2bc950fe34 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -541,5 +541,172 @@ void SupportsClipper::render_cut() const } + +using namespace AssembleViewDataObjects; +AssembleViewDataPool::AssembleViewDataPool(GLCanvas3D* canvas) + : m_canvas(canvas) +{ + using c = AssembleViewDataID; + m_data[c::ModelObjectsInfo].reset(new ModelObjectsInfo(this)); + m_data[c::ModelObjectsClipper].reset(new ModelObjectsClipper(this)); +} + +void AssembleViewDataPool::update(AssembleViewDataID required) +{ + assert(check_dependencies(required)); + for (auto& [id, data] : m_data) { + if (int(required) & int(AssembleViewDataID(id))) + data->update(); + else + if (data->is_valid()) + data->release(); + } +} + + +ModelObjectsInfo* AssembleViewDataPool::model_objects_info() const +{ + ModelObjectsInfo* sel_info = dynamic_cast(m_data.at(AssembleViewDataID::ModelObjectsInfo).get()); + assert(sel_info); + return sel_info->is_valid() ? sel_info : nullptr; +} + + +ModelObjectsClipper* AssembleViewDataPool::model_objects_clipper() const +{ + ModelObjectsClipper* oc = dynamic_cast(m_data.at(AssembleViewDataID::ModelObjectsClipper).get()); + // ObjectClipper is used from outside the gizmos to report current clipping plane. + // This function can be called when oc is nullptr. + return (oc && oc->is_valid()) ? oc : nullptr; +} + +#ifndef NDEBUG +// Check the required resources one by one and return true if all +// dependencies are met. +bool AssembleViewDataPool::check_dependencies(AssembleViewDataID required) const +{ + // This should iterate over currently required data. Each of them should + // be asked about its dependencies and it must check that all dependencies + // are also in required and before the current one. + for (auto& [id, data] : m_data) { + // in case we don't use this, the deps are irrelevant + if (!(int(required) & int(AssembleViewDataID(id)))) + continue; + + + AssembleViewDataID deps = data->get_dependencies(); + assert(int(deps) == (int(deps) & int(required))); + } + + +return true; +} +#endif // NDEBUG + + + + +void ModelObjectsInfo::on_update() +{ + if (!get_pool()->get_canvas()->get_model()->objects.empty()) { + m_model_objects = get_pool()->get_canvas()->get_model()->objects; + } +} + +void ModelObjectsInfo::on_release() +{ + m_model_objects.clear(); +} + +//int ModelObjectsInfo::get_active_instance() const +//{ +// const Selection& selection = get_pool()->get_canvas()->get_selection(); +// return selection.get_instance_idx(); +//} + + +void ModelObjectsClipper::on_update() +{ + const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects(); + if (model_objects.empty()) + return; + + // which mesh should be cut? + std::vector meshes; + + if (meshes.empty()) + for (auto mo : model_objects) { + for (const ModelVolume* mv : mo->volumes) + meshes.push_back(&mv->mesh()); + } + + if (meshes != m_old_meshes) { + m_clippers.clear(); + for (const TriangleMesh* mesh : meshes) { + m_clippers.emplace_back(new MeshClipper); + m_clippers.back()->set_mesh(*mesh); + } + m_old_meshes = meshes; + + m_active_inst_bb_radius = get_pool()->get_canvas()->volumes_bounding_box().radius(); + } +} + + +void ModelObjectsClipper::on_release() +{ + m_clippers.clear(); + m_old_meshes.clear(); + m_clp.reset(); + m_clp_ratio = 0.; + +} + +void ModelObjectsClipper::render_cut() const +{ + if (m_clp_ratio == 0.) + return; + const ModelObjectPtrs model_objects = get_pool()->model_objects_info()->model_objects(); + + size_t clipper_id = 0; + for (const ModelObject* mo : model_objects) { + Geometry::Transformation assemble_objects_trafo = mo->instances[0]->get_assemble_transformation(); + auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly(); + for (const ModelVolume* mv : mo->volumes) { + Geometry::Transformation vol_trafo = mv->get_transformation(); + Geometry::Transformation trafo = assemble_objects_trafo * vol_trafo; + trafo.set_offset(trafo.get_offset() + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0) + offset_to_assembly * (GLVolume::explosion_ratio - 1.0)); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_plane(*m_clp); + clipper->set_transformation(trafo); + glsafe(::glPushMatrix()); + // BBS + glsafe(::glColor3f(0.25f, 0.25f, 0.25f)); + clipper->render_cut(); + glsafe(::glPopMatrix()); + + ++clipper_id; + } + } +} + + +void ModelObjectsClipper::set_position(double pos, bool keep_normal) +{ + Vec3d camera_dir = wxGetApp().plater()->get_camera().get_dir_forward(); + Vec3d normal = -camera_dir; + const Vec3d& center = get_pool()->get_canvas()->volumes_bounding_box().center(); + float dist = normal.dot(center); + + if (pos < 0.) + pos = m_clp_ratio; + + m_clp_ratio = pos; + m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius * GLVolume::explosion_ratio) - m_clp_ratio * 2 * m_active_inst_bb_radius * GLVolume::explosion_ratio))); + get_pool()->get_canvas()->set_as_dirty(); +} + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 228f5b58c3..91c1bde44c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -39,6 +39,7 @@ enum class SLAGizmoEventType : unsigned char { class CommonGizmosDataBase; +class AssembleViewDataBase; namespace CommonGizmosDataObjects { class SelectionInfo; class InstancesHider; @@ -48,6 +49,11 @@ namespace CommonGizmosDataObjects { class SupportsClipper; } +namespace AssembleViewDataObjects { + class ModelObjectsInfo; + class ModelObjectsClipper; +} + // Some of the gizmos use the same data that need to be updated ocassionally. // It is also desirable that the data are not recalculated when the gizmos // are just switched, but on the other hand, they should be released when @@ -306,9 +312,120 @@ private: } // namespace CommonGizmosDataObjects +enum class AssembleViewDataID { + None = 0, + ModelObjectsInfo = 1 << 0, + ModelObjectsClipper = 1 << 4, +}; + +class AssembleViewDataPool { +public: + AssembleViewDataPool(GLCanvas3D* canvas); + + // Update all resources and release what is not used. + // Accepts a bitmask of currently required resources. + void update(AssembleViewDataID required); + + // Getters for the data that need to be accessed from the gizmos directly. + AssembleViewDataObjects::ModelObjectsInfo* model_objects_info() const; + AssembleViewDataObjects::ModelObjectsClipper* model_objects_clipper() const; + + GLCanvas3D* get_canvas() const { return m_canvas; } + +private: + std::map> m_data; + GLCanvas3D* m_canvas; + +#ifndef NDEBUG + bool check_dependencies(AssembleViewDataID required) const; +#endif +}; + +// Base class for a wrapper object managing a single resource. +// Each of the enum values above (safe None) will have an object of this kind. +class AssembleViewDataBase { +public: + // Pass a backpointer to the pool, so the individual + // objects can communicate with one another. + explicit AssembleViewDataBase(AssembleViewDataPool* cgdp) + : m_common{ cgdp } {} + virtual ~AssembleViewDataBase() {} + + // Update the resource. + void update() { on_update(); m_is_valid = true; } + + // Release any data that are stored internally. + void release() { on_release(); m_is_valid = false; } + + // Returns whether the resource is currently maintained. + bool is_valid() const { return m_is_valid; } + +#ifndef NDEBUG + // Return a bitmask of all resources that this one relies on. + // The dependent resource must have higher ID than the one + // it depends on. + virtual AssembleViewDataID get_dependencies() const { return AssembleViewDataID::None; } +#endif // NDEBUG + +protected: + virtual void on_release() = 0; + virtual void on_update() = 0; + AssembleViewDataPool* get_pool() const { return m_common; } +private: + bool m_is_valid = false; + AssembleViewDataPool* m_common = nullptr; +}; +namespace AssembleViewDataObjects +{ +class ModelObjectsInfo : public AssembleViewDataBase +{ +public: + explicit ModelObjectsInfo(AssembleViewDataPool* cgdp) + : AssembleViewDataBase(cgdp) {} + + ModelObjectPtrs model_objects() const { return m_model_objects; } + //int get_active_instance() const; + float get_sla_shift() const { return m_z_shift; } + +protected: + void on_update() override; + void on_release() override; + +private: + ModelObjectPtrs m_model_objects; + float m_z_shift = 0.f; +}; + +class ModelObjectsClipper : public AssembleViewDataBase +{ +public: + explicit ModelObjectsClipper(AssembleViewDataPool* cgdp) + : AssembleViewDataBase(cgdp) {} +#ifndef NDEBUG + AssembleViewDataID get_dependencies() const override { return AssembleViewDataID::ModelObjectsInfo; } +#endif // NDEBUG + + void set_position(double pos, bool keep_normal); + double get_position() const { return m_clp_ratio; } + ClippingPlane* get_clipping_plane() const { return m_clp.get(); } + void render_cut() const; + + +protected: + void on_update() override; + void on_release() override; + +private: + std::vector m_old_meshes; + std::vector> m_clippers; + std::unique_ptr m_clp; + double m_clp_ratio = 0.; + double m_active_inst_bb_radius = 0.; +}; +} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index b8b3ddef99..ea92b9adf2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -155,6 +155,7 @@ bool GLGizmosManager::init() //m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); + m_assemble_view_data.reset(new AssembleViewDataPool(&m_parent)); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { @@ -341,6 +342,16 @@ void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos)); } +void GLGizmosManager::update_assemble_view_data() +{ + if (m_assemble_view_data) { + if (m_parent.get_canvas_type() != GLCanvas3D::CanvasAssembleView) + m_assemble_view_data->update(AssembleViewDataID(0)); + else + m_assemble_view_data->update(AssembleViewDataID((int)AssembleViewDataID::ModelObjectsInfo | (int)AssembleViewDataID::ModelObjectsClipper)); + } +} + void GLGizmosManager::update_data() { if (!m_enabled) @@ -359,10 +370,11 @@ void GLGizmosManager::update_data() enable_grabber(Scale, i, enable_scale_xyz); } - if (m_common_gizmos_data) + if (m_common_gizmos_data) { m_common_gizmos_data->update(get_current() - ? get_current()->get_requirements() - : CommonGizmosDataID(0)); + ? get_current()->get_requirements() + : CommonGizmosDataID(0)); + } if (selection.is_single_full_instance()) { @@ -587,6 +599,18 @@ ClippingPlane GLGizmosManager::get_clipping_plane() const } } +ClippingPlane GLGizmosManager::get_assemble_view_clipping_plane() const +{ + if (!m_assemble_view_data + || !m_assemble_view_data->model_objects_clipper() + || m_assemble_view_data->model_objects_clipper()->get_position() == 0.) + return ClippingPlane::ClipsNothing(); + else { + const ClippingPlane& clp = *m_assemble_view_data->model_objects_clipper()->get_clipping_plane(); + return ClippingPlane(-clp.get_normal(), clp.get_data()[3]); + } +} + bool GLGizmosManager::wants_reslice_supports_on_undo() const { return (m_current == SlaSupports @@ -614,6 +638,12 @@ void GLGizmosManager::render_painter_gizmo() const gizmo->render_painter_gizmo(); } +void GLGizmosManager::render_painter_assemble_view() const +{ + if (m_assemble_view_data) + m_assemble_view_data->model_objects_clipper()->render_cut(); +} + void GLGizmosManager::render_current_gizmo_for_picking_pass() const { if (! m_enabled || m_current == Undefined) @@ -649,7 +679,14 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); - if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown() + // BBS +#ifdef __WXOSX_MAC__ + , evt.RawControlDown() +#else + , evt.ControlDown() +#endif + )) processed = true; } @@ -675,7 +712,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.Moving()) { m_tooltip = update_hover_state(mouse_pos); if (m_current == MmuSegmentation || m_current == FdmSupports) - gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown()); + // BBS + gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); } else if (evt.LeftUp()) { if (m_mouse_capture.left) { processed = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index c1338fdf38..c47aed6ba4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -146,6 +146,7 @@ private: std::map icon_list; public: + std::unique_ptr m_assemble_view_data; enum MENU_ICON_NAME { IC_TOOLBAR_RESET = 0, IC_TOOLBAR_RESET_HOVER, @@ -222,6 +223,7 @@ public: void update(const Linef3& mouse_ray, const Point& mouse_pos); void update_data(); + void update_assemble_view_data(); EType get_current_type() const { return m_current; } GLGizmoBase* get_current() const; @@ -265,6 +267,7 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); ClippingPlane get_clipping_plane() const; + ClippingPlane get_assemble_view_clipping_plane() const; bool wants_reslice_supports_on_undo() const; bool is_in_editing_mode(bool error_notification = false) const; @@ -273,6 +276,7 @@ public: void render_current_gizmo() const; void render_current_gizmo_for_picking_pass() const; void render_painter_gizmo() const; + void render_painter_assemble_view() const; void render_overlay() const; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 8a72a514cb..7b7e4a5216 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -89,13 +89,6 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); m_new_position = volume->get_instance_offset(); - // Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible. - if (m_world_coordinates && ! m_uniform_scale && - ! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) { - // Manipulating an instance in the world coordinate system, rotation is not multiples of ninety degrees, therefore enforce uniform scaling. - m_uniform_scale = true; - } - if (m_world_coordinates) { m_new_rotate_label_string = L("Rotate"); m_new_rotation = volume->get_instance_rotation() * (180. / M_PI); @@ -463,9 +456,6 @@ void GizmoObjectManipulation::set_uniform_scaling(const bool new_value) } } m_uniform_scale = new_value; - AppConfig* config = wxGetApp().app_config; - if (config) - config->set("uniform_scale", new_value ? "1": "0"); } static const char* label_values[2][3] = { @@ -897,9 +887,6 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w ImGui::Separator(); - AppConfig* config = wxGetApp().app_config; - if (config) - this->m_uniform_scale = config->get("uniform_scale") == "1" ? true : false; bool uniform_scale = this->m_uniform_scale; const Selection &selection = m_glcanvas.get_selection(); diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp index ea446b3828..39ceee5dbf 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp @@ -81,7 +81,6 @@ public: Vec3d m_buffered_size; bool m_new_enabled {true}; bool m_uniform_scale {true}; - bool m_uniform_config {false}; // Does the object manipulation panel work in World or Local coordinates? bool m_world_coordinates = true; diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp new file mode 100644 index 0000000000..1a289c611f --- /dev/null +++ b/src/slic3r/GUI/HintNotification.cpp @@ -0,0 +1,1155 @@ +#include "HintNotification.hpp" +#include "ImGuiWrapper.hpp" +#include "format.hpp" +#include "I18N.hpp" +#include "GUI_ObjectList.hpp" +#include "GLCanvas3D.hpp" +#include "MainFrame.hpp" +#include "Tab.hpp" +#include "libslic3r/AppConfig.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Config.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Preset.hpp" +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HINTS_CEREAL_VERSION 1 +// structure for writing used hints into binary file with version +struct HintsCerealData +{ +std::vector my_data; +// cereal will supply the version automatically when loading or saving +// The version number comes from the CEREAL_CLASS_VERSION macro +template +void serialize(Archive& ar, std::uint32_t const version) +{ +// You can choose different behaviors depending on the version +// This is useful if you need to support older variants of your codebase +// interacting with newer ones +if (version > HINTS_CEREAL_VERSION) + throw Slic3r::IOError("Version of hints.cereal is higher than current version."); +else + ar(my_data); +} +}; +// version of used hints binary file +CEREAL_CLASS_VERSION(HintsCerealData, HINTS_CEREAL_VERSION); + +namespace Slic3r { +namespace GUI { + +const std::string BOLD_MARKER_START = ""; +const std::string BOLD_MARKER_END = ""; +const std::string HYPERTEXT_MARKER_START = ""; +const std::string HYPERTEXT_MARKER_END = ""; + +namespace { + inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity) + { + if (fading_out) + ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity)); + else + ImGui::PushStyleColor(idx, col); + } + + void write_used_binary(const std::vector& ids) + { + boost::nowide::ofstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal").string(), std::ios::binary); + cereal::BinaryOutputArchive archive(file); + HintsCerealData cd{ ids }; + try + { + archive(cd); + } + catch (const std::exception& ex) + { + BOOST_LOG_TRIVIAL(error) << "Failed to write to hints.cereal. " << ex.what(); + } + } + void read_used_binary(std::vector& ids) + { + boost::filesystem::path path(boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"); + if (!boost::filesystem::exists(path)) { + BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string(); + return; + } + boost::nowide::ifstream file(path.string()); + cereal::BinaryInputArchive archive(file); + HintsCerealData cd; + try + { + archive(cd); + } + catch (const std::exception& ex) + { + BOOST_LOG_TRIVIAL(error) << "Failed to load to hints.cereal. " << ex.what(); + return; + } + ids = cd.my_data; + } + enum TagCheckResult + { + TagCheckAffirmative, + TagCheckNegative, + TagCheckNotCompatible + }; + // returns if in mode defined by tag + TagCheckResult tag_check_mode(const std::string& tag) + { + std::vector allowed_tags = { "simple", "advanced", "expert" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) + { + ConfigOptionMode config_mode = wxGetApp().get_mode(); + if (config_mode == ConfigOptionMode::comSimple) return (tag == "simple" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comAdvanced) return (tag == "advanced" ? TagCheckAffirmative : TagCheckNegative); + //else if (config_mode == ConfigOptionMode::comDevelop) return (tag == "develop" ? TagCheckAffirmative : TagCheckNegative); + } + return TagCheckNotCompatible; + } + + TagCheckResult tag_check_tech(const std::string& tag) + { + std::vector allowed_tags = { "FFF", "MMU", "SLA" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { + const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (tech == ptFFF) { + // MMU / FFF + bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; + if (tag == "MMU") return (is_mmu ? TagCheckAffirmative : TagCheckNegative); + return (tag == "FFF" ? TagCheckAffirmative : TagCheckNegative); + } + else { + // SLA + return (tag == "SLA" ? TagCheckAffirmative : TagCheckNegative); + } + } + return TagCheckNotCompatible; + } + + TagCheckResult tag_check_system(const std::string& tag) + { + std::vector allowed_tags = { "Windows", "Linux", "OSX" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { + if (tag == "Windows") +#ifdef WIN32 + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // WIN32 + + if (tag == "Linux") +#ifdef __linux__ + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // __linux__ + + if (tag == "OSX") +#ifdef __APPLE__ + return TagCheckAffirmative; +#else + return TagCheckNegative; +#endif // __apple__ + } + return TagCheckNotCompatible; + } + + TagCheckResult tag_check_material(const std::string& tag) + { + if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)) { + // search PrintConfig filament_type to find if allowed tag + if (wxGetApp().app_config->get("filament_type").find(tag)) { + const Preset& preset = tab->m_presets->get_edited_preset(); + const auto* opt = preset.config.opt("filament_type"); + if (opt->values[0] == tag) + return TagCheckAffirmative; + return TagCheckNegative; + } + return TagCheckNotCompatible; + } + /* TODO: SLA materials + else if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_SLA_MATERIAL)) { + //if (wxGetApp().app_config->get("material_type").find(tag)) { + const Preset& preset = tab->m_presets->get_edited_preset(); + const auto* opt = preset.config.opt("material_type"); + if (opt->values[0] == tag) + return TagCheckAffirmative; + return TagCheckNegative; + //} + return TagCheckNotCompatible; + }*/ + return TagCheckNotCompatible; + } + + // return true if NOT in disabled mode. + bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) + { + if (disabled_tags.empty() && enabled_tags.empty()) + return true; + // enabled tags must ALL return affirmative or check fails + if (!enabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < enabled_tags.size(); i++) { + if (enabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (enabled_tags[i] != ';') { + tag += enabled_tags[i]; + } + if (enabled_tags[i] == ';' || i == enabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_system(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_material(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in enabled_tags not compatible."; + // non compatible in enabled means return false since all enabled must be affirmative. + return false; + } + } + } + } + // disabled tags must all NOT return affirmative or check fails + if (!disabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < disabled_tags.size(); i++) { + if (disabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (disabled_tags[i] != ';') { + tag += disabled_tags[i]; + } + if (disabled_tags[i] == ';' || i == disabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + result = tag_check_system(tag); + if (result == TagCheckResult::TagCheckAffirmative) + return false; + if (result == TagCheckResult::TagCheckNegative) + continue; + result = tag_check_material(tag); + if (result == TagCheckResult::TagCheckAffirmative) + return false; + if (result == TagCheckResult::TagCheckNegative) + continue; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in disabled_tags not compatible."; + } + } + } + } + return true; + } + void launch_browser_if_allowed(const std::string& url) + { + wxGetApp().open_browser_with_warning_dialog(url); + } +} //namespace +HintDatabase::~HintDatabase() +{ + if (m_initialized) { + write_used_binary(m_used_ids); + } +} +void HintDatabase::uninit() +{ + if (m_initialized) { + write_used_binary(m_used_ids); + } + m_initialized = false; + m_loaded_hints.clear(); + m_sorted_hints = false; + m_used_ids.clear(); + m_used_ids_loaded = false; +} +void HintDatabase::init() +{ + load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini")); + m_initialized = true; +} +void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) +{ + namespace pt = boost::property_tree; + pt::ptree tree; + boost::nowide::ifstream ifs(path.string()); + try { + pt::read_ini(ifs, tree); + } + catch (const boost::property_tree::ini_parser::ini_parser_error& err) { + throw Slic3r::RuntimeError(format("Failed loading hints file \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str()); + } + + for (const auto& section : tree) { + if (boost::starts_with(section.first, "hint:")) { + // create std::map with tree data + std::map dict; + for (const auto& data : section.second) { + dict.emplace(data.first, data.second.data()); + } + // unique id string [hint:id] (trim "hint:") + std::string id_string = section.first.substr(5); + id_string = std::to_string(std::hash{}(id_string)); + // unescaping and translating all texts and saving all data common for all hint types + std::string fulltext; + std::string text1; + std::string hypertext_text; + std::string follow_text; + // tags + std::string disabled_tags; + std::string enabled_tags; + // optional link to documentation (accessed from button) + std::string documentation_link; + // randomized weighted order variables + size_t weight = 1; + bool was_displayed = is_used(id_string); + //unescape text1 + unescape_string_cstyle(dict["text"], fulltext); + fulltext = _utf8(fulltext); +#ifdef __APPLE__ + boost::replace_all(fulltext, "Ctrl+", "?"); +#endif //__APPLE__ + // replace and for imgui markers + std::string marker_s(1, ImGui::ColorMarkerStart); + std::string marker_e(1, ImGui::ColorMarkerEnd); + // start marker + size_t marker_pos = fulltext.find(BOLD_MARKER_START); + while (marker_pos != std::string::npos) { + fulltext.replace(marker_pos, 3, marker_s); + marker_pos = fulltext.find(BOLD_MARKER_START, marker_pos); + } + // end marker + marker_pos = fulltext.find(BOLD_MARKER_END); + while (marker_pos != std::string::npos) { + fulltext.replace(marker_pos, 4, marker_e); + marker_pos = fulltext.find(BOLD_MARKER_END, marker_pos); + } + // divide fulltext + size_t hypertext_start = fulltext.find(HYPERTEXT_MARKER_START); + if (hypertext_start != std::string::npos) { + //hypertext exists + fulltext.erase(hypertext_start, HYPERTEXT_MARKER_START.size()); + if (fulltext.find(HYPERTEXT_MARKER_START) != std::string::npos) { + // This must not happen - only 1 hypertext allowed + BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertexts: " << _utf8(dict["text"]); + continue; + } + size_t hypertext_end = fulltext.find(HYPERTEXT_MARKER_END); + if (hypertext_end == std::string::npos) { + // hypertext was not correctly ended + BOOST_LOG_TRIVIAL(error) << "Hint notification without hypertext end marker: " << _utf8(dict["text"]); + continue; + } + fulltext.erase(hypertext_end, HYPERTEXT_MARKER_END.size()); + if (fulltext.find(HYPERTEXT_MARKER_END) != std::string::npos) { + // This must not happen - only 1 hypertext end allowed + BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertext end markers: " << _utf8(dict["text"]); + continue; + } + + text1 = fulltext.substr(0, hypertext_start); + hypertext_text = fulltext.substr(hypertext_start, hypertext_end - hypertext_start); + follow_text = fulltext.substr(hypertext_end); + } + else { + text1 = fulltext; + } + + if (dict.find("disabled_tags") != dict.end()) { + disabled_tags = dict["disabled_tags"]; + } + if (dict.find("enabled_tags") != dict.end()) { + enabled_tags = dict["enabled_tags"]; + } + if (dict.find("documentation_link") != dict.end()) { + documentation_link = dict["documentation_link"]; + } + + if (dict.find("weight") != dict.end()) { + weight = (size_t)std::max(1, std::atoi(dict["weight"].c_str())); + } + + // create HintData + if (dict.find("hypertext_type") != dict.end()) { + //link to internet + if (dict["hypertext_type"] == "link") { + std::string hypertext_link = dict["hypertext_link"]; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } }; + m_loaded_hints.emplace_back(hint_data); + // highlight settings + } + else if (dict["hypertext_type"] == "settings") { + std::string opt = dict["hypertext_settings_opt"]; + Preset::Type type = static_cast(std::atoi(dict["hypertext_settings_type"].c_str())); + std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]); + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; + m_loaded_hints.emplace_back(hint_data); + // open preferences + } + else if (dict["hypertext_type"] == "preferences") { + std::string page = dict["hypertext_preferences_page"]; + std::string item = dict["hypertext_preferences_item"]; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page, item]() { wxGetApp().open_preferences(1, page); } };// 1 is to modify + m_loaded_hints.emplace_back(hint_data); + } + else if (dict["hypertext_type"] == "plater") { + std::string item = dict["hypertext_plater_item"]; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; + m_loaded_hints.emplace_back(hint_data); + } + else if (dict["hypertext_type"] == "gizmo") { + std::string item = dict["hypertext_gizmo_item"]; + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; + m_loaded_hints.emplace_back(hint_data); + } + else if (dict["hypertext_type"] == "gallery") { + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { + // Deselect all objects, otherwise gallery wont show. + wxGetApp().plater()->canvas3D()->deselect_all(); + //wxGetApp().obj_list()->load_shape_object_from_gallery(); } + } }; + m_loaded_hints.emplace_back(hint_data); + } + else if (dict["hypertext_type"] == "menubar") { + wxString menu(_("&" + dict["hypertext_menubar_menu_name"])); + wxString item(_(dict["hypertext_menubar_item_name"])); + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [menu, item]() { wxGetApp().mainframe->open_menubar_item(menu, item); } }; + m_loaded_hints.emplace_back(hint_data); + } + } + else { + // plain text without hypertext + HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link }; + m_loaded_hints.emplace_back(hint_data); + } + } + } +} +HintData* HintDatabase::get_hint(bool new_hint/* = true*/) +{ + if (!m_initialized) { + init(); + new_hint = true; + } + if (m_loaded_hints.empty()) + { + BOOST_LOG_TRIVIAL(error) << "There were no hints loaded from hints.ini file."; + return nullptr; + } + + try + { + if (new_hint) + m_hint_id = get_next(); + } + catch (const std::exception&) + { + return nullptr; + } + + + + return &m_loaded_hints[m_hint_id]; +} + +size_t HintDatabase::get_next() +{ + if (!m_sorted_hints) + { + auto compare_wieght = [](const HintData& a, const HintData& b) { return a.weight < b.weight; }; + std::sort(m_loaded_hints.begin(), m_loaded_hints.end(), compare_wieght); + m_sorted_hints = true; + srand(time(NULL)); + } + std::vector candidates; // index in m_loaded_hints + // total weight + size_t total_weight = 0; + for (size_t i = 0; i < m_loaded_hints.size(); i++) { + if (!m_loaded_hints[i].was_displayed && tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) { + candidates.emplace_back(i); + total_weight += m_loaded_hints[i].weight; + } + } + // all were shown + if (total_weight == 0) { + clear_used(); + for (size_t i = 0; i < m_loaded_hints.size(); i++) { + m_loaded_hints[i].was_displayed = false; + if (tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) { + candidates.emplace_back(i); + total_weight += m_loaded_hints[i].weight; + } + } + } + if (total_weight == 0) { + BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed. No suitable hint was found."; + throw std::exception(); + } + size_t random_number = rand() % total_weight + 1; + size_t current_weight = 0; + for (size_t i = 0; i < candidates.size(); i++) { + current_weight += m_loaded_hints[candidates[i]].weight; + if (random_number <= current_weight) { + set_used(m_loaded_hints[candidates[i]].id_string); + m_loaded_hints[candidates[i]].was_displayed = true; + return candidates[i]; + } + } + BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed."; + throw std::exception(); +} + +bool HintDatabase::is_used(const std::string& id) +{ + // load used ids from file + if (!m_used_ids_loaded) { + read_used_binary(m_used_ids); + m_used_ids_loaded = true; + } + // check if id is in used + for (const std::string& used_id : m_used_ids) { + if (used_id == id) + { + return true; + } + } + return false; +} +void HintDatabase::set_used(const std::string& id) +{ + // check needed? + if (!is_used(id)) + { + m_used_ids.emplace_back(id); + } +} +void HintDatabase::clear_used() +{ + m_used_ids.clear(); +} + +void NotificationManager::HintNotification::count_spaces() +{ + //determine line width + m_line_height = ImGui::CalcTextSize("A").y; + + + std::string text; + text = ImGui::WarningMarker; + float picture_width = ImGui::CalcTextSize(text.c_str()).x; + m_left_indentation = picture_width * 1.5f + m_line_height / 2; + + // no left button picture + //m_left_indentation = m_line_height; + + if (m_documentation_link.empty()) + m_window_width_offset = m_left_indentation + m_line_height * 3.f; + else + m_window_width_offset = m_left_indentation + m_line_height * 5.5f; + + m_window_width = m_line_height * 25; +} + +static int get_utf8_seq_length(const char* seq, size_t size) +{ + int length = 0; + unsigned char c = seq[0]; + if (c < 0x80) { // 0x00-0x7F + // is ASCII letter + length++; + } + // Bytes 0x80 to 0xBD are trailer bytes in a multibyte sequence. + // pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes. + else if (c < 0xC0) { // 0x80-0xBF + length++; + while (length < size) { + c = seq[length]; + if (c < 0x80 || c >= 0xC0) { + break; // prevent overrun + } + length++; // add a utf-8 trailer byte + } + } + // Bytes 0xC0 to 0xFD are header bytes in a multibyte sequence. + // The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence. + else if (c < 0xE0) { // 0xC0-0xDF + // add a utf-8 sequence (2 bytes) + if (2 > size) { + return size; // prevent overrun + } + length += 2; + } + else if (c < 0xF0) { // 0xE0-0xEF + // add a utf-8 sequence (3 bytes) + if (3 > size) { + return size; // prevent overrun + } + length += 3; + } + else if (c < 0xF8) { // 0xF0-0xF7 + // add a utf-8 sequence (4 bytes) + if (4 > size) { + return size; // prevent overrun + } + length += 4; + } + else if (c < 0xFC) { // 0xF8-0xFB + // add a utf-8 sequence (5 bytes) + if (5 > size) { + return size; // prevent overrun + } + length += 5; + } + else if (c < 0xFE) { // 0xFC-0xFD + // add a utf-8 sequence (6 bytes) + if (6 > size) { + return size; // prevent overrun + } + length += 6; + } + else { // 0xFE-0xFF + // not a utf-8 sequence + length++; + } + return length; +} + +static int get_utf8_seq_length(const std::string& text, size_t pos) +{ + assert(pos < text.size()); + return get_utf8_seq_length(text.c_str() + pos, text.size() - pos); +} + +void NotificationManager::HintNotification::count_lines() +{ + std::string text = m_text1; + size_t last_end = 0; + m_lines_count = 0; + + if (text.empty()) + return; + + m_endlines.clear(); + while (last_end < text.length() - 1) + { + size_t next_hard_end = text.find_first_of('\n', last_end); + if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) { + //next line is ended by '/n' + m_endlines.push_back(next_hard_end); + last_end = next_hard_end + 1; + } + else { + // find next suitable endline + if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) { + // more than one line till end + size_t next_space = text.find_first_of(' ', last_end); + if (next_space > 0 && next_space < text.length()) { + size_t next_space_candidate = text.find_first_of(' ', next_space + 1); + while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) { + next_space = next_space_candidate; + next_space_candidate = text.find_first_of(' ', next_space + 1); + } + } + else { + next_space = text.length(); + } + // when one word longer than line. + if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset || + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 5 * 3 + ) { + float width_of_a = ImGui::CalcTextSize("a").x; + int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a); + while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) { + letter_count += get_utf8_seq_length(text, last_end + letter_count); + } + m_endlines.push_back(last_end + letter_count); + last_end += letter_count; + } + else { + m_endlines.push_back(next_space); + last_end = next_space + 1; + } + } + else { + m_endlines.push_back(text.length()); + last_end = text.length(); + } + + } + m_lines_count++; + } + int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0; + int size_of_last_line = ImGui::CalcTextSize(text.substr(prev_end, last_end - prev_end).c_str()).x; + // hypertext calculation + if (!m_hypertext.empty()) { + if (size_of_last_line + ImGui::CalcTextSize(m_hypertext.c_str()).x > m_window_width - m_window_width_offset) { + // hypertext on new line + size_of_last_line = ImGui::CalcTextSize((m_hypertext + " ").c_str()).x; + m_endlines.push_back(last_end); + m_lines_count++; + } + else { + size_of_last_line += ImGui::CalcTextSize((m_hypertext + " ").c_str()).x; + } + } + if (!m_text2.empty()) { + text = m_text2; + last_end = 0; + m_endlines2.clear(); + // if size_of_last_line too large to fit anything + size_t first_end = std::min(text.find_first_of('\n'), text.find_first_of(' ')); + if (size_of_last_line >= m_window_width - m_window_width_offset - ImGui::CalcTextSize(text.substr(0, first_end).c_str()).x) { + m_endlines2.push_back(0); + size_of_last_line = 0; + } + while (last_end < text.length() - 1) + { + size_t next_hard_end = text.find_first_of('\n', last_end); + if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) { + //next line is ended by '/n' + m_endlines2.push_back(next_hard_end); + last_end = next_hard_end + 1; + } + else { + // find next suitable endline + if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset - size_of_last_line) { + // more than one line till end + size_t next_space = text.find_first_of(' ', last_end); + if (next_space > 0) { + size_t next_space_candidate = text.find_first_of(' ', next_space + 1); + while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) { + next_space = next_space_candidate; + next_space_candidate = text.find_first_of(' ', next_space + 1); + } + } + else { + next_space = text.length(); + } + // when one word longer than line. + if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset - size_of_last_line || + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 5 * 3 + ) { + float width_of_a = ImGui::CalcTextSize("a").x; + int letter_count = (int)((m_window_width - m_window_width_offset - size_of_last_line) / width_of_a); + while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) { + letter_count += get_utf8_seq_length(text, last_end + letter_count); + } + m_endlines2.push_back(last_end + letter_count); + last_end += letter_count; + } + else { + m_endlines2.push_back(next_space); + last_end = next_space + 1; + } + } + else { + m_endlines2.push_back(text.length()); + last_end = text.length(); + } + + } + if (size_of_last_line == 0) // if first line is continuation of previous text, do not add to line count. + m_lines_count++; + size_of_last_line = 0; // should countain value only for first line (with hypertext) + + } + } +} + +void NotificationManager::HintNotification::init() +{ + // Do not init closing notification + if (is_finished()) + return; + + count_spaces(); + count_lines(); + + m_multiline = true; + + m_notification_start = GLCanvas3D::timestamp_now(); + if (m_state == EState::Unknown) + m_state = EState::Shown; +} + +void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& imgui) +{ + /* + m_window_height = m_multiline ? + (m_lines_count + 1.f) * m_line_height : + 4.f * m_line_height; + m_window_height += 1 * m_line_height; // top and bottom + */ + + m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 5.f * m_line_height); +} + +bool NotificationManager::HintNotification::on_text_click() +{ + if (m_hypertext_callback != nullptr && (!m_runtime_disable || tags_check(m_disabled_tags, m_enabled_tags))) + m_hypertext_callback(); + return false; +} + +void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + if (!m_has_hint_data) { + retrieve_data(); + } + + float x_offset = m_left_indentation; + int last_end = 0; + float starting_y = (m_lines_count < 4 ? m_line_height / 2 * (4 - m_lines_count + 1) : m_line_height / 2); + float shift_y = m_line_height; + std::string line; + + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_HyperTextColor); + for (size_t i = 0; i < (m_multiline ? /*m_lines_count*/m_endlines.size() : 2); i++) { + line.clear(); + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(starting_y + i * shift_y); + if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) { + if (i == 1 && m_endlines.size() > 2 && !m_multiline) { + // second line with "more" hypertext + line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) { + line = line.substr(0, line.length() - 1); + } + line += ".."; + } + else { + // regural line + line = m_text1.substr(last_end, m_endlines[i] - last_end); + } + // first line is headline (for hint notification it must be divided by \n) + if (m_text1.find('\n') >= m_endlines[i]) { + line = ImGui::ColorMarkerStart + line + ImGui::ColorMarkerEnd; + } + // Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line) + if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) { + line = ImGui::ColorMarkerStart + line; + } + + last_end = m_endlines[i]; + if (m_text1.size() > m_endlines[i]) + last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0); + imgui.text(line.c_str()); + } + + } + //hyperlink text + if (!m_multiline && m_lines_count > 2) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true); + } + else if (!m_hypertext.empty()) { + render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext); + } + + // text2 + if (!m_text2.empty() && m_multiline) { + starting_y += (m_endlines.size() - 1) * shift_y; + last_end = 0; + for (size_t i = 0; i < (m_multiline ? m_endlines2.size() : 2); i++) { + if (i == 0) //first line X is shifted by hypertext + ImGui::SetCursorPosX(x_offset + ImGui::CalcTextSize((line + m_hypertext + (line.empty() ? " " : " ")).c_str()).x); + else + ImGui::SetCursorPosX(x_offset); + + ImGui::SetCursorPosY(starting_y + i * shift_y); + line.clear(); + if (m_endlines2.size() > i && m_text2.size() >= m_endlines2[i]) { + + // regural line + line = m_text2.substr(last_end, m_endlines2[i] - last_end); + + // Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line) + if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) { + line = ImGui::ColorMarkerStart + line; + } + + last_end = m_endlines2[i]; + if (m_text2.size() > m_endlines2[i]) + last_end += (m_text2[m_endlines2[i]] == '\n' || m_text2[m_endlines2[i]] == ' ' ? 1 : 0); + imgui.text(line.c_str()); + } + + } + } + ImGui::PopStyleColor(1); +} + +void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + + std::string button_text; + button_text = ImGui::CloseNotifButton; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y), + ImVec2(win_pos.x, win_pos.y + win_size.y - 2 * m_line_height), + true)) + { + button_text = ImGui::CloseNotifHoverButton; + } + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + m_close_b_w = button_size.y; + if (m_lines_count <= 3) { + m_close_b_y = win_size.y / 2 - button_size.y * 1.25f; + ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f); + ImGui::SetCursorPosY(m_close_b_y); + } + else { + ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + } + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + close(); + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.125, win_size.y - 2 * m_line_height)) + { + close(); + } + + ImGui::PopStyleColor(5); + + + //render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + //render_logo(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + render_preferences_button(imgui, win_pos_x, win_pos_y); + if (!m_documentation_link.empty() && wxGetApp().app_config->get("suppress_hyperlinks") != "1") + { + render_documentation_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } + +} + +void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) +{ + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + + std::string button_text; + button_text = ImGui::PreferencesButton; + //hover + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 15.f, win_pos_y + m_window_height - 1.75f * m_line_height), + ImVec2(win_pos_x, win_pos_y + m_window_height), + true)) { + button_text = ImGui::PreferencesHoverButton; + // tooltip + long time_now = wxGetLocalTime(); + if (m_prefe_hover_time > 0 && m_prefe_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Preferences.")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_prefe_hover_time == 0) + m_prefe_hover_time = time_now; + } + else + m_prefe_hover_time = 0; + + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(m_window_width - m_line_height * 1.75f); + if (m_lines_count <= 3) { + ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f); + } + else { + ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f); + } + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + wxGetApp().open_preferences(1, "GUI");// 1 is to modify + } + + ImGui::PopStyleColor(5); + // preferences button is in place of minimize button + m_minimize_b_visible = true; +} +void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + // Used for debuging + + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::string button_text; + button_text = ImGui::RightArrowButton; + + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + + ImGui::SetCursorPosX(m_window_width - m_line_height * 3.f); + if (m_lines_count <= 3) + ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f); + else + ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f); + if (imgui.button(button_text.c_str(), button_size.x * 0.8f, button_size.y * 1.f)) + { + retrieve_data(); + } + + ImGui::PopStyleColor(5); +} +void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + //std::string placeholder_text; + //placeholder_text = ImGui::EjectButton; + //ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); + //std::wstring text; + //text = ImGui::ClippyMarker; + //ImGui::SetCursorPosX(button_pic_size.x / 3); + //ImGui::SetCursorPosY(win_size_y / 2 - button_pic_size.y * 2.f); + //imgui.text(text.c_str()); +} +void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::wstring button_text; + button_text = ImGui::DocumentationButton; + std::string placeholder_text; + placeholder_text = ImGui::EjectButton; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y), + ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y - 2 * m_line_height), + true)) + { + button_text = ImGui::DocumentationHoverButton; + // tooltip + long time_now = wxGetLocalTime(); + if (m_docu_hover_time > 0 && m_docu_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Documentation in web browser.")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_docu_hover_time == 0) + m_docu_hover_time = time_now; + } + else + m_docu_hover_time = 0; + + ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + open_documentation(); + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.f, win_size.y - 2 * m_line_height)) + { + open_documentation(); + } + + ImGui::PopStyleColor(5); +} + +void NotificationManager::HintNotification::open_documentation() +{ + if (!m_documentation_link.empty()) + { + launch_browser_if_allowed(m_documentation_link); + } +} +void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true*/) +{ + HintData* hint_data = HintDatabase::get_instance().get_hint(new_hint); + if (hint_data == nullptr) + close(); + + if (hint_data != nullptr) + { + NotificationData nd{ NotificationType::DidYouKnowHint, + NotificationLevel::HintNotificationLevel, + 0, + hint_data->text, + hint_data->hypertext, nullptr, + hint_data->follow_text }; + m_hypertext_callback = hint_data->callback; + m_disabled_tags = hint_data->disabled_tags; + m_enabled_tags = hint_data->enabled_tags; + m_runtime_disable = hint_data->runtime_disable; + m_documentation_link = hint_data->documentation_link; + m_has_hint_data = true; + update(nd); + } +} +} //namespace Slic3r +} //namespace GUI diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp new file mode 100644 index 0000000000..b345c9f248 --- /dev/null +++ b/src/slic3r/GUI/HintNotification.hpp @@ -0,0 +1,122 @@ +#ifndef slic3r_GUI_HintNotification_hpp_ +#define slic3r_GUI_HintNotification_hpp_ + +#include "NotificationManager.hpp" + +namespace Slic3r {namespace GUI { + +// Database of hints updatable +struct HintData +{ + std::string id_string; + std::string text; + size_t weight; + bool was_displayed; + std::string hypertext; + std::string follow_text; + std::string disabled_tags; + std::string enabled_tags; + bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode + std::string documentation_link; + std::function callback{ nullptr }; +}; + +class HintDatabase +{ +public: + static HintDatabase& get_instance() + { + static HintDatabase instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } +private: + HintDatabase() + : m_hint_id(0) + {} +public: + ~HintDatabase(); + HintDatabase(HintDatabase const&) = delete; + void operator=(HintDatabase const&) = delete; + + // return true if HintData filled; + HintData* get_hint(bool new_hint = true); + size_t get_count() { + if (!m_initialized) + return 0; + return m_loaded_hints.size(); + } + // resets m_initiailized to false and writes used if was initialized + // used when reloading in runtime - like change language + void uninit(); +private: + void init(); + void load_hints_from_file(const boost::filesystem::path& path); + bool is_used(const std::string& id); + void set_used(const std::string& id); + void clear_used(); + // Returns position in m_loaded_hints with next hint chosed randomly with weights + size_t get_next(); + size_t m_hint_id; + bool m_initialized{ false }; + std::vector m_loaded_hints; + bool m_sorted_hints{ false }; + std::vector m_used_ids; + bool m_used_ids_loaded{ false }; +}; +// Notification class - shows current Hint ("Did you know") +class NotificationManager::HintNotification : public NotificationManager::PopNotification +{ +public: + HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool new_hint) + : PopNotification(n, id_provider, evt_handler) + { + retrieve_data(new_hint); + } + virtual void init() override; + void open_next() { retrieve_data(); } +protected: + virtual void set_next_window_size(ImGuiWrapper& imgui) override; + virtual void count_spaces() override; + virtual void count_lines() override; + virtual bool on_text_click() override; + virtual void render_text(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) override; + virtual void render_close_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) override; + virtual void render_minimize_button(ImGuiWrapper& imgui, + const float win_pos_x, const float win_pos_y) override {} + void render_preferences_button(ImGuiWrapper& imgui, + const float win_pos_x, const float win_pos_y); + void render_right_arrow_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + void render_documentation_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + void render_logo(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + // recursion counter -1 tells to retrieve same hint as last time + void retrieve_data(bool new_hint = true); + void open_documentation(); + + bool m_has_hint_data{ false }; + std::function m_hypertext_callback; + std::string m_disabled_tags; + std::string m_enabled_tags; + bool m_runtime_disable; + std::string m_documentation_link; + float m_close_b_y{ 0 }; + float m_close_b_w{ 0 }; + // hover of buttons + long m_docu_hover_time{ 0 }; + long m_prefe_hover_time{ 0 }; +}; + +} //namespace Slic3r +} //namespace GUI + +#endif //slic3r_GUI_HintNotification_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index 3345e7df76..12a1ee18d5 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -88,14 +88,21 @@ bool check_color_change(PrintObject *object, size_t frst_layer_id, size_t layers static std::string gcode(Type type) { - const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); + Slic3r::DynamicPrintConfig config = wxGetApp().preset_bundle->full_config(); switch (type) { //BBS - //case ColorChange: return config.color_change_gcode; - case PausePrint: return config.machine_pause_gcode; - //case Template: return config.template_custom_gcode; + case Template: return config.opt_string("template_custom_gcode"); default: return ""; } + + //const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); + //switch (type) { + ////BBS + ////case ColorChange: return config.color_change_gcode; + //case PausePrint: return config.machine_pause_gcode; + //case Template: return config.template_custom_gcode; + //default: return ""; + //} } static std::string short_and_splitted_time(const std::string &time) @@ -222,9 +229,9 @@ bool TickCodeInfo::add_tick(const int tick, Type type, const int extruder, doubl std::string extra; if (type == Custom) // custom Gcode { - /*extra = get_custom_code(custom_gcode, print_z); - if (extra.empty()) return false; - custom_gcode = extra;*/ + //extra = get_custom_code(custom_gcode, print_z); + //if (extra.empty()) return false; + //custom_gcode = extra; } else if (type == PausePrint) { //BBS do not set pause extra message //extra = get_pause_print_msg(pause_print_msg, print_z); @@ -262,9 +269,10 @@ bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) if (it->color == edited_value) return false; changed_tick.color = edited_value; } else if (it->type == Template) { - if (gcode(Template) == edited_value) return false; - changed_tick.extra = edited_value; - changed_tick.type = Custom; + //if (gcode(Template) == edited_value) return false; + //changed_tick.extra = edited_value; + //changed_tick.type = Custom; + ; } else if (it->type == Custom || it->type == PausePrint) { if (it->extra == edited_value) return false; changed_tick.extra = edited_value; @@ -625,6 +633,21 @@ void IMSlider::post_ticks_changed_event(Type type) m_is_need_post_tick_changed_event = true; } +void IMSlider::add_custom_gcode(std::string custom_gcode) +{ + if (m_selection == ssUndef) return; + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + + const auto it = m_ticks.ticks.find(TickCode{ tick }); + + if (it != m_ticks.ticks.end()) { + m_ticks.ticks.erase(it); + } + m_ticks.ticks.emplace(TickCode{ tick, Custom, std::max(1, m_only_extruder), "", custom_gcode }); + + post_ticks_changed_event(Custom); +} + void IMSlider::add_code_as_tick(Type type, int selected_extruder) { if (m_selection == ssUndef) return; @@ -682,8 +705,11 @@ bool IMSlider::check_ticks_changed_event(Type type) // switch on/off one layer mode -void IMSlider::switch_one_layer_mode() +bool IMSlider::switch_one_layer_mode() { + if (m_show_custom_gcode_window) + return false; + m_is_one_layer = !m_is_one_layer; if (!m_is_one_layer) { SetLowerValue(m_min_value); @@ -692,6 +718,7 @@ void IMSlider::switch_one_layer_mode() m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (m_selection == ssUndef) m_selection = ssHigher; set_as_dirty(); + return true; } void IMSlider::draw_background(const ImRect& groove) { @@ -1194,6 +1221,8 @@ bool IMSlider::render(int canvas_width, int canvas_height) float scale = (float) wxGetApp().em_unit() / 10.0f; + render_input_custom_gcode(); + if (is_horizontal()) { float pos_x = std::max(LEFT_MARGIN, 0.2f * canvas_width); float pos_y = (canvas_height - HORIZONTAL_SLIDER_SIZE.y * m_scale); @@ -1247,6 +1276,70 @@ bool IMSlider::render(int canvas_width, int canvas_height) return result; } +void IMSlider::render_input_custom_gcode() +{ + if (!m_show_custom_gcode_window) + return; + ImGuiWrapper& imgui = *wxGetApp().imgui(); + static bool move_to_center = true; + if (move_to_center) { + move_to_center = false; + auto pos_x = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_width() / 2; + auto pos_y = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height() / 2; + imgui.set_next_window_pos(pos_x, pos_y, ImGuiCond_Always, 0.5f, 0.5f); + } + + imgui.push_common_window_style(m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 12.f * m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 3) * m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10, 7) * m_scale); + int windows_flag = + ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse; + imgui.begin(_u8L("Custom G-code"), windows_flag); + imgui.text(_u8L("Enter Custom G-code used on current layer:")); + int text_height = 6; + ImGui::InputTextMultiline("##text", m_custom_gcode, sizeof(m_custom_gcode), ImVec2(-1, ImGui::GetTextLineHeight() * text_height)); + //text_height = 5; + //for (int i = 0; m_custom_gcode[i] != '\0'; ++i){ + // if ('\n' == m_custom_gcode[i] && text_height < 12) + // ++text_height; + //} + + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetStyle().WindowPadding.x * 14); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f / 255.f, 174.f / 255.f, 66.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(61.f / 255.f, 203.f / 255.f, 115.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(27.f / 255.f, 136.f / 255.f, 68.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.f, 1.f, 1.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f)); + if (imgui.bbl_button(_L("OK"))) { + m_show_custom_gcode_window = false; + add_custom_gcode(m_custom_gcode); + move_to_center = true; + } + ImGui::PopStyleColor(5); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(255.f / 255.f, 255.f / 255.f, 255.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(238.f / 255.f, 238.f / 255.f, 238.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(206.f / 255.f, 206.f / 255.f, 206.f / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 0.f, 0.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(38.f / 255.0f, 46.f / 255.0f, 48.f / 255.0f, 1.00f)); + if (imgui.bbl_button(_L("Cancel"))) { + m_show_custom_gcode_window = false; + move_to_center = true; + } + ImGui::PopStyleColor(5); + + imgui.end(); + ImGui::PopStyleVar(3); + imgui.pop_common_window_style(); +} + void IMSlider::render_menu() { ImGuiWrapper::push_menu_style(m_scale); @@ -1267,6 +1360,14 @@ void IMSlider::render_menu() if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { add_code_as_tick(PausePrint); } + if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) { + m_show_custom_gcode_window = true; + } + if (!gcode(Template).empty()) { + if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) { + add_code_as_tick(Template); + } + } } //BBS render this menu item only when extruder_num > 1 diff --git a/src/slic3r/GUI/IMSlider.hpp b/src/slic3r/GUI/IMSlider.hpp index 0fffbb2a69..5b6e7ce983 100644 --- a/src/slic3r/GUI/IMSlider.hpp +++ b/src/slic3r/GUI/IMSlider.hpp @@ -261,15 +261,18 @@ public: void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } + void add_custom_gcode(std::string custom_gcode); void add_code_as_tick(Type type, int selected_extruder = -1); void post_ticks_changed_event(Type type = Custom); bool check_ticks_changed_event(Type type); - void switch_one_layer_mode(); + bool switch_one_layer_mode(); bool render(int canvas_width, int canvas_height); void render_menu(); + void render_input_custom_gcode(); + //BBS update scroll value changed bool is_dirty() { return m_dirty; } void set_as_dirty(bool dirty = true) { m_dirty = dirty; } @@ -327,6 +330,7 @@ private: bool m_is_one_layer = false; bool m_is_focused = false; bool m_show_menu = false; + bool m_show_custom_gcode_window = false; bool m_force_mode_apply = true; bool m_enable_action_icon = true; bool m_enable_cog_icon = false; @@ -367,6 +371,8 @@ private: Type m_tick_change_event_type; std::vector m_alternate_values; + + char m_custom_gcode[1024] = { 0 }; }; } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e78dfcbab2..77503e14ed 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1687,6 +1687,33 @@ void ImGuiWrapper::pop_menu_style() ImGuiWrapper::pop_toolbar_style(); } +void ImGuiWrapper::push_common_window_style(const float scale) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 10.0f) * scale); + ImGui::PushStyleVar(ImGuiStyleVar_WindowTitleAlign, ImVec2(0.05f, 0.50f) * scale); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f * scale); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(38 / 255.0f, 46 / 255.0f, 48 / 255.0f, 1.00f)); // 1 + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGuiWrapper::COL_WINDOW_BG); // 2 + ImGui::PushStyleColor(ImGuiCol_TitleBg, ImGuiWrapper::COL_TITLE_BG); // 3 + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImGuiWrapper::COL_TITLE_BG); // 4 + ImGui::PushStyleColor(ImGuiCol_Separator, ImGuiWrapper::COL_SEPARATOR); // 5 + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); // 6 + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); // 7 + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); // 8 + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 9 + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10 + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11 + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); // 12 + ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, ImGuiWrapper::COL_GREEN_LIGHT); // 13 + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); // 14 +} + +void ImGuiWrapper::pop_common_window_style() { + ImGui::PopStyleColor(14); + ImGui::PopStyleVar(5); +} + void ImGuiWrapper::init_font(bool compress) { destroy_font(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index dee1553eb2..e0efa6b06e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -192,6 +192,8 @@ public: static void pop_toolbar_style(); static void push_menu_style(const float scale); static void pop_menu_style(); + static void push_common_window_style(const float scale); + static void pop_common_window_style(); //BBS static int TOOLBAR_WINDOW_FLAGS; diff --git a/src/slic3r/GUI/ImageGrid.cpp b/src/slic3r/GUI/ImageGrid.cpp index d604a1faad..67f436229d 100644 --- a/src/slic3r/GUI/ImageGrid.cpp +++ b/src/slic3r/GUI/ImageGrid.cpp @@ -8,6 +8,12 @@ #include +#ifdef __WXMSW__ +#include +#endif +#ifdef __APPLE__ +#include "../Utils/MacDarkMode.hpp" +#endif BEGIN_EVENT_TABLE(Slic3r::GUI::ImageGrid, wxPanel) @@ -102,7 +108,8 @@ void Slic3r::GUI::ImageGrid::SetGroupMode(int mode) void Slic3r::GUI::ImageGrid::SetSelecting(bool selecting) { m_selecting = selecting; - if (!m_selecting) m_file_sys->SelectAll(false); + if (m_file_sys) + m_file_sys->SelectAll(false); Refresh(); } @@ -140,29 +147,53 @@ void Slic3r::GUI::ImageGrid::DoAction(size_t index, int action) { if (action == 0) { m_file_sys->DeleteFiles(index); - } else { + } else if (action == 1) { if (index != -1) { auto &file = m_file_sys->GetFile(index); if (file.IsDownload() && file.progress >= -1) { if (file.progress >= 100) { + if (!m_file_sys->DownloadCheckFile(index)) { + wxMessageBox(wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), _L("Error"), wxOK); + Refresh(); + return; + } #ifdef __WXMSW__ - wxExecute("cmd /c start " + from_u8(m_save_path + "\\" + file.name), wxEXEC_HIDE_CONSOLE); + auto wfile = boost::filesystem::path(file.path).wstring(); + SHELLEXECUTEINFO info{sizeof(info), 0, NULL, L"open", wfile.c_str(), L"", SW_HIDE}; + ::ShellExecuteEx(&info); #else - wxShell("open " + m_save_path + "/" + file.name); + wxShell("open " + file.path); #endif } else { m_file_sys->DownloadCancel(index); } return; } - if (m_save_path.empty()) { - wxDirDialog dlg(NULL, _L("Choose save directory"), "", wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - if (dlg.ShowModal() == wxID_CANCEL) - return; - m_save_path = dlg.GetPath().ToUTF8().data(); + } + m_file_sys->DownloadFiles(index, wxGetApp().app_config->get("download_path")); + } else if (action == 2) { + if (index != -1) { + auto &file = m_file_sys->GetFile(index); + if (file.IsDownload() && file.progress >= -1) { + if (file.progress >= 100) { + if (!m_file_sys->DownloadCheckFile(index)) { + wxMessageBox(wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), _L("Error"), wxOK); + Refresh(); + return; + } +#ifdef __WIN32__ + wxExecute(L"explorer.exe /select," + from_u8(file.path)); +#elif __APPLE__ + openFolderForFile(from_u8(file.path)); +#else +#endif + } else { + m_file_sys->DownloadCancel(index); + } + return; } } - m_file_sys->DownloadFiles(index, m_save_path); + m_file_sys->DownloadFiles(index, wxGetApp().app_config->get("download_path")); } } @@ -188,8 +219,10 @@ void ImageGrid::UpdateLayout() if (!m_file_sys) return; wxSize size = GetClientSize(); wxSize mask_size{0, 60 * em_unit(this) / 10}; - if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) + if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { mask_size.y = 20 * em_unit(this) / 10; + size.y -= mask_size.y; + } int cell_width = m_cell_size.GetWidth(); int cell_height = m_cell_size.GetHeight(); int ncol = (size.GetWidth() - cell_width + m_image_size.GetWidth()) / cell_width; @@ -254,7 +287,9 @@ std::pair Slic3r::GUI::ImageGrid::HitTest(wxPoint const &pt) if (index >= m_file_sys->GetCount()) { return {HIT_NONE, -1}; } if (!m_selecting) { wxRect hover_rect{0, m_image_size.y - 40, m_image_size.GetWidth(), 40}; - if (hover_rect.Contains(off.x, off.y)) { return {HIT_ACTION, index * 2 + off.x * 2 / hover_rect.GetWidth()}; } // Two buttons + auto & file = m_file_sys->GetFile(index); + int btn = file.IsDownload() && file.progress >= 100 ? 3 : 2; + if (hover_rect.Contains(off.x, off.y)) { return {HIT_ACTION, index * 4 + off.x * btn / hover_rect.GetWidth()}; } // Two buttons } return {HIT_ITEM, index}; } @@ -316,7 +351,7 @@ void ImageGrid::mouseReleased(wxMouseEvent& event) if (m_hit_type == HIT_ITEM) Select(m_hit_item); else if (m_hit_type == HIT_ACTION) - DoAction(m_hit_item / 2, m_hit_item & 1); + DoAction(m_hit_item / 4, m_hit_item & 3); else if (m_hit_type == HIT_MODE) SetGroupMode(static_cast(2 - m_hit_item)); else if (m_hit_type == HIT_STATUS) @@ -349,8 +384,12 @@ void Slic3r::GUI::ImageGrid::changedEvent(wxCommandEvent& evt) { evt.Skip(); BOOST_LOG_TRIVIAL(info) << "ImageGrid::changedEvent: " << evt.GetEventType() << " index: " << evt.GetInt() << " name: " << evt.GetString() << " extra: " << evt.GetExtraLong(); - if (evt.GetEventType() == EVT_MODE_CHANGED - || evt.GetEventType() == EVT_FILE_CHANGED) + if (evt.GetEventType() == EVT_FILE_CHANGED) { + if (evt.GetInt() == -1) + m_file_sys->DownloadCheckFiles(wxGetApp().app_config->get("download_path")); + UpdateFileSystem(); + } + else if (evt.GetEventType() == EVT_MODE_CHANGED) UpdateFileSystem(); else Refresh(); @@ -365,13 +404,16 @@ void ImageGrid::paintEvent(wxPaintEvent& evt) size_t Slic3r::GUI::ImageGrid::firstItem(wxSize const &size, wxPoint &off) { + int size_y = size.y; + if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) + size_y -= m_mask.GetHeight(); int offx = (size.x - (m_col_count - 1) * m_cell_size.GetWidth() - m_image_size.GetWidth()) / 2; int offy = (m_row_offset + 1 < m_row_count || m_row_count == 0) ? m_cell_size.GetHeight() - m_image_size.GetHeight() - m_row_offset * m_cell_size.GetHeight() / 4 + m_row_offset / 4 * m_cell_size.GetHeight() : - size.y - (size.y + m_image_size.GetHeight() - 1) / m_cell_size.GetHeight() * m_cell_size.GetHeight(); + size_y - (size_y + m_image_size.GetHeight() - 1) / m_cell_size.GetHeight() * m_cell_size.GetHeight(); int index = (m_row_offset + 1 < m_row_count || m_row_count == 0) ? m_row_offset / 4 * m_col_count : - ((m_file_sys->GetCount() + m_col_count - 1) / m_col_count - (size.y + m_image_size.GetHeight() - 1) / m_cell_size.GetHeight()) * m_col_count; + ((m_file_sys->GetCount() + m_col_count - 1) / m_col_count - (size_y + m_image_size.GetHeight() - 1) / m_cell_size.GetHeight()) * m_col_count; if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) offy += m_mask.GetHeight(); off = wxPoint{offx, offy}; @@ -455,7 +497,7 @@ void ImageGrid::render(wxDC& dc) constexpr wchar_t const * formats[] = {_T("%Y-%m-%d"), _T("%Y-%m"), _T("%Y")}; size_t start = index; size_t end = index; - size_t hit_image = m_selecting ? size_t(-1) : m_hit_type == HIT_ITEM ? m_hit_item : m_hit_type == HIT_ACTION ? m_hit_item / 2 :size_t(-1); + size_t hit_image = m_selecting ? size_t(-1) : m_hit_type == HIT_ITEM ? m_hit_item : m_hit_type == HIT_ACTION ? m_hit_item / 4 :size_t(-1); // Draw items with background while (off.y < size.y) { @@ -475,15 +517,17 @@ void ImageGrid::render(wxDC& dc) dc.DrawBitmap(m_mask, pt); } } + bool show_download_state_always = true; // Draw checked icon - if (m_selecting) + if (m_selecting && !show_download_state_always) dc.DrawBitmap(file.IsSelect() ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); - // can' handle alpha + // can't handle alpha // dc.GradientFillLinear({pt.x, pt.y, m_image_size.GetWidth(), 60}, wxColour(0x6F, 0x6F, 0x6F, 0x99), wxColour(0x6F, 0x6F, 0x6F, 0), wxBOTTOM); else if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { wxString nonHoverText; wxString secondAction = _L("Download"); + wxString thirdAction; int states = 0; // Draw download progress if (file.IsDownload()) { @@ -495,7 +539,8 @@ void ImageGrid::render(wxDC& dc) nonHoverText = _L("Download failed"); states = StateColor::Checked; } else if (file.progress >= 100) { - secondAction = _L("Open"); + secondAction = _L("Play"); + thirdAction = _L("Open Folder"); nonHoverText = _L("Download finished"); } else { secondAction = _L("Cancel"); @@ -505,10 +550,14 @@ void ImageGrid::render(wxDC& dc) // Draw buttons on hovered item wxRect rect{pt.x, pt.y + m_image_size.y - m_buttons_background.GetHeight(), m_image_size.GetWidth(), m_buttons_background.GetHeight()}; if (hit_image == index) { - renderButtons(dc, {_L("Delete"), (wxChar const *) secondAction, nullptr}, rect, m_hit_type == HIT_ACTION ? m_hit_item & 1 : -1, states); + renderButtons(dc, {_L("Delete"), (wxChar const *) secondAction, thirdAction.IsEmpty() ? nullptr : (wxChar const *) thirdAction, nullptr}, rect, + m_hit_type == HIT_ACTION ? m_hit_item & 3 : -1, states); } else if (!nonHoverText.IsEmpty()) { renderButtons(dc, {(wxChar const *) nonHoverText, nullptr}, rect, -1, states); } + if (m_selecting && show_download_state_always) + dc.DrawBitmap(file.IsSelect() ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), + pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); } else { auto date = wxDateTime((time_t) file.time).Format(_L(formats[m_file_sys->GetGroupMode()])); dc.DrawText(date, pt + wxPoint{24, 16}); @@ -569,12 +618,12 @@ void Slic3r::GUI::ImageGrid::renderButtons(wxDC &dc, wxStringList const &texts, for (size_t i = 0; i < texts.size(); ++i) { int states2 = hit == i ? state : 0; // Draw button background - rect.Deflate(10, 5); //dc.Blit(rect.GetTopLeft(), rect.GetSize(), &mdc, {m_buttonBackgroundColor.colorIndexForStates(states) * 128, 0}); //dc.DrawBitmap(m_button_background, rect2.GetTopLeft()); // Draw button splitter if (i > 0) dc.DrawLine(rect.GetLeftTop(), rect.GetLeftBottom()); // Draw button text + rect.Deflate(10, 5); renderText(dc, texts[i], rect, states | states2); rect.Inflate(10, 5); rect.Offset(rect.GetWidth(), 0); diff --git a/src/slic3r/GUI/ImageGrid.h b/src/slic3r/GUI/ImageGrid.h index f6d07c5427..516bd3f6b6 100644 --- a/src/slic3r/GUI/ImageGrid.h +++ b/src/slic3r/GUI/ImageGrid.h @@ -96,8 +96,6 @@ private: wxBitmap m_status_icon; wxString m_status_msg; - std::string m_save_path; - ScalableBitmap m_checked_icon; ScalableBitmap m_unchecked_icon; StateColor m_buttonBackgroundColor; diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 99b8cc825f..077ec43809 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -503,10 +503,6 @@ void ArrangeJob::process() if (params.is_seq_print) params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius)); - if (params.avoid_extrusion_cali_region) - m_plater->get_partplate_list().preprocess_nonprefered_areas(m_unselected, MAX_NUM_PLATES); - - double skirt_distance = print.has_skirt() ? print.config().skirt_distance.value : 0; double brim_max = 0; std::for_each(m_selected.begin(), m_selected.end(), [&](ArrangePolygon ap) { brim_max = std::max(brim_max, ap.brim_width); }); @@ -531,6 +527,9 @@ void ArrangeJob::process() std::for_each(m_selected.begin(), m_selected.end(), [&](auto& ap) {ap.inflation = params.min_obj_distance / 2; }); std::for_each(m_unselected.begin(), m_unselected.end(), [&](auto& ap) {ap.inflation = ap.is_virt_object ? scaled(params.brim_skirt_distance) : params.min_obj_distance / 2; }); + if (params.avoid_extrusion_cali_region && print.full_print_config().opt_bool("scan_first_layer")) + m_plater->get_partplate_list().preprocess_nonprefered_areas(m_unselected, MAX_NUM_PLATES); + m_plater->get_partplate_list().preprocess_exclude_areas(params.excluded_regions, 1); // shrink bed by moving to center by dist diff --git a/src/slic3r/GUI/Jobs/BindJob.cpp b/src/slic3r/GUI/Jobs/BindJob.cpp index 74d8ecf19a..12bdb1b6f9 100644 --- a/src/slic3r/GUI/Jobs/BindJob.cpp +++ b/src/slic3r/GUI/Jobs/BindJob.cpp @@ -103,7 +103,10 @@ void BindJob::process() msg = _L("Logging in"); } if (code != 0) { - msg = _L("Login failed") + wxString::Format("(code=%d,info=%s)", code, info); + msg = _L("Login failed") + wxString::Format("(code=%d,info=%s). ", code, info); + if (code == BAMBU_NETWORK_ERR_TIMEOUT) { + msg += _L("Please check the printer network connection."); + } } update_status(curr_percent, msg); } diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index f56c8e546b..7db397c196 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -85,6 +85,7 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body) else { wxString unkown_text = _L("Unkown Error."); unkown_text += wxString::Format("status=%u, body=%s", status, body); + BOOST_LOG_TRIVIAL(error) << "http_error: status=" << status << ", code=" << code << ", error=" << error; return unkown_text; } @@ -151,6 +152,7 @@ void PrintJob::process() params.task_layer_inspect = this->task_layer_inspect; params.task_record_timelapse= this->task_record_timelapse; params.ams_mapping = this->task_ams_mapping; + params.ams_mapping_info = this->task_ams_mapping_info; params.connection_type = this->connection_type; params.task_use_ams = this->task_use_ams; diff --git a/src/slic3r/GUI/Jobs/PrintJob.hpp b/src/slic3r/GUI/Jobs/PrintJob.hpp index 86b13fa585..449dba499a 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.hpp +++ b/src/slic3r/GUI/Jobs/PrintJob.hpp @@ -47,6 +47,7 @@ public: bool task_record_timelapse; bool task_layer_inspect; std::string task_ams_mapping; + std::string task_ams_mapping_info; std::string connection_type; bool cloud_print_only { false }; bool has_sdcard { false }; diff --git a/src/slic3r/GUI/Jobs/SendJob.cpp b/src/slic3r/GUI/Jobs/SendJob.cpp new file mode 100644 index 0000000000..5d4cbc040b --- /dev/null +++ b/src/slic3r/GUI/Jobs/SendJob.cpp @@ -0,0 +1,314 @@ +#include "SendJob.hpp" +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" + +namespace Slic3r { +namespace GUI { + +static wxString check_gcode_failed_str = _L("Abnormal print file data. Please slice again"); +static wxString printjob_cancel_str = _L("Task canceled"); +static wxString timeout_to_upload_str = _L("Upload task timed out. Please check the network problem and try again"); +static wxString failed_in_cloud_service_str = _L("Cloud service connection failed. Please try again."); +static wxString file_is_not_exists_str = _L("Print file not found, please slice again"); +static wxString file_over_size_str = _L("The print file exceeds the maximum allowable size (1GB). Please simplify the model and slice again"); +static wxString print_canceled_str = _L("Task canceled"); +static wxString upload_failed_str = _L("Failed uploading print file"); +static wxString upload_login_failed_str = _L("Wrong Access code"); + + +static wxString sending_over_lan_str = _L("Sending gcode file over LAN"); +static wxString sending_over_cloud_str = _L("Sending gcode file through cloud service"); + +SendJob::SendJob(std::shared_ptr pri, Plater* plater, std::string dev_id) +: PlaterJob{ std::move(pri), plater }, + m_dev_id(dev_id) +{ + m_print_job_completed_id = plater->get_send_finished_event(); +} + +void SendJob::prepare() +{ + m_plater->get_print_job_data(&job_data); +} + +void SendJob::on_exception(const std::exception_ptr &eptr) +{ + try { + if (eptr) + std::rethrow_exception(eptr); + } catch (std::exception &e) { + PlaterJob::on_exception(eptr); + } +} + +wxString SendJob::get_http_error_msg(unsigned int status, std::string body) +{ + int code = 0; + std::string error; + std::string message; + wxString result; + if (status >= 400 && status < 500) + try { + json j = json::parse(body); + if (j.contains("code")) { + if (!j["code"].is_null()) + code = j["code"].get(); + } + if (j.contains("error")) { + if (!j["error"].is_null()) + error = j["error"].get(); + } + if (j.contains("message")) { + if (!j["message"].is_null()) + message = j["message"].get(); + } + switch (status) { + ; + } + } + catch (...) { + ; + } + else if (status == 503) { + return _L("Service Unavailable"); + } + else { + wxString unkown_text = _L("Unkown Error."); + unkown_text += wxString::Format("status=%u, body=%s", status, body); + return unkown_text; + } + + BOOST_LOG_TRIVIAL(error) << "http_error: status=" << status << ", code=" << code << ", error=" << error; + + result = wxString::Format("code=%u, error=%s", code, from_u8(error)); + return result; +} + +inline std::string get_transform_string(int bytes) +{ + float ms = (float)bytes / 1024.0f / 1024.0f; + float ks = (float)bytes / 1024.0f; + char buffer[32]; + if (ms > 0) + ::sprintf(buffer, "%.1fM", ms); + else if (ks > 0) + ::sprintf(buffer, "%.1fK", ks); + else + ::sprintf(buffer, "%.1fK", ks); + return buffer; +} + +void SendJob::process() +{ + /* display info */ + wxString msg; + int curr_percent = 10; + NetworkAgent* m_agent = wxGetApp().getAgent(); + AppConfig* config = wxGetApp().app_config; + + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file through cloud service"); + } + + int result = -1; + unsigned int http_code; + std::string http_body; + + int total_plate_num = m_plater->get_partplate_list().get_plate_count(); + + PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx); + if (plate == nullptr) { + plate = m_plater->get_partplate_list().get_curr_plate(); + if (plate == nullptr) + return; + } + + /* check gcode is valid */ + if (!plate->is_valid_gcode_file()) { + update_status(curr_percent, check_gcode_failed_str); + return; + } + + if (was_canceled()) { + update_status(curr_percent, printjob_cancel_str); + return; + } + + std::string project_name = wxGetApp().plater()->get_project_name().ToUTF8().data(); + int curr_plate_idx = 0; + if (job_data.plate_idx >= 0) + curr_plate_idx = job_data.plate_idx + 1; + else if (job_data.plate_idx == PLATE_CURRENT_IDX) + curr_plate_idx = m_plater->get_partplate_list().get_curr_plate_index() + 1; + + BBL::PrintParams params; + params.dev_id = m_dev_id; + //params.project_name = wxGetApp().plater()->get_project_name().ToUTF8().data(); + params.project_name = wxGetApp().plater()->get_project_name().utf8_string(); + + params.preset_name = wxGetApp().preset_bundle->prints.get_selected_preset_name(); + params.filename = job_data._3mf_path.string(); + params.config_filename = job_data._3mf_config_path.string(); + params.plate_index = curr_plate_idx; + params.ams_mapping = this->task_ams_mapping; + params.connection_type = this->connection_type; + params.task_use_ams = this->task_use_ams; + + // local print access + params.dev_ip = m_dev_ip; + params.username = "bblp"; + params.password = m_access_code; + wxString error_text; + wxString msg_text; + + auto update_fn = [this, &msg, &curr_percent, &error_text](int stage, int code, std::string info) { + if (stage == SendingPrintJobStage::PrintingStageCreate) { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } else { + msg = _L("Sending gcode file to sdcard"); + } + curr_percent = 25; + } + else if (stage == SendingPrintJobStage::PrintingStageUpload) { + if (code == 0 && !info.empty()) { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file to sdcard"); + } + msg += wxString::Format("(%s)", info); + curr_percent = 40; + this->update_status(curr_percent, msg); + } + } + else if (stage == SendingPrintJobStage::PrintingStageFinished) { + curr_percent = 100; + msg = wxString::Format(_L("Successfully sent. Close current page in %s s"), info); + } + else { + if (this->connection_type == "lan") { + msg = _L("Sending gcode file over LAN"); + } + else { + msg = _L("Sending gcode file over LAN"); + //msg = _L("Sending gcode file through cloud service"); + } + } + if (code != 0) { + error_text = this->get_http_error_msg(code, info); + msg += wxString::Format("[%s]", error_text); + } + this->update_status(curr_percent, msg); + }; + + auto cancel_fn = [this]() { + return was_canceled(); + }; + + + if (params.connection_type != "lan") { + if (params.dev_ip.empty()) + params.comments = "no_ip"; + else if (this->cloud_print_only) + params.comments = "low_version"; + else if (!this->has_sdcard) + params.comments = "no_sdcard"; + else if (params.password.empty()) + params.comments = "no_password"; + + if (!params.password.empty() + && !params.dev_ip.empty() + && this->has_sdcard) { + // try to send local with record + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn); + if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { + params.comments = "wrong_code"; + } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { + params.comments = "upload_failed"; + } else { + params.comments = (boost::format("failed(%1%)") % result).str(); + } + if (result < 0) { + // try to send with cloud + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + } + } else { + BOOST_LOG_TRIVIAL(info) << "send_job: try to send gcode file to printer"; + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + } + } else { + if (this->has_sdcard) { + this->update_status(curr_percent, _L("Sending gcode file over LAN")); + result = m_agent->start_send_gcode_to_sdcard(params, update_fn, cancel_fn); + } else { + this->update_status(curr_percent, _L("An SD card needs to be inserted before printing via LAN.")); + return; + } + } + + if (was_canceled()) { + update_status(curr_percent, printjob_cancel_str); + return; + } + + if (result < 0) { + if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { + msg_text = upload_failed_str; + } if (result == BAMBU_NETWORK_ERR_FILE_NOT_EXIST) { + msg_text = file_is_not_exists_str; + } else if (result == BAMBU_NETWORK_ERR_FILE_OVER_SIZE) { + msg_text = file_over_size_str; + } else if (result == BAMBU_NETWORK_ERR_CHECK_MD5_FAILED) { + msg_text = failed_in_cloud_service_str; + } else if (result == BAMBU_NETWORK_ERR_INVALID_PARAMS) { + msg_text = upload_failed_str; + } else if (result == BAMBU_NETWORK_ERR_CANCELED) { + msg_text = print_canceled_str; + } else if (result == BAMBU_NETWORK_ERR_TIMEOUT) { + msg_text = timeout_to_upload_str; + } else if (result == BAMBU_NETWORK_ERR_INVALID_RESULT) { + msg_text = upload_failed_str; + } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { + msg_text = upload_failed_str; + } else { + update_status(curr_percent, failed_in_cloud_service_str); + } + if (!error_text.IsEmpty()) + msg_text += wxString::Format("[%s]", error_text); + update_status(curr_percent, msg_text); + BOOST_LOG_TRIVIAL(error) << "send_job: failed, result = " << result; + } else { + BOOST_LOG_TRIVIAL(error) << "send_job: send ok."; + //m_success_fun(); + wxCommandEvent* evt = new wxCommandEvent(m_print_job_completed_id); + evt->SetString(m_dev_id); + wxQueueEvent(m_plater, evt); + m_job_finished = true; + } +} + +void SendJob::on_success(std::function success) +{ + m_success_fun = success; +} + + +void SendJob::finalize() { + if (was_canceled()) return; + + Job::finalize(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/SendJob.hpp b/src/slic3r/GUI/Jobs/SendJob.hpp new file mode 100644 index 0000000000..7411b2b1ad --- /dev/null +++ b/src/slic3r/GUI/Jobs/SendJob.hpp @@ -0,0 +1,60 @@ +#ifndef SendJOB_HPP +#define SendJOB_HPP + +#include +#include +#include "PlaterJob.hpp" +#include "PrintJob.hpp" + +namespace fs = boost::filesystem; + +namespace Slic3r { +namespace GUI { + +typedef std::function OnUpdateStatusFn; +typedef std::function WasCancelledFn; + +class SendJob : public PlaterJob +{ + PrintPrepareData job_data; + std::string m_dev_id; + bool m_job_finished{ false }; + int m_print_job_completed_id = 0; + std::function m_success_fun{nullptr}; + +protected: + + void prepare() override; + + void on_exception(const std::exception_ptr &) override; +public: + SendJob(std::shared_ptr pri, Plater *plater, std::string dev_id = ""); + + + std::string m_dev_ip; + std::string m_access_code; + std::string task_bed_type; + std::string task_ams_mapping; + std::string connection_type; + + bool cloud_print_only { false }; + bool has_sdcard { false }; + bool task_use_ams { true }; + + wxWindow* m_parent{nullptr}; + + int status_range() const override + { + return 100; + } + + wxString get_http_error_msg(unsigned int status, std::string body); + bool is_finished() { return m_job_finished; } + void process() override; + void on_success(std::function success); + void finalize() override; +}; + +}} + +#endif diff --git a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp index 8b53cb303b..827cd88434 100644 --- a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp +++ b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp @@ -9,7 +9,8 @@ namespace GUI { wxDEFINE_EVENT(EVT_UPGRADE_UPDATE_MESSAGE, wxCommandEvent); wxDEFINE_EVENT(EVT_UPGRADE_NETWORK_SUCCESS, wxCommandEvent); -wxDEFINE_EVENT(EVT_UPGRADE_NETWORK_FAILED, wxCommandEvent); +wxDEFINE_EVENT(EVT_DOWNLOAD_NETWORK_FAILED, wxCommandEvent); +wxDEFINE_EVENT(EVT_INSTALL_NETWORK_FAILED, wxCommandEvent); UpgradeNetworkJob::UpgradeNetworkJob(std::shared_ptr pri) @@ -88,7 +89,7 @@ void UpgradeNetworkJob::process() if (result < 0) { update_status(0, _L("Download failed")); - wxCommandEvent event(EVT_UPGRADE_NETWORK_FAILED); + wxCommandEvent event(EVT_DOWNLOAD_NETWORK_FAILED); event.SetEventObject(m_event_handle); wxPostEvent(m_event_handle, event); return; @@ -112,7 +113,7 @@ void UpgradeNetworkJob::process() if (result != 0) { update_status(0, _L("Install failed")); - wxCommandEvent event(EVT_UPGRADE_NETWORK_FAILED); + wxCommandEvent event(EVT_INSTALL_NETWORK_FAILED); event.SetEventObject(m_event_handle); wxPostEvent(m_event_handle, event); return; diff --git a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp index 54c3c14c44..f26dcba37f 100644 --- a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp +++ b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.hpp @@ -49,7 +49,8 @@ public: wxDECLARE_EVENT(EVT_UPGRADE_UPDATE_MESSAGE, wxCommandEvent); wxDECLARE_EVENT(EVT_UPGRADE_NETWORK_SUCCESS, wxCommandEvent); -wxDECLARE_EVENT(EVT_UPGRADE_NETWORK_FAILED, wxCommandEvent); +wxDECLARE_EVENT(EVT_DOWNLOAD_NETWORK_FAILED, wxCommandEvent); +wxDECLARE_EVENT(EVT_INSTALL_NETWORK_FAILED, wxCommandEvent); }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1d87386633..c126ad771e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -200,6 +200,25 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ // Font is already set in DPIFrame constructor */ +#ifdef __APPLE__ + m_reset_title_text_colour_timer = new wxTimer(); + m_reset_title_text_colour_timer->SetOwner(this); + Bind(wxEVT_TIMER, [this](auto& e) { + set_title_colour_after_set_title(GetHandle()); + m_reset_title_text_colour_timer->Stop(); + }); + this->Bind(wxEVT_FULLSCREEN, [this](wxFullScreenEvent& e) { + set_tag_when_enter_full_screen(e.IsFullScreen()); + if (!e.IsFullScreen()) { + if (m_reset_title_text_colour_timer) { + m_reset_title_text_colour_timer->Stop(); + m_reset_title_text_colour_timer->Start(500); + } + } + e.Skip(); + }); +#endif + #ifdef __APPLE__ // Initialize the docker task bar icon. switch (wxGetApp().get_app_mode()) { @@ -375,7 +394,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ // declare events Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< "received close_widow event"; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": mainframe received close_widow event"; if (event.CanVeto() && m_plater->get_view3D_canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) { // prevents to open the save dirty project dialog event.Veto(); @@ -428,7 +447,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ this->shutdown(); // propagate event event.Skip(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< "finished process close_widow event"; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": mainframe finished process close_widow event"; }); //FIXME it seems this method is not called on application start-up, at least not on Windows. Why? @@ -499,6 +518,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ } return;} #endif + if (evt.CmdDown() && evt.GetKeyCode() == 'J') { m_printhost_queue_dlg->Show(); return; } if (evt.CmdDown() && evt.GetKeyCode() == 'N') { m_plater->new_project(); return;} if (evt.CmdDown() && evt.GetKeyCode() == 'O') { m_plater->load_project(); return;} if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'S') { if (m_plater) m_plater->save_project(true); return;} @@ -702,6 +722,7 @@ void MainFrame::update_layout() // Called when closing the application and when switching the application language. void MainFrame::shutdown() { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter"; // BBS: backup Slic3r::set_backup_callback(nullptr); #ifdef _WIN32 @@ -763,6 +784,8 @@ void MainFrame::shutdown() wxGetApp().shutdown(); // BBS: why clear ? //wxGetApp().plater_ = nullptr; + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown exit"; } void MainFrame::update_title() @@ -772,8 +795,8 @@ void MainFrame::update_title() void MainFrame::update_title_colour_after_set_title() { -#ifdef __WXOSX__ - set_title_colour_after_set_title(); +#ifdef __APPLE__ + set_title_colour_after_set_title(GetHandle()); #endif } @@ -1269,8 +1292,12 @@ wxBoxSizer* MainFrame::create_side_tools() wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_GCODE)); else if (m_print_select == eSendGcode) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_GCODE)); + else if (m_print_select == eUploadGcode) + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_UPLOAD_GCODE)); else if (m_print_select == eExportSlicedFile) - wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); + else if (m_print_select == eSendToPrinter) + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER)); }); // only support single plate currently @@ -1322,60 +1349,92 @@ wxBoxSizer* MainFrame::create_side_tools() p->Dismiss(); }); #endif - SideButton* print_plate_btn = new SideButton(p, _L("Print"), ""); - print_plate_btn->SetCornerRadius(0); - print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { +#if ENABEL_PRINT_ALL + p->append_button(print_all_btn); +#endif + if (wxGetApp().preset_bundle + && !wxGetApp().preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(wxGetApp().preset_bundle)) { + // ThirdParty Buttons + SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), ""); + export_gcode_btn->SetCornerRadius(0); + export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Export G-code file")); + m_print_select = eExportGcode; + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + p->Dismiss(); + }); + + // upload and print + SideButton* send_gcode_btn = new SideButton(p, _L("Print"), ""); + send_gcode_btn->SetCornerRadius(0); + send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Print")); + m_print_select = eSendGcode; + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + p->Dismiss(); + }); + + // upload only + /*SideButton* upload_gcode_btn = new SideButton(p, _L("Send"), ""); + upload_gcode_btn->SetCornerRadius(0); + upload_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Send")); + m_print_select = eUploadGcode; + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + p->Dismiss(); + });*/ + + p->append_button(send_gcode_btn); + //p->append_button(upload_gcode_btn); + p->append_button(export_gcode_btn); + } else { + //Bambu Studio Buttons + SideButton* print_plate_btn = new SideButton(p, _L("Print"), ""); + print_plate_btn->SetCornerRadius(0); + + /*SideButton* send_to_printer_btn = new SideButton(p, _L("Send"), ""); + send_to_printer_btn->SetCornerRadius(0);*/ + + SideButton* export_sliced_file_btn = new SideButton(p, _L("Export sliced file"), ""); + export_sliced_file_btn->SetCornerRadius(0); + + print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { m_print_btn->SetLabel(_L("Print")); m_print_select = ePrintPlate; m_print_enable = get_enable_print_status(); m_print_btn->Enable(m_print_enable); this->Layout(); p->Dismiss(); - }); + }); - SideButton* send_gcode_btn = new SideButton(p, _L("Send"), ""); - send_gcode_btn->SetCornerRadius(0); - send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Send to print")); - m_print_select = eSendGcode; - if (m_print_enable) - m_print_enable = get_enable_print_status() && can_send_gcode(); + /*send_to_printer_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Send")); + m_print_select = eSendToPrinter; + m_print_enable = get_enable_print_status(); m_print_btn->Enable(m_print_enable); this->Layout(); p->Dismiss(); - }); + });*/ - SideButton* export_sliced_file_3mf_btn = new SideButton(p, _L("Export sliced file (.3mf)"), ""); - export_sliced_file_3mf_btn->SetCornerRadius(0); - export_sliced_file_3mf_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export Sliced File (.3mf)")); + export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { + m_print_btn->SetLabel(_L("Export Sliced File")); m_print_select = eExportSlicedFile; - if (m_print_enable) - m_print_enable = get_enable_print_status(); + m_print_enable = get_enable_print_status(); m_print_btn->Enable(m_print_enable); this->Layout(); p->Dismiss(); - }); + }); + p->append_button(print_plate_btn); + //p->append_button(send_to_printer_btn); + p->append_button(export_sliced_file_btn); + } - SideButton* export_sliced_file_gcode_btn = new SideButton(p, _L("Export sliced file (.gcode)"), ""); - export_sliced_file_gcode_btn->SetCornerRadius(0); - export_sliced_file_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export Sliced File (.gcode)")); - m_print_select = eExportGcode; - if (m_print_enable) - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - p->Dismiss(); - }); - -#if ENABEL_PRINT_ALL - p->append_button(print_all_btn); -#endif - p->append_button(print_plate_btn); - p->append_button(send_gcode_btn); - p->append_button(export_sliced_file_3mf_btn); - p->append_button(export_sliced_file_gcode_btn); p->Popup(m_print_btn); } ); @@ -1461,13 +1520,34 @@ bool MainFrame::get_enable_print_status() enable = false; } } + else if (m_print_select == eSendGcode) + { + if (!current_plate->is_slice_result_valid()) + enable = false; + if (!can_send_gcode()) + enable = false; + } + else if (m_print_select == eUploadGcode) + { + if (!current_plate->is_slice_result_valid()) + enable = false; + if (!can_send_gcode()) + enable = false; + } else if (m_print_select == eExportSlicedFile) { if (!current_plate->is_slice_result_ready_for_print()) { enable = false; } - } + } + else if (m_print_select == eSendToPrinter) + { + if (!current_plate->is_slice_result_ready_for_print()) + { + enable = false; + } + } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": m_print_select %1%, enable= %2% ")%m_print_select %enable; @@ -1680,6 +1760,10 @@ static wxMenu* generate_help_menu() append_menu_item(helpMenu, wxID_ANY, about_title, about_title, [](wxCommandEvent&) { Slic3r::GUI::about(); }); #endif + append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the Day"), _L("Show Tip of the Day"), [](wxCommandEvent&) { + wxGetApp().plater()->get_notification_manager()->push_hint_notification(false); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + }); append_menu_item(helpMenu, wxID_ANY, _L("Open Network Test"), _L("Open Network Test"), [](wxCommandEvent&) { NetworkTestDialog dlg(wxGetApp().mainframe); @@ -2613,6 +2697,28 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const m_plater->on_config_change(*config); // propagate config change events to the plater } +void MainFrame::set_print_button_to_default(PrintSelectType select_type) +{ + if (select_type == PrintSelectType::ePrintPlate) { + m_print_btn->SetLabel(_L("Print")); + m_print_select = ePrintPlate; + if (m_print_enable) + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + } else if (select_type == PrintSelectType::eSendGcode) { + m_print_btn->SetLabel(_L("Print")); + m_print_select = eSendGcode; + if (m_print_enable) + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + } else { + //unsupport + return; + } +} + void MainFrame::add_to_recent_projects(const wxString& filename) { if (wxFileExists(filename)) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index affaf2fc13..416552743b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -84,6 +84,7 @@ protected: class MainFrame : public DPIFrame { bool m_loaded {false}; + wxTimer* m_reset_title_text_colour_timer{ nullptr }; wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; @@ -167,17 +168,6 @@ class MainFrame : public DPIFrame ESettingsLayout m_layout{ ESettingsLayout::Unknown }; - //BBS GUI refactor - enum PrintSelectType - { - ePrintAll = 0, - ePrintPlate = 1, - eExportSlicedFile = 2, - eExportGcode = 3, - eSendGcode = 4, - - }; - enum SliceSelectType { eSliceAll = 0, @@ -193,7 +183,6 @@ protected: #endif public: - MainFrame(); ~MainFrame() = default; @@ -208,6 +197,18 @@ public: tpProject = 4, }; + //BBS GUI refactor + enum PrintSelectType + { + ePrintAll = 0, + ePrintPlate = 1, + eExportSlicedFile = 2, + eExportGcode = 3, + eSendGcode = 4, + eSendToPrinter = 5, + eUploadGcode = 6 + }; + //BBS: add slice&&print status update logic enum SlicePrintEventType { @@ -288,6 +289,7 @@ public: void select_view(const std::string& direction); // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; + void set_print_button_to_default(PrintSelectType select_type); bool can_save() const; bool can_save_as() const; diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 1885d3f6b1..ca5e0066eb 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -73,7 +73,8 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) type_sizer->Add(m_button_timelapse, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 24); type_sizer->Add(m_button_video, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 24); m_type_panel->SetSizer(type_sizer); - //top_sizer->Add(m_type_panel, 0, wxALIGN_CENTER_VERTICAL); + m_type_panel->Hide(); + // top_sizer->Add(m_type_panel, 0, wxALIGN_CENTER_VERTICAL); // File management m_manage_panel = new ::StaticBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); @@ -82,19 +83,18 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) m_button_delete->SetToolTip(L("Delete selected files from printer.")); m_button_download = new ::Button(m_manage_panel, _L("Download")); m_button_download->SetToolTip(L("Download selected files from printer.")); - m_button_management = new ::Button(m_manage_panel, _L("Management")); + m_button_management = new ::Button(m_manage_panel, _L("Select")); m_button_management->SetToolTip(L("Batch manage files.")); for (auto b : {m_button_delete, m_button_download, m_button_management}) { - b->SetBackgroundColor(StateColor()); b->SetFont(Label::Body_12); b->SetCornerRadius(12); b->SetPaddingSize({10, 6}); b->SetCanFocus(false); } - m_button_delete->SetBorderColor(wxColor("#FF6F00")); - m_button_delete->SetTextColor(wxColor("#FF6F00")); + m_button_delete->SetBorderColorNormal(wxColor("#FF6F00")); + m_button_delete->SetTextColorNormal(wxColor("#FF6F00")); m_button_management->SetBorderWidth(0); - m_button_management->SetBackgroundColor(wxColor("#00AE42")); + m_button_management->SetBackgroundColorNormal(wxColor("#00AE42")); wxBoxSizer *manage_sizer = new wxBoxSizer(wxHORIZONTAL); manage_sizer->AddStretchSpacer(1); @@ -166,15 +166,16 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) // File management m_button_management->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { e.Skip(); - bool selecting = !m_image_grid->IsSelecting(); - m_image_grid->SetSelecting(selecting); - m_button_management->SetLabel(selecting ? _L("Finish") : _L("Management")); - m_manage_panel->GetSizer()->Show(m_button_download, selecting); - m_manage_panel->GetSizer()->Show(m_button_delete, selecting); - m_manage_panel->Layout(); + SetSelecting(!m_image_grid->IsSelecting()); + }); + m_button_download->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { + m_image_grid->DoActionOnSelection(1); + SetSelecting(false); + }); + m_button_delete->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { + m_image_grid->DoActionOnSelection(0); + SetSelecting(false); }); - m_button_download->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { m_image_grid->DoActionOnSelection(1); }); - m_button_delete->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { m_image_grid->DoActionOnSelection(0); }); auto onShowHide = [this](auto &e) { e.Skip(); @@ -214,6 +215,8 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) fs->Unbind(EVT_MODE_CHANGED, &MediaFilePanel::modeChanged, this); fs->Stop(true); } + m_button_management->Enable(false); + SetSelecting(false); if (m_machine.empty()) { m_image_grid->SetStatus(m_bmp_failed.bmp(), _L("No printers.")); } else if (m_lan_ip.empty() && (m_lan_mode && !m_tutk_support)) { @@ -223,8 +226,26 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) fs->Attached(); m_image_grid->SetFileType(m_last_type); m_image_grid->SetFileSystem(fs); + fs->Bind(EVT_FILE_CHANGED, [this, wfs = boost::weak_ptr(fs)](auto &e) { + e.Skip(); + boost::shared_ptr fs(wfs.lock()); + if (m_image_grid->GetFileSystem() != fs) // canceled + return; + m_button_management->Enable(fs->GetCount() > 0); + if (fs->GetCount() == 0) + SetSelecting(false); + }); + fs->Bind(EVT_SELECT_CHANGED, [this, wfs = boost::weak_ptr(fs)](auto &e) { + e.Skip(); + boost::shared_ptr fs(wfs.lock()); + if (m_image_grid->GetFileSystem() != fs) // canceled + return; + m_button_delete->Enable(e.GetInt() > 0); + m_button_download->Enable(e.GetInt() > 0); + }); fs->Bind(EVT_MODE_CHANGED, &MediaFilePanel::modeChanged, this); fs->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(fs)](auto &e) { + e.Skip(); boost::shared_ptr fs(wfs.lock()); if (m_image_grid->GetFileSystem() != fs) // canceled return; @@ -271,6 +292,15 @@ void MediaFilePanel::Rescale() m_image_grid->Rescale(); } +void MediaFilePanel::SetSelecting(bool selecting) +{ + m_image_grid->SetSelecting(selecting); + m_button_management->SetLabel(selecting ? _L("Cancel") : _L("Select")); + m_manage_panel->GetSizer()->Show(m_button_download, selecting); + m_manage_panel->GetSizer()->Show(m_button_delete, selecting); + m_manage_panel->Layout(); +} + void MediaFilePanel::modeChanged(wxCommandEvent& e1) { e1.Skip(); diff --git a/src/slic3r/GUI/MediaFilePanel.h b/src/slic3r/GUI/MediaFilePanel.h index 3b47ac8d9f..6a07fe28d8 100644 --- a/src/slic3r/GUI/MediaFilePanel.h +++ b/src/slic3r/GUI/MediaFilePanel.h @@ -39,6 +39,8 @@ public: public: void Rescale(); + void SetSelecting(bool selecting); + private: void modeChanged(wxCommandEvent & e); diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index ddab118b9c..0df1b9fcad 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -71,11 +71,13 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) { std::string machine = obj ? obj->dev_id : ""; if (obj && obj->is_function_supported(PrinterFunction::FUNC_CAMERA_VIDEO)) { - m_lan_mode = obj->is_lan_mode_printer(); + m_camera_exists = obj->has_ipcam; + m_lan_mode = obj->is_lan_mode_printer(); m_lan_ip = obj->is_function_supported(PrinterFunction::FUNC_LOCAL_TUNNEL) ? obj->dev_ip : ""; - m_lan_passwd = obj->access_code; + m_lan_passwd = obj->is_function_supported(PrinterFunction::FUNC_LOCAL_TUNNEL) ? obj->access_code : ""; m_tutk_support = obj->is_function_supported(PrinterFunction::FUNC_REMOTE_TUNNEL); } else { + m_camera_exists = false; m_lan_mode = false; m_lan_ip.clear(); m_lan_passwd.clear(); @@ -93,7 +95,7 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) if (m_next_retry.IsValid()) Play(); else - SetStatus(""); + SetStatus("", false); } void MediaPlayCtrl::Play() @@ -102,12 +104,17 @@ void MediaPlayCtrl::Play() return; if (!IsShownOnScreen()) return; + if (m_last_state != MEDIASTATE_IDLE) { + return; + } if (m_machine.empty()) { Stop(); SetStatus(_L("Initialize failed (No Device)!")); return; } - if (m_last_state != MEDIASTATE_IDLE) { + if (!m_camera_exists) { + Stop(); + SetStatus(_L("Initialize failed (No Camera Device)!")); return; } @@ -135,9 +142,17 @@ void MediaPlayCtrl::Play() return; } - if (m_lan_mode && !m_tutk_support) { // not support tutk + if (m_lan_mode) { Stop(); - SetStatus(_L("Initialize failed (Not supported)!")); + SetStatus(m_lan_passwd.empty() + ? _L("Initialize failed (Not supported with LAN-only mode)!") + : _L("Initialize failed (Not accessible in LAN-only mode)!")); + return; + } + + if (!m_tutk_support) { // not support tutk + Stop(); + SetStatus(_L("Initialize failed (Not supported without remote video tunnel)!")); return; } @@ -145,14 +160,13 @@ void MediaPlayCtrl::Play() if (agent) { agent->get_camera_url(m_machine, [this, m = m_machine](std::string url) { BOOST_LOG_TRIVIAL(info) << "camera_url: " << url; - if (m != m_machine) return; - CallAfter([this, url] { + CallAfter([this, m, url] { + if (m != m_machine) return; m_url = url; if (m_last_state == MEDIASTATE_INITIALIZING) { - if (url.empty()) { + if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { Stop(); - m_failed_code = 1; - SetStatus(_L("Initialize failed [%d]!")); + SetStatus(wxString::Format(_L("Initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url))); } else { m_last_state = MEDIASTATE_LOADING; SetStatus(_L("Loading...")); @@ -179,7 +193,10 @@ void MediaPlayCtrl::Stop() m_tasks.push_back(""); m_cond.notify_all(); m_last_state = MEDIASTATE_IDLE; - SetStatus(_L("Stopped.")); + if (m_failed_code) + SetStatus(_L("Stopped [%d]!"), true); + else + SetStatus(_L("Stopped."), false); if (m_failed_code >= 100) // not keep retry on local error m_next_retry = wxDateTime(); } @@ -200,7 +217,7 @@ void MediaPlayCtrl::TogglePlay() } } -void MediaPlayCtrl::SetStatus(wxString const& msg2) +void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink) { auto msg = wxString::Format(msg2, m_failed_code); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::SetStatus: " << msg.ToUTF8().data(); @@ -211,10 +228,11 @@ void MediaPlayCtrl::SetStatus(wxString const& msg2) #endif // __WXMSW__ m_label_status->SetLabel(msg); long style = m_label_status->GetWindowStyle() & ~LB_HYPERLINK; - if (m_failed_code && msg != msg2) { + if (hyperlink) { style |= LB_HYPERLINK; } m_label_status->SetWindowStyle(style); + m_label_status->InvalidateBestSize(); Layout(); } @@ -271,6 +289,7 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent& event) } if ((last_state == wxMEDIASTATE_PAUSED || last_state == wxMEDIASTATE_PLAYING) && state == wxMEDIASTATE_STOPPED) { + m_failed_code = m_media_ctrl->GetLastError(); Stop(); return; } @@ -280,7 +299,7 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent& event) m_failed_code = m_media_ctrl->GetLastError(); if (size.GetWidth() > 1000) { m_last_state = state; - SetStatus(_L("Playing...")); + SetStatus(_L("Playing..."), false); m_failed_retry = 0; boost::unique_lock lock(m_mutex); m_tasks.push_back(""); @@ -291,9 +310,9 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent& event) if (m_failed_code == 0) m_failed_code = 2; SetStatus(_L("Load failed [%d]!")); - } else { - m_last_state = last_state; } + } else { + m_last_state = state; } } diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index f8f3945b32..6cf059eb2a 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -44,7 +44,7 @@ protected: void TogglePlay(); - void SetStatus(wxString const & msg); + void SetStatus(wxString const &msg, bool hyperlink = true); private: void media_proc(); @@ -61,6 +61,7 @@ private: std::string m_lan_ip; std::string m_lan_user; std::string m_lan_passwd; + bool m_camera_exists = false; bool m_lan_mode = false; bool m_tutk_support = false; wxString m_url; diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index 625b9fcf14..3563ecde61 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -177,16 +177,19 @@ MonitorPanel::~MonitorPanel() m_connection_info->SetTextColor(*wxWHITE); m_connection_info->SetFont(::Label::Body_13); m_connection_info->SetCornerRadius(0); - m_connection_info->SetSize(wxSize(FromDIP(220), FromDIP(25))); - m_connection_info->SetMinSize(wxSize(FromDIP(220), FromDIP(25))); - m_connection_info->SetMaxSize(wxSize(FromDIP(220), FromDIP(25))); + m_connection_info->SetSize(wxSize(FromDIP(-1), FromDIP(25))); + m_connection_info->SetMinSize(wxSize(FromDIP(-1), FromDIP(25))); + m_connection_info->SetMaxSize(wxSize(FromDIP(-1), FromDIP(25))); + + wxBoxSizer* connection_sizer = new wxBoxSizer(wxVERTICAL); + m_hyperlink = new wxHyperlinkCtrl(m_connection_info, wxID_ANY, _L("Failed to connect to the server"), wxT("https://wiki.bambulab.com/en/software/bambu-studio/failed-to-connect-printer"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + connection_sizer->Add(m_hyperlink, 0, wxALIGN_CENTER | wxALL, 5); + m_hyperlink->SetBackgroundColour(wxColour(255, 111, 0)); + m_connection_info->SetSizer(connection_sizer); + m_connection_info->Layout(); + connection_sizer->Fit(m_connection_info); + m_connection_info->Hide(); - /*sizer_boxv->Add(m_connection_info, 0, wxALIGN_CENTER, 0); - sizer_boxh->Add(sizer_boxv, 1, wxALIGN_CENTER, 0); - warning_panel->SetSizer(sizer_boxh); - warning_panel->Layout();*/ - - sizer_side_tools->Add(m_connection_info, 0, wxEXPAND, 0); @@ -201,13 +204,11 @@ MonitorPanel::~MonitorPanel() m_status_info_panel = new StatusPanel(m_tabpanel); m_tabpanel->AddPage(m_status_info_panel, _L("Status"), "", true); -#if !BBL_RELEASE_TO_PUBLIC m_media_file_panel = new MediaFilePanel(m_tabpanel); m_tabpanel->AddPage(m_media_file_panel, _L("Media"), "", false); m_upgrade_panel = new UpgradePanel(m_tabpanel); m_tabpanel->AddPage(m_upgrade_panel, _L("Update"), "", false); -#endif m_hms_panel = new HMSPanel(m_tabpanel); m_tabpanel->AddPage(m_hms_panel, _L("HMS"),"", false); @@ -254,10 +255,8 @@ void MonitorPanel::msw_rescale() m_tabpanel->Rescale(); //m_status_add_machine_panel->msw_rescale(); m_status_info_panel->msw_rescale(); -#if !BBL_RELEASE_TO_PUBLIC m_media_file_panel->Rescale(); m_upgrade_panel->msw_rescale(); -#endif m_hms_panel->msw_rescale(); m_connection_info->SetCornerRadius(0); @@ -318,6 +317,7 @@ void MonitorPanel::on_printer_clicked(wxMouseEvent &event) if (!m_side_tools->is_in_interval()) { wxPoint pos = m_side_tools->ClientToScreen(wxPoint(0, 0)); pos.y += m_side_tools->GetRect().height; + pos.x = pos.x < 0? 0:pos.x; m_select_machine.Position(pos, wxSize(0, 0)); #ifdef __linux__ @@ -407,15 +407,11 @@ void MonitorPanel::update_all() } m_status_info_panel->obj = obj; -#if !BBL_RELEASE_TO_PUBLIC m_upgrade_panel->update(obj); -#endif m_status_info_panel->m_media_play_ctrl->SetMachineObject(obj); -#if !BBL_RELEASE_TO_PUBLIC m_media_file_panel->SetMachineObject(obj); -#endif update_status(obj); @@ -514,9 +510,14 @@ void MonitorPanel::show_status(int status) if (((status & (int) MonitorStatus::MONITOR_DISCONNECTED) != 0) || ((status & (int) MonitorStatus::MONITOR_DISCONNECTED_SERVER) != 0)) { if ((status & (int) MonitorStatus::MONITOR_DISCONNECTED_SERVER)) - m_connection_info->SetLabel(_L("Failed to connect to the server")); + m_hyperlink->SetLabel(_L("Failed to connect to the server")); + //m_connection_info->SetLabel(_L("Failed to connect to the server")); else - m_connection_info->SetLabel(_L("Failed to connect to the printer")); + m_hyperlink->SetLabel(_L("Failed to connect to the printer")); + //m_connection_info->SetLabel(_L("Failed to connect to the printer")); + + m_hyperlink->Show(); + m_connection_info->SetLabel(wxEmptyString); m_connection_info->Show(); m_connection_info->SetBackgroundColor(wxColour(255, 111, 0)); m_connection_info->SetBorderColor(wxColour(255, 111, 0)); @@ -526,6 +527,7 @@ void MonitorPanel::show_status(int status) } else if ((status & (int) MonitorStatus::MONITOR_NORMAL) != 0) { m_connection_info->Hide(); } else if ((status & (int) MonitorStatus::MONITOR_CONNECTING) != 0) { + m_hyperlink->Hide(); m_connection_info->SetLabel(_L("Connecting...")); m_connection_info->SetBackgroundColor(wxColour(0, 174, 66)); m_connection_info->SetBorderColor(wxColour(0, 174, 66)); diff --git a/src/slic3r/GUI/Monitor.hpp b/src/slic3r/GUI/Monitor.hpp index 6522b74be4..998004ea38 100644 --- a/src/slic3r/GUI/Monitor.hpp +++ b/src/slic3r/GUI/Monitor.hpp @@ -84,6 +84,7 @@ private: UpgradePanel* m_upgrade_panel; HMSPanel* m_hms_panel; Button * m_connection_info{nullptr}; + wxHyperlinkCtrl* m_hyperlink{nullptr}; /* side tools */ SideTools* m_side_tools{nullptr}; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index f8c86033ad..0da971614d 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1,5 +1,6 @@ #include "NotificationManager.hpp" +#include "HintNotification.hpp" #include "GUI.hpp" #include "ImGuiWrapper.hpp" #include "wxExtensions.hpp" @@ -1009,7 +1010,7 @@ void NotificationManager::PrintHostUploadNotification::init() } void NotificationManager::PrintHostUploadNotification::count_spaces() { - //determine line width + //determine line width m_line_height = ImGui::CalcTextSize("A").y; m_left_indentation = m_line_height; @@ -1078,7 +1079,6 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2)); break; } - imgui.text(text.c_str()); } @@ -1983,7 +1983,27 @@ int NotificationManager::progress_indicator_get_range() const void NotificationManager::push_hint_notification(bool open_next) { - return; + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::DidYouKnowHint) { + (dynamic_cast(notification.get()))->open_next(); + return; + } + } + + NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::HintNotificationLevel, 300, "" }; + // from user - open now + if (!open_next) { + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, open_next), 0); + stop_delayed_notifications_of_type(NotificationType::DidYouKnowHint); + // at startup - delay for half a second to let other notification pop up, than try every 30 seconds + // show only if no notifications are shown + } + else { + auto condition = [&self = std::as_const(*this)]() { + return self.get_notification_count() == 0; + }; + push_delayed_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, open_next), condition, 500, 30000); + } } bool NotificationManager::is_hint_notification_open() diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 6fe9b99532..a758f1652a 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -257,7 +257,8 @@ public: const t_opt_map& opt_map() const throw() { return m_opt_map; } void set_config_category_and_type(const wxString &category, int type) { m_config_category = category; m_config_type = type; } - void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; } + void set_config(DynamicPrintConfig* config) { + m_config = config; m_modelconfig = nullptr; } Option get_option(const std::string& opt_key, int opt_index = -1); Line create_single_option_line(const std::string& title, const std::string& path = std::string(), int idx = -1) /*const*/{ Option option = get_option(title, idx); diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index 2922f7bc99..15d42631c1 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -275,11 +275,61 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c wxVSCROLL) // hide hori-bar will cause hidden field mis-position { // ShowScrollBar(GetHandle(), SB_BOTH, FALSE); + Bind(wxEVT_SCROLL_CHANGED, [this](auto &e) { + wxWindow *child = dynamic_cast(e.GetEventObject()); + if (child != this) + EnsureVisible(child); + }); } virtual bool ShouldScrollToChildOnFocus(wxWindow *child) { + EnsureVisible(child); return false; } + void EnsureVisible(wxWindow* win) + { + const wxRect viewRect(m_targetWindow->GetClientRect()); + const wxRect winRect(m_targetWindow->ScreenToClient(win->GetScreenPosition()), win->GetSize()); + if (viewRect.Contains(winRect)) { + return; + } + if (winRect.GetWidth() > viewRect.GetWidth() || winRect.GetHeight() > viewRect.GetHeight()) { + return; + } + int stepx, stepy; + GetScrollPixelsPerUnit(&stepx, &stepy); + + int startx, starty; + GetViewStart(&startx, &starty); + // first in vertical direction: + if (stepy > 0) { + int diff = 0; + + if (winRect.GetTop() < 0) { + diff = winRect.GetTop(); + } else if (winRect.GetBottom() > viewRect.GetHeight()) { + diff = winRect.GetBottom() - viewRect.GetHeight() + 1; + // round up to next scroll step if we can't get exact position, + // so that the window is fully visible: + diff += stepy - 1; + } + starty = (starty * stepy + diff) / stepy; + } + // then horizontal: + if (stepx > 0) { + int diff = 0; + if (winRect.GetLeft() < 0) { + diff = winRect.GetLeft(); + } else if (winRect.GetRight() > viewRect.GetWidth()) { + diff = winRect.GetRight() - viewRect.GetWidth() + 1; + // round up to next scroll step if we can't get exact position, + // so that the window is fully visible: + diff += stepx - 1; + } + startx = (startx * stepx + diff) / stepx; + } + Scroll(startx, starty); + } }; m_page_view = new PageScrolledWindow(page_parent); diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 0f250fbbb8..a3295aa421 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -4106,7 +4106,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list) ps.total_used_filament *= 1000; //koef gcode_result->toolpath_outside = plate_data_list[i]->toolpath_outside; m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info; - + gcode_result->warnings = plate_data_list[i]->warnings; if (!plate_data_list[i]->thumbnail_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) { m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->thumbnail_file); diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index ab6324d200..b194a96efa 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -347,6 +347,7 @@ public: Print* fff_print() { return m_print; } //return the slice result GCodeProcessorResult* get_slice_result() { return m_gcode_result; } + std::string get_tmp_gcode_path(); std::string get_temp_config_3mf_path(); //this API should only be used for command line usage diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index bbaac31abd..aca9071c48 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -49,13 +49,51 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent) : #ifndef _WIN32 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #endif - + + // input the preset name + Tab *tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); + m_presets = tab->get_presets(); + const Preset &sel_preset = m_presets->get_selected_preset(); + std::string suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); + std::string preset_name = sel_preset.is_default ? "Untitled" : sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() : sel_preset.name; + + auto input_sizer = new wxBoxSizer(wxVERTICAL); + + wxStaticText *label_top = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as"))) % into_u8(tab->title())).str())); + label_top->SetFont(::Label::Body_13); + label_top->SetForegroundColour(wxColour(38,46,48)); + + m_input_area = new RoundedRectangle(this, wxColor(172, 172, 172), wxDefaultPosition, wxSize(-1,-1), 3, 1); + m_input_area->SetMinSize(wxSize(FromDIP(360), FromDIP(32))); + + wxBoxSizer *input_sizer_h = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *input_sizer_v = new wxBoxSizer(wxVERTICAL); + + m_input_ctrl = new wxTextCtrl(m_input_area, -1, from_u8(preset_name), wxDefaultPosition, wxSize(wxSize(FromDIP(360), FromDIP(32)).x, -1), 0 | wxBORDER_NONE); + m_input_ctrl->SetBackgroundColour(wxColour(255, 255, 255)); + m_input_ctrl->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { update(); }); + + + input_sizer_v->Add(m_input_ctrl, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 12); + input_sizer_h->Add(input_sizer_v, 0, wxALIGN_CENTER, 0); + + m_input_area->SetSizer(input_sizer_h); + m_input_area->Layout(); + + m_valid_label = new wxStaticText(this, wxID_ANY, ""); + m_valid_label->SetForegroundColour(wxColor(255, 111, 0)); + + input_sizer->Add(label_top, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, BORDER_W); + input_sizer->Add(m_input_area, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, BORDER_W); + input_sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT | wxRIGHT, BORDER_W); + + m_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; m_optgroup = new ConfigOptionsGroup(this, _L("Print Host upload"), m_config); build_printhost_settings(m_optgroup); wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + btnOK = static_cast(this->FindWindowById(wxID_OK, this)); wxGetApp().UpdateDarkUI(btnOK); btnOK->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::OnOK, this); @@ -65,12 +103,13 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent) : wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); // topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); + topSizer->Add(input_sizer , 0, wxEXPAND | wxALL, BORDER_W); topSizer->Add(m_optgroup->sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W); - topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); + topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W); SetSizer(topSizer); topSizer->SetSizeHints(this); - this->CenterOnScreen(); + this->CenterOnParent(); } PhysicalPrinterDialog::~PhysicalPrinterDialog() @@ -99,7 +138,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr auto printhost_browse = [=](wxWindow* parent) { - auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "browse", _L("Browse") + " " + dots); + auto sizer = create_sizer_with_btn(parent, &m_printhost_browse_btn, "printer_host_browser", _L("Browse") + " " + dots); m_printhost_browse_btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { BonjourDialog dialog(this, Preset::printer_technology(*m_config)); if (dialog.show_and_lookup()) { @@ -112,7 +151,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr }; auto print_host_test = [=](wxWindow* parent) { - auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "test", _L("Test")); + auto sizer = create_sizer_with_btn(parent, &m_printhost_test_btn, "printer_host_test", _L("Test")); m_printhost_test_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { std::unique_ptr host(PrintHost::get_print_host(m_config)); @@ -139,7 +178,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr auto print_host_printers = [this, create_sizer_with_btn](wxWindow* parent) { //add_scaled_button(parent, &m_printhost_port_browse_btn, "browse", _(L("Refresh Printers")), wxBU_LEFT | wxBU_EXACTFIT); - auto sizer = create_sizer_with_btn(parent, &m_printhost_port_browse_btn, "browse", _(L("Refresh Printers"))); + auto sizer = create_sizer_with_btn(parent, &m_printhost_port_browse_btn, "monitor_signal_strong", _(L("Refresh Printers"))); ScalableButton* btn = m_printhost_port_browse_btn; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { update_printers(); }); @@ -174,7 +213,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr Line cafile_line = m_optgroup->create_single_option_line(option); auto printhost_cafile_browse = [=](wxWindow* parent) { - auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "browse", _L("Browse") + " " + dots); + auto sizer = create_sizer_with_btn(parent, &m_printhost_cafile_browse_btn, "monitor_signal_strong", _L("Browse") + " " + dots); m_printhost_cafile_browse_btn->Bind(wxEVT_BUTTON, [this, m_optgroup](wxCommandEvent e) { static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"); wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); @@ -270,8 +309,88 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr void PhysicalPrinterDialog::update_printhost_buttons() { std::unique_ptr host(PrintHost::get_print_host(m_config)); - m_printhost_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); - m_printhost_browse_btn->Enable(host->has_auto_discovery()); + if (host) { + m_printhost_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); + m_printhost_browse_btn->Enable(host->has_auto_discovery()); + } +} + +void PhysicalPrinterDialog::update_preset_input() { + m_preset_name = into_u8(m_input_ctrl->GetValue()); + + m_valid_type = Valid; + wxString info_line; + + const char *unusable_symbols = "<>[]:/\\|?*\""; + + const std::string unusable_suffix = PresetCollection::get_suffix_modified(); //"(modified)"; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + info_line = _L("Name is invalid;") + "\n" + _L("illegal characters:") + " " + unusable_symbols; + m_valid_type = NoValid; + break; + } + } + + + if (m_valid_type == Valid && m_preset_name.find(unusable_suffix) != std::string::npos) { + info_line = _L("Name is invalid;") + "\n" + _L("illegal suffix:") + "\n\t" + from_u8(PresetCollection::get_suffix_modified()); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && + (m_preset_name == "Default Setting" || m_preset_name == "Default Filament" || m_preset_name == "Default Printer")) { + info_line = _L("Name is unavailable."); + m_valid_type = NoValid; + } + + const Preset *existing = m_presets->find_preset(m_preset_name, false); + if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) { + info_line = _L("Overwrite a system profile is not allowed"); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) { + if (existing->is_compatible) + info_line = from_u8((boost::format(_u8L("Preset \"%1%\" already exists.")) % m_preset_name).str()); + else + info_line = from_u8((boost::format(_u8L("Preset \"%1%\" already exists and is incompatible with current printer.")) % m_preset_name).str()); + info_line += "\n" + _L("Please note that saving action will replace this preset"); + m_valid_type = Warning; + } + + if (m_valid_type == Valid && m_preset_name.empty()) { + info_line = _L("The name is not allowed to be empty."); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && m_preset_name.find_first_of(' ') == 0) { + info_line = _L("The name is not allowed to start with space character."); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && m_preset_name.find_last_of(' ') == m_preset_name.length() - 1) { + info_line = _L("The name is not allowed to end with space character."); + m_valid_type = NoValid; + } + + if (m_valid_type == Valid && m_presets->get_preset_name_by_alias(m_preset_name) != m_preset_name) { + info_line = _L("The name cannot be the same as a preset alias name."); + m_valid_type = NoValid; + } + + m_valid_label->SetLabel(info_line); + m_input_area->Refresh(); + m_valid_label->Show(!info_line.IsEmpty()); + + if (m_valid_type == NoValid) { + if (btnOK) + btnOK->Disable(); + } + else { + if (btnOK) + btnOK->Enable(); + } } void PhysicalPrinterDialog::update(bool printer_change) @@ -317,6 +436,8 @@ void PhysicalPrinterDialog::update(bool printer_change) m_optgroup->show_field("printhost_port", supports_multiple_printers); m_printhost_port_browse_btn->Show(supports_multiple_printers); + update_preset_input(); + update_printhost_buttons(); this->SetSize(this->GetBestSize()); @@ -395,7 +516,7 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) void PhysicalPrinterDialog::OnOK(wxEvent& event) { - wxGetApp().get_tab(Preset::TYPE_PRINTER)->save_preset(); + wxGetApp().get_tab(Preset::TYPE_PRINTER)->save_preset("", false, false, true, m_preset_name ); event.Skip(); } diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp index e6d2015e63..ee88e34f7c 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp @@ -7,6 +7,7 @@ #include "libslic3r/Preset.hpp" #include "GUI_Utils.hpp" +#include "Widgets/RoundedRectangle.hpp" class wxString; class wxTextCtrl; @@ -34,6 +35,10 @@ class PhysicalPrinterDialog : public DPIDialog ScalableButton* m_printhost_client_cert_browse_btn {nullptr}; ScalableButton* m_printhost_port_browse_btn {nullptr}; + RoundedRectangle* m_input_area {nullptr}; + wxStaticText* m_valid_label {nullptr}; + wxTextCtrl* m_input_ctrl {nullptr}; + wxButton* btnOK {nullptr}; void build_printhost_settings(ConfigOptionsGroup* optgroup); void OnOK(wxEvent& event); @@ -42,8 +47,19 @@ public: PhysicalPrinterDialog(wxWindow* parent); ~PhysicalPrinterDialog(); + enum ValidationType + { + Valid, + NoValid, + Warning + }; + PresetCollection* m_presets {nullptr}; + ValidationType m_valid_type; + std::string m_preset_name; + void update(bool printer_change = false); void update_host_type(bool printer_change); + void update_preset_input(); void update_printhost_buttons(); void update_printers(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index eb3ff6fcbd..d583fb889d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -84,6 +84,7 @@ #include "Jobs/NotificationProgressIndicator.hpp" #include "BackgroundSlicingProcess.hpp" #include "SelectMachine.hpp" +#include "SendToPrinter.hpp" #include "PublishDialog.hpp" #include "ConfigWizard.hpp" #include "../Utils/ASCIIFolding.hpp" @@ -151,6 +152,8 @@ wxDEFINE_EVENT(EVT_PUBLISH, wxCommandEvent); // BBS: backup & restore wxDEFINE_EVENT(EVT_RESTORE_PROJECT, wxCommandEvent); wxDEFINE_EVENT(EVT_PRINT_FINISHED, wxCommandEvent); +wxDEFINE_EVENT(EVT_SEND_FINISHED, wxCommandEvent); +wxDEFINE_EVENT(EVT_PUBLISH_FINISHED, wxCommandEvent); //BBS: repair model wxDEFINE_EVENT(EVT_REPAIR_MODEL, wxCommandEvent); wxDEFINE_EVENT(EVT_FILAMENT_COLOR_CHANGED, wxCommandEvent); @@ -504,17 +507,17 @@ Sidebar::Sidebar(Plater *parent) combo_printer->edit_btn = edit_btn; p->combo_printer = combo_printer; - ScalableButton* connection_btn = new ScalableButton(p->m_panel_printer_content, wxID_ANY, "monitor_signal_strong"); + connection_btn = new ScalableButton(p->m_panel_printer_content, wxID_ANY, "monitor_signal_strong"); connection_btn->SetBackgroundColour(wxColour(255, 255, 255)); connection_btn->SetToolTip(_L("Connection")); connection_btn->Bind(wxEVT_BUTTON, [this, combo_printer](wxCommandEvent) - { - PhysicalPrinterDialog dlg(this->GetParent()); - dlg.ShowModal(); - }); + { + PhysicalPrinterDialog dlg(this->GetParent()); + dlg.ShowModal(); + }); - wxBoxSizer *vsizer_printer = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *hsizer_printer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* vsizer_printer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* hsizer_printer = new wxBoxSizer(wxHORIZONTAL); hsizer_printer->Add(combo_printer, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); hsizer_printer->Add(edit_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); @@ -706,7 +709,7 @@ Sidebar::Sidebar(Plater *parent) bSizer39->Add(del_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(5)); bSizer39->Add(FromDIP(20), 0, 0, 0, 0); - ScalableButton *ams_btn = new ScalableButton(p->m_panel_filament_title, wxID_ANY, "ams_fila_sync", wxEmptyString, wxDefaultSize, wxDefaultPosition, + ams_btn = new ScalableButton(p->m_panel_filament_title, wxID_ANY, "ams_fila_sync", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 18); ams_btn->SetToolTip(_L("Sync material list from AMS")); ams_btn->Bind(wxEVT_BUTTON, [this, scrolled_sizer](wxCommandEvent &e) { @@ -906,6 +909,21 @@ void Sidebar::update_all_preset_comboboxes() PresetBundle &preset_bundle = *wxGetApp().preset_bundle; const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); + bool is_bbl_preset = preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle); + + if (is_bbl_preset) { + //only show connection button for not-BBL printer + connection_btn->Hide(); + //only show sync-ams button for BBL printer + ams_btn->Show(); + //update print button default value for bbl or third-party printer + wxGetApp().mainframe->set_print_button_to_default(MainFrame::PrintSelectType::ePrintPlate); + } else { + connection_btn->Show(); + ams_btn->Hide(); + wxGetApp().mainframe->set_print_button_to_default(MainFrame::PrintSelectType::eSendGcode); + } + // Update the print choosers to only contain the compatible presets, update the dirty flags. //BBS @@ -1472,6 +1490,7 @@ struct Plater::priv MenuFactory menus; SelectMachineDialog* m_select_machine_dlg = nullptr; + SendToPrinterDialog* m_send_to_sdcard_dlg = nullptr; PublishDialog *m_publish_dlg = nullptr; // Data @@ -1594,6 +1613,7 @@ struct Plater::priv std::string label_btn_send; bool show_render_statistic_dialog{ false }; + bool show_wireframe{ false }; static const std::regex pattern_bundle; static const std::regex pattern_3mf; @@ -1671,6 +1691,7 @@ struct Plater::priv // BBS void hide_select_machine_dlg() { m_select_machine_dlg->EndModal(wxID_OK); } + void hide_send_to_printer_dlg() { m_send_to_sdcard_dlg->EndModal(wxID_OK); } void update_preview_bottom_toolbar(); @@ -1850,6 +1871,7 @@ struct Plater::priv void on_action_print_all(SimpleEvent&); void on_action_export_gcode(SimpleEvent&); void on_action_send_gcode(SimpleEvent&); + void on_action_upload_gcode(SimpleEvent&); void on_action_export_sliced_file(SimpleEvent&); void on_action_select_sliced_plate(wxCommandEvent& evt); @@ -1931,14 +1953,14 @@ struct Plater::priv void update_fff_scene_only_shells(bool only_shells = true); //BBS: add popup object table logic bool PopupObjectTable(int object_id, int volume_id, const wxPoint& position); - + void on_action_send_to_printer(); private: void update_fff_scene(); void update_sla_scene(); void undo_redo_to(std::vector::const_iterator it_snapshot); void update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool temp_snapshot_was_taken = false); - + void on_action_export_to_sdcard(SimpleEvent&); // path to project folder stored with no extension boost::filesystem::path m_project_folder; @@ -2201,7 +2223,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) preview->on_tick_changed(tick_event_type); // BBS set to invalid state only - if (tick_event_type == Type::ToolChange) { + if (tick_event_type == Type::ToolChange || tick_event_type == Type::Custom || tick_event_type == Type::Template) { PartPlate *plate = this->q->get_partplate_list().get_curr_plate(); if (plate) { plate->update_slice_result_valid_state(false); @@ -2239,11 +2261,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->Bind(EVT_GLTOOLBAR_PRINT_ALL, &priv::on_action_print_all, this); q->Bind(EVT_GLTOOLBAR_EXPORT_GCODE, &priv::on_action_export_gcode, this); q->Bind(EVT_GLTOOLBAR_SEND_GCODE, &priv::on_action_send_gcode, this); + q->Bind(EVT_GLTOOLBAR_UPLOAD_GCODE, &priv::on_action_upload_gcode, this); q->Bind(EVT_GLTOOLBAR_EXPORT_SLICED_FILE, &priv::on_action_export_sliced_file, this); + q->Bind(EVT_GLTOOLBAR_SEND_TO_PRINTER, &priv::on_action_export_to_sdcard, this); q->Bind(EVT_GLCANVAS_PLATE_SELECT, &priv::on_plate_selected, this); q->Bind(EVT_DOWNLOAD_PROJECT, &priv::on_action_download_project, this); q->Bind(EVT_IMPORT_MODEL_ID, &priv::on_action_request_model_id, this); q->Bind(EVT_PRINT_FINISHED, [q](wxCommandEvent &evt) { q->print_job_finished(evt); }); + q->Bind(EVT_SEND_FINISHED, [q](wxCommandEvent &evt) { q->send_job_finished(evt); }); + q->Bind(EVT_PUBLISH_FINISHED, [q](wxCommandEvent &evt) { q->publish_job_finished(evt);}); //q->Bind(EVT_GLVIEWTOOLBAR_ASSEMBLE, [q](SimpleEvent&) { q->select_view_3D("Assemble"); }); } @@ -2514,6 +2540,16 @@ wxColour Plater::get_next_color_for_filament() return colors[curr_color_filamenet++ % 7]; } +wxString Plater::get_slice_warning_string(GCodeProcessorResult::SliceWarning& warning) +{ + if (warning.msg == BED_TEMP_TOO_HIGH_THAN_FILAMENT) { + return _L("The bed temperature exceeds filament's vitrification temperature. Please open the front door of printer before printing to avoid nozzle clog."); + } + else { + return wxString(warning.msg); + } +} + void Plater::priv::apply_free_camera_correction(bool apply/* = true*/) { bool use_perspective_camera = get_config("use_perspective_camera").compare("true") == 0; @@ -3467,8 +3503,12 @@ fs::path Plater::priv::get_export_file_path(GUI::FileType file_type) // for 3mf take the path from the project filename, if any output_file = into_path(get_project_filename(".3mf")); - if (output_file.empty()) - output_file = into_path(get_project_name() + ".3mf"); + //bbs name the project using the part name + if (output_file.empty()) { + if (get_project_name() != _L("Untitled")) { + output_file = into_path(get_project_name() + ".3mf"); + } + } if (output_file.empty()) { @@ -5647,6 +5687,14 @@ void Plater::priv::on_action_print_plate(SimpleEvent&) m_select_machine_dlg->ShowModal(); } + +void Plater::priv::on_action_send_to_printer() +{ + if (!m_send_to_sdcard_dlg) m_send_to_sdcard_dlg = new SendToPrinterDialog(q); + m_send_to_sdcard_dlg->prepare(partplate_list.get_curr_plate_index()); + m_send_to_sdcard_dlg->ShowModal(); +} + void Plater::priv::on_action_select_sliced_plate(wxCommandEvent &evt) { if (q != nullptr) { @@ -5677,11 +5725,19 @@ void Plater::priv::on_action_export_gcode(SimpleEvent&) } } +void Plater::priv::on_action_upload_gcode(SimpleEvent&) +{ + if (q != nullptr) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received export gcode event\n"; + q->send_gcode_legacy(-1, nullptr, true); + } +} + void Plater::priv::on_action_send_gcode(SimpleEvent&) { if (q != nullptr) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received export gcode event\n" ; - q->send_gcode_legacy(); + q->send_gcode_legacy(-1, nullptr, false); } } @@ -5693,6 +5749,14 @@ void Plater::priv::on_action_export_sliced_file(SimpleEvent&) } } +void Plater::priv::on_action_export_to_sdcard(SimpleEvent&) +{ + if (q != nullptr) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received export sliced file event\n"; + q->send_to_printer(); + } +} + //BBS: add plate select logic void Plater::priv::on_plate_selected(SimpleEvent&) { @@ -6072,11 +6136,22 @@ void Plater::get_print_job_data(PrintPrepareData* data) data->_3mf_config_path = p->m_print_job_data._3mf_config_path; } } + int Plater::get_print_finished_event() { return EVT_PRINT_FINISHED; } +int Plater::get_send_finished_event() +{ + return EVT_SEND_FINISHED; +} + +int Plater::get_publish_finished_event() +{ + return EVT_PUBLISH_FINISHED; +} + void Plater::priv::set_current_canvas_as_dirty() { if (current_panel == view3D) @@ -7123,6 +7198,11 @@ void Plater::add_model(bool imperial_units/* = false*/) auto strategy = LoadStrategy::LoadModel; if (imperial_units) strategy = strategy | LoadStrategy::ImperialUnits; if (!load_files(paths, strategy, ask_multi).empty()) { + + if (get_project_name() == _L("Untitled") && paths.size() > 0) { + p->set_project_filename(wxString(paths[0].string())); + } + wxGetApp().mainframe->update_title(); } } @@ -8315,6 +8395,11 @@ void Plater::export_gcode(bool prefer_removable) } } +void Plater::send_to_printer() +{ + p->on_action_send_to_printer(); +} + //BBS export gcode 3mf to file void Plater::export_gcode_3mf() { @@ -8952,7 +9037,7 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject & // and let the background processing start. this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } -void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn) +void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn, bool upload_only) { // if physical_printer is selected, send gcode for this printer // DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); @@ -8989,7 +9074,7 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn) wxBusyCursor wait; upload_job.printhost->get_groups(groups); } - + PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups); if (dlg.ShowModal() == wxID_OK) { upload_job.upload_data.upload_path = dlg.filename(); @@ -9020,7 +9105,7 @@ int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn) return -1; } - SaveStrategy strategy = SaveStrategy::Silence | SaveStrategy::SkipModel | SaveStrategy::WithGcode; + SaveStrategy strategy = SaveStrategy::Silence | SaveStrategy::SkipModel | SaveStrategy::WithGcode | SaveStrategy::SkipAuxiliary; #if !BBL_RELEASE_TO_PUBLIC //only save model in QA environment std::string sel = get_app_config()->get("iot_environment"); @@ -9053,7 +9138,7 @@ int Plater::export_config_3mf(int plate_idx, Export3mfProgressFn proFn) return -1; } - SaveStrategy strategy = SaveStrategy::Silence | SaveStrategy::SkipModel | SaveStrategy::WithSliceInfo; + SaveStrategy strategy = SaveStrategy::Silence | SaveStrategy::SkipModel | SaveStrategy::WithSliceInfo | SaveStrategy::SkipAuxiliary; result = export_3mf(p->m_print_job_data._3mf_config_path, strategy, plate_idx, proFn); return result; @@ -9074,6 +9159,26 @@ void Plater::print_job_finished(wxCommandEvent &evt) curr_monitor->get_tabpanel()->ChangeSelection(MonitorPanel::PrinterTab::PT_STATUS); } +void Plater::send_job_finished(wxCommandEvent& evt) +{ + Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + dev->set_selected_machine(evt.GetString().ToStdString()); + + p->hide_send_to_printer_dlg(); + //p->main_frame->request_select_tab(MainFrame::TabPosition::tpMonitor); + ////jump to monitor and select device status panel + //MonitorPanel* curr_monitor = p->main_frame->m_monitor; + //if (curr_monitor) + // curr_monitor->get_tabpanel()->ChangeSelection(MonitorPanel::PrinterTab::PT_STATUS); +} + +void Plater::publish_job_finished(wxCommandEvent &evt) +{ + p->m_publish_dlg->EndModal(wxID_OK); + GUI::wxGetApp().load_url(evt.GetString()); +} + // Called when the Eject button is pressed. void Plater::eject_drive() { @@ -10459,6 +10564,16 @@ bool Plater::is_render_statistic_dialog_visible() const return p->show_render_statistic_dialog; } +void Plater::toggle_show_wireframe() +{ + p->show_wireframe = !p->show_wireframe; +} + +bool Plater::is_show_wireframe() const +{ + return p->show_wireframe; +} + /*Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name) : TakeSnapshot(plater, from_u8(snapshot_name)) {} @@ -10526,4 +10641,4 @@ SuppressBackgroundProcessingUpdate::~SuppressBackgroundProcessingUpdate() wxGetApp().plater()->schedule_background_process(m_was_scheduled); } -}} // namespace Slic3r::GUI +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 37843cbcb2..4c5b947b60 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -20,6 +20,7 @@ #include "PartPlate.hpp" #include "GUI_App.hpp" #include "Jobs/PrintJob.hpp" +#include "Jobs/SendJob.hpp" #include "libslic3r/Model.hpp" class wxButton; @@ -160,6 +161,8 @@ private: wxBoxSizer* m_scrolled_sizer = nullptr; ComboBox* m_bed_type_list = nullptr; + ScalableButton* connection_btn = nullptr; + ScalableButton* ams_btn = nullptr; }; class Plater: public wxPanel @@ -232,6 +235,7 @@ public: static void setPrintSpeedTable(Slic3r::GlobalSpeedMap& printSpeedMap); static void setExtruderParams(std::map& extParas); static wxColour get_next_color_for_filament(); + static wxString get_slice_warning_string(GCodeProcessorResult::SliceWarning& warning); // BBS: restore std::vector load_files(const std::vector& input_files, LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig, bool ask_multi = false); @@ -296,6 +300,7 @@ public: void segment(size_t obj_idx, size_t instance_idx, double smoothing_alpha=0.5, int segment_number=5); void merge(size_t obj_idx, std::vector& vol_indeces); + void send_to_printer(); void export_gcode(bool prefer_removable); void export_gcode_3mf(); void export_core_3mf(); @@ -330,10 +335,12 @@ public: /* -1: send current gcode if not specified * -2: send all gcode to target machine */ int send_gcode(int plate_idx = -1, Export3mfProgressFn proFn = nullptr); - void send_gcode_legacy(int plate_idx = -1, Export3mfProgressFn proFn = nullptr); + void send_gcode_legacy(int plate_idx = -1, Export3mfProgressFn proFn = nullptr, bool upload_only = false); int export_config_3mf(int plate_idx = -1, Export3mfProgressFn proFn = nullptr); //BBS jump to nonitor after print job finished void print_job_finished(wxCommandEvent &evt); + void send_job_finished(wxCommandEvent& evt); + void publish_job_finished(wxCommandEvent& evt); void eject_drive(); void take_snapshot(const std::string &snapshot_name); @@ -397,6 +404,8 @@ public: //BBS: add print job releated functions void get_print_job_data(PrintPrepareData* data); int get_print_finished_event(); + int get_send_finished_event(); + int get_publish_finished_event(); void set_current_canvas_as_dirty(); void unbind_canvas_event_handlers(); @@ -613,6 +622,9 @@ public: void toggle_render_statistic_dialog(); bool is_render_statistic_dialog_visible() const; + void toggle_show_wireframe(); + bool is_show_wireframe() const; + // Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu. bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition); bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); } @@ -675,4 +687,4 @@ private: } // namespace GUI } // namespace Slic3r -#endif +#endif \ No newline at end of file diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 870ea4f1a4..7801123aa6 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -457,9 +457,12 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa m_sizer_checkbox->Add(checkbox, 0, wxALIGN_CENTER, 0); m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8); - auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxSize(-1, -1), 0); + auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0); checkbox_title->SetForegroundColour(DESIGN_GRAY900_COLOR); checkbox_title->SetFont(::Label::Body_13); + + auto size = checkbox_title->GetTextExtent(title); + checkbox_title->SetMinSize(wxSize(size.x + FromDIP(40), -1)); checkbox_title->Wrap(-1); m_sizer_checkbox->Add(checkbox_title, 0, wxALIGN_CENTER | wxALL, 3); @@ -526,6 +529,57 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa return m_sizer_checkbox; } +wxWindow* PreferencesDialog::create_item_downloads(wxWindow* parent, int padding_left, std::string param) +{ + wxString download_path = wxString::FromUTF8(app_config->get("download_path")); + auto item_panel = new wxWindow(parent, wxID_ANY); + item_panel->SetBackgroundColour(*wxWHITE); + wxBoxSizer* m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL); + + m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 23); + auto m_staticTextPath = new wxStaticText(item_panel, wxID_ANY, download_path, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); + //m_staticTextPath->SetMaxSize(wxSize(FromDIP(440), -1)); + m_staticTextPath->SetForegroundColour(DESIGN_GRAY600_COLOR); + m_staticTextPath->SetFont(::Label::Body_13); + m_staticTextPath->Wrap(-1); + + auto m_button_download = new Button(item_panel, _L("Browse")); + + StateColor abort_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + m_button_download->SetBackgroundColor(abort_bg); + StateColor abort_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + m_button_download->SetBorderColor(abort_bd); + StateColor abort_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + m_button_download->SetTextColor(abort_text); + m_button_download->SetFont(Label::Body_10); + m_button_download->SetMinSize(wxSize(FromDIP(58), FromDIP(22))); + m_button_download->SetSize(wxSize(FromDIP(58), FromDIP(22))); + m_button_download->SetCornerRadius(FromDIP(12)); + + m_button_download->Bind(wxEVT_BUTTON, [this, m_staticTextPath, item_panel](auto& e) { + wxString defaultPath = wxT("/"); + wxDirDialog dialog(this, _L("Choose Download Directory"), defaultPath, wxDD_NEW_DIR_BUTTON); + + if (dialog.ShowModal() == wxID_OK) { + wxString download_path = dialog.GetPath(); + std::string download_path_str = download_path.ToUTF8().data(); + app_config->set("download_path", download_path_str); + m_staticTextPath->SetLabelText(download_path); + item_panel->Layout(); + } + }); + + m_sizer_checkbox->Add(m_staticTextPath, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + m_sizer_checkbox->Add(m_button_download, 0, wxALL, FromDIP(5)); + + item_panel->SetSizer(m_sizer_checkbox); + item_panel->Layout(); + + return item_panel; +} + wxWindow *PreferencesDialog ::create_item_radiobox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, int groupid, std::string param) { wxWindow *item = new wxWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(28))); @@ -688,6 +742,8 @@ wxWindow* PreferencesDialog::create_general_page() std::vector Units = {_L("Metric"), _L("Imperial")}; auto item_currency = create_item_combobox(_L("Units"), page, _L("Units"), "use_inches", Units); + auto item_hints = create_item_checkbox(_L("Show \"Tip of the day\" notification after start"), page, _L("If enabled, useful hints are displayed at startup."), 50, "show_hints"); + auto title_sync_settings = create_item_title(_L("User sync"), page, _L("User sync")); auto item_user_sync = create_item_checkbox(_L("Auto sync user presets(Printer/Filament/Process)"), page, _L("User Sync"), 50, "sync_user_preset"); @@ -709,10 +765,15 @@ wxWindow* PreferencesDialog::create_general_page() auto item_backup = create_item_checkbox(_L("Auto-Backup"), page,_L("Auto-Backup"), 50, "backup_switch"); auto item_backup_interval = create_item_backup_input(_L("Backup interval"), page, _L("Backup interval"), "backup_interval"); + //downloads + auto title_downloads = create_item_title(_L("Downloads"), page, _L("Downloads")); + auto item_downloads = create_item_downloads(page,50,"download_path"); + sizer_page->Add(title_general_settings, 0, wxEXPAND, 0); sizer_page->Add(item_language, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_region, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_currency, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3)); sizer_page->Add(title_sync_settings, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3)); #ifdef _WIN32 @@ -726,6 +787,9 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_backup_interval, 0, wxTOP,FromDIP(3)); //sizer_page->Add(0, 0, 0, wxTOP, 26); + sizer_page->Add(title_downloads, 0, wxTOP| wxEXPAND, FromDIP(20)); + sizer_page->Add(item_downloads, 0, wxEXPAND, FromDIP(3)); + page->SetSizer(sizer_page); page->Layout(); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 4457cf6e9c..5d73080403 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -20,6 +20,7 @@ namespace Slic3r { namespace GUI { #define DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248) #define DESIGN_GRAY900_COLOR wxColour(38, 46, 48) #define DESIGN_GRAY800_COLOR wxColour(50, 58, 61) +#define DESIGN_GRAY600_COLOR wxColour(144, 144, 144) #define DESIGN_GRAY400_COLOR wxColour(166, 169, 170) class Selector @@ -106,7 +107,8 @@ public: wxBoxSizer *create_item_language_combobox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param, std::vector vlist); wxBoxSizer *create_item_loglevel_combobox(wxString title, wxWindow *parent, wxString tooltip, std::vector vlist); wxBoxSizer *create_item_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); - wxBoxSizer *create_item_backup_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); + wxWindow* create_item_downloads(wxWindow* parent, int padding_left, std::string param); + wxBoxSizer* create_item_backup_checkbox(wxString title, wxWindow* parent, wxString tooltip, int padding_left, std::string param); wxBoxSizer *create_item_backup_input(wxString title, wxWindow *parent, wxString tooltip, std::string param); wxBoxSizer *create_item_multiple_combobox( wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string parama, std::vector vlista, std::vector vlistb); @@ -125,7 +127,6 @@ public: void create_select_domain_widget(); void Split(const std::string &src, const std::string &separator, std::vector &dest); - int m_current_language_selected = {0}; protected: diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index ce59f85c48..0f8705f112 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -50,8 +50,9 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _L("Use forward slashes ( / ) as a directory separator if needed.")); label_dir_hint->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); - content_sizer->Add(txt_filename, 0, wxEXPAND); - content_sizer->Add(label_dir_hint); + content_sizer->Add(txt_filename, 0, wxEXPAND | wxALL, FromDIP(10)); + content_sizer->Add(FromDIP(10), FromDIP(10), 0, 0); + content_sizer->Add(label_dir_hint, 0, 0, FromDIP(10)); content_sizer->AddSpacer(VERT_SPACING); if (combo_groups != nullptr) { diff --git a/src/slic3r/GUI/Printer/BambuTunnel.h b/src/slic3r/GUI/Printer/BambuTunnel.h index 316b52a352..11504a70c9 100644 --- a/src/slic3r/GUI/Printer/BambuTunnel.h +++ b/src/slic3r/GUI/Printer/BambuTunnel.h @@ -1,28 +1,35 @@ -#pragma once +#ifndef _BAMBU__TUNNEL_H_ +#define _BAMBU__TUNNEL_H_ #ifdef BAMBU_DYNAMIC # define BAMBU_EXPORT # define BAMBU_FUNC(x) (*x) #else -# ifdef __WIN32__ -# define BAMBU_EXPORT __declspec(dllexport) +# ifdef _WIN32 +# ifdef BAMBU_EXPORTS +# define BAMBU_EXPORT __declspec(dllexport) +# else +# define BAMBU_EXPORT __declspec(dllimport) +# endif // BAMBU_EXPORTS # else # define BAMBU_EXPORT # endif // __WIN32__ # define BAMBU_FUNC(x) x -#endif +#endif // BAMBU_DYNAMIC #ifdef __cplusplus extern "C" { #endif // __cplusplus -struct Bambu_Session; -#ifdef __WIN32__ -typedef wchar_t Bambu_Message; +#ifdef _WIN32 + typedef wchar_t tchar; #else -typedef char Bambu_Message; + typedef char tchar; #endif -typedef void (*Logger)(Bambu_Session * session, int level, Bambu_Message const * msg); + +typedef void* Bambu_Tunnel; + +typedef void (*Logger)(void * context, int level, tchar const* msg); enum Bambu_StreamType { @@ -89,48 +96,39 @@ enum Bambu_Error { Bambu_success, Bambu_stream_end, - Bambu_would_block -}; - -struct Bambu_Session -{ - int gSID = -1; - int avIndex = -1; - int block = 0; - int block_next = 0; - Logger logger = nullptr; - void * buffer = nullptr; - int buffer_size = 0; - void * extra = 0; - void * dump_file1 = nullptr; - void * dump_file2 = nullptr; - - void Log(int unused, int level, wchar_t const * format, ...); + Bambu_would_block, + Bambu_buffer_limit }; #ifdef BAMBU_DYNAMIC struct BambuLib { #endif -BAMBU_EXPORT int BAMBU_FUNC(Bambu_Open)(Bambu_Session* session, char const* uid); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_Create)(Bambu_Tunnel* tunnel, char const* path); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_StartStream)(Bambu_Session* session); +BAMBU_EXPORT void BAMBU_FUNC(Bambu_SetLogger)(Bambu_Tunnel tunnel, Logger logger, void * context); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_GetStreamCount)(Bambu_Session* session); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_Open)(Bambu_Tunnel tunnel); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_GetStreamInfo)(Bambu_Session* session, int index, Bambu_StreamInfo* info); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_StartStream)(Bambu_Tunnel tunnel, bool video); -BAMBU_EXPORT unsigned long BAMBU_FUNC(Bambu_GetDuration)(Bambu_Session* session); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_GetStreamCount)(Bambu_Tunnel tunnel); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_Seek)(Bambu_Session* session, unsigned long time); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_GetStreamInfo)(Bambu_Tunnel tunnel, int index, Bambu_StreamInfo* info); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_ReadSample)(Bambu_Session* session, Bambu_Sample* sample); +BAMBU_EXPORT unsigned long BAMBU_FUNC(Bambu_GetDuration)(Bambu_Tunnel tunnel); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_SendMessage)(Bambu_Session* session, int ctrl, char const* data, int len); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_Seek)(Bambu_Tunnel tunnel, unsigned long time); -BAMBU_EXPORT int BAMBU_FUNC(Bambu_RecvMessage)(Bambu_Session* session, int* ctrl, char* data, int* len); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_ReadSample)(Bambu_Tunnel tunnel, Bambu_Sample* sample); -BAMBU_EXPORT void BAMBU_FUNC(Bambu_Close)(Bambu_Session* session); +BAMBU_EXPORT int BAMBU_FUNC(Bambu_SendMessage)(Bambu_Tunnel tunnel, int ctrl, char const* data, int len); + +BAMBU_EXPORT int BAMBU_FUNC(Bambu_RecvMessage)(Bambu_Tunnel tunnel, int* ctrl, char* data, int* len); + +BAMBU_EXPORT void BAMBU_FUNC(Bambu_Close)(Bambu_Tunnel tunnel); + +BAMBU_EXPORT void BAMBU_FUNC(Bambu_Destroy)(Bambu_Tunnel tunnel); BAMBU_EXPORT int BAMBU_FUNC(Bambu_Init)(); @@ -138,7 +136,7 @@ BAMBU_EXPORT void BAMBU_FUNC(Bambu_Deinit)(); BAMBU_EXPORT char const* BAMBU_FUNC(Bambu_GetLastErrorMsg)(); -BAMBU_EXPORT void BAMBU_FUNC(Bambu_FreeLogMsg)(Bambu_Message const* msg); +BAMBU_EXPORT void BAMBU_FUNC(Bambu_FreeLogMsg)(tchar const* msg); #ifdef BAMBU_DYNAMIC }; @@ -147,3 +145,5 @@ BAMBU_EXPORT void BAMBU_FUNC(Bambu_FreeLogMsg)(Bambu_Message const* msg); #ifdef __cplusplus } #endif // __cplusplus + +#endif // _BAMBU__TUNNEL_H_ diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index f14ce46c8d..70ad4f4894 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -2,6 +2,7 @@ #include "libslic3r/Utils.hpp" #include "../../Utils/NetworkAgent.hpp" +#include "../BitmapCache.hpp" #include #include @@ -10,9 +11,14 @@ #include +#ifndef NDEBUG +//#define PRINTER_FILE_SYSTEM_TEST +#endif + wxDEFINE_EVENT(EVT_STATUS_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_MODE_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_FILE_CHANGED, wxCommandEvent); +wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent); @@ -22,21 +28,26 @@ static wxBitmap default_thumbnail; struct StaticBambuLib : BambuLib { static StaticBambuLib & get(); - static int Fake_Bambu_Open(Bambu_Session * session, char const* uid) { return -2; } + static int Fake_Bambu_Create(Bambu_Tunnel*, char const*) { return -2; } }; PrinterFileSystem::PrinterFileSystem() : BambuLib(StaticBambuLib::get()) { if (!default_thumbnail.IsOk()) - default_thumbnail = wxImage(Slic3r::encode_path(Slic3r::var("live_stream_default.png").c_str())); + default_thumbnail = *Slic3r::GUI::BitmapCache().load_svg("printer_file", 0, 0); m_session.owner = this; - //auto time = wxDateTime::Now(); - //for (int i = 0; i < 240; ++i) { - // m_file_list.push_back({"", time.GetTicks(), 0, default_thumbnail, FF_DOWNLOAD, i - 130}); - // time.Add(wxDateSpan::Days(-1)); - //} - //BuildGroups(); +#ifdef PRINTER_FILE_SYSTEM_TEST + auto time = wxDateTime::Now(); + for (int i = 0; i < 100; ++i) { + auto name = wxString::Format(L"img-%03d.jpg", i + 1); + wxImage im(L"D:\\work\\pic\\" + name); + m_file_list.push_back({name.ToUTF8().data(), time.GetTicks(), 26937, im, i < 20 ? FF_DOWNLOAD : 0, i * 10 - 40}); + time.Add(wxDateSpan::Days(-1)); + } + m_file_list[0].thumbnail = default_thumbnail; + BuildGroups(); +#endif } PrinterFileSystem::~PrinterFileSystem() @@ -102,6 +113,7 @@ void PrinterFileSystem::ListAllFiles() iter1->thumbnail = iter2->thumbnail; iter1->flags = iter2->flags; iter1->progress = iter2->progress; + iter1->path = iter2->path; ++iter1; ++iter2; } else if (*iter1 < *iter2) { ++iter1; @@ -154,6 +166,7 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path) if ((file.flags & FF_DOWNLOAD) != 0 && file.progress >= 0) continue; file.flags |= FF_DOWNLOAD; file.progress = -1; + file.path = (boost::filesystem::path(path) / file.name).string(); ++n; } if (n == 0) return; @@ -165,9 +178,40 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path) return; file.flags |= FF_DOWNLOAD; file.progress = -1; + file.path = (boost::filesystem::path(path) / file.name).string(); } if ((m_task_flags & FF_DOWNLOAD) == 0) - DownloadNextFile(path); + DownloadNextFile(); +} + +void PrinterFileSystem::DownloadCheckFiles(std::string const &path) +{ + for (size_t i = 0; i < m_file_list.size(); ++i) { + auto &file = m_file_list[i]; + if ((file.flags & FF_DOWNLOAD) != 0 && file.progress >= 0) continue; + auto path2 = boost::filesystem::path(path) / file.name; + boost::system::error_code ec; + if (boost::filesystem::file_size(path2, ec) == file.size) { + file.flags |= FF_DOWNLOAD; + file.progress = 100; + file.path = path2.string(); + } + } +} + +bool PrinterFileSystem::DownloadCheckFile(size_t index) +{ + if (index >= m_file_list.size()) return false; + auto &file = m_file_list[index]; + if ((file.flags & FF_DOWNLOAD) == 0) return false; + if (!boost::filesystem::exists(file.path)) { + file.flags &= ~FF_DOWNLOAD; + file.progress = 0; + file.path.clear(); + SendChangedEvent(EVT_DOWNLOAD, index, file.path); + return false; + } + return true; } void PrinterFileSystem::DownloadCancel(size_t index) @@ -201,17 +245,30 @@ size_t PrinterFileSystem::GetIndexAtTime(boost::uint32_t time) void PrinterFileSystem::ToggleSelect(size_t index) { - if (index < m_file_list.size()) m_file_list[index].flags ^= FF_SELECT; + if (index < m_file_list.size()) { + m_file_list[index].flags ^= FF_SELECT; + if (m_file_list[index].flags & FF_SELECT) + ++m_select_count; + else + --m_select_count; + } + SendChangedEvent(EVT_SELECT_CHANGED, m_select_count); } void PrinterFileSystem::SelectAll(bool select) { - if (select) + if (select) { for (auto &f : m_file_list) f.flags |= FF_SELECT; - else + m_select_count = m_file_list.size(); + } else { for (auto &f : m_file_list) f.flags &= ~FF_SELECT; + m_select_count = 0; + } + SendChangedEvent(EVT_SELECT_CHANGED, m_select_count); } +size_t PrinterFileSystem::GetSelectCount() const { return m_select_count; } + void PrinterFileSystem::SetFocusRange(size_t start, size_t count) { m_lock_start = start; @@ -234,7 +291,7 @@ int PrinterFileSystem::RecvData(std::function const & int result = 0; while (true) { Bambu_Sample sample; - result = Bambu_ReadSample(&m_session, &sample); + result = Bambu_ReadSample(m_session.tunnel, &sample); if (result == Bambu_success) { result = callback(sample); if (result == 1) @@ -320,7 +377,7 @@ void PrinterFileSystem::DeleteFilesContinue() std::vector indexes; std::vector names; for (size_t i = 0; i < m_file_list.size(); ++i) - if ((m_file_list[i].flags & FF_SELECT) && !m_file_list[i].name.empty()) { + if ((m_file_list[i].flags & FF_DELETED) && !m_file_list[i].name.empty()) { indexes.push_back(i); names.push_back(m_file_list[i].name); if (names.size() >= 64) @@ -338,14 +395,14 @@ void PrinterFileSystem::DeleteFilesContinue() FILE_DEL, req, nullptr, [indexes, names, this](int, Void const &) { // TODO: - for (size_t i = indexes.size() - 1; i >= 0; --i) + for (size_t i = indexes.size() - 1; i != size_t(-1); --i) FileRemoved(indexes[i], names[i]); - SendChangedEvent(EVT_FILE_CHANGED); + SendChangedEvent(EVT_FILE_CHANGED, indexes.size()); DeleteFilesContinue(); }); } -void PrinterFileSystem::DownloadNextFile(std::string const &path) +void PrinterFileSystem::DownloadNextFile() { size_t index = size_t(-1); for (size_t i = 0; i < m_file_list.size(); ++i) { @@ -363,16 +420,16 @@ void PrinterFileSystem::DownloadNextFile(std::string const &path) SendChangedEvent(EVT_DOWNLOAD, index, m_file_list[index].name); struct Download { - int index; - std::string name; - std::string path; + size_t index; + std::string name; + std::string path; boost::filesystem::ofstream ofs; - boost::uuids::detail::md5 boost_md5; + boost::uuids::detail::md5 boost_md5; }; std::shared_ptr download(new Download); download->index = index; download->name = m_file_list[index].name; - download->path = path; + download->path = m_file_list[index].path; m_task_flags |= FF_DOWNLOAD; m_download_seq = SendRequest( FILE_DOWNLOAD, req, @@ -382,7 +439,7 @@ void PrinterFileSystem::DownloadNextFile(std::string const &path) prog.size = resp["offset"]; prog.total = resp["total"]; if (prog.size == 0) { - download->ofs.open(download->path + "/" + download->name, std::ios::binary); + download->ofs.open(download->path, std::ios::binary); if (!download->ofs) return FILE_OPEN_ERR; } // receive data @@ -412,9 +469,9 @@ void PrinterFileSystem::DownloadNextFile(std::string const &path) return result; }, [this, download](int result, Progress const &data) { - if (download->index >= 0) + if (download->index != size_t(-1)) download->index = FindFile(download->index, download->name); - if (download->index >= 0) { + if (download->index != size_t(-1)) { int progress = data.size * 100 / data.total; if (result > CONTINUE) progress = -2; @@ -423,10 +480,10 @@ void PrinterFileSystem::DownloadNextFile(std::string const &path) file.flags &= ~FF_DOWNLOAD; else if (file.progress != progress) { file.progress = progress; - SendChangedEvent(EVT_DOWNLOAD, download->index, m_file_list[download->index].name, data.size); + SendChangedEvent(EVT_DOWNLOAD, download->index, file.path, data.size); } } - if (result != CONTINUE) DownloadNextFile(download->path); + if (result != CONTINUE) DownloadNextFile(); }); } @@ -556,15 +613,15 @@ void PrinterFileSystem::SendChangedEvent(wxEventType type, size_t index, std::st wxPostEvent(this, event); } -void PrinterFileSystem::DumpLog(Bambu_Session *session, int level, Bambu_Message const *msg) +void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg) { BOOST_LOG_TRIVIAL(info) << "PrinterFileSystem: " << msg; - StaticBambuLib::get().Bambu_FreeLogMsg(msg); + static_cast(thiz)->Bambu_FreeLogMsg(msg); } boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback) { - if (m_session.gSID < 0) { + if (m_session.tunnel == nullptr) { boost::unique_lock l(m_mutex); m_cond.notify_all(); return 0; @@ -640,7 +697,7 @@ void PrinterFileSystem::RecvMessageThread() if (!m_messages.empty()) { auto & msg = m_messages.front(); l.unlock(); - int n = Bambu_SendMessage(&m_session, CTRL_TYPE, msg.c_str(), msg.length()); + int n = Bambu_SendMessage(m_session.tunnel, CTRL_TYPE, msg.c_str(), msg.length()); l.lock(); if (n == 0) m_messages.pop_front(); @@ -650,7 +707,7 @@ void PrinterFileSystem::RecvMessageThread() } } l.unlock(); - int n = Bambu_ReadSample(&m_session, &sample); + int n = Bambu_ReadSample(m_session.tunnel, &sample); l.lock(); if (n == 0) { HandleResponse(l, sample); @@ -726,9 +783,12 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock &l, Bamb void PrinterFileSystem::Reconnect(boost::unique_lock &l, int result) { - if (m_session.gSID >= 0) { + if (m_session.tunnel) { + auto tunnel = m_session.tunnel; + m_session.tunnel = nullptr; l.unlock(); - Bambu_Close(&m_session); + Bambu_Close(tunnel); + Bambu_Destroy(tunnel); l.lock(); } if (m_session.owner == nullptr) @@ -761,11 +821,16 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul l.unlock(); m_status = Status::Connecting; SendChangedEvent(EVT_STATUS_CHANGED, m_status); - m_session.logger = &PrinterFileSystem::DumpLog; - int ret = Bambu_Open(&m_session, url.c_str() + 9); // skip bambu:/// sync + Bambu_Tunnel tunnel = nullptr; + int ret = Bambu_Create(&tunnel, url.c_str()); + if (ret == 0) { + Bambu_SetLogger(tunnel, DumpLog, this); + ret = Bambu_Open(tunnel); + } if (ret == 0) - ret = Bambu_StartStream(&m_session); + ret = Bambu_StartStream(tunnel, false); l.lock(); + m_session.tunnel = tunnel; if (ret == 0) break; m_last_error = ret; @@ -776,7 +841,11 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul } m_status = Status::ListSyncing; SendChangedEvent(EVT_STATUS_CHANGED, m_status); +#ifdef PRINTER_FILE_SYSTEM_TEST + PostCallback([this] { SendChangedEvent(EVT_FILE_CHANGED); }); +#else PostCallback([this] { ListAllFiles(); }); +#endif } @@ -818,6 +887,9 @@ StaticBambuLib &StaticBambuLib::get() { static StaticBambuLib lib; // first load the library + + if (lib.Bambu_Open) + return lib; if (!module) { module = Slic3r::NetworkAgent::get_bambu_source_entry(); @@ -827,14 +899,17 @@ StaticBambuLib &StaticBambuLib::get() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", can not Load Library"; } + GET_FUNC(Bambu_Create); GET_FUNC(Bambu_Open); GET_FUNC(Bambu_StartStream); GET_FUNC(Bambu_SendMessage); GET_FUNC(Bambu_ReadSample); GET_FUNC(Bambu_Close); + GET_FUNC(Bambu_Destroy); + GET_FUNC(Bambu_SetLogger); GET_FUNC(Bambu_FreeLogMsg); if (!lib.Bambu_Open) - lib.Bambu_Open = Fake_Bambu_Open; + lib.Bambu_Create = Fake_Bambu_Create; return lib; } diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index eb84525dec..7757216217 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -18,6 +18,7 @@ using nlohmann::json; wxDECLARE_EVENT(EVT_STATUS_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_MODE_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_FILE_CHANGED, wxCommandEvent); +wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent); @@ -95,6 +96,7 @@ public: wxBitmap thumbnail; int flags = 0; int progress = -1; // -1: waiting + std::string path; bool IsSelect() const { return flags & FF_SELECT; } bool IsDownload() const { return flags & FF_DOWNLOAD; } @@ -124,6 +126,10 @@ public: void DownloadFiles(size_t index, std::string const &path); + void DownloadCheckFiles(std::string const &path); + + bool DownloadCheckFile(size_t index); + void DownloadCancel(size_t index); size_t GetCount() const; @@ -134,6 +140,8 @@ public: void SelectAll(bool select); + size_t GetSelectCount() const; + void SetFocusRange(size_t start, size_t count); File const &GetFile(size_t index); @@ -164,7 +172,7 @@ private: void DeleteFilesContinue(); - void DownloadNextFile(std::string const &path); + void DownloadNextFile(); void UpdateFocusThumbnail(); @@ -174,7 +182,7 @@ private: void SendChangedEvent(wxEventType type, size_t index = (size_t)-1, std::string const &str = {}, long extra = 0); - static void DumpLog(Bambu_Session *session, int level, Bambu_Message const *msg); + static void DumpLog(void* context, int level, tchar const *msg); private: template using Translator = std::function; @@ -260,13 +268,15 @@ protected: std::vector m_group_month; private: + size_t m_select_count = 0; size_t m_lock_start = 0; size_t m_lock_end = 0; int m_task_flags = 0; private: - struct Session : Bambu_Session + struct Session { + Bambu_Tunnel tunnel = nullptr; PrinterFileSystem * owner; }; Session m_session; diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index 6b576d9bd2..28ecd8fd21 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -57,12 +57,12 @@ ReleaseNoteDialog::ReleaseNoteDialog(Plater *plater /*= nullptr*/) m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); - m_scrollwindw_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL); - m_scrollwindw_release_note->SetScrollRate(5, 5); - m_scrollwindw_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); - m_scrollwindw_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); + m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL); + m_vebview_release_note->SetScrollRate(5, 5); + m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); + m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); - m_sizer_right->Add(m_scrollwindw_release_note, 0, wxEXPAND | wxRIGHT, FromDIP(20)); + m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT, FromDIP(20)); m_sizer_body->Add(m_sizer_right, 1, wxBOTTOM | wxEXPAND, FromDIP(30)); m_sizer_main->Add(m_sizer_body, 0, wxEXPAND, 0); @@ -84,11 +84,11 @@ void ReleaseNoteDialog::update_release_note(wxString release_note, std::string v { m_text_up_info->SetLabel(wxString::Format(_L("version %s update information :"), version)); wxBoxSizer * sizer_text_release_note = new wxBoxSizer(wxVERTICAL); - auto m_staticText_release_note = new wxStaticText(m_scrollwindw_release_note, wxID_ANY, release_note, wxDefaultPosition, wxDefaultSize, 0); + auto m_staticText_release_note = new wxStaticText(m_vebview_release_note, wxID_ANY, release_note, wxDefaultPosition, wxDefaultSize, 0); m_staticText_release_note->Wrap(FromDIP(530)); sizer_text_release_note->Add(m_staticText_release_note, 0, wxALL, 5); - m_scrollwindw_release_note->SetSizer(sizer_text_release_note); - m_scrollwindw_release_note->Layout(); + m_vebview_release_note->SetSizer(sizer_text_release_note); + m_vebview_release_note->Layout(); } void UpdateVersionDialog::alter_choice(wxCommandEvent& event) @@ -130,12 +130,22 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); + m_simplebook_release_note = new wxSimplebook(this); + m_simplebook_release_note->SetSize(wxSize(FromDIP(560), FromDIP(430))); + m_simplebook_release_note->SetMinSize(wxSize(FromDIP(560), FromDIP(430))); + m_simplebook_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); + m_simplebook_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); + + m_scrollwindows_release_note = new wxScrolledWindow(m_simplebook_release_note, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL); + m_scrollwindows_release_note->SetScrollRate(5, 5); + m_scrollwindows_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); + //webview - m_scrollwindw_release_note = CreateTipView(this); - m_scrollwindw_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); - m_scrollwindw_release_note->SetSize(wxSize(FromDIP(560), FromDIP(430))); - m_scrollwindw_release_note->SetMinSize(wxSize(FromDIP(560), FromDIP(430))); - m_scrollwindw_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); + m_vebview_release_note = CreateTipView(m_simplebook_release_note); + m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); + m_vebview_release_note->SetSize(wxSize(FromDIP(560), FromDIP(430))); + m_vebview_release_note->SetMinSize(wxSize(FromDIP(560), FromDIP(430))); + m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); fs::path ph(data_dir()); ph /= "resources/tooltip/common/releasenote.html"; @@ -146,7 +156,10 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) auto url = ph.string(); std::replace(url.begin(), url.end(), '\\', '/'); url = "file:///" + url; - m_scrollwindw_release_note->LoadURL(from_u8(url)); + m_vebview_release_note->LoadURL(from_u8(url)); + + m_simplebook_release_note->AddPage(m_scrollwindows_release_note, wxEmptyString, false); + m_simplebook_release_note->AddPage(m_vebview_release_note, wxEmptyString, false); m_remind_choice = new wxCheckBox( this, wxID_ANY, _L("Don't remind me of this version again"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -193,7 +206,7 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) sizer_button->Add(m_button_cancel, 0, wxALL, FromDIP(5)); - m_sizer_right->Add(m_scrollwindw_release_note, 0, wxEXPAND | wxRIGHT, FromDIP(20)); + m_sizer_right->Add(m_simplebook_release_note, 0, wxEXPAND | wxRIGHT, FromDIP(20)); m_sizer_right->Add(sizer_button, 0, wxEXPAND | wxRIGHT, FromDIP(20)); m_sizer_body->Add(m_sizer_right, 1, wxBOTTOM | wxEXPAND, FromDIP(8)); @@ -263,7 +276,7 @@ bool UpdateVersionDialog::ShowReleaseNote(std::string content) void UpdateVersionDialog::RunScript(std::string script) { - WebView::RunScript(m_scrollwindw_release_note, script); + WebView::RunScript(m_vebview_release_note, script); script.clear(); } @@ -272,32 +285,177 @@ void UpdateVersionDialog::on_dpi_changed(const wxRect &suggested_rect) { m_button_cancel->Rescale(); } -void UpdateVersionDialog::update_version_info(std::string url) -{ - /*m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version)); - wxBoxSizer *sizer_text_release_note = new wxBoxSizer(wxVERTICAL); - auto m_staticText_release_note = new wxStaticText(m_scrollwindw_release_note, wxID_ANY, release_note, wxDefaultPosition, wxDefaultSize, 0); - m_staticText_release_note->Wrap(FromDIP(530)); - sizer_text_release_note->Add(m_staticText_release_note, 0, wxALL, 5); - m_scrollwindw_release_note->SetSizer(sizer_text_release_note); - m_scrollwindw_release_note->Layout();*/ +std::vector UpdateVersionDialog::splitWithStl(std::string str,std::string pattern) +{ + std::string::size_type pos; + std::vector result; + str += pattern; + int size = str.size(); + for (int i = 0; i < size; i++) + { + pos = str.find(pattern, i); + if (pos < size) + { + std::string s = str.substr(i, pos - i); + result.push_back(s); + i = pos + pattern.size() - 1; + } + } + return result; +} - if (url.empty()) { - fs::path ph(data_dir()); - ph /= "resources/tooltip/common/releasenote.html"; - if (!fs::exists(ph)) { - ph = resources_dir(); - ph /= "tooltip/releasenote.html"; - } - auto url = ph.string(); - std::replace(url.begin(), url.end(), '\\', '/'); - url = "file:///" + url; - m_scrollwindw_release_note->LoadURL(from_u8(url)); +void UpdateVersionDialog::update_version_info(wxString release_note, wxString version) +{ + //bbs check whether the web display is used + bool use_web_link = false; + std::string url_line = ""; + auto split_array = splitWithStl(release_note.ToStdString(), "###"); + + for (auto i = 0; i < split_array.size(); i++) { + std::string url = split_array[i]; + if (std::strstr (url.c_str(), "http://") != NULL || std::strstr (url.c_str(), "https://") != NULL) { + use_web_link = true; + url_line = url; + break; + } + } + + if (use_web_link) { + m_simplebook_release_note->SetSelection(1); + m_vebview_release_note->LoadURL(from_u8(url_line)); } else { - m_scrollwindw_release_note->LoadURL(from_u8(url)); + m_simplebook_release_note->SetSelection(0); + m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version)); + wxBoxSizer* sizer_text_release_note = new wxBoxSizer(wxVERTICAL); + auto m_staticText_release_note = new wxStaticText(m_scrollwindows_release_note, wxID_ANY, release_note, wxDefaultPosition, wxDefaultSize, 0); + m_staticText_release_note->Wrap(FromDIP(530)); + sizer_text_release_note->Add(m_staticText_release_note, 0, wxALL, 5); + m_scrollwindows_release_note->SetSizer(sizer_text_release_note); + m_scrollwindows_release_note->Layout(); + } +} + +SecondaryCheckDialog::SecondaryCheckDialog(wxWindow* parent) + :DPIDialog(parent, wxID_ANY, _L("Confirm"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + SetBackgroundColour(*wxWHITE); + wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL); + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(480), 1)); + m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); + m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(5)); + + wxBoxSizer* m_sizer_right = new wxBoxSizer(wxVERTICAL); + + m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); + + m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); + m_vebview_release_note->SetScrollRate(0, 5); + m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); + m_vebview_release_note->SetSize(wxSize(FromDIP(280), FromDIP(280))); + m_vebview_release_note->SetMinSize(wxSize(FromDIP(280), FromDIP(280))); + m_vebview_release_note->SetMaxSize(wxSize(FromDIP(280), FromDIP(280))); + + + auto sizer_button = new wxBoxSizer(wxHORIZONTAL); + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_ok = new Button(this, _L("OK")); + m_button_ok->SetBackgroundColor(btn_bg_green); + m_button_ok->SetBorderColor(*wxWHITE); + m_button_ok->SetTextColor(*wxWHITE); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + + m_button_ok->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) { + EndModal(wxID_YES); + }); + + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(*wxWHITE); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + + m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) { + EndModal(wxID_NO); + }); + + sizer_button->AddStretchSpacer(); + sizer_button->Add(m_button_ok, 0, wxALL, FromDIP(5)); + sizer_button->Add(m_button_cancel, 0, wxALL, FromDIP(5)); + + + m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(20)); + m_sizer_right->Add(sizer_button, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(20)); + + + SetSizer(m_sizer_right); + Layout(); + m_sizer_main->Fit(this); + + CenterOnParent(); +} + +void SecondaryCheckDialog::update_text(wxString text) +{ + wxBoxSizer* sizer_text_release_note = new wxBoxSizer(wxVERTICAL); + auto m_staticText_release_note = new wxStaticText(m_vebview_release_note, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_staticText_release_note->SetSize(wxSize(FromDIP(260), -1)); + m_staticText_release_note->SetMaxSize(wxSize(FromDIP(260), -1)); + m_staticText_release_note->SetMinSize(wxSize(FromDIP(260), -1)); + + text = format_text(m_staticText_release_note, text, FromDIP(240)); + + m_staticText_release_note->SetLabelText(text); + m_staticText_release_note->Wrap(FromDIP(240)); + sizer_text_release_note->Add(m_staticText_release_note, 0, wxALIGN_CENTER, 5); + m_vebview_release_note->SetSizer(sizer_text_release_note); + m_vebview_release_note->Layout(); + //Fit(); +} + +wxString SecondaryCheckDialog::format_text(wxStaticText* st, wxString str, int warp) +{ + if (wxGetApp().app_config->get("language") != "zh_CN") { return str; } + + wxString out_txt = str; + wxString count_txt = ""; + int new_line_pos = 0; + + for (int i = 0; i < str.length(); i++) { + auto text_size = st->GetTextExtent(count_txt); + if (text_size.x < warp) { + count_txt += str[i]; + } + else { + out_txt.insert(i - 1, '\n'); + count_txt = ""; + } } - + return out_txt; +} + +SecondaryCheckDialog::~SecondaryCheckDialog() +{ + +} + +void SecondaryCheckDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + } }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/ReleaseNote.hpp b/src/slic3r/GUI/ReleaseNote.hpp index e78240094c..36466c395b 100644 --- a/src/slic3r/GUI/ReleaseNote.hpp +++ b/src/slic3r/GUI/ReleaseNote.hpp @@ -47,7 +47,7 @@ public: void update_release_note(wxString release_note, std::string version); wxStaticText * m_text_up_info{nullptr}; - wxScrolledWindow *m_scrollwindw_release_note {nullptr}; + wxScrolledWindow *m_vebview_release_note {nullptr}; }; class UpdateVersionDialog : public DPIDialog @@ -63,11 +63,14 @@ public: bool ShowReleaseNote(std::string content); void RunScript(std::string script); void on_dpi_changed(const wxRect& suggested_rect) override; - void update_version_info(std::string url); + void update_version_info(wxString release_note, wxString version); void alter_choice(wxCommandEvent& event); + std::vector splitWithStl(std::string str, std::string pattern); wxStaticText * m_text_up_info{nullptr}; - wxWebView* m_scrollwindw_release_note{nullptr}; + wxWebView* m_vebview_release_note{nullptr}; + wxSimplebook* m_simplebook_release_note{nullptr}; + wxScrolledWindow* m_scrollwindows_release_note{nullptr}; wxBoxSizer * sizer_text_release_note{nullptr}; wxStaticText * m_staticText_release_note{nullptr}; wxCheckBox* m_remind_choice; @@ -75,6 +78,20 @@ public: Button* m_button_cancel; }; +class SecondaryCheckDialog : public DPIDialog +{ +public: + SecondaryCheckDialog(wxWindow* parent); + void update_text(wxString text); + wxString format_text(wxStaticText* st, wxString str, int warp); + ~SecondaryCheckDialog(); + void on_dpi_changed(const wxRect& suggested_rect) override; + + wxScrolledWindow *m_vebview_release_note {nullptr}; + Button* m_button_ok; + Button* m_button_cancel; +}; + }} // namespace Slic3r::GUI #endif diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index ae71b707ae..e66e0536f6 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -388,6 +388,19 @@ std::string SavePresetDialog::get_name(Preset::Type type) return ""; } +void SavePresetDialog::input_name_from_other(std::string new_preset_name) { + //only work for one-item + Item* curr_item = m_items[0]; + curr_item->m_input_ctrl->SetValue(new_preset_name); +} + +void SavePresetDialog::confirm_from_other() { + for (Item *item : m_items) { + item->accept(); + if (item->type() == Preset::TYPE_PRINTER) update_physical_printers(item->preset_name()); + } +} + // BBS: add project relate bool SavePresetDialog::get_save_to_project_selection(Preset::Type type) { diff --git a/src/slic3r/GUI/SavePresetDialog.hpp b/src/slic3r/GUI/SavePresetDialog.hpp index 1b2089f898..0644a3a190 100644 --- a/src/slic3r/GUI/SavePresetDialog.hpp +++ b/src/slic3r/GUI/SavePresetDialog.hpp @@ -97,6 +97,8 @@ public: std::string get_name(); std::string get_name(Preset::Type type); + void input_name_from_other(std::string new_preset_name); + void confirm_from_other(); bool enable_ok_btn() const; void add_info_for_edit_ph_printer(wxBoxSizer *sizer); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 9f0277d9bf..85f662cb6e 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -365,8 +365,8 @@ void OptionsSearcher::show_dialog(Preset::Type type, wxWindow *parent, wxTextCtr { if (parent == nullptr || input == nullptr) return; auto search_dialog = new SearchDialog(this, type, parent, input, ssearch_btn); - wxPoint pos = parent->ClientToScreen(wxPoint(0, 0)); - pos.y += parent->GetRect().height; + wxPoint pos = input->GetParent()->ClientToScreen(wxPoint(0, 0)); + pos.y += input->GetParent()->GetRect().height; search_dialog->SetPosition(pos); search_dialog->Popup(); } @@ -644,7 +644,7 @@ void SearchDialog::Dismiss() void SearchDialog::Die() { - Destroy(); + wxPopupTransientWindow::Dismiss(); wxCommandEvent event(wxCUSTOMEVT_EXIT_SEARCH); wxPostEvent(search_line, event); } diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 6644474c03..40cb0bc04c 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -21,6 +21,8 @@ #include "Plater.hpp" #include "BitmapCache.hpp" #include "BindDialog.hpp" +#include "ConfirmHintDialog.hpp" +#include "ReleaseNote.hpp" namespace Slic3r { namespace GUI { @@ -38,6 +40,8 @@ wxDEFINE_EVENT(EVT_EDIT_PRINT_NAME, wxCommandEvent); #define LIST_REFRESH_INTERVAL 200 #define MACHINE_LIST_REFRESH_INTERVAL 2000 +#define WRAP_GAP FromDIP(10) + static wxString task_canceled_text = _L("Task canceled"); @@ -88,6 +92,8 @@ std::string get_print_status_info(PrintDialogStatus status) return "PrintStatusLanModeNoSdcard"; case PrintStatusNoSdcard: return "PrintStatusNoSdcard"; + case PrintStatusTimelapseNoSdcard: + return "PrintStatusTimelapseNoSdcard"; } return "unknown"; } @@ -395,13 +401,13 @@ SelectMachinePopup::SelectMachinePopup(wxWindow *parent) m_sizxer_scrolledWindow->Fit(m_scrolledWindow); #if !BBL_RELEASE_TO_PUBLIC && defined(__WINDOWS__) - m_sizer_search_bar = new wxBoxSizer(wxVERTICAL); - m_search_bar = new wxSearchCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_sizer_search_bar = new wxBoxSizer(wxVERTICAL); + m_search_bar = new wxSearchCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); m_search_bar->ShowSearchButton( true ); m_search_bar->ShowCancelButton( false ); m_sizer_search_bar->Add( m_search_bar, 1, wxALL| wxEXPAND, 1 ); - m_sizer_main->Add(m_sizer_search_bar, 0, wxALL | wxEXPAND, FromDIP(2)); - m_search_bar->Bind( wxEVT_COMMAND_TEXT_UPDATED, &SelectMachinePopup::update_machine_list, this ); + m_sizer_main->Add(m_sizer_search_bar, 0, wxALL | wxEXPAND, FromDIP(2)); + m_search_bar->Bind( wxEVT_COMMAND_TEXT_UPDATED, &SelectMachinePopup::update_machine_list, this ); #endif auto own_title = create_title_panel(_L("My Device")); m_sizer_my_devices = new wxBoxSizer(wxVERTICAL); @@ -550,6 +556,7 @@ void SelectMachinePopup::update_other_devices() this->Freeze(); m_scrolledWindow->Freeze(); int i = 0; + for (auto &elem : m_free_machine_list) { MachineObject * mobj = elem.second; /* do not show printer bind state is empty */ @@ -568,9 +575,9 @@ void SelectMachinePopup::update_other_devices() op = m_other_list_machine_panel[i]->mPanel; op->Show(); #if !BBL_RELEASE_TO_PUBLIC && defined(__WINDOWS__) - if (!search_for_printer(mobj)) { - op->Hide(); - } + if (!search_for_printer(mobj)) { + op->Hide(); + } #endif } else { op = new MachineObjectPanel(m_scrolledWindow, wxID_ANY); @@ -626,13 +633,37 @@ void SelectMachinePopup::update_other_devices() m_other_list_machine_panel[j]->mPanel->update_machine_info(nullptr); m_other_list_machine_panel[j]->mPanel->Hide(); } - m_sizer_other_devices->Layout(); + + if (m_placeholder_panel != nullptr) { + m_scrolledWindow->RemoveChild(m_placeholder_panel); + m_placeholder_panel->Destroy(); + m_placeholder_panel = nullptr; + } + + m_placeholder_panel = new wxWindow(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxSize(-1,FromDIP(26))); + wxBoxSizer* placeholder_sizer = new wxBoxSizer(wxVERTICAL); + + auto m_hyperlink = new wxHyperlinkCtrl(m_placeholder_panel, wxID_ANY, _L("Can't find my devices?"), wxT("https://wiki.bambulab.com/en/software/bambu-studio/failed-to-connect-printer"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + placeholder_sizer->Add(m_hyperlink, 0, wxALIGN_CENTER | wxALL, 5); + + + m_placeholder_panel->SetSizer(placeholder_sizer); + m_placeholder_panel->Layout(); + placeholder_sizer->Fit(m_placeholder_panel); + + m_placeholder_panel->SetBackgroundColour(*wxWHITE); + m_sizer_other_devices->Add(m_placeholder_panel, 0, wxEXPAND, 0); + + //m_sizer_other_devices->Layout(); + if(m_other_devices_count != i) { + m_scrolledWindow->Fit(); + } m_scrolledWindow->Layout(); - m_scrolledWindow->Fit(); - m_scrolledWindow->Thaw(); - Layout(); - Fit(); - this->Thaw(); + m_scrolledWindow->Thaw(); + Layout(); + Fit(); + this->Thaw(); + m_other_devices_count = i; BOOST_LOG_TRIVIAL(trace) << "SelectMachinePopup update_other_devices end"; } @@ -653,6 +684,7 @@ void SelectMachinePopup::update_user_devices() this->Freeze(); m_scrolledWindow->Freeze(); int i = 0; + for (auto& elem : m_bind_machine_list) { MachineObject* mobj = elem.second; MachineObjectPanel* op = nullptr; @@ -660,9 +692,9 @@ void SelectMachinePopup::update_user_devices() op = m_user_list_machine_panel[i]->mPanel; op->Show(); #if !BBL_RELEASE_TO_PUBLIC && defined(__WINDOWS__) - if (!search_for_printer(mobj)) { - op->Hide(); - } + if (!search_for_printer(mobj)) { + op->Hide(); + } #endif } else { op = new MachineObjectPanel(m_scrolledWindow, wxID_ANY); @@ -752,27 +784,31 @@ void SelectMachinePopup::update_user_devices() m_user_list_machine_panel[j]->mPanel->Hide(); } //m_sizer_my_devices->Layout(); + + if (m_my_devices_count != i) { + m_scrolledWindow->Fit(); + } m_scrolledWindow->Layout(); - m_scrolledWindow->Fit(); m_scrolledWindow->Thaw(); - Layout(); - Fit(); - this->Thaw(); + Layout(); + Fit(); + this->Thaw(); + m_my_devices_count = i; } bool SelectMachinePopup::search_for_printer(MachineObject* obj) { - std::string search_text = std::string((m_search_bar->GetValue()).mb_str()); - if (search_text.empty()) { - return true; - } - auto name = obj->dev_name; - auto ip = obj->dev_ip; - auto name_it = name.find(search_text); - auto ip_it = ip.find(search_text); - if ((name_it != std::string::npos)||(ip_it != std::string::npos)) { - return true; - } + std::string search_text = std::string((m_search_bar->GetValue()).mb_str()); + if (search_text.empty()) { + return true; + } + auto name = obj->dev_name; + auto ip = obj->dev_ip; + auto name_it = name.find(search_text); + auto ip_it = ip.find(search_text); + if ((name_it != std::string::npos)||(ip_it != std::string::npos)) { + return true; + } return false; } @@ -954,7 +990,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) wxBoxSizer *m_sizer_printer = new wxBoxSizer(wxHORIZONTAL); - m_stext_printer_title = new wxStaticText(this, wxID_ANY, L("Printer"), wxDefaultPosition, wxSize(-1, -1), 0); + m_stext_printer_title = new wxStaticText(this, wxID_ANY, _L("Printer"), wxDefaultPosition, wxSize(-1, -1), 0); m_stext_printer_title->SetFont(::Label::Head_14); m_stext_printer_title->Wrap(-1); m_stext_printer_title->SetForegroundColour(m_colour_bold_color); @@ -984,17 +1020,21 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_statictext_printer_msg->SetFont(::Label::Body_13); m_statictext_printer_msg->Hide(); - m_sizer_select = new wxGridSizer(0, 2, 0, 0); + //m_sizer_select = new wxGridSizer(0, 2, 0, 0); + m_sizer_select = new wxWrapSizer(); select_bed = create_item_checkbox(_L("Bed Leveling"), this, _L("Bed Leveling"), "bed_leveling"); select_flow = create_item_checkbox(_L("Flow Calibration"), this, _L("Flow Calibration"), "flow_cali"); + select_timelapse = create_item_checkbox(_L("Timelapse"), this, _L("Timelapse"), "timelapse"); select_use_ams = create_ams_checkbox(_L("Enable AMS"), this, _L("Enable AMS")); - m_sizer_select->Add(select_bed); - m_sizer_select->Add(select_flow); - m_sizer_select->Add(select_use_ams); + m_sizer_select->Add(select_bed, 0, wxLEFT | wxRIGHT, WRAP_GAP); + m_sizer_select->Add(select_flow, 0, wxLEFT | wxRIGHT, WRAP_GAP); + m_sizer_select->Add(select_timelapse, 0, wxLEFT | wxRIGHT, WRAP_GAP); + m_sizer_select->Add(select_use_ams, 0, wxLEFT | wxRIGHT, WRAP_GAP); select_bed->Show(true); select_flow->Show(true); + select_timelapse->Show(false); select_use_ams->Show(true); // line schedule @@ -1007,11 +1047,23 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) // perpare mode m_panel_prepare = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_panel_prepare->SetBackgroundColour(m_colour_def_color); - // m_panel_prepare->SetBackgroundColour(wxColour(135,206,250)); + //m_panel_prepare->SetBackgroundColour(wxColour(135,206,250)); wxBoxSizer *m_sizer_prepare = new wxBoxSizer(wxVERTICAL); wxBoxSizer *m_sizer_pcont = new wxBoxSizer(wxHORIZONTAL); - m_sizer_prepare->Add(0, 0, 1, wxTOP, FromDIP(22)); + m_sizer_prepare->Add(0, 0, 1, wxTOP, FromDIP(12)); + + auto hyperlink_sizer = new wxBoxSizer( wxHORIZONTAL ); + auto m_hyperlink = new wxHyperlinkCtrl(m_panel_prepare, wxID_ANY, _L("Can't find my devices?"), wxT("https://wiki.bambulab.com/en/software/bambu-studio/failed-to-connect-printer"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE); + + //auto linkimg = new wxStaticBitmap(m_panel_prepare, wxID_ANY, create_scaled_bitmap("link_wiki_img", this, 18), wxDefaultPosition, wxSize(FromDIP(18), FromDIP(18)), 0); + + hyperlink_sizer->Add(m_hyperlink, 0, wxALIGN_CENTER | wxALL, 5); + //hyperlink_sizer->Add(linkimg, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_sizer_prepare->Add(hyperlink_sizer, 0, wxALIGN_CENTER | wxALL, 5); + + m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); m_button_ensure = new Button(m_panel_prepare, _L("Send")); m_button_ensure->SetBackgroundColor(btn_bg_enable); @@ -1021,7 +1073,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); m_button_ensure->SetCornerRadius(FromDIP(12)); - m_button_ensure->Bind(wxEVT_BUTTON, &SelectMachineDialog::on_ok, this); + m_button_ensure->Bind(wxEVT_BUTTON, &SelectMachineDialog::on_ok_btn, this); m_sizer_pcont->Add(m_button_ensure, 0, wxEXPAND | wxBOTTOM, FromDIP(10)); m_sizer_prepare->Add(m_sizer_pcont, 0, wxEXPAND, 0); m_panel_prepare->SetSizer(m_sizer_prepare); @@ -1131,23 +1183,23 @@ wxWindow *SelectMachineDialog::create_ams_checkbox(wxString title, wxWindow *par auto img_ams_tip = new wxStaticBitmap(checkbox, wxID_ANY, create_scaled_bitmap("enable_ams", this, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)), 0); sizer_checkbox->Add(img_ams_tip, 0, wxALIGN_CENTER | wxLEFT, FromDIP(5)); - img_ams_tip->Bind(wxEVT_ENTER_WINDOW, [this, img_ams_tip](auto &e) { - wxPoint pos = img_ams_tip->ClientToScreen(wxPoint(0, 0)); - pos.y += img_ams_tip->GetRect().height; - m_mapping_tip_popup.Position(pos, wxSize(0,0)); + img_ams_tip->Bind(wxEVT_ENTER_WINDOW, [this, img_ams_tip](auto& e) { + wxPoint img_pos = img_ams_tip->ClientToScreen(wxPoint(0, 0)); + wxPoint popup_pos(img_pos.x, img_pos.y + img_ams_tip->GetRect().height); + m_mapping_tip_popup.Position(popup_pos, wxSize(0, 0)); m_mapping_tip_popup.Popup(); - }); - img_ams_tip->Bind(wxEVT_LEAVE_WINDOW, [this, img_ams_tip](wxMouseEvent &e) { - auto region = m_mapping_tip_popup.GetClientRect(); - if(e.GetPosition().x > region.GetLeftTop().x && e.GetPosition().y > region.GetLeftTop().y && e.GetPosition().x < region.GetRightBottom().x && e.GetPosition().x < region.GetRightBottom().y) - ; - else + if (m_mapping_tip_popup.ClientToScreen(wxPoint(0, 0)).y < img_pos.y) { m_mapping_tip_popup.Dismiss(); + popup_pos = wxPoint(img_pos.x, img_pos.y - m_mapping_tip_popup.GetRect().height); + m_mapping_tip_popup.Position(popup_pos, wxSize(0, 0)); + m_mapping_tip_popup.Popup(); + } }); - m_mapping_tip_popup.Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) { - m_mapping_tip_popup.Dismiss(); - }); + + img_ams_tip->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { + m_mapping_tip_popup.Dismiss(); + }); checkbox->SetSizer(sizer_checkbox); checkbox->Layout(); @@ -1177,10 +1229,12 @@ wxWindow *SelectMachineDialog::create_item_checkbox(wxString title, wxWindow *pa sizer_checkbox->Add(sizer_check, 0, wxEXPAND, FromDIP(5)); sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(11)); - auto text = new wxStaticText(checkbox, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0); + auto text = new wxStaticText(checkbox, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); text->SetFont(::Label::Body_13); text->SetForegroundColour(wxColour(107, 107, 107)); text->Wrap(-1); + text->SetMinSize(wxSize(FromDIP(120), -1)); + text->SetMaxSize(wxSize(FromDIP(120), -1)); sizer_checkbox->Add(text, 0, wxBOTTOM | wxEXPAND | wxTOP, FromDIP(5)); checkbox->SetSizer(sizer_checkbox); @@ -1209,6 +1263,13 @@ void SelectMachineDialog::update_select_layout(MachineObject *obj) select_bed->Hide(); } + if (obj && obj->is_function_supported(PrinterFunction::FUNC_TIMELAPSE) + && obj->is_support_print_with_timelapse()) { + //always hide timelapse + select_timelapse->Hide(); + } else { + select_timelapse->Hide(); + } Fit(); } @@ -1319,7 +1380,8 @@ bool SelectMachineDialog::do_ams_mapping(MachineObject *obj_) if (result == 0) { print_ams_mapping_result(m_ams_mapping_result); std::string ams_array; - get_ams_mapping_result(ams_array); + std::string mapping_info; + get_ams_mapping_result(ams_array, mapping_info); if (ams_array.empty()) { reset_ams_material(); BOOST_LOG_TRIVIAL(info) << "ams_mapping_array=[]"; @@ -1345,7 +1407,7 @@ bool SelectMachineDialog::do_ams_mapping(MachineObject *obj_) return true; } -bool SelectMachineDialog::get_ams_mapping_result(std::string &mapping_array_str) +bool SelectMachineDialog::get_ams_mapping_result(std::string &mapping_array_str, std::string &ams_mapping_info) { if (m_ams_mapping_result.empty()) return false; @@ -1363,16 +1425,39 @@ bool SelectMachineDialog::get_ams_mapping_result(std::string &mapping_array_str) return false; } else { json j = json::array(); + json mapping_info_json = json::array(); + for (int i = 0; i < wxGetApp().preset_bundle->filament_presets.size(); i++) { int tray_id = -1; + json mapping_item; + mapping_item["ams"] = tray_id; + mapping_item["targetColor"] = ""; + mapping_item["filamentId"] = ""; + mapping_item["filamentType"] = ""; + for (int k = 0; k < m_ams_mapping_result.size(); k++) { if (m_ams_mapping_result[k].id == i) { tray_id = m_ams_mapping_result[k].tray_id; + mapping_item["ams"] = tray_id; + mapping_item["filamentType"] = m_filaments[k].type; + auto it = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i]); + if (it != nullptr) { + mapping_item["filamentId"] = it->filament_id; + } + //convert #RRGGBB to RRGGBBAA + if (m_filaments[k].color.size() > 6) { + mapping_item["sourceColor"] = m_filaments[k].color.substr(1, 6) + "FF"; + } else { + mapping_item["sourceColor"] = m_filaments[k].color; + } + mapping_item["targetColor"] = m_ams_mapping_result[k].color; } } j.push_back(tray_id); + mapping_info_json.push_back(mapping_item); } mapping_array_str = j.dump(); + ams_mapping_info = mapping_info_json.dump(); return valid_mapping_result; } return true; @@ -1464,6 +1549,22 @@ void SelectMachineDialog::update_print_status_msg(wxString msg, bool is_warning, } } +bool SelectMachineDialog::has_tips(MachineObject* obj) +{ + if (!obj) return false; + + // must set to a status if return true + if (select_timelapse->IsShown() && + m_checkbox_list["timelapse"]->GetValue()) { + if (!obj->has_sdcard()) { + show_status(PrintDialogStatus::PrintStatusTimelapseNoSdcard); + return true; + } + } + + return false; +} + void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector params) { if (m_print_status != status) @@ -1564,7 +1665,8 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vectorEndModal(wxID_CANCEL); } -void SelectMachineDialog::on_ok(wxCommandEvent &event) +bool SelectMachineDialog::is_same_printer_model() +{ + bool result = true; + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return result; + + MachineObject* obj_ = dev->get_selected_machine(); + assert(obj_->dev_id == m_printer_last_select); + if (obj_ == nullptr) { + return result; + } + + PresetBundle* preset_bundle = wxGetApp().preset_bundle; + if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) != obj_->printer_type) { + BOOST_LOG_TRIVIAL(info) << "printer_model: source = " << preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); + BOOST_LOG_TRIVIAL(info) << "printer_model: target = " << obj_->printer_type; + return false; + } + + return true; +} + +void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) +{ + wxString confirm_text = _L("Please check the following infomation:\n"); + + //Check Printer Model Id + bool is_same_printer_type = is_same_printer_model(); + if (!is_same_printer_type) + confirm_text += _L("The printer type used to generate G-code is not the same type as the currently selected physical printer. It is recommend to re-slice by selecting the same printer type.\n"); + + //Check slice warnings + bool has_slice_warnings = false; + PartPlate* plate = m_plater->get_partplate_list().get_curr_plate(); + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (dev) { + MachineObject* obj_ = dev->get_selected_machine(); + for (auto warning : plate->get_slice_result()->warnings) { + if (warning.msg == BED_TEMP_TOO_HIGH_THAN_FILAMENT) { + if ((obj_->printer_type == "BL-P001" || obj_->printer_type == "BL-P002")) { + confirm_text += Plater::get_slice_warning_string(warning) + "\n"; + has_slice_warnings = true; + } + } + else { + has_slice_warnings = true; + } + } + } + + if (!is_same_printer_type + || has_slice_warnings + ) { + wxString confirm_title = _L("Confirm"); + SecondaryCheckDialog confirm_dlg(this); + confirm_dlg.update_text(confirm_text); + if (confirm_dlg.ShowModal() == wxID_YES) { + this->on_ok(); + } + } else { + this->on_ok(); + } +} + +void SelectMachineDialog::on_ok() { BOOST_LOG_TRIVIAL(info) << "print_job: on_ok to send"; m_is_canceled = false; @@ -1690,7 +1861,8 @@ void SelectMachineDialog::on_ok(wxCommandEvent &event) // get ams_mapping_result std::string ams_mapping_array; - get_ams_mapping_result(ams_mapping_array); + std::string ams_mapping_info; + get_ams_mapping_result(ams_mapping_array, ams_mapping_info); result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool &cancel) { if (this->m_is_canceled) return; @@ -1730,10 +1902,13 @@ void SelectMachineDialog::on_ok(wxCommandEvent &event) m_print_job->m_dev_ip = obj_->dev_ip; m_print_job->m_access_code = obj_->access_code; m_print_job->connection_type = obj_->connection_type(); - if (obj_->is_support_ams_mapping()) + if (obj_->is_support_ams_mapping()) { m_print_job->task_ams_mapping = ams_mapping_array; - else + m_print_job->task_ams_mapping_info = ams_mapping_info; + } else { m_print_job->task_ams_mapping = ""; + m_print_job->task_ams_mapping_info = ""; + } m_print_job->has_sdcard = obj_->has_sdcard(); @@ -1741,13 +1916,16 @@ void SelectMachineDialog::on_ok(wxCommandEvent &event) m_print_job->cloud_print_only = true; } + + bool timelapse_option = select_timelapse->IsShown() ? m_checkbox_list["timelapse"]->GetValue() : true; + m_print_job->set_print_config( MachineBedTypeString[0], m_checkbox_list["bed_leveling"]->GetValue(), m_checkbox_list["flow_cali"]->GetValue(), false, false, - true); + timelapse_option); if (obj_->has_ams()) { m_print_job->task_use_ams = ams_check->GetValue(); @@ -1755,6 +1933,7 @@ void SelectMachineDialog::on_ok(wxCommandEvent &event) m_print_job->task_use_ams = false; } + BOOST_LOG_TRIVIAL(info) << "print_job: timelapse_option = " << timelapse_option; BOOST_LOG_TRIVIAL(info) << "print_job: use_ams = " << m_print_job->task_use_ams; m_print_job->on_success([this]() { finish_mode(); }); @@ -1809,6 +1988,9 @@ void SelectMachineDialog::on_set_finish_mapping(wxCommandEvent &evt) for (auto i = 0; i < m_ams_mapping_result.size(); i++) { if (m_ams_mapping_result[i].id == wxAtoi(selection_data_arr[4])) { m_ams_mapping_result[i].tray_id = evt.GetInt(); + auto ams_colour = wxColour(wxAtoi(selection_data_arr[0]), wxAtoi(selection_data_arr[1]), wxAtoi(selection_data_arr[2])); + auto color = wxString::Format("%sFF", ams_colour.GetAsString(wxC2S_HTML_SYNTAX).substr(1, ams_colour.GetAsString(wxC2S_HTML_SYNTAX).size()-1)); + m_ams_mapping_result[i].color = color.ToStdString(); } BOOST_LOG_TRIVIAL(trace) << "The ams mapping result: id is " << m_ams_mapping_result[i].id << "tray_id is " << m_ams_mapping_result[i].tray_id; } @@ -1973,6 +2155,8 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event) obj->command_get_version(); obj->command_request_push_all(); dev->set_selected_machine(m_printer_last_select); + // Has changed machine unrecoverably + GUI::wxGetApp().sidebar().load_ams_list(obj->amsList); update_select_layout(obj); } else { BOOST_LOG_TRIVIAL(error) << "on_selection_changed dev_id not found"; @@ -2007,10 +2191,14 @@ void SelectMachineDialog::update_show_status() NetworkAgent* agent = Slic3r::GUI::wxGetApp().getAgent(); DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); - if (!agent) return; + if (!agent) { + update_ams_check(nullptr); + return; + } if (!dev) return; MachineObject* obj_ = dev->get_my_machine(m_printer_last_select); if (!obj_) { + update_ams_check(nullptr); if (agent) { if (agent->is_user_login()) { show_status(PrintDialogStatus::PrintStatusInvalidPrinter); @@ -2075,13 +2263,11 @@ void SelectMachineDialog::update_show_status() show_status(PrintDialogStatus::PrintStatusInPrinting); return; } - else if (!obj_->is_function_supported(PrinterFunction::FUNC_PRINT_WITHOUT_SD)) { + else if (!obj_->is_function_supported(PrinterFunction::FUNC_PRINT_WITHOUT_SD) && !obj_->has_sdcard()) { show_status(PrintDialogStatus::PrintStatusNoSdcard); return; } - - // check sdcard when if lan mode printer if (obj_->is_lan_mode_printer()) { if (!obj_->has_sdcard()) { @@ -2092,7 +2278,8 @@ void SelectMachineDialog::update_show_status() // no ams if (!obj_->has_ams()) { - show_status(PrintDialogStatus::PrintStatusReadingFinished); + if (!has_tips(obj_)) + show_status(PrintDialogStatus::PrintStatusReadingFinished); return; } @@ -2144,10 +2331,13 @@ void SelectMachineDialog::update_show_status() } else { if (obj_->is_valid_mapping_result(m_ams_mapping_result)) { - show_status(PrintDialogStatus::PrintStatusAmsMappingValid); + if (!has_tips(obj_)) + show_status(PrintDialogStatus::PrintStatusAmsMappingValid); + return; } else { show_status(PrintDialogStatus::PrintStatusAmsMappingInvalid); + return; } } } @@ -2250,6 +2440,7 @@ void SelectMachineDialog::set_default() // checkbox default values m_checkbox_list["bed_leveling"]->SetValue(true); m_checkbox_list["flow_cali"]->SetValue(true); + m_checkbox_list["timelapse"]->SetValue(true); ams_check->SetValue(true); // thumbmail diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp index fcd0620ba9..fe83a1cc6c 100644 --- a/src/slic3r/GUI/SelectMachine.hpp +++ b/src/slic3r/GUI/SelectMachine.hpp @@ -195,6 +195,9 @@ public: bool was_dismiss() { return m_dismiss; } private: + int m_my_devices_count{0}; + int m_other_devices_count{0}; + wxWindow* m_placeholder_panel{nullptr}; wxBoxSizer * m_sizer_body{nullptr}; wxBoxSizer * m_sizer_my_devices{nullptr}; wxBoxSizer * m_sizer_other_devices{nullptr}; @@ -249,7 +252,9 @@ enum PrintDialogStatus { PrintStatusSending, PrintStatusSendingCanceled, PrintStatusLanModeNoSdcard, - PrintStatusNoSdcard + PrintStatusNoSdcard, + PrintStatusTimelapseNoSdcard, + PrintStatusNotOnTheSameLAN }; std::string get_print_status_info(PrintDialogStatus status); @@ -308,7 +313,8 @@ protected: int m_current_filament_id; bool m_is_in_sending_mode { false }; - wxGridSizer *m_sizer_select; + //wxGridSizer *m_sizer_select; + wxWrapSizer *m_sizer_select; wxBoxSizer * sizer_thumbnail; wxGridSizer *m_sizer_material; wxBoxSizer * m_sizer_main; @@ -323,6 +329,7 @@ protected: wxWindow *select_bed{nullptr}; wxWindow *select_flow{nullptr}; + wxWindow *select_timelapse { nullptr }; wxWindow *select_use_ams{nullptr}; CheckBox *ams_check{nullptr}; @@ -346,11 +353,14 @@ public: void sync_ams_mapping_result(std::vector& result); bool do_ams_mapping(MachineObject *obj_); - bool get_ams_mapping_result(std::string &mapping_array_str); + bool get_ams_mapping_result(std::string &mapping_array_str, std::string &ams_mapping_info); void prepare(int print_plate_idx); + bool has_tips(MachineObject* obj); void show_status(PrintDialogStatus status, std::vector params = std::vector()); PrintDialogStatus get_status() { return m_print_status; } + bool is_same_printer_model(); + bool Show(bool show); /* model */ @@ -386,7 +396,8 @@ protected: // Virtual event handlers, overide them in your derived class void update_printer_combobox(wxCommandEvent &event); void on_cancel(wxCloseEvent &event); - void on_ok(wxCommandEvent &event); + void on_ok_btn(wxCommandEvent &event); + void on_ok(); void on_refresh(wxCommandEvent &event); void on_set_finish_mapping(wxCommandEvent &evt); void on_print_job_cancel(wxCommandEvent &evt); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index b949bfd025..fa13ca0aeb 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1105,6 +1105,12 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type else v.set_instance_scaling_factor(scale); } + + // update the instance assemble transform + ModelObject* object = m_model->objects[v.object_idx()]; + Geometry::Transformation assemble_transform = object->instances[v.instance_idx()]->get_assemble_transformation(); + assemble_transform.set_scaling_factor(v.get_instance_scaling_factor()); + object->instances[v.instance_idx()]->set_assemble_transformation(assemble_transform); } else if (is_single_volume() || is_single_modifier()) v.set_volume_scaling_factor(scale); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp new file mode 100644 index 0000000000..8f619058c3 --- /dev/null +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -0,0 +1,1009 @@ +#include "SendToPrinter.hpp" +#include "I18N.hpp" + +#include "libslic3r/Utils.hpp" +#include "libslic3r/Thread.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "GUI_Preview.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Widgets/ProgressDialog.hpp" +#include "Widgets/RoundedRectangle.hpp" +#include "Widgets/StaticBox.hpp" +#include "ConnectPrinter.hpp" + +#include +#include +#include +#include +#include +#include "BitmapCache.hpp" + +namespace Slic3r { +namespace GUI { + +#define INITIAL_NUMBER_OF_MACHINES 0 +#define LIST_REFRESH_INTERVAL 200 +#define MACHINE_LIST_REFRESH_INTERVAL 2000 + +wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent); +wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent); +wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent); + +void SendToPrinterDialog::stripWhiteSpace(std::string& str) +{ + if (str == "") { return; } + + string::iterator cur_it; + cur_it = str.begin(); + + while (cur_it != str.end()) { + if ((*cur_it) == '\n' || (*cur_it) == ' ') { + cur_it = str.erase(cur_it); + } + else { + cur_it++; + } + } +} + +wxString SendToPrinterDialog::format_text(wxString &m_msg) +{ + + if (wxGetApp().app_config->get("language") != "zh_CN") { return m_msg; } + + wxString out_txt = m_msg; + wxString count_txt = ""; + int new_line_pos = 0; + + for (int i = 0; i < m_msg.length(); i++) { + auto text_size = m_statictext_printer_msg->GetTextExtent(count_txt); + if (text_size.x < (FromDIP(400))) { + count_txt += m_msg[i]; + } + else { + out_txt.insert(i - 1, '\n'); + count_txt = ""; + } + } + return out_txt; +} + +SendToPrinterDialog::SendToPrinterDialog(Plater *plater) + : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, _L("Send to Printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) + , m_plater(plater), m_export_3mf_cancel(false) +{ +#ifdef __WINDOWS__ + SetDoubleBuffered(true); +#endif //__WINDOWS__ + + // bind + Bind(wxEVT_CLOSE_WINDOW, &SendToPrinterDialog::on_cancel, this); + + // font + SetFont(wxGetApp().normal_font()); + + // icon + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + Freeze(); + SetBackgroundColour(m_colour_def_color); + + m_sizer_main = new wxBoxSizer(wxVERTICAL); + + m_sizer_main->SetMinSize(wxSize(0, -1)); + m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); + + m_scrollable_region = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_sizer_scrollable_region = new wxBoxSizer(wxVERTICAL); + + m_panel_image = new wxPanel(m_scrollable_region, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_image->SetBackgroundColour(m_colour_def_color); + + sizer_thumbnail = new wxBoxSizer(wxVERTICAL); + m_thumbnailPanel = new ThumbnailPanel(m_panel_image); + m_thumbnailPanel->SetSize(wxSize(FromDIP(256), FromDIP(256))); + m_thumbnailPanel->SetMinSize(wxSize(FromDIP(256), FromDIP(256))); + m_thumbnailPanel->SetMaxSize(wxSize(FromDIP(256), FromDIP(256))); + sizer_thumbnail->Add(m_thumbnailPanel, 0, wxEXPAND, 0); + m_panel_image->SetSizer(sizer_thumbnail); + m_panel_image->Layout(); + + wxBoxSizer *m_sizer_basic = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_basic_weight = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_basic_time = new wxBoxSizer(wxHORIZONTAL); + + auto timeimg = new wxStaticBitmap(m_scrollable_region, wxID_ANY, create_scaled_bitmap("print-time", this, 18), wxDefaultPosition, wxSize(FromDIP(18), FromDIP(18)), 0); + m_sizer_basic_weight->Add(timeimg, 1, wxEXPAND | wxALL, FromDIP(5)); + m_stext_time = new wxStaticText(m_scrollable_region, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_sizer_basic_weight->Add(m_stext_time, 0, wxALL, FromDIP(5)); + m_sizer_basic->Add(m_sizer_basic_weight, 0, wxALIGN_CENTER, 0); + m_sizer_basic->Add(0, 0, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + + auto weightimg = new wxStaticBitmap(m_scrollable_region, wxID_ANY, create_scaled_bitmap("print-weight", this, 18), wxDefaultPosition, wxSize(FromDIP(18), FromDIP(18)), 0); + m_sizer_basic_time->Add(weightimg, 1, wxEXPAND | wxALL, FromDIP(5)); + m_stext_weight = new wxStaticText(m_scrollable_region, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + m_sizer_basic_time->Add(m_stext_weight, 0, wxALL, FromDIP(5)); + m_sizer_basic->Add(m_sizer_basic_time, 0, wxALIGN_CENTER, 0); + + m_line_materia = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_materia->SetForegroundColour(wxColour(238, 238, 238)); + m_line_materia->SetBackgroundColour(wxColour(238, 238, 238)); + + wxBoxSizer *m_sizer_printer = new wxBoxSizer(wxHORIZONTAL); + + m_stext_printer_title = new wxStaticText(this, wxID_ANY, L("Printer"), wxDefaultPosition, wxSize(-1, -1), 0); + m_stext_printer_title->SetFont(::Label::Head_14); + m_stext_printer_title->Wrap(-1); + m_stext_printer_title->SetForegroundColour(m_colour_bold_color); + m_stext_printer_title->SetBackgroundColour(m_colour_def_color); + + m_sizer_printer->Add(m_stext_printer_title, 0, wxALL | wxLEFT, FromDIP(5)); + m_sizer_printer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(12)); + + m_comboBox_printer = new ::ComboBox(this, wxID_ANY, L(""), wxDefaultPosition, wxSize(FromDIP(250), -1), 0, nullptr, wxCB_READONLY); + m_comboBox_printer->Bind(wxEVT_COMBOBOX, &SendToPrinterDialog::on_selection_changed, this); + + m_sizer_printer->Add(m_comboBox_printer, 1, wxEXPAND | wxRIGHT, FromDIP(5)); + btn_bg_enable = StateColor(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_button_refresh = new Button(this, _L("Refresh")); + m_button_refresh->SetBackgroundColor(btn_bg_enable); + m_button_refresh->SetBorderColor(btn_bg_enable); + m_button_refresh->SetTextColor(*wxWHITE); + m_button_refresh->SetSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetCornerRadius(FromDIP(10)); + m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this); + m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5)); + + m_statictext_printer_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + m_statictext_printer_msg->SetFont(::Label::Body_13); + m_statictext_printer_msg->Hide(); + + // line schedule + m_line_schedule = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1)); + m_line_schedule->SetBackgroundColour(wxColour(238, 238, 238)); + + m_sizer_bottom = new wxBoxSizer(wxVERTICAL); + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_DIALOG_SIMBOOK_SIZE, 0); + + // perpare mode + m_panel_prepare = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_prepare->SetBackgroundColour(m_colour_def_color); + // m_panel_prepare->SetBackgroundColour(wxColour(135,206,250)); + wxBoxSizer *m_sizer_prepare = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *m_sizer_pcont = new wxBoxSizer(wxHORIZONTAL); + + m_sizer_prepare->Add(0, 0, 1, wxTOP, FromDIP(22)); + m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); + m_button_ensure = new Button(m_panel_prepare, _L("Send")); + m_button_ensure->SetBackgroundColor(btn_bg_enable); + m_button_ensure->SetBorderColor(btn_bg_enable); + m_button_ensure->SetTextColor(*wxWHITE); + m_button_ensure->SetSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetCornerRadius(FromDIP(12)); + + m_button_ensure->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_ok, this); + m_sizer_pcont->Add(m_button_ensure, 0, wxEXPAND | wxBOTTOM, FromDIP(10)); + m_sizer_prepare->Add(m_sizer_pcont, 0, wxEXPAND, 0); + m_panel_prepare->SetSizer(m_sizer_prepare); + m_panel_prepare->Layout(); + m_simplebook->AddPage(m_panel_prepare, wxEmptyString, true); + + // sending mode + m_status_bar = std::make_shared(m_simplebook); + m_panel_sending = m_status_bar->get_panel(); + m_simplebook->AddPage(m_panel_sending, wxEmptyString, false); + + // finish mode + m_panel_finish = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_finish->SetBackgroundColour(wxColour(135, 206, 250)); + wxBoxSizer *m_sizer_finish = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *m_sizer_finish_v = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *m_sizer_finish_h = new wxBoxSizer(wxHORIZONTAL); + + auto imgsize = FromDIP(25); + auto completedimg = new wxStaticBitmap(m_panel_finish, wxID_ANY, create_scaled_bitmap("completed", m_panel_finish, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0); + m_sizer_finish_h->Add(completedimg, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_statictext_finish = new wxStaticText(m_panel_finish, wxID_ANY, L("send completed"), wxDefaultPosition, wxDefaultSize, 0); + m_statictext_finish->Wrap(-1); + m_statictext_finish->SetForegroundColour(wxColour(0, 174, 66)); + m_sizer_finish_h->Add(m_statictext_finish, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_sizer_finish_v->Add(m_sizer_finish_h, 1, wxALIGN_CENTER, 0); + + m_sizer_finish->Add(m_sizer_finish_v, 1, wxALIGN_CENTER, 0); + + m_panel_finish->SetSizer(m_sizer_finish); + m_panel_finish->Layout(); + m_sizer_finish->Fit(m_panel_finish); + m_simplebook->AddPage(m_panel_finish, wxEmptyString, false); + + // bind + Bind(EVT_UPDATE_USER_MACHINE_LIST, &SendToPrinterDialog::update_printer_combobox, this); + Bind(EVT_PRINT_JOB_CANCEL, &SendToPrinterDialog::on_print_job_cancel, this); + + + m_sizer_scrollable_region->Add(m_panel_image, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_scrollable_region->Add(0, 0, 0, wxTOP, FromDIP(10)); + m_sizer_scrollable_region->Add(m_sizer_basic, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_scrollable_region->SetSizer(m_sizer_scrollable_region); + m_scrollable_region->Layout(); + + + m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(22)); + m_sizer_main->Add(m_scrollable_region, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); + m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(5)); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); + m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(20)); + m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(12)); + m_sizer_main->Add(m_line_schedule, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(m_simplebook, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_sizer_main->Add(m_sizer_bottom, 0, wxALIGN_CENTER_HORIZONTAL); + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(15)); + + SetSizer(m_sizer_main); + Layout(); + Fit(); + Thaw(); + + init_bind(); + init_timer(); + // CenterOnParent(); + Centre(wxBOTH); +} + +void SendToPrinterDialog::prepare_mode() +{ + m_is_in_sending_mode = false; + if (m_send_job) { + m_send_job->join(); + } + + if (wxIsBusy()) + wxEndBusyCursor(); + Enable_Send_Button(true); + + m_status_bar->reset(); + if (m_simplebook->GetSelection() != 0) { + m_simplebook->SetSelection(0); + } +} + +void SendToPrinterDialog::sending_mode() +{ + m_is_in_sending_mode = true; + if (m_simplebook->GetSelection() != 1){ + m_simplebook->SetSelection(1); + Layout(); + Fit(); + } +} + +void SendToPrinterDialog::prepare(int print_plate_idx) +{ + m_print_plate_idx = print_plate_idx; +} + +void SendToPrinterDialog::update_priner_status_msg(wxString msg, bool is_warning) +{ + auto colour = is_warning ? wxColour(0xFF, 0x6F, 0x00) : wxColour(0x6B, 0x6B, 0x6B); + m_statictext_printer_msg->SetForegroundColour(colour); + + if (msg.empty()) { + if (!m_statictext_printer_msg->GetLabel().empty()) { + m_statictext_printer_msg->SetLabel(wxEmptyString); + m_statictext_printer_msg->Hide(); + Layout(); + Fit(); + } + } else { + msg = format_text(msg); + + auto str_new = msg.ToStdString(); + stripWhiteSpace(str_new); + + auto str_old = m_statictext_printer_msg->GetLabel().ToStdString(); + stripWhiteSpace(str_old); + + if (str_new != str_old) { + if (m_statictext_printer_msg->GetLabel() != msg) { + m_statictext_printer_msg->SetLabel(msg); + m_statictext_printer_msg->SetMinSize(wxSize(FromDIP(400), -1)); + m_statictext_printer_msg->SetMaxSize(wxSize(FromDIP(400), -1)); + m_statictext_printer_msg->Wrap(FromDIP(400)); + m_statictext_printer_msg->Show(); + Layout(); + Fit(); + } + } + } +} + +void SendToPrinterDialog::update_print_status_msg(wxString msg, bool is_warning, bool is_printer_msg) +{ + if (is_printer_msg) { + update_priner_status_msg(msg, is_warning); + } else { + update_priner_status_msg(wxEmptyString, false); + } +} + + +void SendToPrinterDialog::init_model() +{ + machine_model = new MachineListModel; + m_dataViewListCtrl_machines->AssociateModel(machine_model.get()); + m_dataViewListCtrl_machines->AppendTextColumn("Printer Name", MachineListModel::Col_MachineName, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_SORTABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("SN(dev_id)", MachineListModel::Col_MachineSN, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("Status", MachineListModel::Col_MachinePrintingStatus, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("TaskName", MachineListModel::Col_MachineTaskName, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); + + m_dataViewListCtrl_machines->AppendTextColumn("Connection", MachineListModel::Col_MachineConnection, wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, + wxDATAVIEW_COL_RESIZABLE); +} + +void SendToPrinterDialog::init_bind() +{ + Bind(wxEVT_TIMER, &SendToPrinterDialog::on_timer, this); +} + +void SendToPrinterDialog::init_timer() +{ + m_refresh_timer = new wxTimer(); + m_refresh_timer->SetOwner(this); +} + +void SendToPrinterDialog::on_cancel(wxCloseEvent &event) +{ + if (m_send_job) { + if (m_send_job->is_running()) { + m_send_job->cancel(); + m_send_job->join(); + } + } + this->EndModal(wxID_CANCEL); +} + +void SendToPrinterDialog::on_ok(wxCommandEvent &event) +{ + BOOST_LOG_TRIVIAL(info) << "print_job: on_ok to send"; + m_is_canceled = false; + Enable_Send_Button(false); + if (m_is_in_sending_mode) + return; + + int result = 0; + if (m_printer_last_select.empty()) { + return; + } + + DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + MachineObject *obj_ = dev->get_selected_machine(); + assert(obj_->dev_id == m_printer_last_select); + if (obj_ == nullptr) { + return; + } + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", print_job: for send task, current printer id = " << m_printer_last_select << std::endl; + show_status(PrintDialogStatus::PrintStatusSending); + + m_status_bar->reset(); + m_status_bar->set_prog_block(); + m_status_bar->set_cancel_callback_fina([this]() { + BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled"; + if (m_send_job) { + if (m_send_job->is_running()) { + BOOST_LOG_TRIVIAL(info) << "send_job: canceled"; + m_send_job->cancel(); + } + m_send_job->join(); + } + m_is_canceled = true; + wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); + wxQueueEvent(this, event); + }); + + if (m_is_canceled) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + // enter sending mode + sending_mode(); + + result = m_plater->send_gcode(m_print_plate_idx, [this](int export_stage, int current, int total, bool &cancel) { + if (this->m_is_canceled) return; + bool cancelled = false; + wxString msg = _L("Preparing print job"); + m_status_bar->update_status(msg, cancelled, 10, true); + m_export_3mf_cancel = cancel = cancelled; + }); + + if (m_is_canceled || m_export_3mf_cancel) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_export_3mf_cancel or m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + if (result < 0) { + wxString msg = _L("Abnormal print file data. Please slice again"); + m_status_bar->set_status_text(msg); + return; + } + + // export config 3mf if needed + if (!obj_->is_lan_mode_printer()) { + result = m_plater->export_config_3mf(m_print_plate_idx); + if (result < 0) { + BOOST_LOG_TRIVIAL(trace) << "export_config_3mf failed, result = " << result; + return; + } + } + if (m_is_canceled || m_export_3mf_cancel) { + BOOST_LOG_TRIVIAL(info) << "send_job: m_export_3mf_cancel or m_is_canceled"; + //m_status_bar->set_status_text(task_canceled_text); + return; + } + + /* std::string file_name = ""; + auto default_output_file = wxGetApp().plater()->get_export_gcode_filename(".3mf"); + if (!default_output_file.empty()) { + fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str()); + file_name = default_output_file_path.filename().string(); + }*/ + + + + m_send_job = std::make_shared(m_status_bar, m_plater, m_printer_last_select); + m_send_job->m_dev_ip = obj_->dev_ip; + m_send_job->m_access_code = obj_->access_code; + m_send_job->connection_type = obj_->connection_type(); + m_send_job->cloud_print_only = true; + m_send_job->has_sdcard = obj_->has_sdcard(); + + + m_send_job->on_success([this]() { + //enable_prepare_mode = true;enable_prepare_mode + m_status_bar->reset(); + prepare_mode(); + //EndModal(wxID_CLOSE); + }); + + enable_prepare_mode = false; + m_send_job->start(); + BOOST_LOG_TRIVIAL(info) << "send_job: send print job"; +} + +void SendToPrinterDialog::update_user_machine_list() +{ + NetworkAgent* m_agent = wxGetApp().getAgent(); + if (m_agent && m_agent->is_user_login()) { + boost::thread get_print_info_thread = Slic3r::create_thread([&] { + NetworkAgent* agent = wxGetApp().getAgent(); + unsigned int http_code; + std::string body; + int result = agent->get_user_print_info(&http_code, &body); + if (result == 0) { + m_print_info = body; + } + else { + m_print_info = ""; + } + wxCommandEvent event(EVT_UPDATE_USER_MACHINE_LIST); + event.SetEventObject(this); + wxPostEvent(this, event); + }); + } else { + wxCommandEvent event(EVT_UPDATE_USER_MACHINE_LIST); + event.SetEventObject(this); + wxPostEvent(this, event); + } +} + +void SendToPrinterDialog::on_refresh(wxCommandEvent &event) +{ + BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh"; + show_status(PrintDialogStatus::PrintStatusRefreshingMachineList); + + update_user_machine_list(); +} + +void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt) +{ + BOOST_LOG_TRIVIAL(info) << "print_job: canceled"; + show_status(PrintDialogStatus::PrintStatusSendingCanceled); + // enter prepare mode + prepare_mode(); +} + +std::vector SendToPrinterDialog::sort_string(std::vector strArray) +{ + std::vector outputArray; + std::sort(strArray.begin(), strArray.end()); + std::vector::iterator st; + for (st = strArray.begin(); st != strArray.end(); st++) { outputArray.push_back(*st); } + + return outputArray; +} + +bool SendToPrinterDialog::is_timeout() +{ + if (timeout_count > 15 * 1000 / LIST_REFRESH_INTERVAL) { + return true; + } + return false; +} + +void SendToPrinterDialog::reset_timeout() +{ + timeout_count = 0; +} + +void SendToPrinterDialog::update_user_printer() +{ + Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + // update user print info + if (!m_print_info.empty()) { + dev->parse_user_print_info(m_print_info); + m_print_info = ""; + } + + // clear machine list + m_list.clear(); + m_comboBox_printer->Clear(); + std::vector machine_list; + wxArrayString machine_list_name; + std::map option_list; + + option_list = dev->get_my_machine_list(); + + // same machine only appear once + for (auto it = option_list.begin(); it != option_list.end(); it++) { + if (it->second && (it->second->is_online() || it->second->is_connected())) { + machine_list.push_back(it->second->dev_name); + } + } + machine_list = sort_string(machine_list); + for (auto tt = machine_list.begin(); tt != machine_list.end(); tt++) { + for (auto it = option_list.begin(); it != option_list.end(); it++) { + if (it->second->dev_name == *tt) { + m_list.push_back(it->second); + wxString dev_name_text = from_u8(it->second->dev_name); + if (it->second->is_lan_mode_printer()) { + dev_name_text += "(LAN)"; + } + machine_list_name.Add(dev_name_text); + break; + } + } + } + + m_comboBox_printer->Set(machine_list_name); + + MachineObject* obj = dev->get_selected_machine(); + if (obj) { + m_printer_last_select = obj->dev_id; + } else { + m_printer_last_select = ""; + } + + if (m_list.size() > 0) { + // select a default machine + if (m_printer_last_select.empty()) { + m_printer_last_select = m_list[0]->dev_id; + m_comboBox_printer->SetSelection(0); + wxCommandEvent event(wxEVT_COMBOBOX); + event.SetEventObject(m_comboBox_printer); + wxPostEvent(m_comboBox_printer, event); + } + for (auto i = 0; i < m_list.size(); i++) { + if (m_list[i]->dev_id == m_printer_last_select) { + m_comboBox_printer->SetSelection(i); + wxCommandEvent event(wxEVT_COMBOBOX); + event.SetEventObject(m_comboBox_printer); + wxPostEvent(m_comboBox_printer, event); + } + } + } + else { + m_printer_last_select = ""; + m_comboBox_printer->SetTextLabel(""); + } + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl; +} + +void SendToPrinterDialog::update_printer_combobox(wxCommandEvent &event) +{ + show_status(PrintDialogStatus::PrintStatusInit); + update_user_printer(); +} + +void SendToPrinterDialog::on_timer(wxTimerEvent &event) +{ + wxGetApp().reset_to_active(); + update_show_status(); +} + +void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event) +{ + /* reset timeout and reading printer info */ + //m_status_bar->reset(); + timeout_count = 0; + + auto selection = m_comboBox_printer->GetSelection(); + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + + MachineObject* obj = nullptr; + for (int i = 0; i < m_list.size(); i++) { + if (i == selection) { + m_printer_last_select = m_list[i]->dev_id; + obj = m_list[i]; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl; + break; + } + } + + if (obj) { + obj->command_get_version(); + dev->set_selected_machine(m_printer_last_select); + } else { + BOOST_LOG_TRIVIAL(error) << "on_selection_changed dev_id not found"; + return; + } + + update_show_status(); +} + +void SendToPrinterDialog::update_show_status() +{ + NetworkAgent* agent = Slic3r::GUI::wxGetApp().getAgent(); + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!agent) return; + if (!dev) return; + MachineObject* obj_ = dev->get_my_machine(m_printer_last_select); + if (!obj_) { + if (agent) { + if (agent->is_user_login()) { + show_status(PrintDialogStatus::PrintStatusInvalidPrinter); + } + else { + show_status(PrintDialogStatus::PrintStatusNoUserLogin); + } + } + return; + } + + /* check cloud machine connections */ + if (!obj_->is_lan_mode_printer()) { + if (!agent->is_server_connected()) { + agent->refresh_connection(); + show_status(PrintDialogStatus::PrintStatusConnectingServer); + reset_timeout(); + return; + } + } + + if (!obj_->is_info_ready()) { + if (is_timeout()) { + (PrintDialogStatus::PrintStatusReadingTimeout); + return; + } + else { + timeout_count++; + show_status(PrintDialogStatus::PrintStatusReading); + return; + } + return; + } + + reset_timeout(); + + // reading done + if (obj_->is_in_upgrading()) { + show_status(PrintDialogStatus::PrintStatusInUpgrading); + return; + } + else if (obj_->is_system_printing()) { + show_status(PrintDialogStatus::PrintStatusInSystemPrinting); + return; + } + + // check sdcard when if lan mode printer + /* if (obj_->is_lan_mode_printer()) { + }*/ + if (!obj_->has_sdcard()) { + show_status(PrintDialogStatus::PrintStatusNoSdcard); + return; + } + + if (obj_->dev_ip.empty()) { + show_status(PrintDialogStatus::PrintStatusNotOnTheSameLAN); + return; + } + + + show_status(PrintDialogStatus::PrintStatusReadingFinished); +} + +void SendToPrinterDialog::Enable_Refresh_Button(bool en) +{ + if (!en) { + if (m_button_refresh->IsEnabled()) { + m_button_refresh->Disable(); + m_button_refresh->SetBackgroundColor(wxColour(0x90, 0x90, 0x90)); + m_button_refresh->SetBorderColor(wxColour(0x90, 0x90, 0x90)); + } + } else { + if (!m_button_refresh->IsEnabled()) { + m_button_refresh->Enable(); + m_button_refresh->SetBackgroundColor(btn_bg_enable); + m_button_refresh->SetBorderColor(btn_bg_enable); + } + } +} + +void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector params) +{ + if (m_print_status != status) + BOOST_LOG_TRIVIAL(info) << "select_machine_dialog: show_status = " << status; + m_print_status = status; + + // m_comboBox_printer + if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) + m_comboBox_printer->Disable(); + else + m_comboBox_printer->Enable(); + + // m_panel_warn m_simplebook + if (status == PrintDialogStatus::PrintStatusSending) { + sending_mode(); + } + + // other + if (status == PrintDialogStatus::PrintStatusInit) { + update_print_status_msg(wxEmptyString, false, false); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusNoUserLogin) { + wxString msg_text = _L("No login account, only printers in LAN mode are displayed"); + update_print_status_msg(msg_text, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusInvalidPrinter) { + update_print_status_msg(wxEmptyString, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusConnectingServer) { + wxString msg_text = _L("Connecting to server"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReading) { + wxString msg_text = _L("Synchronizing device information"); + update_print_status_msg(msg_text, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReadingFinished) { + update_print_status_msg(wxEmptyString, false, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusReadingTimeout) { + wxString msg_text = _L("Synchronizing device information time out"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusInUpgrading) { + wxString msg_text = _L("Cannot send the print task when the upgrade is in progress"); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) { + update_print_status_msg(wxEmptyString, false, true); + Enable_Send_Button(false); + Enable_Refresh_Button(false); + } + else if (status == PrintDialogStatus::PrintStatusSending) { + Enable_Send_Button(false); + Enable_Refresh_Button(false); + } + else if (status == PrintDialogStatus::PrintStatusSendingCanceled) { + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusNoSdcard) { + wxString msg_text = _L("An SD card needs to be inserted before printing via LAN."); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else if (status == PrintDialogStatus::PrintStatusNotOnTheSameLAN) { + wxString msg_text = _L("The printer is required to be in the same LAN as Bambu Studio."); + update_print_status_msg(msg_text, true, true); + Enable_Send_Button(false); + Enable_Refresh_Button(true); + } + else { + Enable_Send_Button(true); + Enable_Refresh_Button(true); + } +} + + +void SendToPrinterDialog::Enable_Send_Button(bool en) +{ + if (!en) { + if (m_button_ensure->IsEnabled()) { + m_button_ensure->Disable(); + m_button_ensure->SetBackgroundColor(wxColour(0x90, 0x90, 0x90)); + m_button_ensure->SetBorderColor(wxColour(0x90, 0x90, 0x90)); + } + } else { + if (!m_button_ensure->IsEnabled()) { + m_button_ensure->Enable(); + m_button_ensure->SetBackgroundColor(btn_bg_enable); + m_button_ensure->SetBorderColor(btn_bg_enable); + } + } +} + +void SendToPrinterDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_button_refresh->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_refresh->SetCornerRadius(FromDIP(12)); + m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); + m_button_ensure->SetCornerRadius(FromDIP(12)); + m_status_bar->msw_rescale(); + Fit(); + Refresh(); +} + +void SendToPrinterDialog::set_default() +{ + enable_prepare_mode = true; + prepare_mode(); + + //clear combobox + m_list.clear(); + m_comboBox_printer->Clear(); + m_printer_last_select = ""; + m_print_info = ""; + m_comboBox_printer->SetValue(wxEmptyString); + m_comboBox_printer->Enable(); + // rset status bar + m_status_bar->reset(); + + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) { + if (agent->is_user_login()) { + show_status(PrintDialogStatus::PrintStatusInit); + } else { + show_status(PrintDialogStatus::PrintStatusNoUserLogin); + } + } + + // thumbmail + //wxBitmap bitmap; + ThumbnailData &data = m_plater->get_partplate_list().get_curr_plate()->thumbnail_data; + if (data.is_valid()) { + wxImage image(data.width, data.height); + image.InitAlpha(); + for (unsigned int r = 0; r < data.height; ++r) { + unsigned int rr = (data.height - 1 - r) * data.width; + for (unsigned int c = 0; c < data.width; ++c) { + unsigned char *px = (unsigned char *) data.pixels.data() + 4 * (rr + c); + image.SetRGB((int) c, (int) r, px[0], px[1], px[2]); + image.SetAlpha((int) c, (int) r, px[3]); + } + } + image = image.Rescale(FromDIP(256), FromDIP(256)); + m_thumbnailPanel->set_thumbnail(image); + } + + std::vector materials; + std::vector display_materials; + { + auto preset_bundle = wxGetApp().preset_bundle; + for (auto filament_name : preset_bundle->filament_presets) { + for (auto iter = preset_bundle->filaments.lbegin(); iter != preset_bundle->filaments.end(); iter++) { + if (filament_name.compare(iter->name) == 0) { + std::string display_filament_type; + std::string filament_type = iter->config.get_filament_type(display_filament_type); + display_materials.push_back(display_filament_type); + materials.push_back(filament_type); + } + } + } + } + + m_scrollable_region->Layout(); + m_scrollable_region->Fit(); + Layout(); + Fit(); + + + wxSize screenSize = wxGetDisplaySize(); + auto dialogSize = this->GetSize(); + + + // basic info + auto aprint_stats = m_plater->get_partplate_list().get_current_fff_print().print_statistics(); + wxString time; + PartPlate *plate = m_plater->get_partplate_list().get_curr_plate(); + if (plate) { + if (plate->get_slice_result()) { time = wxString::Format("%s", get_bbl_monitor_time_dhm(plate->get_slice_result()->print_statistics.modes[0].time)); } + } + + char weight[64]; + ::sprintf(weight, " %.2f g", aprint_stats.total_weight); + + m_stext_time->SetLabel(time); + m_stext_weight->SetLabel(weight); +} + +bool SendToPrinterDialog::Show(bool show) +{ + show_status(PrintDialogStatus::PrintStatusInit); + + // set default value when show this dialog + if (show) { + wxGetApp().reset_to_active(); + set_default(); + update_user_machine_list(); + } + + if (show) { + m_refresh_timer->Start(LIST_REFRESH_INTERVAL); + } else { + m_refresh_timer->Stop(); + } + + Layout(); + Fit(); + if (show) { CenterOnParent(); } + return DPIDialog::Show(show); +} + +SendToPrinterDialog::~SendToPrinterDialog() +{ + delete m_refresh_timer; +} + +} +} \ No newline at end of file diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp new file mode 100644 index 0000000000..9490d025f2 --- /dev/null +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_GUI_SendToSDcard_hpp_ +#define slic3r_GUI_SendToSDcard_hpp_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SelectMachine.hpp" +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "DeviceManager.hpp" +#include "Plater.hpp" +#include "BBLStatusBar.hpp" +#include "BBLStatusBarSend.hpp" +#include "Widgets/Label.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/CheckBox.hpp" +#include "Widgets/ComboBox.hpp" +#include "Widgets/ScrolledWindow.hpp" +#include +#include + +namespace Slic3r { +namespace GUI { + +class SendToPrinterDialog : public DPIDialog +{ +private: + void init_model(); + void init_bind(); + void init_timer(); + + int m_print_plate_idx; + PrintDialogStatus m_print_status { PrintStatusInit }; + + std::string m_printer_last_select; + std::vector m_bedtype_list; + std::map m_checkbox_list; + + wxColour m_colour_def_color{ wxColour(255, 255, 255) }; + wxColour m_colour_bold_color{ wxColour(38, 46, 48) }; + +protected: + Plater* m_plater{ nullptr }; + wxPanel* m_line_top{ nullptr }; + wxPanel* m_panel_image{ nullptr }; + wxStaticText* m_stext_time{ nullptr }; + wxStaticText* m_stext_weight{ nullptr }; + wxPanel* m_line_materia{ nullptr }; + wxStaticText* m_stext_printer_title{ nullptr }; + + wxStaticText* m_statictext_printer_msg{ nullptr }; + wxStaticBitmap* m_staticbitmap{ nullptr }; + ThumbnailPanel* m_thumbnailPanel{ nullptr }; + + ::ComboBox* m_comboBox_printer{ nullptr }; + ::ComboBox* m_comboBox_bed{ nullptr }; + wxStaticText* m_staticText_bed_title{ nullptr }; + wxPanel* m_line_schedule{ nullptr }; + wxPanel* m_panel_sending{ nullptr }; + wxStaticText* m_stext_sending{ nullptr }; + wxPanel* m_panel_prepare{ nullptr }; + Button* m_button_refresh{ nullptr }; + Button* m_button_ensure{ nullptr }; + wxPanel* m_panel_finish{ nullptr }; + wxSimplebook* m_simplebook{ nullptr }; + wxStaticText* m_statictext_finish{ nullptr }; + + StateColor btn_bg_enable; + int m_current_filament_id; + bool m_is_in_sending_mode{ false }; + + wxBoxSizer* sizer_thumbnail; + wxBoxSizer* m_sizer_main; + wxBoxSizer* m_sizer_bottom; + + bool enable_prepare_mode{true}; + bool m_need_adaptation_screen{ false }; + wxPanel* m_scrollable_region; + wxBoxSizer* m_sizer_scrollable_region; + + + void stripWhiteSpace(std::string& str); + wxString format_text(wxString& m_msg); + void update_priner_status_msg(wxString msg, bool is_warning = false); + void update_print_status_msg(wxString msg, bool is_warning = false, bool is_printer = true); + +public: + SendToPrinterDialog(Plater* plater = nullptr); + ~SendToPrinterDialog(); + + void prepare_mode(); + void sending_mode(); + void prepare(int print_plate_idx); + bool Show(bool show); + + /* model */ + wxObjectDataPtr machine_model; + std::shared_ptr m_status_bar; + bool m_export_3mf_cancel{ false }; + bool m_is_canceled{ false }; + +protected: + std::vector m_list; + wxDataViewCtrl* m_dataViewListCtrl_machines{ nullptr }; + wxStaticText* m_staticText_left{ nullptr }; + wxHyperlinkCtrl* m_hyperlink_add_machine{ nullptr }; + wxGauge* m_gauge_job_progress{ nullptr }; + wxPanel* m_panel_status{ nullptr }; + wxButton* m_button_cancel{ nullptr }; + + std::string m_print_info; + int timeout_count = 0; + bool is_timeout(); + void reset_timeout(); + void update_user_printer(); + void update_show_status(); + + wxTimer* m_refresh_timer{ nullptr }; + + std::shared_ptr m_send_job{nullptr}; + + // Virtual event handlers, overide them in your derived class + void update_printer_combobox(wxCommandEvent& event); + void on_cancel(wxCloseEvent& event); + void on_ok(wxCommandEvent& event); + void on_refresh(wxCommandEvent& event); + void on_print_job_cancel(wxCommandEvent& evt); + void set_default(); + void on_timer(wxTimerEvent& event); + void on_selection_changed(wxCommandEvent& event); + void Enable_Refresh_Button(bool en); + void show_status(PrintDialogStatus status, std::vector params = std::vector()); + void Enable_Send_Button(bool en); + void on_dpi_changed(const wxRect& suggested_rect) override; + void update_user_machine_list(); + std::vector sort_string(std::vector strArray); +}; + +} +} + +#endif diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index a0a955e886..484754cb46 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -177,6 +177,7 @@ void StatusBasePanel::init_bitmaps() m_thumbnail_sdcard = ScalableBitmap(this, "monitor_sdcard_thumbnail", 120); //m_bitmap_camera = create_scaled_bitmap("monitor_camera", nullptr, 18); m_bitmap_extruder = *cache.load_png("monitor_extruder", FromDIP(28), FromDIP(70), false, false); + m_bitmap_extruder_load = *cache.load_png("monitor_extruder_load", FromDIP(28), FromDIP(70), false, false); m_bitmap_sdcard_state_on = create_scaled_bitmap("sdcard_state_on", nullptr, 20); m_bitmap_sdcard_state_off = create_scaled_bitmap("sdcard_state_off", nullptr, 20); } @@ -741,7 +742,7 @@ wxBoxSizer *StatusBasePanel::create_misc_control(wxWindow *parent) m_switch_nozzle_fan->SetLabels(_L("Part Cooling"), _L("Part Cooling")); m_switch_nozzle_fan->SetPadding(FromDIP(3)); m_switch_nozzle_fan->SetBorderWidth(FromDIP(2)); - m_switch_nozzle_fan->SetFont(SWITCH_FONT); + m_switch_nozzle_fan->SetFont(::Label::Body_10); m_switch_nozzle_fan->SetTextColor(StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_FAN_TEXT_COL, (int) StateColor::Normal))); line_sizer->Add(m_switch_nozzle_fan, 1, wxALIGN_CENTER | wxALL, 0); @@ -754,7 +755,7 @@ wxBoxSizer *StatusBasePanel::create_misc_control(wxWindow *parent) m_switch_printing_fan->SetMinSize(MISC_BUTTON_SIZE); m_switch_printing_fan->SetPadding(FromDIP(3)); m_switch_printing_fan->SetBorderWidth(FromDIP(2)); - m_switch_printing_fan->SetFont(SWITCH_FONT); + m_switch_printing_fan->SetFont(::Label::Body_10); m_switch_printing_fan->SetLabels(_L("Aux Cooling"), _L("Aux Cooling")); m_switch_printing_fan->SetTextColor( StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_FAN_TEXT_COL, (int) StateColor::Normal))); @@ -770,11 +771,11 @@ void StatusBasePanel::reset_temp_misc_control() { // reset temp string m_tempCtrl_nozzle->SetLabel(TEMP_BLANK_STR); - m_tempCtrl_nozzle->GetTextCtrl()->SetLabel(TEMP_BLANK_STR); + m_tempCtrl_nozzle->GetTextCtrl()->SetValue(TEMP_BLANK_STR); m_tempCtrl_bed->SetLabel(TEMP_BLANK_STR); - m_tempCtrl_bed->GetTextCtrl()->SetLabel(TEMP_BLANK_STR); + m_tempCtrl_bed->GetTextCtrl()->SetValue(TEMP_BLANK_STR); m_tempCtrl_frame->SetLabel(TEMP_BLANK_STR); - m_tempCtrl_frame->GetTextCtrl()->SetLabel(TEMP_BLANK_STR); + m_tempCtrl_frame->GetTextCtrl()->SetValue(TEMP_BLANK_STR); m_button_unload->Show(); m_tempCtrl_nozzle->Enable(true); @@ -1103,6 +1104,8 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co Bind(EVT_AMS_REFRESH_RFID, &StatusPanel::on_ams_refresh_rfid, this); Bind(EVT_AMS_ON_SELECTED, &StatusPanel::on_ams_selected, this); Bind(EVT_AMS_ON_FILAMENT_EDIT, &StatusPanel::on_filament_edit, this); + Bind(EVT_AMS_GUIDE_WIKI, &StatusPanel::on_ams_guide, this); + Bind(EVT_AMS_RETRY, &StatusPanel::on_ams_retry, this); m_switch_speed->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this); m_calibration_btn->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this); @@ -1305,6 +1308,13 @@ void StatusPanel::update(MachineObject *obj) update_temp_ctrl(obj); update_misc_ctrl(obj); + if (obj && obj->is_filament_at_extruder()) { + m_bitmap_extruder_img->SetBitmap(m_bitmap_extruder_load); + } + else { + m_bitmap_extruder_img->SetBitmap(m_bitmap_extruder); + } + // BBS hide tasklist info // update_tasklist(obj); update_ams(obj); @@ -1351,7 +1361,7 @@ void StatusPanel::update(MachineObject *obj) m_tempCtrl_frame->Enable(); } else { m_tempCtrl_frame->SetLabel(TEMP_BLANK_STR); - m_tempCtrl_frame->GetTextCtrl()->SetLabel(TEMP_BLANK_STR); + m_tempCtrl_frame->GetTextCtrl()->SetValue(TEMP_BLANK_STR); m_tempCtrl_frame->Disable(); } @@ -1386,6 +1396,7 @@ void StatusPanel::update_error_message() } if (before_error_code != obj->print_error) { + before_error_code = obj->print_error; if (wxGetApp().get_hms_query()) { char buf[32]; ::sprintf(buf, "%08X", obj->print_error); @@ -1398,11 +1409,11 @@ void StatusPanel::update_error_message() print_error_str); show_error_message(error_msg); //hint dialog - ConfirmHintDialog print_error_dlg(this->GetParent(), wxID_ANY, _L("Warning")); + BOOST_LOG_TRIVIAL(info) << "Print error! " << error_msg; + ConfirmHintDialog print_error_dlg(this->GetParent(), wxID_ANY, _L("Warning"), ConfirmHintDialog::ButtonStyle::ONLY_CONFIRM); print_error_dlg.SetHint(error_msg); print_error_dlg.ShowModal(); } - before_error_code = obj->print_error; } } @@ -1764,7 +1775,7 @@ void StatusPanel::update_cali(MachineObject *obj) { if (!obj) return; - if (obj->is_in_calibration()) { + if (obj->is_calibration_running()) { m_calibration_btn->SetLabel(_L("Calibrating")); if (calibration_dlg && calibration_dlg->IsShown()) { m_calibration_btn->Disable(); @@ -1840,7 +1851,8 @@ void StatusPanel::update_subtask(MachineObject *obj) { if (!obj) return; - if (obj->is_system_printing()) { + if (obj->is_system_printing() + || obj->is_in_calibration()) { reset_printing_values(); } else if (obj->is_in_printing() || obj->print_status == "FINISH") { if (obj->is_in_prepare()) { @@ -2103,11 +2115,17 @@ void StatusPanel::on_set_nozzle_temp() } void StatusPanel::on_ams_load(SimpleEvent &event) +{ + BOOST_LOG_TRIVIAL(info) << "on_ams_load"; + on_ams_load_curr(); +} + +void StatusPanel::on_ams_load_curr() { if (obj) { std::string curr_ams_id = m_ams_control->GetCurentAms(); std::string curr_can_id = m_ams_control->GetCurrentCan(curr_ams_id); - std::map::iterator it = obj->amsList.find(curr_ams_id); + std::map::iterator it = obj->amsList.find(curr_ams_id); if (it == obj->amsList.end()) { BOOST_LOG_TRIVIAL(trace) << "ams: find " << curr_ams_id << " failed"; return; @@ -2118,8 +2136,8 @@ void StatusPanel::on_ams_load(SimpleEvent &event) return; } - AmsTray *curr_tray = obj->get_curr_tray(); - AmsTray *targ_tray = obj->get_ams_tray(curr_ams_id, curr_can_id); + AmsTray* curr_tray = obj->get_curr_tray(); + AmsTray* targ_tray = obj->get_ams_tray(curr_ams_id, curr_can_id); if (curr_tray && targ_tray) { int old_temp = -1; int new_temp = -1; @@ -2128,12 +2146,14 @@ void StatusPanel::on_ams_load(SimpleEvent &event) old_temp = (atoi(curr_tray->nozzle_temp_min.c_str()) + atoi(curr_tray->nozzle_temp_max.c_str())) / 2; if (!targ_tray->nozzle_temp_max.empty() && !targ_tray->nozzle_temp_min.empty()) new_temp = (atoi(targ_tray->nozzle_temp_min.c_str()) + atoi(targ_tray->nozzle_temp_max.c_str())) / 2; - } catch (...) { + } + catch (...) { ; } int tray_index = atoi(curr_ams_id.c_str()) * 4 + atoi(tray_it->second->id.c_str()); obj->command_ams_switch(tray_index, old_temp, new_temp); - } else { + } + else { int tray_index = atoi(curr_ams_id.c_str()) * 4 + atoi(tray_it->second->id.c_str()); obj->command_ams_switch(tray_index, -1, -1); } @@ -2257,6 +2277,20 @@ void StatusPanel::on_ams_selected(wxCommandEvent &event) } } +void StatusPanel::on_ams_guide(wxCommandEvent& event) +{ + wxString ams_wiki_url = "https://wiki.bambulab.com/en/software/bambu-studio/use-ams-on-bambu-studio"; + wxLaunchDefaultBrowser(ams_wiki_url); +} + +void StatusPanel::on_ams_retry(wxCommandEvent& event) +{ + BOOST_LOG_TRIVIAL(info) << "on_ams_retry"; + if (obj) { + obj->command_ams_control("resume"); + } +} + void StatusPanel::on_bed_temp_kill_focus(wxFocusEvent &event) { event.Skip(); diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 4d4f4e4485..af182c7854 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -81,6 +81,7 @@ protected: ScalableBitmap m_bitmap_use_time; ScalableBitmap m_bitmap_use_weight; wxBitmap m_bitmap_extruder; + wxBitmap m_bitmap_extruder_load; CameraRecordingStatus m_state_recording{CameraRecordingStatus::RECORDING_NONE}; CameraTimelapseStatus m_state_timelapse{CameraTimelapseStatus::TIMELAPSE_NONE}; @@ -300,11 +301,14 @@ protected: /* extruder apis */ void on_ams_load(SimpleEvent &event); + void on_ams_load_curr(); void on_ams_unload(SimpleEvent &event); void on_ams_setting_click(SimpleEvent &event); void on_filament_edit(wxCommandEvent &event); void on_ams_refresh_rfid(wxCommandEvent &event); void on_ams_selected(wxCommandEvent &event); + void on_ams_guide(wxCommandEvent &event); + void on_ams_retry(wxCommandEvent &event); void on_switch_speed(wxCommandEvent &event); void on_lamp_switch(wxCommandEvent &event); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 88dea6e6fd..3a85200848 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -318,7 +318,7 @@ void Tab::create_preset_tab() this->GetParent()->Update(); this->GetParent()->Layout(); - wxGetApp().plater()->search(false, m_type, m_search_item, m_search_input, m_btn_search); + wxGetApp().plater()->search(false, m_type, m_top_panel->GetParent(), m_search_input, m_btn_search); m_search_input->SetFocus(); Thaw(); @@ -1382,19 +1382,14 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) auto timelapse_type = m_config->option>("timelapse_type"); bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth; if (!boost::any_cast(value) && timelapse_enabled) { - MessageDialog dlg(wxGetApp().plater(), _L("Prime tower is required by timeplase. Are you sure you want to disable both of them?"), + MessageDialog dlg(wxGetApp().plater(), _L("Prime tower is required by smooth timeplase. If whthout prime tower, there will be flaws on the model. Are you sure you want to disable prime tower?"), _L("Warning"), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) { - DynamicPrintConfig new_conf = *m_config; - new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(TimelapseType::tlNone)); - m_config_manipulation.apply(m_config, &new_conf); - wxGetApp().plater()->update(); - } - else { + if (dlg.ShowModal() == wxID_NO) { DynamicPrintConfig new_conf = *m_config; new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); m_config_manipulation.apply(m_config, &new_conf); } + wxGetApp().plater()->update(); } update_wiping_button_visibility(); } @@ -1403,7 +1398,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "timelapse_type") { bool wipe_tower_enabled = m_config->option("enable_prime_tower")->value; if (!wipe_tower_enabled && boost::any_cast(value) == int(TimelapseType::tlSmooth)) { - MessageDialog dlg(wxGetApp().plater(), _L("Prime tower is required by timelapse. Do you want to enable both of them?"), + MessageDialog dlg(wxGetApp().plater(), _L("Prime tower is required by smooth timelapse. If whthout prime tower, there will be flaws on the model. Do you want to enable prime tower?"), _L("Warning"), wxICON_WARNING | wxYES | wxNO); if (dlg.ShowModal() == wxID_YES) { DynamicPrintConfig new_conf = *m_config; @@ -1411,11 +1406,6 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) m_config_manipulation.apply(m_config, &new_conf); wxGetApp().plater()->update(); } - else { - DynamicPrintConfig new_conf = *m_config; - new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(TimelapseType::tlNone)); - m_config_manipulation.apply(m_config, &new_conf); - } } else { wxGetApp().plater()->update(); } @@ -1515,8 +1505,14 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category) Field* field = get_field(opt_key); // focused selected field - if (field) + if (field) { set_focus(field->getWindow()); + if (!field->getWindow()->HasFocus()) { + wxScrollEvent evt(wxEVT_SCROLL_CHANGED); + evt.SetEventObject(field->getWindow()); + wxPostEvent(m_page_view, evt); + } + } //else if (category == "Single extruder MM setup") { // // When we show and hide "Single extruder MM setup" page, // // related options are still in the search list @@ -1807,9 +1803,9 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Infill"), L"param_infill"); optgroup->append_single_option_line("sparse_infill_density"); - optgroup->append_single_option_line("sparse_infill_pattern"); - optgroup->append_single_option_line("top_surface_pattern"); - optgroup->append_single_option_line("bottom_surface_pattern"); + optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse"); + optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); + optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface"); optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); optgroup->append_single_option_line("infill_wall_overlap"); @@ -1932,19 +1928,7 @@ void TabPrint::build() optgroup->append_single_option_line("print_sequence"); optgroup->append_single_option_line("spiral_mode", "spiral-vase"); optgroup->append_single_option_line("timelapse_type", "Timelapse"); - //BBS: todo remove clearance to machine -#if 0 - //line = { L("Extruder radius"), "" }; - //line.append_option(optgroup->get_option("extruder_clearance_radius")); - //optgroup->append_line(line); - ////BBS: new line for extruder_clearance_height_to_lid as there is not enough space for a single line - //line = { L("Height to rod"), "" }; - //line.append_option(optgroup->get_option("extruder_clearance_height_to_rod")); - //optgroup->append_line(line); - //line = { L("Height to lid"), "" }; - //line.append_option(optgroup->get_option("extruder_clearance_height_to_lid")); - //optgroup->append_line(line); -#endif + optgroup->append_single_option_line("fuzzy_skin"); optgroup->append_single_option_line("fuzzy_skin_point_distance"); optgroup->append_single_option_line("fuzzy_skin_thickness"); @@ -2888,6 +2872,11 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("machine_load_filament_time"); optgroup->append_single_option_line("machine_unload_filament_time"); + optgroup = page->new_optgroup(L("Extruder Clearance")); + optgroup->append_single_option_line("extruder_clearance_radius"); + optgroup->append_single_option_line("extruder_clearance_height_to_rod"); + optgroup->append_single_option_line("extruder_clearance_height_to_lid"); + optgroup = page->new_optgroup(L("Accessory") /*, L"param_accessory"*/); optgroup->append_single_option_line("nozzle_type"); optgroup->append_single_option_line("auxiliary_fan"); @@ -2953,6 +2942,15 @@ void TabPrinter::build_fff() option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Template Custom G-code"), L"param_gcode", 0); + optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup, opt_key, value); + }; + option = optgroup->get_option("template_custom_gcode"); + option.opt.is_code = true; + option.opt.height = gcode_field_height;//150; + optgroup->append_single_option_line(option); + #if 0 //page = add_options_page(L("Dependencies"), "advanced"); // optgroup = page->new_optgroup(L("Profile dependencies")); @@ -3417,17 +3415,26 @@ void TabPrinter::toggle_options() if (!m_active_page || m_presets->get_edited_preset().printer_technology() == ptSLA) return; - bool have_multiple_extruders = m_extruders_count > 1; - if (m_active_page->title() == "Custom G-code") { - toggle_option("change_filament_gcode", have_multiple_extruders); + //BBS: whether the preset is Bambu Lab printer + bool is_BBL_printer = false; + if (m_preset_bundle) { + is_BBL_printer = m_preset_bundle->printers.get_edited_preset().is_bbl_vendor_preset(m_preset_bundle); } - if (m_active_page->title() == "General") { + + bool have_multiple_extruders = m_extruders_count > 1; + //if (m_active_page->title() == "Custom G-code") { + // toggle_option("change_filament_gcode", have_multiple_extruders); + //} + if (m_active_page->title() == "Basic information") { toggle_option("single_extruder_multi_material", have_multiple_extruders); auto flavor = m_config->option>("gcode_flavor")->value; bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware; // Disable silent mode for non-marlin firmwares. toggle_option("silent_mode", is_marlin_flavor); + //BBS: extruder clearance of BBL printer can't be edited. + for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) + toggle_option(el, !is_BBL_printer); } wxString extruder_number; @@ -3476,9 +3483,10 @@ void TabPrinter::toggle_options() || m_config->option>("gcode_flavor")->value == gcfMarlinFirmware); bool silent_mode = m_config->opt_bool("silent_mode"); int max_field = silent_mode ? 2 : 1; + //BBS: limits of BBL printer can't be edited. for (const std::string &opt : Preset::machine_limits_options()) for (int i = 0; i < max_field; ++ i) - toggle_option(opt, true, i); + toggle_option(opt, !is_BBL_printer, i); } } @@ -4285,12 +4293,21 @@ void Tab::compare_preset() // Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method // opens a Slic3r::GUI::SavePresetDialog dialog. //BBS: add project embedded preset relate logic -void Tab::save_preset(std::string name /*= ""*/, bool detach, bool save_to_project) +void Tab::save_preset(std::string name /*= ""*/, bool detach, bool save_to_project, bool from_input, std::string input_name ) { // since buttons(and choices too) don't get focus on Mac, we set focus manually // to the treectrl so that the EVT_* events are fired for the input field having // focus currently.is there anything better than this ? //! m_tabctrl->OnSetFocus(); + if (from_input) { + SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : ""); + dlg.Show(false); + dlg.input_name_from_other(input_name); + wxCommandEvent evt(wxEVT_TEXT, GetId()); + dlg.GetEventHandler()->ProcessEvent(evt); + dlg.confirm_from_other(); + name = input_name; + } if (name.empty()) { SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : ""); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index d490b06854..512e77a8e8 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -333,7 +333,7 @@ public: void compare_preset(); //BBS: add project embedded preset relate logic - void save_preset(std::string name = std::string(), bool detach = false, bool save_to_project = false); + void save_preset(std::string name = std::string(), bool detach = false, bool save_to_project = false, bool from_input = false, std::string input_name = ""); //void save_preset(std::string name = std::string(), bool detach = false); void delete_preset(); diff --git a/src/slic3r/GUI/UpgradePanel.cpp b/src/slic3r/GUI/UpgradePanel.cpp index b46ad150c5..7dc39ae083 100644 --- a/src/slic3r/GUI/UpgradePanel.cpp +++ b/src/slic3r/GUI/UpgradePanel.cpp @@ -228,6 +228,7 @@ wxPanel *MachineInfoPanel::create_caption_panel(wxWindow *parent) m_upgrade_status_img = new wxStaticBitmap(caption_panel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(5), FromDIP(5))); m_upgrade_status_img->SetBitmap(upgrade_gray_icon); + m_upgrade_status_img->Hide(); m_caption_sizer->Add(m_upgrade_status_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); m_caption_text = new wxStaticText(caption_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize); diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index 13affe5736..b50a9d7e71 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -649,6 +649,34 @@ int GuideFrame::SaveProfile() return 0; } +static std::set get_new_added_presets(const std::map& old_data, const std::map& new_data) +{ + auto get_aliases = [](const std::map& data) { + std::set old_aliases; + for (auto item : data) { + const std::string& name = item.first; + size_t pos = name.find("@"); + old_aliases.emplace(pos == std::string::npos ? name : name.substr(0, pos-1)); + } + return old_aliases; + }; + + std::set old_aliases = get_aliases(old_data); + std::set new_aliases = get_aliases(new_data); + std::set diff; + std::set_difference(new_aliases.begin(), new_aliases.end(), old_aliases.begin(), old_aliases.end(), std::inserter(diff, diff.begin())); + + return diff; +} + +static std::string get_first_added_preset(const std::map& old_data, const std::map& new_data) +{ + std::set diff = get_new_added_presets(old_data, new_data); + if (diff.empty()) + return std::string(); + return *diff.begin(); +} + bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes) { const auto enabled_vendors = m_appconfig_new.vendors(); @@ -714,13 +742,67 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle BOOST_LOG_TRIVIAL(info) << "No bundles need to be removed"; } + std::string preferred_model; + std::string preferred_variant; + PrinterTechnology preferred_pt = ptFFF; + auto get_preferred_printer_model = [preset_bundle, enabled_vendors, old_enabled_vendors, preferred_pt](const std::string& bundle_name, std::string& variant) { + const auto config = enabled_vendors.find(bundle_name); + if (config == enabled_vendors.end()) + return std::string(); + + const std::map>& model_maps = config->second; + //for (const auto& vendor_profile : preset_bundle->vendors) { + for (const auto model_it: model_maps) { + if (model_it.second.size() > 0) { + variant = *model_it.second.begin(); + const auto config_old = old_enabled_vendors.find(bundle_name); + if (config_old == old_enabled_vendors.end()) + return model_it.first; + const auto model_it_old = config_old->second.find(model_it.first); + if (model_it_old == config_old->second.end()) + return model_it.first; + else if (model_it_old->second != model_it.second) { + for (const auto& var : model_it.second) + if (model_it_old->second.find(var) == model_it_old->second.end()) { + variant = var; + return model_it.first; + } + } + } + } + //} + if (!variant.empty()) + variant.clear(); + return std::string(); + }; + // Prusa printers are considered first, then 3rd party. + if (preferred_model = get_preferred_printer_model(PresetBundle::BBL_BUNDLE, preferred_variant); + preferred_model.empty()) { + for (const auto& bundle : enabled_vendors) { + if (bundle.first == PresetBundle::BBL_BUNDLE) { continue; } + if (preferred_model = get_preferred_printer_model(bundle.first, preferred_variant); + !preferred_model.empty()) + break; + } + } + + std::string first_added_filament; + auto get_first_added_material_preset = [this, app_config](const std::string& section_name, std::string& first_added_preset) { + if (m_appconfig_new.has_section(section_name)) { + // get first of new added preset names + const std::map& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map(); + first_added_preset = get_first_added_preset(old_presets, m_appconfig_new.get_section(section_name)); + } + }; + get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament); + //update the app_config app_config->set_section(AppConfig::SECTION_FILAMENTS, enabled_filaments); app_config->set_vendors(m_appconfig_new); if (check_unsaved_preset_changes) preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, - {PresetBundle::BBL_DEFAULT_PRINTER_MODEL, PresetBundle::BBL_DEFAULT_PRINTER_VARIANT, PresetBundle::BBL_DEFAULT_FILAMENT, std::string()}); + {preferred_model, preferred_variant, first_added_filament, std::string()}); // Update the selections from the compatibilty. preset_bundle->export_selections(*app_config); @@ -902,6 +984,7 @@ int GuideFrame::LoadProfile() //} while (_findnext(handle, &findData) == 0); // 查找目录中的下一个文件 + //load BBL bundle from user data path string targetPath = bbl_bundle_path.make_preferred().string(); boost::filesystem::path myPath(targetPath); boost::filesystem::directory_iterator endIter; @@ -914,11 +997,30 @@ int GuideFrame::LoadProfile() //cout << iter->path().string() << endl; wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); strVendor = strVendor.AfterLast( '\\'); - strVendor = strVendor.AfterLast('\/'); + strVendor = strVendor.AfterLast('\/'); - LoadProfileFamily(w2s(strVendor), iter->path().string()); + if (w2s(strVendor) == PresetBundle::BBL_BUNDLE) + LoadProfileFamily(w2s(strVendor), iter->path().string()); } + } + + //string others_targetPath = rsrc_vendor_dir.string(); + boost::filesystem::directory_iterator others_endIter; + for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; iter++) { + if (boost::filesystem::is_directory(*iter)) { + //cout << "is dir" << endl; + //cout << iter->path().string() << endl; + } else { + //cout << "is a file" << endl; + //cout << iter->path().string() << endl; + wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); + strVendor = strVendor.AfterLast( '\\'); + strVendor = strVendor.AfterLast('\/'); + + if (w2s(strVendor) != PresetBundle::BBL_BUNDLE) + LoadProfileFamily(w2s(strVendor), iter->path().string()); } + } //LoadProfileFamily(PresetBundle::BBL_BUNDLE, bbl_bundle_path.string()); @@ -1309,15 +1411,15 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath json pPrinters = pm["compatible_printers"]; int nPrinter = pPrinters.size(); std::string ModelList = ""; - for (int i = 0; i < nPrinter; i++) - { + for (int i = 0; i < nPrinter; i++) + { std::string sP = pPrinters.at(i); - if (m_ProfileJson["machine"].contains(sP)) + if (m_ProfileJson["machine"].contains(sP)) { std::string mModel = m_ProfileJson["machine"][sP]["model"]; std::string mNozzle = m_ProfileJson["machine"][sP]["nozzle"]; std::string NewModel = mModel + "++" + mNozzle; - + ModelList = (boost::format("%1%[%2%]") % ModelList % NewModel).str(); } } diff --git a/src/slic3r/GUI/Widgets/AMSControl.cpp b/src/slic3r/GUI/Widgets/AMSControl.cpp index 89a70ebe73..e0f45aa5e2 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.cpp +++ b/src/slic3r/GUI/Widgets/AMSControl.cpp @@ -31,6 +31,8 @@ wxDEFINE_EVENT(EVT_AMS_ON_SELECTED, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_ON_FILAMENT_EDIT, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_CLIBRATION_AGAIN, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_CLIBRATION_CANCEL, wxCommandEvent); +wxDEFINE_EVENT(EVT_AMS_GUIDE_WIKI, wxCommandEvent); +wxDEFINE_EVENT(EVT_AMS_RETRY, wxCommandEvent); inline int hex_digit_to_int(const char c) { @@ -1467,14 +1469,26 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_simplebook_right->AddPage(m_filament_unload_step, wxEmptyString, false); wxBoxSizer *m_sizer_right_bottom = new wxBoxSizer(wxHORIZONTAL); - m_sizer_right_bottom->Add(0, 0, 1, wxEXPAND, FromDIP(5)); - m_button_ams_setting = new Button(m_amswin, _L("AMS Settings")); - m_button_ams_setting->SetBackgroundColor(btn_bg_white); - m_button_ams_setting->SetBorderColor(btn_bd_white); - m_button_ams_setting->SetFont(Label::Body_13); - //m_button_ams_setting->Hide(); - m_sizer_right_bottom->Add(m_button_ams_setting, 0, wxTOP, FromDIP(20)); - m_sizer_right->Add(m_sizer_right_bottom, 0, wxEXPAND, FromDIP(5)); + m_button_ams_setting = new Button(m_amswin, "", "ams_setting_normal", wxBORDER_NONE, FromDIP(24)); + m_button_ams_setting->SetPaddingSize(wxSize(0, 0)); + m_button_ams_setting->SetBackgroundColor(m_amswin->GetBackgroundColour()); + + m_button_guide = new Button(m_amswin, _L("Guide")); + m_button_guide->SetFont(Label::Body_13); + m_button_guide->SetCornerRadius(FromDIP(12)); + m_button_guide->SetMinSize(wxSize(-1, FromDIP(24))); + m_button_guide->SetBackgroundColor(btn_bg_white); + + m_button_retry = new Button(m_amswin, _L("Retry")); + m_button_retry->SetFont(Label::Body_13); + m_button_retry->SetCornerRadius(FromDIP(12)); + m_button_retry->SetMinSize(wxSize(-1, FromDIP(24))); + m_button_retry->SetBackgroundColor(btn_bg_white); + + m_sizer_right_bottom->Add(m_button_ams_setting, 0); + m_sizer_right_bottom->Add(m_button_guide, 0, wxLEFT, FromDIP(10)); + m_sizer_right_bottom->Add(m_button_retry, 0, wxLEFT, FromDIP(10)); + m_sizer_right->Add(m_sizer_right_bottom, 0, wxEXPAND | wxTOP, FromDIP(20)); m_sizer_bottom->Add(m_sizer_right, 0, wxEXPAND, FromDIP(5)); m_sizer_body->Add(m_simplebook_amsitems, 0, wxEXPAND, 0); @@ -1485,7 +1499,6 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_amswin->SetSizer(m_sizer_body); m_amswin->Layout(); m_amswin->Fit(); - //Thaw(); SetSize(m_amswin->GetSize()); @@ -1566,6 +1579,25 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_button_extruder_feed->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AMSControl::on_filament_load), NULL, this); m_button_extruder_back->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AMSControl::on_filament_unload), NULL, this); m_button_ams_setting->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AMSControl::on_ams_setting_click), NULL, this); + m_button_ams_setting->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& e) { + m_button_ams_setting->SetIcon("ams_setting_hover"); + e.Skip(); + }); + m_button_ams_setting->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) { + m_button_ams_setting->SetIcon("ams_setting_press"); + e.Skip(); + }); + m_button_ams_setting->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent&e) { + m_button_ams_setting->SetIcon("ams_setting_normal"); + e.Skip(); + }); + + m_button_guide->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + post_event(wxCommandEvent(EVT_AMS_GUIDE_WIKI)); + }); + m_button_retry->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + post_event(wxCommandEvent(EVT_AMS_RETRY)); + }); CreateAms(); SetSelection(0); @@ -1578,8 +1610,6 @@ void AMSControl::init_scaled_buttons() m_button_extruder_feed->SetCornerRadius(FromDIP(12)); m_button_extruder_back->SetMinSize(wxSize(-1, FromDIP(24))); m_button_extruder_back->SetCornerRadius(FromDIP(12)); - m_button_ams_setting->SetMinSize(wxSize(-1, FromDIP(33))); - m_button_ams_setting->SetCornerRadius(FromDIP(12)); } std::string AMSControl::GetCurentAms() { return m_current_ams; } @@ -1709,7 +1739,9 @@ void AMSControl::msw_rescale() m_extruder->msw_rescale(); m_button_extruder_back->SetMinSize(wxSize(-1, FromDIP(24))); m_button_extruder_feed->SetMinSize(wxSize(-1, FromDIP(24))); - m_button_ams_setting->SetMinSize(wxSize(-1, FromDIP(33))); + m_button_ams_setting->SetMinSize(wxSize(FromDIP(25), FromDIP(24))); + m_button_guide->SetMinSize(wxSize(-1, FromDIP(24))); + m_button_retry->SetMinSize(wxSize(-1, FromDIP(24))); for (auto i = 0; i < m_ams_cans_list.GetCount(); i++) { AmsCansWindow *cans = m_ams_cans_list[i]; diff --git a/src/slic3r/GUI/Widgets/AMSControl.hpp b/src/slic3r/GUI/Widgets/AMSControl.hpp index 45f4670c96..5694b076dc 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.hpp +++ b/src/slic3r/GUI/Widgets/AMSControl.hpp @@ -461,7 +461,9 @@ protected: Button *m_button_extruder_feed = {nullptr}; Button *m_button_extruder_back = {nullptr}; - Button *m_button_ams_setting = {nullptr}; + Button* m_button_ams_setting = {nullptr}; + Button *m_button_guide = {nullptr}; + Button *m_button_retry = {nullptr}; wxHyperlinkCtrl *m_hyperlink = {nullptr}; @@ -518,6 +520,8 @@ wxDECLARE_EVENT(EVT_AMS_ON_SELECTED, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_ON_FILAMENT_EDIT, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_CLIBRATION_AGAIN, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_CLIBRATION_CANCEL, wxCommandEvent); +wxDECLARE_EVENT(EVT_AMS_GUIDE_WIKI, wxCommandEvent); +wxDECLARE_EVENT(EVT_AMS_RETRY, wxCommandEvent); }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp index be988020d7..fa1dc15ee2 100644 --- a/src/slic3r/GUI/Widgets/Button.cpp +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -7,6 +7,7 @@ BEGIN_EVENT_TABLE(Button, StaticBox) EVT_LEFT_DOWN(Button::mouseDown) EVT_LEFT_UP(Button::mouseReleased) +EVT_MOUSE_CAPTURE_LOST(Button::mouseCaptureLost) EVT_KEY_DOWN(Button::keyDownUp) EVT_KEY_UP(Button::keyDownUp) @@ -23,7 +24,6 @@ END_EVENT_TABLE() Button::Button() : paddingSize(10, 8) - , text_color(*wxBLACK) { background_color = StateColor( std::make_pair(0xF0F0F0, (int) StateColor::Disabled), @@ -31,6 +31,9 @@ Button::Button() std::make_pair(0x00AE42, (int) StateColor::Checked), std::make_pair(*wxLIGHT_GREY, (int) StateColor::Hovered), std::make_pair(*wxWHITE, (int) StateColor::Normal)); + text_color = StateColor( + std::make_pair(*wxLIGHT_GREY, (int) StateColor::Disabled), + std::make_pair(*wxBLACK, (int) StateColor::Normal)); } Button::Button(wxWindow* parent, wxString text, wxString icon, long style, int iconSize) @@ -242,12 +245,19 @@ void Button::mouseReleased(wxMouseEvent& event) event.Skip(); if (pressedDown) { pressedDown = false; - ReleaseMouse(); + if (HasCapture()) + ReleaseMouse(); if (wxRect({0, 0}, GetSize()).Contains(event.GetPosition())) sendButtonEvent(); } } +void Button::mouseCaptureLost(wxMouseCaptureLostEvent &event) +{ + wxMouseEvent evt; + mouseReleased(evt); +} + void Button::keyDownUp(wxKeyEvent &event) { if (event.GetKeyCode() == WXK_SPACE || event.GetKeyCode() == WXK_RETURN) { diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index 5ab6e4cd9a..43aac6b9fb 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -67,6 +67,7 @@ private: // some useful events void mouseDown(wxMouseEvent& event); void mouseReleased(wxMouseEvent& event); + void mouseCaptureLost(wxMouseCaptureLostEvent &event); void keyDownUp(wxKeyEvent &event); void sendButtonEvent(); diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 0b8e27d6fb..01d7d9614c 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -236,7 +236,7 @@ void ComboBox::keyDown(wxKeyEvent& event) case WXK_RETURN: case WXK_SPACE: if (drop_down) { - drop.Hide(); + drop.DismissAndNotify(); } else if (drop.HasDismissLongTime()) { drop.autoPosition(); drop_down = true; diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index a7ed6be9fb..c0074b531d 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -9,6 +9,7 @@ BEGIN_EVENT_TABLE(DropDown, wxPopupTransientWindow) EVT_LEFT_DOWN(DropDown::mouseDown) EVT_LEFT_UP(DropDown::mouseReleased) +EVT_MOUSE_CAPTURE_LOST(DropDown::mouseCaptureLost) EVT_MOTION(DropDown::mouseMove) EVT_MOUSEWHEEL(DropDown::mouseWheelMoved) @@ -377,6 +378,7 @@ void DropDown::mouseDown(wxMouseEvent& event) // force calc hover item again mouseMove(event); pressedDown = true; + CaptureMouse(); dragStart = event.GetPosition(); } @@ -385,6 +387,8 @@ void DropDown::mouseReleased(wxMouseEvent& event) if (pressedDown) { dragStart = wxPoint(); pressedDown = false; + if (HasCapture()) + ReleaseMouse(); if (hover_item >= 0) { // not moved sendDropDownEvent(); DismissAndNotify(); @@ -392,6 +396,12 @@ void DropDown::mouseReleased(wxMouseEvent& event) } } +void DropDown::mouseCaptureLost(wxMouseCaptureLostEvent &event) +{ + wxMouseEvent evt; + mouseReleased(evt); +} + void DropDown::mouseMove(wxMouseEvent &event) { wxPoint pt = event.GetPosition(); diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index 60a404b5de..b2b7eaa73e 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -98,6 +98,7 @@ private: // some useful events void mouseDown(wxMouseEvent& event); void mouseReleased(wxMouseEvent &event); + void mouseCaptureLost(wxMouseCaptureLostEvent &event); void mouseMove(wxMouseEvent &event); void mouseWheelMoved(wxMouseEvent &event); diff --git a/src/slic3r/GUI/Widgets/Label.cpp b/src/slic3r/GUI/Widgets/Label.cpp index d2b04e4c7b..4a541f3478 100644 --- a/src/slic3r/GUI/Widgets/Label.cpp +++ b/src/slic3r/GUI/Widgets/Label.cpp @@ -109,6 +109,8 @@ Label::Label(wxWindow *parent, wxFont const &font, wxString const &text, long st void Label::SetWindowStyleFlag(long style) { + if (style == GetWindowStyle()) + return; wxStaticText::SetWindowStyleFlag(style); if (style & LB_HYPERLINK) { this->color = GetForegroundColour(); diff --git a/src/slic3r/GUI/Widgets/SideButton.cpp b/src/slic3r/GUI/Widgets/SideButton.cpp index eb6b57e5d3..b69a42f2f3 100644 --- a/src/slic3r/GUI/Widgets/SideButton.cpp +++ b/src/slic3r/GUI/Widgets/SideButton.cpp @@ -171,7 +171,9 @@ void SideButton::paintEvent(wxPaintEvent& evt) #else wxDC & dc2(dc); #endif - render(dc2); + + wxDC & dctext(dc); + dorender(dc2, dctext); } /* @@ -179,7 +181,7 @@ void SideButton::paintEvent(wxPaintEvent& evt) * method so that it can work no matter what type of DC * (e.g. wxPaintDC or wxClientDC) is used. */ -void SideButton::render(wxDC& dc) +void SideButton::dorender(wxDC& dc, wxDC& text_dc) { wxSize size = GetSize(); @@ -266,9 +268,10 @@ void SideButton::render(wxDC& dc) auto text = GetLabel(); if (!text.IsEmpty()) { pt.y += (rcContent.height - textSize.y) / 2; - dc.SetFont(GetFont()); - dc.SetTextForeground(text_color.colorForStates(states)); - dc.DrawText(text, pt); + + text_dc.SetFont(GetFont()); + text_dc.SetTextForeground(text_color.colorForStates(states)); + text_dc.DrawText(text, pt); } } diff --git a/src/slic3r/GUI/Widgets/SideButton.hpp b/src/slic3r/GUI/Widgets/SideButton.hpp index b7b4fe03fe..4f8d893f93 100644 --- a/src/slic3r/GUI/Widgets/SideButton.hpp +++ b/src/slic3r/GUI/Widgets/SideButton.hpp @@ -79,7 +79,7 @@ private: void paintEvent(wxPaintEvent& evt); - void render(wxDC& dc); + void dorender(wxDC& dc, wxDC& text_dc); void messureSize(); diff --git a/src/slic3r/GUI/Widgets/StaticBox.cpp b/src/slic3r/GUI/Widgets/StaticBox.cpp index e318a3916f..1a806e0007 100644 --- a/src/slic3r/GUI/Widgets/StaticBox.cpp +++ b/src/slic3r/GUI/Widgets/StaticBox.cpp @@ -18,9 +18,11 @@ END_EVENT_TABLE() StaticBox::StaticBox() : state_handler(this) - , border_color(0x303A3C) , radius(8) { + border_color = StateColor( + std::make_pair(*wxLIGHT_GREY, (int) StateColor::Disabled), + std::make_pair(0x303A3C, (int) StateColor::Normal)); } StaticBox::StaticBox(wxWindow* parent, @@ -62,6 +64,12 @@ void StaticBox::SetBorderColor(StateColor const &color) Refresh(); } +void StaticBox::SetBorderColorNormal(wxColor const &color) +{ + border_color.setColorForStates(color, 0); + Refresh(); +} + void StaticBox::SetBackgroundColor(StateColor const &color) { background_color = color; @@ -69,6 +77,12 @@ void StaticBox::SetBackgroundColor(StateColor const &color) Refresh(); } +void StaticBox::SetBackgroundColorNormal(wxColor const &color) +{ + background_color.setColorForStates(color, 0); + Refresh(); +} + void StaticBox::SetBackgroundColor2(StateColor const &color) { background_color2 = color; diff --git a/src/slic3r/GUI/Widgets/StaticBox.hpp b/src/slic3r/GUI/Widgets/StaticBox.hpp index e3e3f06849..871c5651d9 100644 --- a/src/slic3r/GUI/Widgets/StaticBox.hpp +++ b/src/slic3r/GUI/Widgets/StaticBox.hpp @@ -29,8 +29,12 @@ public: void SetBorderColor(StateColor const & color); + void SetBorderColorNormal(wxColor const &color); + void SetBackgroundColor(StateColor const &color); + void SetBackgroundColorNormal(wxColor const &color); + void SetBackgroundColor2(StateColor const &color); static wxColor GetParentBackgroundColor(wxWindow * parent); diff --git a/src/slic3r/GUI/Widgets/StepCtrl.cpp b/src/slic3r/GUI/Widgets/StepCtrl.cpp index e64c9fb0fe..fa47545815 100644 --- a/src/slic3r/GUI/Widgets/StepCtrl.cpp +++ b/src/slic3r/GUI/Widgets/StepCtrl.cpp @@ -8,6 +8,7 @@ BEGIN_EVENT_TABLE(StepCtrl, StepCtrlBase) EVT_LEFT_DOWN(StepCtrl::mouseDown) EVT_MOTION(StepCtrl::mouseMove) EVT_LEFT_UP(StepCtrl::mouseUp) +EVT_MOUSE_CAPTURE_LOST(StepCtrl::mouseCaptureLost) END_EVENT_TABLE() StepCtrlBase::StepCtrlBase(wxWindow * parent, @@ -133,6 +134,7 @@ void StepCtrl::mouseDown(wxMouseEvent &event) if (rcThumb.Contains(pt)) { pos_thumb = wxPoint{circleX, size.y / 2}; drag_offset = pos_thumb - pt; + CaptureMouse(); } else if (rcBar.Contains(pt)) { if (pt.x < circleX) { if (step > 0) SelectItem(step - 1); @@ -151,6 +153,10 @@ void StepCtrl::mouseMove(wxMouseEvent &event) wxSize size = GetSize(); int itemWidth = size.x / steps.size(); int index = pos_thumb.x / itemWidth; + if (index < 0) + index = 0; + else if (index >= steps.size()) + index = steps.size() - 1; if (index != pos_thumb.y) { pos_thumb.y = index; Refresh(); @@ -163,8 +169,20 @@ void StepCtrl::mouseUp(wxMouseEvent &event) wxSize size = GetSize(); int itemWidth = size.x / steps.size(); int index = pos_thumb.x / itemWidth; - pos_thumb = {0, 0}; - SelectItem(index < steps.size() ? index : steps.size() - 1); + if (index < 0) + index = 0; + else if (index >= steps.size()) + index = steps.size() - 1; + pos_thumb = {0, 0}; + SelectItem(index); + if (HasCapture()) + ReleaseMouse(); +} + +void StepCtrl::mouseCaptureLost(wxMouseCaptureLostEvent &event) +{ + wxMouseEvent evt; + mouseUp(evt); } void StepCtrl::doRender(wxDC &dc) diff --git a/src/slic3r/GUI/Widgets/StepCtrl.hpp b/src/slic3r/GUI/Widgets/StepCtrl.hpp index 4986e39ca2..c7c5ebd475 100644 --- a/src/slic3r/GUI/Widgets/StepCtrl.hpp +++ b/src/slic3r/GUI/Widgets/StepCtrl.hpp @@ -77,6 +77,7 @@ private: void mouseDown(wxMouseEvent &event); void mouseMove(wxMouseEvent &event); void mouseUp(wxMouseEvent &event); + void mouseCaptureLost(wxMouseCaptureLostEvent &event); void doRender(wxDC &dc) override; diff --git a/src/slic3r/GUI/wxMediaCtrl2.cpp b/src/slic3r/GUI/wxMediaCtrl2.cpp index 3bcfca1bda..a4d2bed127 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.cpp +++ b/src/slic3r/GUI/wxMediaCtrl2.cpp @@ -4,6 +4,7 @@ #ifdef __WIN32__ #include #include +#include #endif wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent) @@ -25,6 +26,8 @@ wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent) wxMediaCtrl::Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxMEDIACTRLPLAYERCONTROLS_NONE); } +#define CLSID_BAMBU_SOURCE L"{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}" + void wxMediaCtrl2::Load(wxURI url) { #ifdef __WIN32__ @@ -46,16 +49,33 @@ void wxMediaCtrl2::Load(wxURI url) return; } { - wxRegKey key1(wxRegKey::HKCR, "CLSID\\{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}\\InProcServer32"); + wxRegKey key1(wxRegKey::HKCR, L"CLSID\\" CLSID_BAMBU_SOURCE L"\\InProcServer32"); wxString path = key1.QueryDefaultValue(); wxRegKey key2(wxRegKey::HKCR, "bambu"); wxString clsid; key2.QueryRawValue("Source Filter", clsid); - if (!wxFile::Exists(path) || clsid != L"{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}") { - Slic3r::GUI::wxGetApp().CallAfter([] { - wxMessageBox(_L("Missing BambuSource component registered for media playing! Please re-install BambuStutio or seek after-sales help."), _L("Error"), - wxOK); - }); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": clsid %1% path %2%") % clsid % path; + + if (path.empty() || !wxFile::Exists(path) || clsid != CLSID_BAMBU_SOURCE) { + if (clsid != CLSID_BAMBU_SOURCE || path.empty()) { + std::string data_dir_str = Slic3r::data_dir(); + boost::filesystem::path data_dir_path(data_dir_str); + auto dll_path = data_dir_path / "plugins" / "BambuSource.dll"; + if (boost::filesystem::exists(dll_path)) { + Slic3r::GUI::wxGetApp().CallAfter( + [dll_path] { + int res = wxMessageBox(_L("BambuSource has not correctly been registered for media playing! Press Yes to re-register it."), _L("Error"), wxYES_NO); + if (res == wxYES) { + SHELLEXECUTEINFO info{sizeof(info), 0, NULL, L"runas", L"regsvr32", dll_path.wstring().c_str(), SW_HIDE }; + ::ShellExecuteEx(&info); + } + }); + } + } else { + Slic3r::GUI::wxGetApp().CallAfter([] { + wxMessageBox(_L("Missing BambuSource component registered for media playing! Please re-install BambuStutio or seek after-sales help."), _L("Error"), wxOK); + }); + } m_error = clsid != L"{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}" ? 101 : path.empty() ? 102 : 103; wxMediaEvent event(wxEVT_MEDIA_STATECHANGED); event.SetId(GetId()); diff --git a/src/slic3r/Utils/MacDarkMode.hpp b/src/slic3r/Utils/MacDarkMode.hpp index 7cdc4e6f73..6696bf45ee 100644 --- a/src/slic3r/Utils/MacDarkMode.hpp +++ b/src/slic3r/Utils/MacDarkMode.hpp @@ -11,8 +11,10 @@ extern bool mac_dark_mode(); extern double mac_max_scaling_factor(); extern void set_miniaturizable(void * window); void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*callback)(wxString const &)); -void set_title_colour_after_set_title(); +void set_tag_when_enter_full_screen(bool isfullscreen); +void set_title_colour_after_set_title(void * window); void initGestures(void * view, wxEvtHandler * handler); +void openFolderForFile(wxString const & file); #endif diff --git a/src/slic3r/Utils/MacDarkMode.mm b/src/slic3r/Utils/MacDarkMode.mm index 5750358dda..2479d8e125 100644 --- a/src/slic3r/Utils/MacDarkMode.mm +++ b/src/slic3r/Utils/MacDarkMode.mm @@ -20,6 +20,7 @@ namespace Slic3r { namespace GUI { NSTextField* mainframe_text_field = nil; +bool is_in_full_screen_mode = false; bool mac_dark_mode() { @@ -60,11 +61,25 @@ void set_miniaturizable(void * window) } } -void set_title_colour_after_set_title() +void set_tag_when_enter_full_screen(bool isfullscreen) { - if(mainframe_text_field){ - [(NSTextField*)mainframe_text_field setTextColor : NSColor.whiteColor]; + is_in_full_screen_mode = isfullscreen; +} + +void set_title_colour_after_set_title(void * window) +{ + NSEnumerator *viewEnum = [[[[[[[(NSView*) window window] contentView] superview] titlebarViewController] view] subviews] objectEnumerator]; + NSView *viewObject; + while(viewObject = (NSView *)[viewEnum nextObject]) { + if([viewObject class] == [NSTextField self]) { + [(NSTextField*)viewObject setTextColor : NSColor.whiteColor]; + mainframe_text_field = viewObject; } + } + + if (mainframe_text_field) { + [(NSTextField*)mainframe_text_field setTextColor : NSColor.whiteColor]; + } } void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*callback)(wxString const &)) @@ -77,6 +92,12 @@ void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*ca }]; } +void openFolderForFile(wxString const & file) +{ + NSArray *fileURLs = [NSArray arrayWithObjects:wxCFStringRef(file).AsNSString(), /* ... */ nil]; + [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs]; +} + } } @@ -90,7 +111,11 @@ void WKWebView_evaluateJavaScript(void * web, wxString const & script, void (*ca if (Slic3r::GUI::mainframe_text_field != self){ [self setTextColor2: textColor]; }else{ - [self setTextColor2 : NSColor.whiteColor]; + if(Slic3r::GUI::is_in_full_screen_mode){ + [self setTextColor2 : NSColor.darkGrayColor]; + }else{ + [self setTextColor2 : NSColor.whiteColor]; + } } } diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index 34c535778b..2779c2c77a 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -72,6 +72,7 @@ func_get_user_selected_machine NetworkAgent::get_user_selected_machine_ptr func_set_user_selected_machine NetworkAgent::set_user_selected_machine_ptr = nullptr; func_start_print NetworkAgent::start_print_ptr = nullptr; func_start_local_print_with_record NetworkAgent::start_local_print_with_record_ptr = nullptr; +func_start_send_gcode_to_sdcard NetworkAgent::start_send_gcode_to_sdcard_ptr = nullptr; func_start_local_print NetworkAgent::start_local_print_ptr = nullptr; func_get_user_presets NetworkAgent::get_user_presets_ptr = nullptr; func_request_setting_id NetworkAgent::request_setting_id_ptr = nullptr; @@ -80,6 +81,7 @@ func_get_setting_list NetworkAgent::get_setting_list_ptr = nullptr func_delete_setting NetworkAgent::delete_setting_ptr = nullptr; func_get_studio_info_url NetworkAgent::get_studio_info_url_ptr = nullptr; func_set_extra_http_header NetworkAgent::set_extra_http_header_ptr = nullptr; +func_get_my_message NetworkAgent::get_my_message_ptr = nullptr; func_check_user_task_report NetworkAgent::check_user_task_report_ptr = nullptr; func_get_user_print_info NetworkAgent::get_user_print_info_ptr = nullptr; func_get_printer_firmware NetworkAgent::get_printer_firmware_ptr = nullptr; @@ -88,6 +90,7 @@ func_get_slice_info NetworkAgent::get_slice_info_ptr = nullptr; func_query_bind_status NetworkAgent::query_bind_status_ptr = nullptr; func_modify_printer_name NetworkAgent::modify_printer_name_ptr = nullptr; func_get_camera_url NetworkAgent::get_camera_url_ptr = nullptr; +func_start_pubilsh NetworkAgent::start_publish_ptr = nullptr; NetworkAgent::NetworkAgent() @@ -206,6 +209,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) set_user_selected_machine_ptr = reinterpret_cast(get_network_function("bambu_network_set_user_selected_machine")); start_print_ptr = reinterpret_cast(get_network_function("bambu_network_start_print")); start_local_print_with_record_ptr = reinterpret_cast(get_network_function("bambu_network_start_local_print_with_record")); + start_send_gcode_to_sdcard_ptr = reinterpret_cast(get_network_function("bambu_network_start_send_gcode_to_sdcard")); start_local_print_ptr = reinterpret_cast(get_network_function("bambu_network_start_local_print")); get_user_presets_ptr = reinterpret_cast(get_network_function("bambu_network_get_user_presets")); request_setting_id_ptr = reinterpret_cast(get_network_function("bambu_network_request_setting_id")); @@ -214,6 +218,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) delete_setting_ptr = reinterpret_cast(get_network_function("bambu_network_delete_setting")); get_studio_info_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_studio_info_url")); set_extra_http_header_ptr = reinterpret_cast(get_network_function("bambu_network_set_extra_http_header")); + get_my_message_ptr = reinterpret_cast(get_network_function("bambu_network_get_my_message")); check_user_task_report_ptr = reinterpret_cast(get_network_function("bambu_network_check_user_task_report")); get_user_print_info_ptr = reinterpret_cast(get_network_function("bambu_network_get_user_print_info")); get_printer_firmware_ptr = reinterpret_cast(get_network_function("bambu_network_get_printer_firmware")); @@ -222,6 +227,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) query_bind_status_ptr = reinterpret_cast(get_network_function("bambu_network_query_bind_status")); modify_printer_name_ptr = reinterpret_cast(get_network_function("bambu_network_modify_printer_name")); get_camera_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_camera_url")); + start_publish_ptr = reinterpret_cast(get_network_function("bambu_network_start_publish")); return 0; } @@ -294,6 +300,7 @@ int NetworkAgent::unload_network_module() set_user_selected_machine_ptr = nullptr; start_print_ptr = nullptr; start_local_print_with_record_ptr = nullptr; + start_send_gcode_to_sdcard_ptr = nullptr; start_local_print_ptr = nullptr; get_user_presets_ptr = nullptr; request_setting_id_ptr = nullptr; @@ -302,6 +309,7 @@ int NetworkAgent::unload_network_module() delete_setting_ptr = nullptr; get_studio_info_url_ptr = nullptr; set_extra_http_header_ptr = nullptr; + get_my_message_ptr = nullptr; check_user_task_report_ptr = nullptr; get_user_print_info_ptr = nullptr; get_printer_firmware_ptr = nullptr; @@ -310,6 +318,7 @@ int NetworkAgent::unload_network_module() query_bind_status_ptr = nullptr; modify_printer_name_ptr = nullptr; get_camera_url_ptr = nullptr; + start_publish_ptr = nullptr; return 0; } @@ -835,6 +844,17 @@ int NetworkAgent::start_local_print_with_record(PrintParams params, OnUpdateStat return ret; } +int NetworkAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) +{ + int ret = 0; + if (network_agent && start_send_gcode_to_sdcard_ptr) { + ret = start_send_gcode_to_sdcard_ptr(network_agent, params, update_fn, cancel_fn); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" : network_agent=%1%, ret=%2%, dev_id=%3%, task_name=%4%, project_name=%5%") + % network_agent % ret % params.dev_id % params.task_name % params.project_name; + } + return ret; +} + int NetworkAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { int ret = 0; @@ -920,6 +940,17 @@ int NetworkAgent::set_extra_http_header(std::map extra return ret; } +int NetworkAgent::get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body) +{ + int ret = 0; + if (network_agent && get_my_message_ptr) { + ret = get_my_message_ptr(network_agent, type, after, limit, http_code, http_body); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%") % network_agent % ret; + } + return ret; +} + int NetworkAgent::check_user_task_report(int* task_id, bool* printable) { int ret = 0; @@ -1006,5 +1037,16 @@ int NetworkAgent::get_camera_url(std::string dev_id, std::function>* user_presets); typedef std::string (*func_request_setting_id)(void *agent, std::string name, std::map* values_map, unsigned int* http_code); @@ -59,6 +60,7 @@ typedef int (*func_get_setting_list)(void *agent, std::string bundle_version, Pr typedef int (*func_delete_setting)(void *agent, std::string setting_id); typedef std::string (*func_get_studio_info_url)(void *agent); typedef int (*func_set_extra_http_header)(void *agent, std::map extra_headers); +typedef int (*func_get_my_message)(void *agent, int type, int after, int limit, unsigned int* http_code, std::string* http_body); typedef int (*func_check_user_task_report)(void *agent, int* task_id, bool* printable); typedef int (*func_get_user_print_info)(void *agent, unsigned int* http_code, std::string* http_body); typedef int (*func_get_printer_firmware)(void *agent, std::string dev_id, unsigned* http_code, std::string* http_body); @@ -67,6 +69,7 @@ typedef int (*func_get_slice_info)(void *agent, std::string project_id, std::str typedef int (*func_query_bind_status)(void *agent, std::vector query_list, unsigned int* http_code, std::string* http_body); typedef int (*func_modify_printer_name)(void *agent, std::string dev_id, std::string dev_name); typedef int (*func_get_camera_url)(void *agent, std::string dev_id, std::function callback); +typedef int (*func_start_pubilsh)(void *agent, PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out); @@ -128,6 +131,7 @@ public: int set_user_selected_machine(std::string dev_id); int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); + int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); int get_user_presets(std::map>* user_presets); std::string request_setting_id(std::string name, std::map* values_map, unsigned int* http_code); @@ -136,6 +140,7 @@ public: int delete_setting(std::string setting_id); std::string get_studio_info_url(); int set_extra_http_header(std::map extra_headers); + int get_my_message(int type, int after, int limit, unsigned int* http_code, std::string* http_body); int check_user_task_report(int* task_id, bool* printable); int get_user_print_info(unsigned int* http_code, std::string* http_body); int get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body); @@ -144,6 +149,7 @@ public: int query_bind_status(std::vector query_list, unsigned int* http_code, std::string* http_body); int modify_printer_name(std::string dev_id, std::string dev_name); int get_camera_url(std::string dev_id, std::function callback); + int start_publish(PublishParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, std::string* out); private: @@ -194,6 +200,7 @@ private: static func_set_user_selected_machine set_user_selected_machine_ptr; static func_start_print start_print_ptr; static func_start_local_print_with_record start_local_print_with_record_ptr; + static func_start_send_gcode_to_sdcard start_send_gcode_to_sdcard_ptr; static func_start_local_print start_local_print_ptr; static func_get_user_presets get_user_presets_ptr; static func_request_setting_id request_setting_id_ptr; @@ -202,6 +209,7 @@ private: static func_delete_setting delete_setting_ptr; static func_get_studio_info_url get_studio_info_url_ptr; static func_set_extra_http_header set_extra_http_header_ptr; + static func_get_my_message get_my_message_ptr; static func_check_user_task_report check_user_task_report_ptr; static func_get_user_print_info get_user_print_info_ptr; static func_get_printer_firmware get_printer_firmware_ptr; @@ -210,6 +218,7 @@ private: static func_query_bind_status query_bind_status_ptr; static func_modify_printer_name modify_printer_name_ptr; static func_get_camera_url get_camera_url_ptr; + static func_start_pubilsh start_publish_ptr; }; } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 4837eb4152..d4eafae624 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -85,13 +85,19 @@ void copy_directory_fix(const fs::path &source, const fs::path &target) std::string name = dir_entry.path().filename().string(); std::string target_file = target.string() + "/" + name; - //CopyFileResult cfr = Slic3r::GUI::copy_file_gui(source_file, target_file, error_message, false); - CopyFileResult cfr = copy_file(source_file, target_file, error_message, false); - if (cfr != CopyFileResult::SUCCESS) { - BOOST_LOG_TRIVIAL(error) << "Copying failed(" << cfr << "): " << error_message; + if (boost::filesystem::is_directory(dir_entry)) { + const auto target_path = target / name; + copy_directory_fix(dir_entry, target_path); + } + else { + //CopyFileResult cfr = Slic3r::GUI::copy_file_gui(source_file, target_file, error_message, false); + CopyFileResult cfr = copy_file(source_file, target_file, error_message, false); + if (cfr != CopyFileResult::SUCCESS) { + BOOST_LOG_TRIVIAL(error) << "Copying failed(" << cfr << "): " << error_message; throw Slic3r::CriticalException(GUI::format( - _L("Copying directory %1% to %2% failed: %3%"), - source, target, error_message)); + _L("Copying directory %1% to %2% failed: %3%"), + source, target, error_message)); + } } } return; @@ -871,6 +877,9 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const { BOOST_LOG_TRIVIAL(info) << "[BBL Updater]:Checking whether the profile from resource is newer"; + AppConfig *app_config = GUI::wxGetApp().app_config; + const auto enabled_vendors = app_config->vendors(); + //BBS: refine the init check logic std::vector bundles; for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_path)) { @@ -883,21 +892,32 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const vendor_name.erase(vendor_name.size() - 5); if (enabled_config_update) { if ( fs::exists(path_in_vendor)) { - Semver resource_ver = get_version_from_json(file_path); - Semver vendor_ver = get_version_from_json(path_in_vendor.string()); + if (enabled_vendors.find(vendor_name) != enabled_vendors.end()) { + Semver resource_ver = get_version_from_json(file_path); + Semver vendor_ver = get_version_from_json(path_in_vendor.string()); - bool version_match = ((resource_ver.maj() == vendor_ver.maj()) && (resource_ver.min() == vendor_ver.min())); + bool version_match = ((resource_ver.maj() == vendor_ver.maj()) && (resource_ver.min() == vendor_ver.min())); - if (!version_match || (vendor_ver < resource_ver)) { - BOOST_LOG_TRIVIAL(info) << "[BBL Updater]:found vendor "<