diff --git a/CMakeLists.txt b/CMakeLists.txt index 85e558a18c..6f8e994758 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,9 @@ if (POLICY CMP0092) cmake_policy(SET CMP0092 NEW) endif () +# project() reads this, so set it first. +set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/ClangClShowIncludes.cmake") + project(OrcaSlicer) # Backward compatibility for old CMake versions diff --git a/cmake/modules/ClangClShowIncludes.cmake b/cmake/modules/ClangClShowIncludes.cmake new file mode 100644 index 0000000000..91b9a18069 --- /dev/null +++ b/cmake/modules/ClangClShowIncludes.cmake @@ -0,0 +1,10 @@ +# ccache does not parse the -clang: arguments CMake uses for clang-cl's gcc-style +# depfile, so a cache hit writes the object and no depfile, and Ninja then records +# no headers for that object. ccache reproduces /showIncludes output on a hit. +foreach (_lang C CXX) + if (CMAKE_${_lang}_COMPILER_ID STREQUAL "Clang" AND + CMAKE_${_lang}_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(CMAKE_DEPFILE_FLAGS_${_lang} "/showIncludes") + set(CMAKE_${_lang}_DEPFILE_FORMAT msvc) + endif () +endforeach () diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index e02186705b..0e7141c12c 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -38,6 +38,14 @@ if(POLICY CMP0135) # DOWNLOAD_EXTRACT_TIMESTAMP cmake_policy(SET CMP0135 NEW) endif() +# project() reads this, so set it first. scripts/flatpak/make_deps_tar.sh packs deps/ +# without cmake/, so the file is missing in a Flatpak build. +set(_rules_override "${CMAKE_CURRENT_LIST_DIR}/../cmake/modules/ClangClShowIncludes.cmake") +if (EXISTS "${_rules_override}") + set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_rules_override}") +endif () +unset(_rules_override) + project(OrcaSlicer-deps) # Backward compatibility for old CMake versions @@ -220,6 +228,7 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE) -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER} -DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER} + -DCMAKE_USER_MAKE_RULES_OVERRIDE:STRING=${CMAKE_USER_MAKE_RULES_OVERRIDE} -DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} @@ -267,6 +276,7 @@ else() -DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH} -DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER} -DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER} + -DCMAKE_USER_MAKE_RULES_OVERRIDE:STRING=${CMAKE_USER_MAKE_RULES_OVERRIDE} -DBUILD_SHARED_LIBS:BOOL=OFF ${_cmake_osx_arch} "${_configs_line}" diff --git a/docs/HLSD/deferred-page-construction.md b/docs/HLSD/deferred-page-construction.md new file mode 100644 index 0000000000..748ca65d52 --- /dev/null +++ b/docs/HLSD/deferred-page-construction.md @@ -0,0 +1,220 @@ +# Deferred Page Construction: High Level Design + +## Why it exists + +The main window is a notebook of tabs. When the first frame appears, one tab is on +screen and the others are not; some of them are opened later in the session, some +never, and some only exist for certain printers. Building a tab before the first frame +adds its cost to every startup, whether or not the tab is used. + +This subsystem builds a tab's panel the first time the tab is shown. It also builds the +remaining tabs while the user is idle after startup, in units of tens of milliseconds, +so a click that lands in the middle of one waits for that unit and no longer. Startup +pays only for what the first frame shows, and the other tabs are usually built before +anyone opens them. + +## The parts + +`src/slic3r/GUI/Lazy.hpp`, `LazyPage.hpp`, `StagedBuild.hpp` and `IdleScheduler.hpp/.cpp` +(with its wx-free `PrebuildQueue.hpp`) are independent. A holder can hold anything a +factory makes, a page is a holder with a placeholder widget, a staged object can live +outside a holder, and the scheduler knows none of them; it runs tasks, which `MainFrame` +makes from the holders. + +### Lazy: the holder + +Holds an object that a factory makes on first use, under a name for the log and a place +in the idle queue, both given by the owner. The header has no wx dependency; the busy +cursor for an on-demand build lives in `Lazy.cpp` and is skipped when there is no app, +so the holder is unit-tested. + +- `get()` is null until the object is completely built; `built()` says the same. +- `ensure()` builds whatever is left now, under a busy cursor, and returns the object, or + null in the two cases below. A click on an unbuilt tab or a first open of a dialog goes + through this, and it logs the units and time it took. +- `build_step()` runs one unit of construction and returns true while more remain. The + first unit is the factory call, and each later unit is one `StagedBuild` step if the + type has them. +- `when_built(fn)` runs `fn(object)` now if it exists, otherwise once it is built. +- `pending()` says whether the idle prebuild has work here: not built, and the factory + has not returned null. A null factory result is logged and the holder stays unbuilt. +- A unit that pumps the event loop cannot re-enter the holder; a nested `build_step()` + does nothing and a nested `ensure()` returns null. +- After a unit throws, the holder and the scheduler still run the next one. + +The holder does not own the object; its wx parent does, as for any window. A type with +one instance in the app derives from `LazyInstance`, which points at that instance's +holder (the holder registers itself, and a recreated `MainFrame`'s holder replaces the +old frame's) and gives the type the static entry points the rest of the app uses, +`T::if_built()`, `T::ensure()` and `T::when_built(fn)`. They return null, or do +nothing, while no holder exists, so a caller needs no `mainframe` check. + +`built()` is an atomic flag, since a job worker reads it through the statics +(`MainFrame::get_calibration_curr_tab()`); it is set after the object is complete. + +`LazyBase` is the holder's type-free interface (`name()`, `built()`, `pending()`, +`build_step()`, `prebuild_order()`) and is what the scheduler side sees. + +### LazyPage: the placeholder + +A `wxPanel` placed in the parent in place of the real panel, and a `Lazy` whose +factory makes the panel inside it (by default `new Panel(parent)`). The notebook needs a +page object for the tab to exist and for `show_device()` to insert and remove tabs by +pointer, and the placeholder is that object. `MainFrame` creates every page once, named +after its `TAB_ID_*`, and keeps it for the frame's life; `show_device()` only moves +pages in and out of the book. + +- `Show()` is forwarded to the panel, so a panel's own `Show()` override stays its + activation hook (refresh timers, machine sync) and `SelectPageByName()` works + unchanged. A show builds the panel unless the frame itself is still hidden, because + the book selects its first page as it is inserted; `MainFrame::Show()` shows the + current page again when the frame becomes visible, which builds it. +- `in_book()` says whether the parent notebook currently lists the page, and + `pending()` is that and not built, so a tab `show_device()` has taken out of the book + is not prebuilt. +- A panel built while its page is hidden stays hidden, and a completed panel gets the + dark-mode pass the frame ran before it existed. + +The Compare presets dialog is a holder without a page. `MainFrame` keeps a +`Lazy` whose factory constructs the dialog and binds its events, the +dialog derives from `LazyInstance`, and its callers use +`DiffPresetDialog::ensure()->show()` and `DiffPresetDialog::if_built()` like a tab's +callers do. Saving a preset refreshes the dialog only while it is shown, since `show()` +reloads the presets. + +### StagedBuild: construction in units + +A mixin for a panel whose constructor is too big to be one unit. The constructor builds +the skeleton (sizers, and the parts other code may touch) and queues the rest with +`add_build_step()`. `build_step()` runs one step, and `add_build_steps_of(child)` +forwards a child's steps so a nested panel is spread the same way; the parent is built +only once the child is, including steps the child queues later. Steps run in order, on +the main thread. A `Lazy` recognises a staged type at compile time and runs its steps +one per unit. + +Nothing may touch what a step builds before the last step has run. In practice: + +- nothing paints the panel before it is complete, since a panel built at idle is hidden + with its page and one built on demand finishes inside `ensure()` before the event loop + runs again; +- members created in steps are initialised to null in the header, so a partially built + panel can be destroyed; +- timers and event handlers that use step content check `built()` first + (`MonitorPanel::update_all()`, `CalibrationPanel::update_all()`), and a child's steps + are queued before anything in the constructor can fire such a handler; +- a destructor that disconnects from step content checks `built()` first; +- a constructor or step does not take focus while the panel is off screen + (`IsShownOnScreen()` before `SetFocus()`), since it may run while the user is typing + elsewhere; +- where a step's widgets must keep their place in a sizer that later steps also fill, the + constructor adds an empty slot sizer in that position and the step fills the slot + (`StatusBasePanel`). + +### IdleScheduler: when to build + +A task is any `LazyBase`: a name for the log, `pending()`, `build_step()` and +`prebuild_order()`. `PrebuildQueue` holds the tasks by order (equal order in the order +added) and runs a slice, the units of the first pending task until it completes, the +budget is spent on the clock it is given, or the interrupt predicate says input arrived. +It has no wx dependency and is unit-tested with a fake clock. A task whose work is gone +is passed over and stays in the queue, so a tab that `show_device()` removes and later +re-inserts is pending again. + +`IdleScheduler` drives the queue with the real clock, the app's input timestamp and the +Windows queue check, and logs each unit and slice. Each slice is a timer message: a +period of 250 ms while waiting for the user to go quiet, and a one-shot of zero after a +slice that left work, so the event loop dispatches whatever it has queued (paint, +timers, input) before the next slice runs. Chaining slices with `CallAfter` would not do +this: wx drains pending events fully before the next native message, on every platform. +On GTK the one-shot is 5 ms, because a due GLib timeout runs ahead of the redraw and +idle sources that paint and deliver posted events. +A slice runs only once the user has been idle for the quiet time, and the timer stops +itself once no task is pending. The tick period, quiet time and slice length are +constants in `IdleScheduler.cpp`. A unit cannot be interrupted once started, so the +largest unit bounds click latency on Windows; on the other platforms a click also waits +for the rest of the slice. A unit that pumps the event loop lets the timer fire inside +its own slice, and that tick does nothing. + +The tasks are made by their owners. `MainFrame::prebuild_pages_when_idle()` registers +every `LazyPage` the frame created, in or out of the book (`pending()` is false for a +page out of the book), the Compare presets holder, and the Prepare sidebar's settings +page from `ParamsPanel::settings_page_prebuild()`, whose first unit selects the default +tab if none is selected yet and whose later units build one option group each. +`show_device()` only restarts the timer. `MainFrame` owns the scheduler because it owns +everything the tasks build, and clearing the queue with the frame is what keeps a task +from outliving its object. + +Idle time comes from `GUI_App::FilterEvent`, which timestamps mouse and keyboard events +(`wxEVT_CATEGORY_USER_INPUT` minus command events, which all claim that category), and +`GUI_App::input_idle_ms()` reports it. On Windows a slice additionally refuses to start +when the message queue holds keyboard, button, touch or pen input. Mouse moves are +excluded because Windows synthesises one whenever a window appears under the cursor, +which every unit does. Other platforms use the timestamp alone. + +Once every registered page is built the timer is stopped and the subsystem costs +nothing. + +## Rules + +**What builds before the first frame.** The Prepare tab's plater, because `post_init()` +needs its GL canvas on screen to initialise OpenGL in every startup state, and the start +page the user configured. Home is built by the first `MainFrame::Show()`, Prepare's +settings page by selecting the tab. `post_init()` passes through the Prepare tab for GL +init under `Freeze()` with `MainFrame::select_prepare_for_gl_init()`, which changes the +selection without the page-changed event, so nothing else is built for that pass. + +**Reaching a lazy object.** A caller uses the type's own statics. `T::if_built()` may +return null and is for telling the object something it can live without (a rescale, a +colour change, a status update). `T::ensure()` builds the object and is for navigating +to it or for a caller that is about to show it. A caller that tells the object something +it would not fetch for itself on construction uses `T::when_built()`, which keeps the +message until the object exists. A panel that pulls its state when constructed (the Home +page requests the recent list on load, the Device tab reads the device manager on show) +is reached with `if_built()`; one that cannot pull gets `when_built()`. + +**Unit size.** A unit cannot be interrupted, so it should stay within the slice length on +a fast machine. A constructor above that is staged. A single widget above it is the +floor unless the widget itself is split. + +**Order.** Cheapest and most likely to be opened first, given where `MainFrame` creates +each holder. The settings page is 0 (the Prepare tab's own content), Home 10, Device 20 +(a Bambu user's usual second stop), Calibration 30, Multi-device 40, the web Device +view 50, Project 60 (a second WebView2 instance) and the Compare presets dialog 100; +steps of ten so a new tab takes a value between its neighbours without renumbering +them. `MainFrame::prebuild_pages_when_idle()` registers the tasks once, from +`post_init()`. + +**Never prebuilt.** A holder with a negative order, for a tab that few sessions open +and that costs more to build unasked than it saves (the Design tab), and plugin-provided +tabs, which are Python-side and not lazy pages. + +## Adopting it + +A lazy tab needs: + +1. The panel derived from `LazyInstance`, since a tab's panel has one instance. +2. A `LazyPage*` member in `MainFrame`, created once in `init_tabpanel()` with + its `TAB_ID_*` as the name, its place by the order rule (and a factory if + `new Panel(parent)` is not enough), added to `m_lazy_pages`, and used wherever the + tab is added to or looked up in the notebook. +3. Every use of the panel outside `MainFrame` going through one of the panel's statics, + chosen by the rule above. When converting an existing member, `grep` for it; the + compiler finds the rest. +4. A constructor that copes with the frame already existing and the user being busy + elsewhere, since `wxGetApp().mainframe` is set, the frame may be shown, and the user + may be typing when a lazy panel is built: no `SetFocus()` while off screen, and + whatever the constructor did through a `MainFrame` accessor before (a mode update, a + deferred URL) done in the constructor itself. + +To stage a heavy constructor, inherit `StagedBuild`, keep the skeleton in the constructor, +move the rest into `add_build_step()` lambdas in the original order, and follow the +staged-panel rules above. Measure the units. A step that is still one big widget has to +be split inside that widget or accepted as the floor. + +Verification is by log. `MainFrame::prebuild_pages_when_idle` lists the queue it +registered, `IdleScheduler::tick` reports each completed task by name at info level and +each slice and unit at debug level, and `Lazy::ensure` reports an object a user built +on demand with the units and time it took. A run from the configured start +page shows every registered page complete, in order, with no unit longer than intended. +A click on a tab mid-prebuild shows the finished panel with an `ensure` line for what was +left, and the slices resume for the remaining tasks once the user is idle again. diff --git a/docs/HLSD/preset-cache.md b/docs/HLSD/preset-cache.md index 6e693dbd6f..666e452f1e 100644 --- a/docs/HLSD/preset-cache.md +++ b/docs/HLSD/preset-cache.md @@ -76,9 +76,10 @@ and the count of errors the original parse hit. Each entry is one preset **in source form**: what its JSON sub-file states and nothing that resolving it derives — the preset's own config diff, the name of the preset it -inherits, and the parse metadata (name, sub-path, description, instantiation, setting -and filament ids, renames). Non-instantiated base presets are stored too; the children -that inherit from them cannot resolve without them. +inherits, the names of the presets it includes, and the parse metadata (name, sub-path, +description, instantiation, setting and filament ids, renames). Non-instantiated base +presets are stored too; the children that inherit from or include them cannot resolve +without them. **The payload names its own keys.** The dictionary holds the distinct `opt_key`s the file uses, the `ConfigOptionType` each was written as, and the distinct enum *value @@ -161,9 +162,9 @@ cache nothing can invalidate is worse than no cache. Vendors load in a fixed order, because filament inheritance crosses exactly one boundary: any vendor's filament may inherit from the shared Orca filament library, -and nothing else reaches across vendors. The library therefore goes first, alone; -every other vendor follows in parallel, resolving against it; and the results are -merged in a stable order: +and nothing else reaches across vendors — an `include` is always vendor-local. The +library therefore goes first, alone; every other vendor follows in parallel, resolving +against it; and the results are merged in a stable order: ```mermaid flowchart LR @@ -211,13 +212,16 @@ and its cache was never written back. Serving from a cache is not a memory-image restore. The entries are deserialized and then installed one by one — inheritance resolved against the presets installed before -them and the currently loaded filament library, configs flattened onto the collection -defaults, validated and registered — by the same function the JSON path calls straight -after parsing a sub-file. The two paths share everything below the parse, which is what -makes a cache-loaded bundle indistinguishable from a JSON-loaded one by construction -rather than by test coverage. Installation also rebuilds each preset's file path from -the local data directory, so a shipped cache never carries the generating machine's -paths. +them and the currently loaded filament library, includes layered in, configs flattened +onto the collection defaults, validated and registered — by the same function the JSON +path calls straight after parsing a sub-file. An `include` layers what the included +base states, between the parent and the preset's own keys: the base's diff against the +default, taken when the base itself was installed and before the per-variant padding +`inherits` sees, so only what a template sets reaches the presets including it. The two +paths share everything below the parse, which is what makes a cache-loaded bundle +indistinguishable from a JSON-loaded one by construction rather than by test coverage. +Installation also rebuilds each preset's file path from the local data directory, so a +shipped cache never carries the generating machine's paths. App upgrades work because a cache normally survives one. Only a deliberate `CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json index 3b52c1e22e..44354ce026 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": "02.08.00.09", + "version": "02.08.00.10", "force_update": "0", "description": "the initial version of BBL configurations", "machine_model_list": [ @@ -67,6 +67,22 @@ "name": "fdm_filament_common", "sub_path": "filament/fdm_filament_common.json" }, + { + "name": "fdm_filament_template_direct_bowden", + "sub_path": "filament/fdm_filament_template_direct_bowden.json" + }, + { + "name": "fdm_filament_template_direct_bowden_e3d", + "sub_path": "filament/fdm_filament_template_direct_bowden_e3d.json" + }, + { + "name": "fdm_filament_template_direct_dual", + "sub_path": "filament/fdm_filament_template_direct_dual.json" + }, + { + "name": "fdm_filament_template_direct_dual_e3d", + "sub_path": "filament/fdm_filament_template_direct_dual_e3d.json" + }, { "name": "fdm_filament_abs", "sub_path": "filament/fdm_filament_abs.json" @@ -8246,261 +8262,9 @@ { "name": "Generic TPU @BBL H2S", "sub_path": "filament/Generic TPU @BBL H2S.json" - }, - { - "name": "fdm_filament_template_direct_bowden", - "sub_path": "filament/fdm_filament_template_direct_bowden.json" - }, - { - "name": "fdm_filament_template_direct_bowden_e3d", - "sub_path": "filament/fdm_filament_template_direct_bowden_e3d.json" - }, - { - "name": "fdm_filament_template_direct_dual", - "sub_path": "filament/fdm_filament_template_direct_dual.json" - }, - { - "name": "fdm_filament_template_direct_dual_e3d", - "sub_path": "filament/fdm_filament_template_direct_dual_e3d.json" } ], "machine_list": [ - { - "name": "fdm_machine_common", - "sub_path": "machine/fdm_machine_common.json" - }, - { - "name": "fdm_bbl_3dp_001_common", - "sub_path": "machine/fdm_bbl_3dp_001_common.json" - }, - { - "name": "fdm_bbl_3dp_002_common", - "sub_path": "machine/fdm_bbl_3dp_002_common.json" - }, - { - "name": "Bambu Lab A1 0.4 nozzle", - "sub_path": "machine/Bambu Lab A1 0.4 nozzle.json" - }, - { - "name": "Bambu Lab A1 mini 0.4 nozzle", - "sub_path": "machine/Bambu Lab A1 mini 0.4 nozzle.json" - }, - { - "name": "Bambu Lab A2L 0.4 nozzle", - "sub_path": "machine/Bambu Lab A2L 0.4 nozzle.json" - }, - { - "name": "Bambu Lab H2S 0.4 nozzle", - "sub_path": "machine/Bambu Lab H2S 0.4 nozzle.json" - }, - { - "name": "Bambu Lab P1P 0.4 nozzle", - "sub_path": "machine/Bambu Lab P1P 0.4 nozzle.json" - }, - { - "name": "Bambu Lab P1S 0.4 nozzle", - "sub_path": "machine/Bambu Lab P1S 0.4 nozzle.json" - }, - { - "name": "Bambu Lab P2S 0.4 nozzle", - "sub_path": "machine/Bambu Lab P2S 0.4 nozzle.json" - }, - { - "name": "Bambu Lab X1 0.4 nozzle", - "sub_path": "machine/Bambu Lab X1 0.4 nozzle.json" - }, - { - "name": "Bambu Lab X1 Carbon 0.4 nozzle", - "sub_path": "machine/Bambu Lab X1 Carbon 0.4 nozzle.json" - }, - { - "name": "Bambu Lab X1E 0.4 nozzle", - "sub_path": "machine/Bambu Lab X1E 0.4 nozzle.json" - }, - { - "name": "Bambu Lab H2C 0.4 nozzle", - "sub_path": "machine/Bambu Lab H2C 0.4 nozzle.json" - }, - { - "name": "Bambu Lab H2D 0.4 nozzle", - "sub_path": "machine/Bambu Lab H2D 0.4 nozzle.json" - }, - { - "name": "Bambu Lab H2D Pro 0.4 nozzle", - "sub_path": "machine/Bambu Lab H2D Pro 0.4 nozzle.json" - }, - { - "name": "Bambu Lab X2D 0.4 nozzle", - "sub_path": "machine/Bambu Lab X2D 0.4 nozzle.json" - }, - { - "name": "Bambu Lab A1 0.2 nozzle", - "sub_path": "machine/Bambu Lab A1 0.2 nozzle.json" - }, - { - "name": "Bambu Lab A1 0.6 nozzle", - "sub_path": "machine/Bambu Lab A1 0.6 nozzle.json" - }, - { - "name": "Bambu Lab A1 0.8 nozzle", - "sub_path": "machine/Bambu Lab A1 0.8 nozzle.json" - }, - { - "name": "Bambu Lab A1 mini 0.2 nozzle", - "sub_path": "machine/Bambu Lab A1 mini 0.2 nozzle.json" - }, - { - "name": "Bambu Lab A1 mini 0.6 nozzle", - "sub_path": "machine/Bambu Lab A1 mini 0.6 nozzle.json" - }, - { - "name": "Bambu Lab A1 mini 0.8 nozzle", - "sub_path": "machine/Bambu Lab A1 mini 0.8 nozzle.json" - }, - { - "name": "Bambu Lab A2L 0.2 nozzle", - "sub_path": "machine/Bambu Lab A2L 0.2 nozzle.json" - }, - { - "name": "Bambu Lab A2L 0.6 nozzle", - "sub_path": "machine/Bambu Lab A2L 0.6 nozzle.json" - }, - { - "name": "Bambu Lab A2L 0.8 nozzle", - "sub_path": "machine/Bambu Lab A2L 0.8 nozzle.json" - }, - { - "name": "Bambu Lab H2S 0.2 nozzle", - "sub_path": "machine/Bambu Lab H2S 0.2 nozzle.json" - }, - { - "name": "Bambu Lab H2S 0.6 nozzle", - "sub_path": "machine/Bambu Lab H2S 0.6 nozzle.json" - }, - { - "name": "Bambu Lab H2S 0.8 nozzle", - "sub_path": "machine/Bambu Lab H2S 0.8 nozzle.json" - }, - { - "name": "Bambu Lab P1P 0.2 nozzle", - "sub_path": "machine/Bambu Lab P1P 0.2 nozzle.json" - }, - { - "name": "Bambu Lab P1P 0.6 nozzle", - "sub_path": "machine/Bambu Lab P1P 0.6 nozzle.json" - }, - { - "name": "Bambu Lab P1P 0.8 nozzle", - "sub_path": "machine/Bambu Lab P1P 0.8 nozzle.json" - }, - { - "name": "Bambu Lab P1S 0.2 nozzle", - "sub_path": "machine/Bambu Lab P1S 0.2 nozzle.json" - }, - { - "name": "Bambu Lab P1S 0.6 nozzle", - "sub_path": "machine/Bambu Lab P1S 0.6 nozzle.json" - }, - { - "name": "Bambu Lab P1S 0.8 nozzle", - "sub_path": "machine/Bambu Lab P1S 0.8 nozzle.json" - }, - { - "name": "Bambu Lab P2S 0.2 nozzle", - "sub_path": "machine/Bambu Lab P2S 0.2 nozzle.json" - }, - { - "name": "Bambu Lab P2S 0.6 nozzle", - "sub_path": "machine/Bambu Lab P2S 0.6 nozzle.json" - }, - { - "name": "Bambu Lab P2S 0.8 nozzle", - "sub_path": "machine/Bambu Lab P2S 0.8 nozzle.json" - }, - { - "name": "Bambu Lab X1 0.2 nozzle", - "sub_path": "machine/Bambu Lab X1 0.2 nozzle.json" - }, - { - "name": "Bambu Lab X1 0.6 nozzle", - "sub_path": "machine/Bambu Lab X1 0.6 nozzle.json" - }, - { - "name": "Bambu Lab X1 0.8 nozzle", - "sub_path": "machine/Bambu Lab X1 0.8 nozzle.json" - }, - { - "name": "Bambu Lab X1 Carbon 0.2 nozzle", - "sub_path": "machine/Bambu Lab X1 Carbon 0.2 nozzle.json" - }, - { - "name": "Bambu Lab X1 Carbon 0.6 nozzle", - "sub_path": "machine/Bambu Lab X1 Carbon 0.6 nozzle.json" - }, - { - "name": "Bambu Lab X1 Carbon 0.8 nozzle", - "sub_path": "machine/Bambu Lab X1 Carbon 0.8 nozzle.json" - }, - { - "name": "Bambu Lab X1E 0.2 nozzle", - "sub_path": "machine/Bambu Lab X1E 0.2 nozzle.json" - }, - { - "name": "Bambu Lab X1E 0.6 nozzle", - "sub_path": "machine/Bambu Lab X1E 0.6 nozzle.json" - }, - { - "name": "Bambu Lab X1E 0.8 nozzle", - "sub_path": "machine/Bambu Lab X1E 0.8 nozzle.json" - }, - { - "name": "Bambu Lab H2C 0.2 nozzle", - "sub_path": "machine/Bambu Lab H2C 0.2 nozzle.json" - }, - { - "name": "Bambu Lab H2C 0.6 nozzle", - "sub_path": "machine/Bambu Lab H2C 0.6 nozzle.json" - }, - { - "name": "Bambu Lab H2C 0.8 nozzle", - "sub_path": "machine/Bambu Lab H2C 0.8 nozzle.json" - }, - { - "name": "Bambu Lab H2D 0.2 nozzle", - "sub_path": "machine/Bambu Lab H2D 0.2 nozzle.json" - }, - { - "name": "Bambu Lab H2D 0.6 nozzle", - "sub_path": "machine/Bambu Lab H2D 0.6 nozzle.json" - }, - { - "name": "Bambu Lab H2D 0.8 nozzle", - "sub_path": "machine/Bambu Lab H2D 0.8 nozzle.json" - }, - { - "name": "Bambu Lab H2D Pro 0.2 nozzle", - "sub_path": "machine/Bambu Lab H2D Pro 0.2 nozzle.json" - }, - { - "name": "Bambu Lab H2D Pro 0.6 nozzle", - "sub_path": "machine/Bambu Lab H2D Pro 0.6 nozzle.json" - }, - { - "name": "Bambu Lab H2D Pro 0.8 nozzle", - "sub_path": "machine/Bambu Lab H2D Pro 0.8 nozzle.json" - }, - { - "name": "Bambu Lab X2D 0.2 nozzle", - "sub_path": "machine/Bambu Lab X2D 0.2 nozzle.json" - }, - { - "name": "Bambu Lab X2D 0.6 nozzle", - "sub_path": "machine/Bambu Lab X2D 0.6 nozzle.json" - }, - { - "name": "Bambu Lab X2D 0.8 nozzle", - "sub_path": "machine/Bambu Lab X2D 0.8 nozzle.json" - }, { "name": "Bambu Lab A1 0.4 nozzle template change_filament_gcode", "sub_path": "machine/Bambu Lab A1 0.4 nozzle template change_filament_gcode.json" @@ -8856,6 +8620,242 @@ { "name": "Bambu Lab X2D 0.4 nozzle template time_lapse_gcode", "sub_path": "machine/Bambu Lab X2D 0.4 nozzle template time_lapse_gcode.json" + }, + { + "name": "fdm_machine_common", + "sub_path": "machine/fdm_machine_common.json" + }, + { + "name": "fdm_bbl_3dp_001_common", + "sub_path": "machine/fdm_bbl_3dp_001_common.json" + }, + { + "name": "fdm_bbl_3dp_002_common", + "sub_path": "machine/fdm_bbl_3dp_002_common.json" + }, + { + "name": "Bambu Lab A1 0.4 nozzle", + "sub_path": "machine/Bambu Lab A1 0.4 nozzle.json" + }, + { + "name": "Bambu Lab A1 mini 0.4 nozzle", + "sub_path": "machine/Bambu Lab A1 mini 0.4 nozzle.json" + }, + { + "name": "Bambu Lab A2L 0.4 nozzle", + "sub_path": "machine/Bambu Lab A2L 0.4 nozzle.json" + }, + { + "name": "Bambu Lab H2S 0.4 nozzle", + "sub_path": "machine/Bambu Lab H2S 0.4 nozzle.json" + }, + { + "name": "Bambu Lab P1P 0.4 nozzle", + "sub_path": "machine/Bambu Lab P1P 0.4 nozzle.json" + }, + { + "name": "Bambu Lab P1S 0.4 nozzle", + "sub_path": "machine/Bambu Lab P1S 0.4 nozzle.json" + }, + { + "name": "Bambu Lab P2S 0.4 nozzle", + "sub_path": "machine/Bambu Lab P2S 0.4 nozzle.json" + }, + { + "name": "Bambu Lab X1 0.4 nozzle", + "sub_path": "machine/Bambu Lab X1 0.4 nozzle.json" + }, + { + "name": "Bambu Lab X1 Carbon 0.4 nozzle", + "sub_path": "machine/Bambu Lab X1 Carbon 0.4 nozzle.json" + }, + { + "name": "Bambu Lab X1E 0.4 nozzle", + "sub_path": "machine/Bambu Lab X1E 0.4 nozzle.json" + }, + { + "name": "Bambu Lab H2C 0.4 nozzle", + "sub_path": "machine/Bambu Lab H2C 0.4 nozzle.json" + }, + { + "name": "Bambu Lab H2D 0.4 nozzle", + "sub_path": "machine/Bambu Lab H2D 0.4 nozzle.json" + }, + { + "name": "Bambu Lab H2D Pro 0.4 nozzle", + "sub_path": "machine/Bambu Lab H2D Pro 0.4 nozzle.json" + }, + { + "name": "Bambu Lab X2D 0.4 nozzle", + "sub_path": "machine/Bambu Lab X2D 0.4 nozzle.json" + }, + { + "name": "Bambu Lab A1 0.2 nozzle", + "sub_path": "machine/Bambu Lab A1 0.2 nozzle.json" + }, + { + "name": "Bambu Lab A1 0.6 nozzle", + "sub_path": "machine/Bambu Lab A1 0.6 nozzle.json" + }, + { + "name": "Bambu Lab A1 0.8 nozzle", + "sub_path": "machine/Bambu Lab A1 0.8 nozzle.json" + }, + { + "name": "Bambu Lab A1 mini 0.2 nozzle", + "sub_path": "machine/Bambu Lab A1 mini 0.2 nozzle.json" + }, + { + "name": "Bambu Lab A1 mini 0.6 nozzle", + "sub_path": "machine/Bambu Lab A1 mini 0.6 nozzle.json" + }, + { + "name": "Bambu Lab A1 mini 0.8 nozzle", + "sub_path": "machine/Bambu Lab A1 mini 0.8 nozzle.json" + }, + { + "name": "Bambu Lab A2L 0.2 nozzle", + "sub_path": "machine/Bambu Lab A2L 0.2 nozzle.json" + }, + { + "name": "Bambu Lab A2L 0.6 nozzle", + "sub_path": "machine/Bambu Lab A2L 0.6 nozzle.json" + }, + { + "name": "Bambu Lab A2L 0.8 nozzle", + "sub_path": "machine/Bambu Lab A2L 0.8 nozzle.json" + }, + { + "name": "Bambu Lab H2S 0.2 nozzle", + "sub_path": "machine/Bambu Lab H2S 0.2 nozzle.json" + }, + { + "name": "Bambu Lab H2S 0.6 nozzle", + "sub_path": "machine/Bambu Lab H2S 0.6 nozzle.json" + }, + { + "name": "Bambu Lab H2S 0.8 nozzle", + "sub_path": "machine/Bambu Lab H2S 0.8 nozzle.json" + }, + { + "name": "Bambu Lab P1P 0.2 nozzle", + "sub_path": "machine/Bambu Lab P1P 0.2 nozzle.json" + }, + { + "name": "Bambu Lab P1P 0.6 nozzle", + "sub_path": "machine/Bambu Lab P1P 0.6 nozzle.json" + }, + { + "name": "Bambu Lab P1P 0.8 nozzle", + "sub_path": "machine/Bambu Lab P1P 0.8 nozzle.json" + }, + { + "name": "Bambu Lab P1S 0.2 nozzle", + "sub_path": "machine/Bambu Lab P1S 0.2 nozzle.json" + }, + { + "name": "Bambu Lab P1S 0.6 nozzle", + "sub_path": "machine/Bambu Lab P1S 0.6 nozzle.json" + }, + { + "name": "Bambu Lab P1S 0.8 nozzle", + "sub_path": "machine/Bambu Lab P1S 0.8 nozzle.json" + }, + { + "name": "Bambu Lab P2S 0.2 nozzle", + "sub_path": "machine/Bambu Lab P2S 0.2 nozzle.json" + }, + { + "name": "Bambu Lab P2S 0.6 nozzle", + "sub_path": "machine/Bambu Lab P2S 0.6 nozzle.json" + }, + { + "name": "Bambu Lab P2S 0.8 nozzle", + "sub_path": "machine/Bambu Lab P2S 0.8 nozzle.json" + }, + { + "name": "Bambu Lab X1 0.2 nozzle", + "sub_path": "machine/Bambu Lab X1 0.2 nozzle.json" + }, + { + "name": "Bambu Lab X1 0.6 nozzle", + "sub_path": "machine/Bambu Lab X1 0.6 nozzle.json" + }, + { + "name": "Bambu Lab X1 0.8 nozzle", + "sub_path": "machine/Bambu Lab X1 0.8 nozzle.json" + }, + { + "name": "Bambu Lab X1 Carbon 0.2 nozzle", + "sub_path": "machine/Bambu Lab X1 Carbon 0.2 nozzle.json" + }, + { + "name": "Bambu Lab X1 Carbon 0.6 nozzle", + "sub_path": "machine/Bambu Lab X1 Carbon 0.6 nozzle.json" + }, + { + "name": "Bambu Lab X1 Carbon 0.8 nozzle", + "sub_path": "machine/Bambu Lab X1 Carbon 0.8 nozzle.json" + }, + { + "name": "Bambu Lab X1E 0.2 nozzle", + "sub_path": "machine/Bambu Lab X1E 0.2 nozzle.json" + }, + { + "name": "Bambu Lab X1E 0.6 nozzle", + "sub_path": "machine/Bambu Lab X1E 0.6 nozzle.json" + }, + { + "name": "Bambu Lab X1E 0.8 nozzle", + "sub_path": "machine/Bambu Lab X1E 0.8 nozzle.json" + }, + { + "name": "Bambu Lab H2C 0.2 nozzle", + "sub_path": "machine/Bambu Lab H2C 0.2 nozzle.json" + }, + { + "name": "Bambu Lab H2C 0.6 nozzle", + "sub_path": "machine/Bambu Lab H2C 0.6 nozzle.json" + }, + { + "name": "Bambu Lab H2C 0.8 nozzle", + "sub_path": "machine/Bambu Lab H2C 0.8 nozzle.json" + }, + { + "name": "Bambu Lab H2D 0.2 nozzle", + "sub_path": "machine/Bambu Lab H2D 0.2 nozzle.json" + }, + { + "name": "Bambu Lab H2D 0.6 nozzle", + "sub_path": "machine/Bambu Lab H2D 0.6 nozzle.json" + }, + { + "name": "Bambu Lab H2D 0.8 nozzle", + "sub_path": "machine/Bambu Lab H2D 0.8 nozzle.json" + }, + { + "name": "Bambu Lab H2D Pro 0.2 nozzle", + "sub_path": "machine/Bambu Lab H2D Pro 0.2 nozzle.json" + }, + { + "name": "Bambu Lab H2D Pro 0.6 nozzle", + "sub_path": "machine/Bambu Lab H2D Pro 0.6 nozzle.json" + }, + { + "name": "Bambu Lab H2D Pro 0.8 nozzle", + "sub_path": "machine/Bambu Lab H2D Pro 0.8 nozzle.json" + }, + { + "name": "Bambu Lab X2D 0.2 nozzle", + "sub_path": "machine/Bambu Lab X2D 0.2 nozzle.json" + }, + { + "name": "Bambu Lab X2D 0.6 nozzle", + "sub_path": "machine/Bambu Lab X2D 0.6 nozzle.json" + }, + { + "name": "Bambu Lab X2D 0.8 nozzle", + "sub_path": "machine/Bambu Lab X2D 0.8 nozzle.json" } ], "process_list": [ diff --git a/resources/profiles/BBL/Bambu Lab A1 mini_cover.png b/resources/profiles/BBL/Bambu Lab A1 mini_cover.png index f224e40b89..060e0a2d2c 100644 Binary files a/resources/profiles/BBL/Bambu Lab A1 mini_cover.png and b/resources/profiles/BBL/Bambu Lab A1 mini_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab A1_cover.png b/resources/profiles/BBL/Bambu Lab A1_cover.png index 93f7121ace..e6a3358061 100644 Binary files a/resources/profiles/BBL/Bambu Lab A1_cover.png and b/resources/profiles/BBL/Bambu Lab A1_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab H2D_cover.png b/resources/profiles/BBL/Bambu Lab H2D_cover.png index 753fbe31aa..3f3a099bc7 100644 Binary files a/resources/profiles/BBL/Bambu Lab H2D_cover.png and b/resources/profiles/BBL/Bambu Lab H2D_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab P1P_cover.png b/resources/profiles/BBL/Bambu Lab P1P_cover.png index 566ce21456..78c0d5d115 100644 Binary files a/resources/profiles/BBL/Bambu Lab P1P_cover.png and b/resources/profiles/BBL/Bambu Lab P1P_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab P1S_cover.png b/resources/profiles/BBL/Bambu Lab P1S_cover.png index 38ec69bb85..04d179dd05 100644 Binary files a/resources/profiles/BBL/Bambu Lab P1S_cover.png and b/resources/profiles/BBL/Bambu Lab P1S_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab X1 Carbon_cover.png b/resources/profiles/BBL/Bambu Lab X1 Carbon_cover.png index eab8a6d5c6..d8b089524c 100644 Binary files a/resources/profiles/BBL/Bambu Lab X1 Carbon_cover.png and b/resources/profiles/BBL/Bambu Lab X1 Carbon_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab X1E_cover.png b/resources/profiles/BBL/Bambu Lab X1E_cover.png index 1daaf9c3a9..5ae4c807d6 100644 Binary files a/resources/profiles/BBL/Bambu Lab X1E_cover.png and b/resources/profiles/BBL/Bambu Lab X1E_cover.png differ diff --git a/resources/profiles/BBL/Bambu Lab X1_cover.png b/resources/profiles/BBL/Bambu Lab X1_cover.png index 5655a9f83c..7673774115 100644 Binary files a/resources/profiles/BBL/Bambu Lab X1_cover.png and b/resources/profiles/BBL/Bambu Lab X1_cover.png differ diff --git a/resources/profiles/Sovol.json b/resources/profiles/Sovol.json index 7f08417ed7..81dab20239 100644 --- a/resources/profiles/Sovol.json +++ b/resources/profiles/Sovol.json @@ -1,7 +1,7 @@ { "name": "Sovol", "url": "", - "version": "02.04.00.04", + "version": "02.04.00.07", "force_update": "0", "description": "Sovol configurations", "machine_model_list": [ @@ -167,10 +167,6 @@ "name": "0.20mm Standard @Sovol SV08 MAX 0.4 nozzle", "sub_path": "process/0.20mm Standard @Sovol SV08 MAX 0.4 nozzle.json" }, - { - "name": "0.20mm Standard @Sovol Zero 0.4 nozzle", - "sub_path": "process/0.20mm Standard @Sovol Zero 0.4 nozzle.json" - }, { "name": "0.28mm Fast @Sovol SV06 ACE", "sub_path": "process/0.28mm Fast @Sovol SV06 ACE 0.4 nozzle.json" @@ -198,6 +194,74 @@ { "name": "0.40mm Standard @Sovol SV08 MAX 0.8 nozzle", "sub_path": "process/0.40mm Standard @Sovol SV08 MAX 0.8 nozzle.json" + }, + { + "name": "fdm_process_zero", + "sub_path": "process/fdm_process_zero.json" + }, + { + "name": "0.06mm Quality @Sovol Zero 0.2 nozzle", + "sub_path": "process/0.06mm Quality @Sovol Zero 0.2 nozzle.json" + }, + { + "name": "0.10mm Standard @Sovol Zero 0.2 nozzle", + "sub_path": "process/0.10mm Standard @Sovol Zero 0.2 nozzle.json" + }, + { + "name": "0.12mm Quality @Sovol Zero 0.4 nozzle", + "sub_path": "process/0.12mm Quality @Sovol Zero 0.4 nozzle.json" + }, + { + "name": "0.14mm Fast @Sovol Zero 0.2 nozzle", + "sub_path": "process/0.14mm Fast @Sovol Zero 0.2 nozzle.json" + }, + { + "name": "0.15mm Quality @Sovol Zero 0.6 nozzle", + "sub_path": "process/0.15mm Quality @Sovol Zero 0.6 nozzle.json" + }, + { + "name": "0.20mm Quality @Sovol Zero 0.8 nozzle", + "sub_path": "process/0.20mm Quality @Sovol Zero 0.8 nozzle.json" + }, + { + "name": "0.20mm Standard @Sovol Zero 0.4 nozzle", + "sub_path": "process/0.20mm Standard @Sovol Zero 0.4 nozzle.json" + }, + { + "name": "0.25mm Quality @Sovol Zero 1.0 nozzle", + "sub_path": "process/0.25mm Quality @Sovol Zero 1.0 nozzle.json" + }, + { + "name": "0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle", + "sub_path": "process/0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle.json" + }, + { + "name": "0.28mm Fast @Sovol Zero 0.4 nozzle", + "sub_path": "process/0.28mm Fast @Sovol Zero 0.4 nozzle.json" + }, + { + "name": "0.30mm Standard @Sovol Zero 0.6 nozzle", + "sub_path": "process/0.30mm Standard @Sovol Zero 0.6 nozzle.json" + }, + { + "name": "0.40mm Standard @Sovol Zero 0.8 nozzle", + "sub_path": "process/0.40mm Standard @Sovol Zero 0.8 nozzle.json" + }, + { + "name": "0.42mm Fast @Sovol Zero 0.6 nozzle", + "sub_path": "process/0.42mm Fast @Sovol Zero 0.6 nozzle.json" + }, + { + "name": "0.50mm Standard @Sovol Zero 1.0 nozzle", + "sub_path": "process/0.50mm Standard @Sovol Zero 1.0 nozzle.json" + }, + { + "name": "0.56mm Fast @Sovol Zero 0.8 nozzle", + "sub_path": "process/0.56mm Fast @Sovol Zero 0.8 nozzle.json" + }, + { + "name": "0.70mm Fast @Sovol Zero 1.0 nozzle", + "sub_path": "process/0.70mm Fast @Sovol Zero 1.0 nozzle.json" } ], "filament_list": [ @@ -249,6 +313,10 @@ "name": "Generic PETG @Sovol Zero", "sub_path": "filament/Generic PETG @Sovol Zero.json" }, + { + "name": "Generic PETG @Sovol Zero Hardened Steel nozzle", + "sub_path": "filament/Generic PETG @Sovol Zero Hardened Steel nozzle.json" + }, { "name": "Generic PLA @Sovol SV06 ACE", "sub_path": "filament/Generic PLA @Sovol SV06 ACE.json" @@ -277,6 +345,14 @@ "name": "Generic PLA @Sovol Zero", "sub_path": "filament/Generic PLA @Sovol Zero.json" }, + { + "name": "Generic PLA @Sovol Zero Hardened Steel nozzle", + "sub_path": "filament/Generic PLA @Sovol Zero Hardened Steel nozzle.json" + }, + { + "name": "Generic PLA SPEEDBENCHY @Sovol Zero", + "sub_path": "filament/Generic PLA SPEEDBENCHY @Sovol Zero.json" + }, { "name": "Generic PLA Silk @Sovol SV08 MAX", "sub_path": "filament/Generic PLA Silk @Sovol SV08 MAX.json" @@ -285,6 +361,10 @@ "name": "Generic PLA Silk @Sovol Zero", "sub_path": "filament/Generic PLA Silk @Sovol Zero.json" }, + { + "name": "Generic PLA Silk @Sovol Zero Hardened Steel nozzle", + "sub_path": "filament/Generic PLA Silk @Sovol Zero Hardened Steel nozzle.json" + }, { "name": "Generic TPU @Sovol SV06 ACE", "sub_path": "filament/Generic TPU @Sovol SV06 ACE.json" @@ -312,18 +392,6 @@ { "name": "SUNLU PETG @Sovol SV08 MAX", "sub_path": "filament/SUNLU PETG @Sovol SV08 MAX.json" - }, - { - "name": "Sovol Zero PETG HS Nozzle", - "sub_path": "filament/Sovol Zero PETG HS Nozzle.json" - }, - { - "name": "Sovol Zero PLA Basic HS Nozzle", - "sub_path": "filament/Sovol Zero PLA Basic HS Nozzle.json" - }, - { - "name": "Sovol Zero PLA Silk HS Nozzle", - "sub_path": "filament/Sovol Zero PLA Silk HS Nozzle.json" } ], "machine_list": [ @@ -415,9 +483,45 @@ "name": "Sovol SV08 MAX 0.8 nozzle", "sub_path": "machine/Sovol SV08 MAX 0.8 nozzle.json" }, + { + "name": "Sovol Zero 0.2 nozzle", + "sub_path": "machine/Sovol Zero 0.2 nozzle.json" + }, { "name": "Sovol Zero 0.4 nozzle", "sub_path": "machine/Sovol Zero 0.4 nozzle.json" + }, + { + "name": "Sovol Zero 0.6 nozzle", + "sub_path": "machine/Sovol Zero 0.6 nozzle.json" + }, + { + "name": "Sovol Zero 0.8 nozzle", + "sub_path": "machine/Sovol Zero 0.8 nozzle.json" + }, + { + "name": "Sovol Zero 1.0 nozzle", + "sub_path": "machine/Sovol Zero 1.0 nozzle.json" + }, + { + "name": "Sovol Zero 0.2 Hardened Steel nozzle", + "sub_path": "machine/Sovol Zero 0.2 Hardened Steel nozzle.json" + }, + { + "name": "Sovol Zero 0.4 Hardened Steel nozzle", + "sub_path": "machine/Sovol Zero 0.4 Hardened Steel nozzle.json" + }, + { + "name": "Sovol Zero 0.6 Hardened Steel nozzle", + "sub_path": "machine/Sovol Zero 0.6 Hardened Steel nozzle.json" + }, + { + "name": "Sovol Zero 0.8 Hardened Steel nozzle", + "sub_path": "machine/Sovol Zero 0.8 Hardened Steel nozzle.json" + }, + { + "name": "Sovol Zero 1.0 Hardened Steel nozzle", + "sub_path": "machine/Sovol Zero 1.0 Hardened Steel nozzle.json" } ] } diff --git a/resources/profiles/Sovol/filament/Generic ABS @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic ABS @Sovol Zero.json index 3b737e2a18..7c24b7a0aa 100644 --- a/resources/profiles/Sovol/filament/Generic ABS @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic ABS @Sovol Zero.json @@ -1,10 +1,11 @@ { "type": "filament", "name": "Generic ABS @Sovol Zero", - "inherits": "Generic ABS @System", "renamed_from": "Sovol Zero ABS", + "inherits": "fdm_filament_abs", "from": "system", "setting_id": "g6sSyyIFYJUzlXsp", + "filament_id": "OFY9muEs", "instantiation": "true", "filament_flow_ratio": [ "0.98" @@ -73,6 +74,39 @@ "0" ], "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ], + "cool_plate_temp": [ + "80" + ], + "eng_plate_temp": [ + "80" + ], + "textured_plate_temp": [ + "80" + ], + "cool_plate_temp_initial_layer": [ + "80" + ], + "eng_plate_temp_initial_layer": [ + "80" + ], + "textured_plate_temp_initial_layer": [ + "80" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_density": [ + "1.10" ] } diff --git a/resources/profiles/Sovol/filament/Generic PC @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic PC @Sovol Zero.json index dd0eb60594..832deb1368 100644 --- a/resources/profiles/Sovol/filament/Generic PC @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic PC @Sovol Zero.json @@ -1,11 +1,12 @@ { "type": "filament", + "filament_id": "OFLakOUI", + "setting_id": "yRKCiPMN6fxibcyg", "name": "Generic PC @Sovol Zero", - "inherits": "Generic PC @System", "renamed_from": "Sovol Zero PC", "from": "system", - "setting_id": "yRKCiPMN6fxibcyg", "instantiation": "true", + "inherits": "fdm_filament_pc", "filament_flow_ratio": [ "0.98" ], @@ -13,7 +14,16 @@ "21" ], "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" ], "nozzle_temperature_initial_layer": [ "290" @@ -51,7 +61,9 @@ "slow_down_min_speed": [ "10" ], - "dont_slow_down_outer_wall": "0", + "dont_slow_down_outer_wall": [ + "0" + ], "overhang_fan_speed": [ "20" ], @@ -61,8 +73,19 @@ "temperature_vitrification": [ "60" ], - "additional_cooling_fan_speed": "0", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "50", - "complete_print_exhaust_fan_speed": "50" + "additional_cooling_fan_speed": [ + "0" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "50" + ], + "complete_print_exhaust_fan_speed": [ + "50" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ] } diff --git a/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero Hardened Steel nozzle.json b/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero Hardened Steel nozzle.json new file mode 100644 index 0000000000..ef6a6324e3 --- /dev/null +++ b/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero Hardened Steel nozzle.json @@ -0,0 +1,116 @@ +{ + "type": "filament", + "filament_id": "OFYPdQJh", + "setting_id": "d9QbCESNz3ZLU6eu", + "name": "Generic PETG @Sovol Zero Hardened Steel nozzle", + "renamed_from": "Sovol Zero PETG HS Nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pet", + "filament_flow_ratio": [ + "0.98" + ], + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.048" + ], + "filament_max_volumetric_speed": [ + "15" + ], + "nozzle_temperature_initial_layer": [ + "265" + ], + "nozzle_temperature": [ + "250" + ], + "nozzle_temperature_range_low": [ + "230" + ], + "nozzle_temperature_range_high": [ + "280" + ], + "hot_plate_temp": [ + "85" + ], + "hot_plate_temp_initial_layer": [ + "85" + ], + "fan_min_speed": [ + "10" + ], + "fan_max_speed": [ + "40" + ], + "fan_cooling_layer_time": [ + "50" + ], + "full_fan_speed_layer": [ + "3" + ], + "slow_down_layer_time": [ + "10" + ], + "slow_down_min_speed": [ + "10" + ], + "overhang_fan_speed": [ + "70" + ], + "overhang_fan_threshold": [ + "10%" + ], + "temperature_vitrification": [ + "60" + ], + "close_fan_the_first_x_layers": [ + "1" + ], + "filament_retraction_length": [ + "0.5" + ], + "filament_z_hop": [ + "0.4" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "50" + ], + "complete_print_exhaust_fan_speed": [ + "50" + ], + "compatible_printers": [ + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ], + "cool_plate_temp": [ + "85" + ], + "eng_plate_temp": [ + "85" + ], + "textured_plate_temp": [ + "85" + ], + "cool_plate_temp_initial_layer": [ + "85" + ], + "eng_plate_temp_initial_layer": [ + "85" + ], + "textured_plate_temp_initial_layer": [ + "85" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero.json index a223521d25..b9632637ad 100644 --- a/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic PETG @Sovol Zero.json @@ -1,16 +1,21 @@ { "type": "filament", + "filament_id": "OFYPdQJh", + "setting_id": "i4OW2eAtXOKdtVNj", "name": "Generic PETG @Sovol Zero", - "inherits": "Generic PETG @System", "renamed_from": "Sovol Zero PETG", "from": "system", - "setting_id": "i4OW2eAtXOKdtVNj", "instantiation": "true", + "inherits": "fdm_filament_pet", "filament_flow_ratio": [ "1.0348" ], - "enable_pressure_advance": "1", - "pressure_advance": "0.046", + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.046" + ], "filament_max_volumetric_speed": [ "15" ], @@ -68,10 +73,44 @@ "filament_z_hop": [ "0.4" ], - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "50", - "complete_print_exhaust_fan_speed": "50", + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "50" + ], + "complete_print_exhaust_fan_speed": [ + "50" + ], "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle" + ], + "cool_plate_temp": [ + "85" + ], + "eng_plate_temp": [ + "85" + ], + "textured_plate_temp": [ + "85" + ], + "cool_plate_temp_initial_layer": [ + "85" + ], + "eng_plate_temp_initial_layer": [ + "85" + ], + "textured_plate_temp_initial_layer": [ + "85" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" ] } diff --git a/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero Hardened Steel nozzle.json b/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero Hardened Steel nozzle.json new file mode 100644 index 0000000000..bba0cbfb51 --- /dev/null +++ b/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero Hardened Steel nozzle.json @@ -0,0 +1,110 @@ +{ + "type": "filament", + "filament_id": "OFDSrzZ8", + "setting_id": "nZgETe4CTpicEjQX", + "name": "Generic PLA @Sovol Zero Hardened Steel nozzle", + "renamed_from": "Sovol Zero PLA Basic HS Nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pla", + "filament_flow_ratio": [ + "1.0348" + ], + "filament_max_volumetric_speed": [ + "21" + ], + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.03" + ], + "compatible_printers": [ + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ], + "nozzle_temperature_initial_layer": [ + "245" + ], + "nozzle_temperature": [ + "230" + ], + "nozzle_temperature_range_low": [ + "190" + ], + "nozzle_temperature_range_high": [ + "250" + ], + "hot_plate_temp": [ + "65" + ], + "hot_plate_temp_initial_layer": [ + "65" + ], + "fan_min_speed": [ + "70" + ], + "fan_max_speed": [ + "100" + ], + "fan_cooling_layer_time": [ + "80" + ], + "full_fan_speed_layer": [ + "3" + ], + "slow_down_layer_time": [ + "5" + ], + "slow_down_min_speed": [ + "10" + ], + "dont_slow_down_outer_wall": [ + "0" + ], + "overhang_fan_speed": [ + "100" + ], + "overhang_fan_threshold": [ + "50%" + ], + "temperature_vitrification": [ + "60" + ], + "additional_cooling_fan_speed": [ + "75%" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "80%" + ], + "complete_print_exhaust_fan_speed": [ + "80%" + ], + "cool_plate_temp": [ + "60" + ], + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero.json index 131867f6b8..c8424af4f4 100644 --- a/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic PLA @Sovol Zero.json @@ -1,21 +1,24 @@ { "type": "filament", + "filament_id": "OFDSrzZ8", + "setting_id": "NNDerIjh7qtuh1I3", "name": "Generic PLA @Sovol Zero", - "inherits": "Generic PLA @System", "renamed_from": "Sovol Zero PLA Basic", "from": "system", - "setting_id": "NNDerIjh7qtuh1I3", "instantiation": "true", + "inherits": "fdm_filament_pla", "filament_flow_ratio": [ "0.98" ], "filament_max_volumetric_speed": [ - "21" + "25" ], - "enable_pressure_advance": "1", - "pressure_advance": "0.032", "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle" ], "nozzle_temperature_initial_layer": [ "230" @@ -24,28 +27,28 @@ "210" ], "nozzle_temperature_range_low": [ - "190" + "180" ], "nozzle_temperature_range_high": [ - "250" + "240" ], "hot_plate_temp": [ - "65" + "60" ], "hot_plate_temp_initial_layer": [ - "65" + "60" ], "fan_min_speed": [ - "70" + "100" ], "fan_max_speed": [ "100" ], "fan_cooling_layer_time": [ - "80" + "60" ], "full_fan_speed_layer": [ - "3" + "2" ], "slow_down_layer_time": [ "5" @@ -53,18 +56,43 @@ "slow_down_min_speed": [ "10" ], - "dont_slow_down_outer_wall": "0", "overhang_fan_speed": [ "100" ], "overhang_fan_threshold": [ - "50%" + "10%" ], "temperature_vitrification": [ + "50" + ], + "filament_z_hop": [ + "0.4" + ], + "filament_retraction_length": [ + "0.4" + ], + "filament_diameter": [ + "1.75" + ], + "cool_plate_temp": [ "60" ], - "additional_cooling_fan_speed": "75%", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "80%", - "complete_print_exhaust_fan_speed": "80%" + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] } diff --git a/resources/profiles/Sovol/filament/Generic PLA SPEEDBENCHY @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic PLA SPEEDBENCHY @Sovol Zero.json new file mode 100644 index 0000000000..a9aefc6bd2 --- /dev/null +++ b/resources/profiles/Sovol/filament/Generic PLA SPEEDBENCHY @Sovol Zero.json @@ -0,0 +1,118 @@ +{ + "type": "filament", + "filament_id": "OFIUkWYN", + "setting_id": "MC2InZ7iKDjIGike", + "name": "Generic PLA SPEEDBENCHY @Sovol Zero", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pla", + "filament_flow_ratio": [ + "1" + ], + "filament_max_volumetric_speed": [ + "50" + ], + "compatible_printers": [ + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle" + ], + "nozzle_temperature_initial_layer": [ + "245" + ], + "nozzle_temperature": [ + "225" + ], + "nozzle_temperature_range_low": [ + "190" + ], + "nozzle_temperature_range_high": [ + "250" + ], + "hot_plate_temp": [ + "65" + ], + "hot_plate_temp_initial_layer": [ + "65" + ], + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.032" + ], + "fan_min_speed": [ + "100" + ], + "fan_max_speed": [ + "100" + ], + "fan_cooling_layer_time": [ + "30" + ], + "full_fan_speed_layer": [ + "3" + ], + "slow_down_layer_time": [ + "1.7" + ], + "slow_down_min_speed": [ + "40" + ], + "overhang_fan_speed": [ + "100" + ], + "overhang_fan_threshold": [ + "75%" + ], + "additional_cooling_fan_speed": [ + "100" + ], + "filament_z_hop": [ + "0" + ], + "filament_retraction_length": [ + "0.35" + ], + "filament_retraction_speed": [ + "90" + ], + "filament_deretraction_speed": [ + "90" + ], + "filament_retract_before_wipe": [ + "0%" + ], + "filament_wipe_distance": [ + "2" + ], + "filament_retract_when_changing_layer": [ + "0" + ], + "filament_diameter": [ + "1.75" + ], + "cool_plate_temp": [ + "60" + ], + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ], + "temperature_vitrification": [ + "100" + ] +} diff --git a/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero Hardened Steel nozzle.json b/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero Hardened Steel nozzle.json new file mode 100644 index 0000000000..7347fa08de --- /dev/null +++ b/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero Hardened Steel nozzle.json @@ -0,0 +1,110 @@ +{ + "type": "filament", + "filament_id": "OFesA6rF", + "setting_id": "LA8X1RsMdV2xNT1o", + "name": "Generic PLA Silk @Sovol Zero Hardened Steel nozzle", + "renamed_from": "Sovol Zero PLA Silk HS Nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_filament_pla", + "filament_flow_ratio": [ + "0.98" + ], + "filament_max_volumetric_speed": [ + "15" + ], + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.027" + ], + "compatible_printers": [ + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ], + "nozzle_temperature_initial_layer": [ + "245" + ], + "nozzle_temperature": [ + "230" + ], + "nozzle_temperature_range_low": [ + "190" + ], + "nozzle_temperature_range_high": [ + "250" + ], + "hot_plate_temp": [ + "65" + ], + "hot_plate_temp_initial_layer": [ + "65" + ], + "fan_min_speed": [ + "70" + ], + "fan_max_speed": [ + "100" + ], + "fan_cooling_layer_time": [ + "80" + ], + "full_fan_speed_layer": [ + "3" + ], + "slow_down_layer_time": [ + "5" + ], + "slow_down_min_speed": [ + "10" + ], + "dont_slow_down_outer_wall": [ + "0" + ], + "overhang_fan_speed": [ + "100" + ], + "overhang_fan_threshold": [ + "50%" + ], + "temperature_vitrification": [ + "60" + ], + "additional_cooling_fan_speed": [ + "75%" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "80%" + ], + "complete_print_exhaust_fan_speed": [ + "80%" + ], + "cool_plate_temp": [ + "60" + ], + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] +} diff --git a/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero.json index 5b46a92d64..6c9c27a00a 100644 --- a/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic PLA Silk @Sovol Zero.json @@ -1,27 +1,30 @@ { "type": "filament", + "filament_id": "OFesA6rF", + "setting_id": "Se2yKerHRStYtWf5", "name": "Generic PLA Silk @Sovol Zero", - "inherits": "Generic PLA Silk @System", "renamed_from": "Sovol Zero PLA Silk", "from": "system", - "setting_id": "Se2yKerHRStYtWf5", "instantiation": "true", - "filament_multitool_ramming_flow": [ - "20" - ], - "filament_retraction_length": [ - "nil" - ], + "inherits": "fdm_filament_pla", "filament_flow_ratio": [ "0.98" ], "filament_max_volumetric_speed": [ "15" ], - "enable_pressure_advance": "1", - "pressure_advance": "0.029", + "enable_pressure_advance": [ + "1" + ], + "pressure_advance": [ + "0.029" + ], "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle" ], "nozzle_temperature_initial_layer": [ "245" @@ -59,7 +62,9 @@ "slow_down_min_speed": [ "10" ], - "dont_slow_down_outer_wall": "0", + "dont_slow_down_outer_wall": [ + "0" + ], "overhang_fan_speed": [ "100" ], @@ -69,8 +74,43 @@ "temperature_vitrification": [ "60" ], - "additional_cooling_fan_speed": "75%", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "80%", - "complete_print_exhaust_fan_speed": "80%" + "additional_cooling_fan_speed": [ + "75%" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "80%" + ], + "complete_print_exhaust_fan_speed": [ + "80%" + ], + "filament_multitool_ramming_flow": [ + "20" + ], + "filament_retraction_length": [ + "nil" + ], + "cool_plate_temp": [ + "60" + ], + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_start_gcode": [ + "; filament start gcode\n" + ] } diff --git a/resources/profiles/Sovol/filament/Generic TPU @Sovol Zero.json b/resources/profiles/Sovol/filament/Generic TPU @Sovol Zero.json index 8c7c18c523..8624043cf9 100644 --- a/resources/profiles/Sovol/filament/Generic TPU @Sovol Zero.json +++ b/resources/profiles/Sovol/filament/Generic TPU @Sovol Zero.json @@ -1,11 +1,12 @@ { "type": "filament", + "filament_id": "OFgbpcy9", + "setting_id": "MlY6WQ41FE8zDPSV", "name": "Generic TPU @Sovol Zero", - "inherits": "Generic TPU @System", "renamed_from": "Sovol Zero TPU", "from": "system", - "setting_id": "MlY6WQ41FE8zDPSV", "instantiation": "true", + "inherits": "fdm_filament_tpu", "filament_flow_ratio": [ "0.98" ], @@ -60,11 +61,55 @@ "filament_z_hop": [ "0.4" ], - "additional_cooling_fan_speed": "50", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "100", - "complete_print_exhaust_fan_speed": "50", + "additional_cooling_fan_speed": [ + "50" + ], + "activate_air_filtration": [ + "1" + ], + "during_print_exhaust_fan_speed": [ + "100" + ], + "complete_print_exhaust_fan_speed": [ + "50" + ], "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.8 nozzle", + "Sovol Zero 1.0 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ], + "cool_plate_temp": [ + "60" + ], + "eng_plate_temp": [ + "60" + ], + "textured_plate_temp": [ + "60" + ], + "cool_plate_temp_initial_layer": [ + "55" + ], + "eng_plate_temp_initial_layer": [ + "55" + ], + "textured_plate_temp_initial_layer": [ + "55" + ], + "filament_end_gcode": [ + "; filament end gcode \n" + ], + "filament_retraction_length": [ + "nil" + ], + "filament_start_gcode": [ + "; filament start gcode\n" ] } diff --git a/resources/profiles/Sovol/filament/Sovol Zero PETG HS Nozzle.json b/resources/profiles/Sovol/filament/Sovol Zero PETG HS Nozzle.json deleted file mode 100644 index ce68719ba9..0000000000 --- a/resources/profiles/Sovol/filament/Sovol Zero PETG HS Nozzle.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "type": "filament", - "name": "Sovol Zero PETG HS Nozzle", - "inherits": "Generic PETG @System", - "from": "system", - "setting_id": "AfxHmpdOkMDJa2fk", - "filament_id": "OFymnal9", - "instantiation": "true", - "filament_flow_ratio": [ - "0.98" - ], - "enable_pressure_advance": "1", - "pressure_advance": "0.048", - "filament_max_volumetric_speed": [ - "15" - ], - "nozzle_temperature_initial_layer": [ - "265" - ], - "nozzle_temperature": [ - "250" - ], - "nozzle_temperature_range_low": [ - "230" - ], - "nozzle_temperature_range_high": [ - "280" - ], - "hot_plate_temp": [ - "85" - ], - "hot_plate_temp_initial_layer": [ - "85" - ], - "fan_min_speed": [ - "10" - ], - "fan_max_speed": [ - "40" - ], - "fan_cooling_layer_time": [ - "50" - ], - "full_fan_speed_layer": [ - "3" - ], - "slow_down_layer_time": [ - "10" - ], - "slow_down_min_speed": [ - "10" - ], - "overhang_fan_speed": [ - "70" - ], - "overhang_fan_threshold": [ - "10%" - ], - "temperature_vitrification": [ - "60" - ], - "close_fan_the_first_x_layers": [ - "1" - ], - "filament_retraction_length": [ - "0.5" - ], - "filament_z_hop": [ - "0.4" - ], - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "50", - "complete_print_exhaust_fan_speed": "50", - "compatible_printers": [ - "Sovol Zero 0.4 nozzle" - ] -} diff --git a/resources/profiles/Sovol/filament/Sovol Zero PLA Basic HS Nozzle.json b/resources/profiles/Sovol/filament/Sovol Zero PLA Basic HS Nozzle.json deleted file mode 100644 index 6dfb630e0a..0000000000 --- a/resources/profiles/Sovol/filament/Sovol Zero PLA Basic HS Nozzle.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "type": "filament", - "name": "Sovol Zero PLA Basic HS Nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "i9wDnSEIrOBPbOZl", - "filament_id": "OFzB4uOs", - "instantiation": "true", - "filament_flow_ratio": [ - "1.0348" - ], - "filament_max_volumetric_speed": [ - "21" - ], - "enable_pressure_advance": "1", - "pressure_advance": "0.03", - "compatible_printers": [ - "Sovol Zero 0.4 nozzle" - ], - "nozzle_temperature_initial_layer": [ - "245" - ], - "nozzle_temperature": [ - "230" - ], - "nozzle_temperature_range_low": [ - "190" - ], - "nozzle_temperature_range_high": [ - "250" - ], - "hot_plate_temp": [ - "65" - ], - "hot_plate_temp_initial_layer": [ - "65" - ], - "fan_min_speed": [ - "70" - ], - "fan_max_speed": [ - "100" - ], - "fan_cooling_layer_time": [ - "80" - ], - "full_fan_speed_layer": [ - "3" - ], - "slow_down_layer_time": [ - "5" - ], - "slow_down_min_speed": [ - "10" - ], - "dont_slow_down_outer_wall": "0", - "overhang_fan_speed": [ - "100" - ], - "overhang_fan_threshold": [ - "50%" - ], - "temperature_vitrification": [ - "60" - ], - "additional_cooling_fan_speed": "75%", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "80%", - "complete_print_exhaust_fan_speed": "80%" -} diff --git a/resources/profiles/Sovol/filament/Sovol Zero PLA Silk HS Nozzle.json b/resources/profiles/Sovol/filament/Sovol Zero PLA Silk HS Nozzle.json deleted file mode 100644 index 97e4050b8c..0000000000 --- a/resources/profiles/Sovol/filament/Sovol Zero PLA Silk HS Nozzle.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "type": "filament", - "name": "Sovol Zero PLA Silk HS Nozzle", - "inherits": "Generic PLA @System", - "from": "system", - "setting_id": "XxEJBNFLRltR2Xwn", - "filament_id": "OFAO9A7J", - "instantiation": "true", - "filament_flow_ratio": [ - "0.98" - ], - "filament_max_volumetric_speed": [ - "15" - ], - "enable_pressure_advance": "1", - "pressure_advance": "0.027", - "compatible_printers": [ - "Sovol Zero 0.4 nozzle" - ], - "nozzle_temperature_initial_layer": [ - "245" - ], - "nozzle_temperature": [ - "230" - ], - "nozzle_temperature_range_low": [ - "190" - ], - "nozzle_temperature_range_high": [ - "250" - ], - "hot_plate_temp": [ - "65" - ], - "hot_plate_temp_initial_layer": [ - "65" - ], - "fan_min_speed": [ - "70" - ], - "fan_max_speed": [ - "100" - ], - "fan_cooling_layer_time": [ - "80" - ], - "full_fan_speed_layer": [ - "3" - ], - "slow_down_layer_time": [ - "5" - ], - "slow_down_min_speed": [ - "10" - ], - "dont_slow_down_outer_wall": "0", - "overhang_fan_speed": [ - "100" - ], - "overhang_fan_threshold": [ - "50%" - ], - "temperature_vitrification": [ - "60" - ], - "additional_cooling_fan_speed": "75%", - "activate_air_filtration": "1", - "during_print_exhaust_fan_speed": "80%", - "complete_print_exhaust_fan_speed": "80%" -} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.2 Hardened Steel nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.2 Hardened Steel nozzle.json new file mode 100644 index 0000000000..89ee9c1c09 --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.2 Hardened Steel nozzle.json @@ -0,0 +1,19 @@ +{ + "type": "machine", + "name": "Sovol Zero 0.2 Hardened Steel nozzle", + "inherits": "Sovol Zero 0.2 nozzle", + "from": "system", + "setting_id": "OkvEInEyzUUJzw9K", + "instantiation": "true", + "printer_model": "Sovol Zero", + "printer_variant": "0.2HS", + "nozzle_diameter": [ + "0.2" + ], + "default_filament_profile": [ + "Generic PLA @Sovol Zero Hardened Steel nozzle" + ], + "nozzle_type": [ + "hardened_steel" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.2 nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.2 nozzle.json new file mode 100644 index 0000000000..33fb16508a --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.2 nozzle.json @@ -0,0 +1,118 @@ +{ + "type": "machine", + "setting_id": "MAF2n6b8uoY92qVH", + "name": "Sovol Zero 0.2 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_machine_common", + "printer_model": "Sovol Zero", + "default_print_profile": "0.10mm Standard @Sovol Zero 0.2 nozzle", + "printer_variant": "0.2", + "nozzle_diameter": [ + "0.2" + ], + "min_layer_height": [ + "0.04" + ], + "max_layer_height": [ + "0.2" + ], + "retract_before_wipe": [ + "0%" + ], + "printable_area": [ + "0x0", + "152.4x0", + "152.4x152.4", + "0x152.4" + ], + "printable_height": "152.4", + "gcode_flavor": "klipper", + "retraction_length": [ + "1.8" + ], + "machine_max_speed_e": [ + "50" + ], + "machine_max_speed_x": [ + "1200" + ], + "machine_max_speed_y": [ + "1200" + ], + "machine_max_speed_z": [ + "30" + ], + "machine_max_acceleration_e": [ + "20000" + ], + "machine_max_acceleration_extruding": [ + "40000" + ], + "machine_max_acceleration_retracting": [ + "20000" + ], + "machine_max_acceleration_travel": [ + "40000" + ], + "machine_max_acceleration_x": [ + "40000" + ], + "machine_max_acceleration_y": [ + "40000" + ], + "machine_max_acceleration_z": [ + "1000" + ], + "machine_max_jerk_e": [ + "2.5" + ], + "machine_max_jerk_x": [ + "5" + ], + "machine_max_jerk_y": [ + "5" + ], + "machine_max_jerk_z": [ + "0.5" + ], + "z_hop": [ + "0.6" + ], + "retraction_speed": [ + "40" + ], + "deretraction_speed": [ + "40" + ], + "retraction_minimum_travel": [ + "0" + ], + "retract_length_toolchange": [ + "2" + ], + "wipe": [ + "1" + ], + "wipe_distance": [ + "3" + ], + "z_hop_types": [ + "Auto Lift" + ], + "thumbnails": [ + "300x300", + "32x32" + ], + "retract_lift_below": [ + "150" + ], + "auxiliary_fan": "1", + "thumbnails_format": "PNG", + "before_layer_change_gcode": "TIMELAPSE_TAKE_FRAME\nG92 E0\nSET_PRINT_STATS_INFO CURRENT_LAYER=[layer_num]\n", + "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", + "machine_end_gcode": "END_PRINT\n", + "default_filament_profile": [ + "Generic PLA @Sovol Zero" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.4 Hardened Steel nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.4 Hardened Steel nozzle.json new file mode 100644 index 0000000000..515c4cf696 --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.4 Hardened Steel nozzle.json @@ -0,0 +1,19 @@ +{ + "type": "machine", + "name": "Sovol Zero 0.4 Hardened Steel nozzle", + "inherits": "Sovol Zero 0.4 nozzle", + "from": "system", + "setting_id": "RqwHKS8MYRdoeJPi", + "instantiation": "true", + "printer_model": "Sovol Zero", + "printer_variant": "0.4HS", + "nozzle_diameter": [ + "0.4" + ], + "default_filament_profile": [ + "Generic PLA @Sovol Zero Hardened Steel nozzle" + ], + "nozzle_type": [ + "hardened_steel" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.4 nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.4 nozzle.json index a5699a9eed..932c5b9121 100644 --- a/resources/profiles/Sovol/machine/Sovol Zero 0.4 nozzle.json +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.4 nozzle.json @@ -1,18 +1,24 @@ { "type": "machine", - "name": "Sovol Zero 0.4 nozzle", - "inherits": "fdm_machine_common", - "from": "system", "setting_id": "QclEa2nmpBiii6Y0", + "name": "Sovol Zero 0.4 nozzle", + "from": "system", "instantiation": "true", + "inherits": "fdm_machine_common", "printer_model": "Sovol Zero", "default_print_profile": "0.20mm Standard @Sovol Zero 0.4 nozzle", "printer_variant": "0.4", "nozzle_diameter": [ "0.4" ], + "min_layer_height": [ + "0.08" + ], + "max_layer_height": [ + "0.32" + ], "retract_before_wipe": [ - "100%" + "0%" ], "printable_area": [ "0x0", @@ -23,7 +29,7 @@ "printable_height": "152.4", "gcode_flavor": "klipper", "retraction_length": [ - "0.8" + "1.8" ], "machine_max_speed_e": [ "50" @@ -71,7 +77,7 @@ "0.5" ], "z_hop": [ - "0.4" + "0.6" ], "retraction_speed": [ "40" @@ -89,7 +95,7 @@ "1" ], "wipe_distance": [ - "2" + "3" ], "z_hop_types": [ "Auto Lift" @@ -104,7 +110,7 @@ "auxiliary_fan": "1", "thumbnails_format": "PNG", "before_layer_change_gcode": "TIMELAPSE_TAKE_FRAME\nG92 E0\nSET_PRINT_STATS_INFO CURRENT_LAYER=[layer_num]\n", - "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", + "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", "machine_end_gcode": "END_PRINT\n", "default_filament_profile": [ "Generic PLA @Sovol Zero" diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.6 Hardened Steel nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.6 Hardened Steel nozzle.json new file mode 100644 index 0000000000..5b53d59ae2 --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.6 Hardened Steel nozzle.json @@ -0,0 +1,19 @@ +{ + "type": "machine", + "name": "Sovol Zero 0.6 Hardened Steel nozzle", + "inherits": "Sovol Zero 0.6 nozzle", + "from": "system", + "setting_id": "s3N3FJkWrzOylFm8", + "instantiation": "true", + "printer_model": "Sovol Zero", + "printer_variant": "0.6HS", + "nozzle_diameter": [ + "0.6" + ], + "default_filament_profile": [ + "Generic PLA @Sovol Zero Hardened Steel nozzle" + ], + "nozzle_type": [ + "hardened_steel" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.6 nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.6 nozzle.json new file mode 100644 index 0000000000..e2cfc7ccc5 --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.6 nozzle.json @@ -0,0 +1,118 @@ +{ + "type": "machine", + "setting_id": "BlngL0WExXTeyrXg", + "name": "Sovol Zero 0.6 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_machine_common", + "printer_model": "Sovol Zero", + "default_print_profile": "0.30mm Standard @Sovol Zero 0.6 nozzle", + "printer_variant": "0.6", + "nozzle_diameter": [ + "0.6" + ], + "min_layer_height": [ + "0.12" + ], + "max_layer_height": [ + "0.48" + ], + "retract_before_wipe": [ + "0%" + ], + "printable_area": [ + "0x0", + "152.4x0", + "152.4x152.4", + "0x152.4" + ], + "printable_height": "152.4", + "gcode_flavor": "klipper", + "retraction_length": [ + "3.0" + ], + "machine_max_speed_e": [ + "50" + ], + "machine_max_speed_x": [ + "1200" + ], + "machine_max_speed_y": [ + "1200" + ], + "machine_max_speed_z": [ + "30" + ], + "machine_max_acceleration_e": [ + "20000" + ], + "machine_max_acceleration_extruding": [ + "40000" + ], + "machine_max_acceleration_retracting": [ + "20000" + ], + "machine_max_acceleration_travel": [ + "40000" + ], + "machine_max_acceleration_x": [ + "40000" + ], + "machine_max_acceleration_y": [ + "40000" + ], + "machine_max_acceleration_z": [ + "1000" + ], + "machine_max_jerk_e": [ + "2.5" + ], + "machine_max_jerk_x": [ + "5" + ], + "machine_max_jerk_y": [ + "5" + ], + "machine_max_jerk_z": [ + "0.5" + ], + "z_hop": [ + "0.6" + ], + "retraction_speed": [ + "40" + ], + "deretraction_speed": [ + "40" + ], + "retraction_minimum_travel": [ + "0" + ], + "retract_length_toolchange": [ + "2" + ], + "wipe": [ + "1" + ], + "wipe_distance": [ + "3" + ], + "z_hop_types": [ + "Auto Lift" + ], + "thumbnails": [ + "300x300", + "32x32" + ], + "retract_lift_below": [ + "150" + ], + "auxiliary_fan": "1", + "thumbnails_format": "PNG", + "before_layer_change_gcode": "TIMELAPSE_TAKE_FRAME\nG92 E0\nSET_PRINT_STATS_INFO CURRENT_LAYER=[layer_num]\n", + "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", + "machine_end_gcode": "END_PRINT\n", + "default_filament_profile": [ + "Generic PLA @Sovol Zero" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.8 Hardened Steel nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.8 Hardened Steel nozzle.json new file mode 100644 index 0000000000..db6bd740fb --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.8 Hardened Steel nozzle.json @@ -0,0 +1,19 @@ +{ + "type": "machine", + "name": "Sovol Zero 0.8 Hardened Steel nozzle", + "inherits": "Sovol Zero 0.8 nozzle", + "from": "system", + "setting_id": "houacf8sQ3bLozqD", + "instantiation": "true", + "printer_model": "Sovol Zero", + "printer_variant": "0.8HS", + "nozzle_diameter": [ + "0.8" + ], + "default_filament_profile": [ + "Generic PLA @Sovol Zero Hardened Steel nozzle" + ], + "nozzle_type": [ + "hardened_steel" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 0.8 nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 0.8 nozzle.json new file mode 100644 index 0000000000..bc67a64ce5 --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 0.8 nozzle.json @@ -0,0 +1,118 @@ +{ + "type": "machine", + "setting_id": "DDpJMY1A2r4k1Cho", + "name": "Sovol Zero 0.8 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_machine_common", + "printer_model": "Sovol Zero", + "default_print_profile": "0.40mm Standard @Sovol Zero 0.8 nozzle", + "printer_variant": "0.8", + "nozzle_diameter": [ + "0.8" + ], + "min_layer_height": [ + "0.08" + ], + "max_layer_height": [ + "0.64" + ], + "retract_before_wipe": [ + "0%" + ], + "printable_area": [ + "0x0", + "152.4x0", + "152.4x152.4", + "0x152.4" + ], + "printable_height": "152.4", + "gcode_flavor": "klipper", + "retraction_length": [ + "3.0" + ], + "machine_max_speed_e": [ + "50" + ], + "machine_max_speed_x": [ + "1200" + ], + "machine_max_speed_y": [ + "1200" + ], + "machine_max_speed_z": [ + "30" + ], + "machine_max_acceleration_e": [ + "20000" + ], + "machine_max_acceleration_extruding": [ + "40000" + ], + "machine_max_acceleration_retracting": [ + "20000" + ], + "machine_max_acceleration_travel": [ + "40000" + ], + "machine_max_acceleration_x": [ + "40000" + ], + "machine_max_acceleration_y": [ + "40000" + ], + "machine_max_acceleration_z": [ + "1000" + ], + "machine_max_jerk_e": [ + "2.5" + ], + "machine_max_jerk_x": [ + "5" + ], + "machine_max_jerk_y": [ + "5" + ], + "machine_max_jerk_z": [ + "0.5" + ], + "z_hop": [ + "0.6" + ], + "retraction_speed": [ + "40" + ], + "deretraction_speed": [ + "40" + ], + "retraction_minimum_travel": [ + "0" + ], + "retract_length_toolchange": [ + "2" + ], + "wipe": [ + "1" + ], + "wipe_distance": [ + "3" + ], + "z_hop_types": [ + "Auto Lift" + ], + "thumbnails": [ + "300x300", + "32x32" + ], + "retract_lift_below": [ + "150" + ], + "auxiliary_fan": "1", + "thumbnails_format": "PNG", + "before_layer_change_gcode": "TIMELAPSE_TAKE_FRAME\nG92 E0\nSET_PRINT_STATS_INFO CURRENT_LAYER=[layer_num]\n", + "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", + "machine_end_gcode": "END_PRINT\n", + "default_filament_profile": [ + "Generic PLA @Sovol Zero" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 1.0 Hardened Steel nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 1.0 Hardened Steel nozzle.json new file mode 100644 index 0000000000..73b99e55ef --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 1.0 Hardened Steel nozzle.json @@ -0,0 +1,19 @@ +{ + "type": "machine", + "name": "Sovol Zero 1.0 Hardened Steel nozzle", + "inherits": "Sovol Zero 1.0 nozzle", + "from": "system", + "setting_id": "sqlEiL0in8LSjmmn", + "instantiation": "true", + "printer_model": "Sovol Zero", + "printer_variant": "1.0HS", + "nozzle_diameter": [ + "1.0" + ], + "default_filament_profile": [ + "Generic PLA @Sovol Zero Hardened Steel nozzle" + ], + "nozzle_type": [ + "hardened_steel" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero 1.0 nozzle.json b/resources/profiles/Sovol/machine/Sovol Zero 1.0 nozzle.json new file mode 100644 index 0000000000..beb6db80bd --- /dev/null +++ b/resources/profiles/Sovol/machine/Sovol Zero 1.0 nozzle.json @@ -0,0 +1,118 @@ +{ + "type": "machine", + "setting_id": "CGunT3KHnCqEAlrN", + "name": "Sovol Zero 1.0 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "fdm_machine_common", + "printer_model": "Sovol Zero", + "default_print_profile": "0.50mm Standard @Sovol Zero 1.0 nozzle", + "printer_variant": "1.0", + "nozzle_diameter": [ + "1.0" + ], + "min_layer_height": [ + "0.16" + ], + "max_layer_height": [ + "0.8" + ], + "retract_before_wipe": [ + "0%" + ], + "printable_area": [ + "0x0", + "152.4x0", + "152.4x152.4", + "0x152.4" + ], + "printable_height": "152.4", + "gcode_flavor": "klipper", + "retraction_length": [ + "3.0" + ], + "machine_max_speed_e": [ + "50" + ], + "machine_max_speed_x": [ + "1200" + ], + "machine_max_speed_y": [ + "1200" + ], + "machine_max_speed_z": [ + "30" + ], + "machine_max_acceleration_e": [ + "20000" + ], + "machine_max_acceleration_extruding": [ + "40000" + ], + "machine_max_acceleration_retracting": [ + "20000" + ], + "machine_max_acceleration_travel": [ + "40000" + ], + "machine_max_acceleration_x": [ + "40000" + ], + "machine_max_acceleration_y": [ + "40000" + ], + "machine_max_acceleration_z": [ + "1000" + ], + "machine_max_jerk_e": [ + "2.5" + ], + "machine_max_jerk_x": [ + "5" + ], + "machine_max_jerk_y": [ + "5" + ], + "machine_max_jerk_z": [ + "0.5" + ], + "z_hop": [ + "0.6" + ], + "retraction_speed": [ + "40" + ], + "deretraction_speed": [ + "40" + ], + "retraction_minimum_travel": [ + "0" + ], + "retract_length_toolchange": [ + "2" + ], + "wipe": [ + "1" + ], + "wipe_distance": [ + "3" + ], + "z_hop_types": [ + "Auto Lift" + ], + "thumbnails": [ + "300x300", + "32x32" + ], + "retract_lift_below": [ + "150" + ], + "auxiliary_fan": "1", + "thumbnails_format": "PNG", + "before_layer_change_gcode": "TIMELAPSE_TAKE_FRAME\nG92 E0\nSET_PRINT_STATS_INFO CURRENT_LAYER=[layer_num]\n", + "machine_start_gcode": "M140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\nG28\nSTART_PRINT\nG28\nG90\nG1 X0 Y0 F12000\nG1 Z0.300 F600\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nM109 S[nozzle_temperature_initial_layer];wait for extruder temp\n{if first_layer_print_min[1] - 6 > print_bed_min[1]}\nG90\nM83\nG1 E-0.5 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 5} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z1 F600\nG1 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4} Y{first_layer_print_min[1] - 4} F12000\nG0 Z0.3 F600 ;Move to start position\nG1 E0.200 F600\n{if first_layer_print_max[0] - first_layer_print_min[0] > 50}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*1} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*4} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*5} E{5 * 0.2}F{outer_wall_volumetric_speed/(24/20) * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*8} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*9} E{5 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 4 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\n{else}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0]) / 2} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG0 X{first_layer_print_min[0] + (first_layer_print_max[0] - first_layer_print_min[0])} E{(first_layer_print_max[0] - first_layer_print_min[0]) / 2 * 0.2}F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\n{endif}\nG1 E-0.300 F600\nG0 Z5 F600\nM400\n{else}\nG90\nM83\nG1 E-0.300 Z3 F600\nG1 X{print_bed_max[1] / 3} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 Z0.3 F600\nG1 E0.300 F600\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3} Y1 F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*1} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*2} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*3} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*4} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*5} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*6} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4 * 60}\nG1 X{print_bed_max[1] / 3 + 5*7} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20) * 60}\nG1 X{print_bed_max[1] / 3 + 5*8} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 X{print_bed_max[1] / 3 + 5*9} E{5 * 0.2} F{outer_wall_volumetric_speed/(24/20)* 60}\nG1 X{print_bed_max[1] / 3 + 5*10} E{5 * 0.2} F{outer_wall_volumetric_speed/(0.3*0.5)/4* 60}\nG1 E-0.300 Z3 F600\nM400\n{endif}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n\n", + "machine_end_gcode": "END_PRINT\n", + "default_filament_profile": [ + "Generic PLA @Sovol Zero" + ] +} diff --git a/resources/profiles/Sovol/machine/Sovol Zero.json b/resources/profiles/Sovol/machine/Sovol Zero.json index 6f99e8f8d0..1c67135cf9 100644 --- a/resources/profiles/Sovol/machine/Sovol Zero.json +++ b/resources/profiles/Sovol/machine/Sovol Zero.json @@ -2,11 +2,11 @@ "type": "machine_model", "name": "Sovol Zero", "model_id": "Sovol-Zero", - "nozzle_diameter": "0.4", + "nozzle_diameter": "0.2;0.4;0.6;0.8;1.0;0.2HS;0.4HS;0.6HS;0.8HS;1.0HS", "machine_tech": "FFF", "family": "Sovol", "bed_model": "sovol_zero_buildplate_model.stl", "bed_texture": "sovol_zero_buildplate_texture.svg", "hotend_model": "", - "default_materials": "Generic PLA @Sovol Zero;Sovol Zero PLA Basic HS Nozzle;Generic PLA Silk @Sovol Zero;Sovol Zero PLA Silk HS Nozzle;Generic ABS @Sovol Zero;Generic PETG @Sovol Zero;Sovol Zero PETG HS Nozzle;Generic TPU @Sovol Zero;Generic PC @Sovol Zero" + "default_materials": "Generic PLA @Sovol Zero;Generic PLA SPEEDBENCHY @Sovol Zero;Generic PLA @Sovol Zero Hardened Steel nozzle;Generic PLA Silk @Sovol Zero;Generic PLA Silk @Sovol Zero Hardened Steel nozzle;Generic ABS @Sovol Zero;Generic PETG @Sovol Zero;Generic PETG @Sovol Zero Hardened Steel nozzle;Generic TPU @Sovol Zero;Generic PC @Sovol Zero" } diff --git a/resources/profiles/Sovol/process/0.06mm Quality @Sovol Zero 0.2 nozzle.json b/resources/profiles/Sovol/process/0.06mm Quality @Sovol Zero 0.2 nozzle.json new file mode 100644 index 0000000000..908bfd9813 --- /dev/null +++ b/resources/profiles/Sovol/process/0.06mm Quality @Sovol Zero 0.2 nozzle.json @@ -0,0 +1,36 @@ +{ + "type": "process", + "name": "0.06mm Quality @Sovol Zero 0.2 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "fWP7jaL7sJmmZ6QN", + "instantiation": "true", + "layer_height": "0.06", + "bottom_shell_layers": "8", + "bottom_shell_thickness": "0.8", + "bridge_speed": "30", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.21", + "line_width": "0.21", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.06", + "initial_layer_line_width": "0.3", + "infill_wall_overlap": "18", + "inner_wall_line_width": "0.22", + "wall_loops": "3", + "internal_solid_infill_line_width": "0.21", + "sparse_infill_line_width": "0.21", + "support_top_z_distance": "0.10", + "support_line_width": "0.21", + "tree_support_tip_diameter": "0.21", + "support_speed": "100", + "top_surface_line_width": "0.21", + "top_shell_layers": "8", + "top_shell_thickness": "1", + "compatible_printers": [ + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.10mm Standard @Sovol Zero 0.2 nozzle.json b/resources/profiles/Sovol/process/0.10mm Standard @Sovol Zero 0.2 nozzle.json new file mode 100644 index 0000000000..15540bb172 --- /dev/null +++ b/resources/profiles/Sovol/process/0.10mm Standard @Sovol Zero 0.2 nozzle.json @@ -0,0 +1,36 @@ +{ + "type": "process", + "name": "0.10mm Standard @Sovol Zero 0.2 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "Gql3487jsaTXyuYZ", + "instantiation": "true", + "layer_height": "0.10", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "50", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.22", + "line_width": "0.22", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.12", + "initial_layer_line_width": "0.3", + "infill_wall_overlap": "20", + "inner_wall_line_width": "0.22", + "wall_loops": "2", + "internal_solid_infill_line_width": "0.22", + "sparse_infill_line_width": "0.22", + "support_top_z_distance": "0.20", + "support_line_width": "0.22", + "tree_support_tip_diameter": "0.22", + "support_speed": "100", + "top_surface_line_width": "0.22", + "top_shell_layers": "4", + "top_shell_thickness": "1", + "compatible_printers": [ + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.12mm Quality @Sovol Zero 0.4 nozzle.json b/resources/profiles/Sovol/process/0.12mm Quality @Sovol Zero 0.4 nozzle.json new file mode 100644 index 0000000000..c1cc06a737 --- /dev/null +++ b/resources/profiles/Sovol/process/0.12mm Quality @Sovol Zero 0.4 nozzle.json @@ -0,0 +1,35 @@ +{ + "type": "process", + "setting_id": "CaEopiv2Wg6pQJjH", + "name": "0.12mm Quality @Sovol Zero 0.4 nozzle", + "from": "system", + "inherits": "fdm_process_zero", + "instantiation": "true", + "layer_height": "0.12", + "bottom_shell_layers": "8", + "bottom_shell_thickness": "0.8", + "bridge_speed": "30", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.42", + "line_width": "0.42", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.12", + "initial_layer_line_width": "0.6", + "sparse_infill_line_width": "0.42", + "infill_wall_overlap": "18", + "inner_wall_line_width": "0.44", + "wall_loops": "3", + "support_top_z_distance": "0.10", + "support_line_width": "0.42", + "tree_support_tip_diameter": "0.42", + "support_speed": "100", + "top_surface_line_width": "0.42", + "internal_solid_infill_line_width": "0.42", + "top_shell_layers": "8", + "compatible_printers": [ + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.14mm Fast @Sovol Zero 0.2 nozzle.json b/resources/profiles/Sovol/process/0.14mm Fast @Sovol Zero 0.2 nozzle.json new file mode 100644 index 0000000000..79fe17eb39 --- /dev/null +++ b/resources/profiles/Sovol/process/0.14mm Fast @Sovol Zero 0.2 nozzle.json @@ -0,0 +1,36 @@ +{ + "type": "process", + "name": "0.14mm Fast @Sovol Zero 0.2 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "I9yoyCcGGTRrDoKu", + "instantiation": "true", + "layer_height": "0.14", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_speed": "60", + "outer_wall_acceleration": "22000", + "top_surface_acceleration": "22000", + "outer_wall_line_width": "0.24", + "line_width": "0.24", + "sparse_infill_density": "10%", + "inner_wall_acceleration": "28000", + "initial_layer_print_height": "0.14", + "initial_layer_line_width": "0.3", + "infill_wall_overlap": "20", + "inner_wall_line_width": "0.24", + "wall_loops": "2", + "internal_solid_infill_line_width": "0.24", + "sparse_infill_line_width": "0.24", + "support_top_z_distance": "0.30", + "support_line_width": "0.24", + "tree_support_tip_diameter": "0.24", + "support_speed": "120", + "top_surface_line_width": "0.24", + "top_shell_layers": "4", + "top_shell_thickness": "1", + "compatible_printers": [ + "Sovol Zero 0.2 nozzle", + "Sovol Zero 0.2 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.15mm Quality @Sovol Zero 0.6 nozzle.json b/resources/profiles/Sovol/process/0.15mm Quality @Sovol Zero 0.6 nozzle.json new file mode 100644 index 0000000000..f532985258 --- /dev/null +++ b/resources/profiles/Sovol/process/0.15mm Quality @Sovol Zero 0.6 nozzle.json @@ -0,0 +1,34 @@ +{ + "type": "process", + "setting_id": "IfDpa3vM99D4ytny", + "name": "0.15mm Quality @Sovol Zero 0.6 nozzle", + "from": "system", + "inherits": "fdm_process_zero", + "instantiation": "true", + "layer_height": "0.15", + "bottom_shell_layers": "5", + "bottom_shell_thickness": "0.8", + "bridge_speed": "30", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.63", + "line_width": "0.63", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_line_width": "0.9", + "sparse_infill_line_width": "0.63", + "infill_wall_overlap": "18", + "inner_wall_line_width": "0.66", + "wall_loops": "3", + "support_top_z_distance": "0.15", + "support_line_width": "0.63", + "tree_support_tip_diameter": "0.63", + "support_speed": "100", + "top_surface_line_width": "0.63", + "top_shell_layers": "5", + "internal_solid_infill_line_width": "0.63", + "compatible_printers": [ + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.20mm Quality @Sovol Zero 0.8 nozzle.json b/resources/profiles/Sovol/process/0.20mm Quality @Sovol Zero 0.8 nozzle.json new file mode 100644 index 0000000000..c9774c6d65 --- /dev/null +++ b/resources/profiles/Sovol/process/0.20mm Quality @Sovol Zero 0.8 nozzle.json @@ -0,0 +1,42 @@ +{ + "type": "process", + "name": "0.20mm Quality @Sovol Zero 0.8 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "MSxikpQZNYdoBynZ", + "instantiation": "true", + "layer_height": "0.20", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "30", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.84", + "line_width": "0.84", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.36", + "initial_layer_line_width": "1.2", + "sparse_infill_line_width": "0.84", + "infill_wall_overlap": "18", + "inner_wall_line_width": "0.88", + "wall_loops": "3", + "support_top_z_distance": "0.20", + "support_line_width": "0.84", + "tree_support_tip_diameter": "0.84", + "support_speed": "100", + "top_surface_line_width": "0.84", + "top_shell_layers": "4", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "50%", + "internal_solid_infill_speed": "200", + "internal_solid_infill_line_width": "0.84", + "top_surface_speed": "200", + "gap_infill_speed": "200", + "sparse_infill_speed": "500", + "compatible_printers": [ + "Sovol Zero 0.8 nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.20mm Standard @Sovol Zero 0.4 nozzle.json b/resources/profiles/Sovol/process/0.20mm Standard @Sovol Zero 0.4 nozzle.json index 323553beaa..d8820163d6 100644 --- a/resources/profiles/Sovol/process/0.20mm Standard @Sovol Zero 0.4 nozzle.json +++ b/resources/profiles/Sovol/process/0.20mm Standard @Sovol Zero 0.4 nozzle.json @@ -1,141 +1,36 @@ { "type": "process", - "name": "0.20mm Standard @Sovol Zero 0.4 nozzle", - "inherits": "fdm_process_common", - "from": "system", "setting_id": "ShJTVnJ492No1j2j", + "name": "0.20mm Standard @Sovol Zero 0.4 nozzle", + "from": "system", + "inherits": "fdm_process_zero", "instantiation": "true", - "reduce_crossing_wall": "1", - "max_travel_detour_distance": "100%", "layer_height": "0.20", - "bottom_surface_pattern": "monotonic", - "bottom_shell_layers": "3", - "bottom_shell_thickness": "0", - "bridge_flow": "1.2", - "internal_bridge_flow": "1.2", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", "bridge_speed": "50", - "internal_bridge_speed": "200", - "brim_type": "auto_brim", - "brim_width": "5", - "brim_object_gap": "0", - "compatible_printers_condition": "", - "print_sequence": "by layer", - "default_acceleration": "40000", "outer_wall_acceleration": "10000", "top_surface_acceleration": "5000", - "bridge_no_support": "0", - "draft_shield": "disabled", - "elefant_foot_compensation": "0.1", - "enable_arc_fitting": "0", - "exclude_object": "1", - "outer_wall_line_width": "0.4", - "wall_infill_order": "inner wall/outer wall/infill", - "line_width": "0.4", - "infill_direction": "45", - "sparse_infill_density": "10%", - "sparse_infill_pattern": "grid", - "internal_solid_infill_acceleration": "50%", - "initial_layer_acceleration": "1000", - "initial_solid_infill_acceleration": "3000", - "travel_acceleration": "40000", + "outer_wall_line_width": "0.44", + "line_width": "0.44", + "sparse_infill_density": "15%", "inner_wall_acceleration": "12000", - "outer_wall_jerk": "5", - "inner_wall_jerk": "5", - "infill_jerk": "5", - "top_surface_jerk": "5", - "initial_layer_jerk": "5", - "travel_jerk": "5", - "initial_layer_line_width": "0.42", - "initial_layer_print_height": "0.25", - "infill_combination": "0", - "sparse_infill_line_width": "0.45", - "infill_wall_overlap": "15%", - "interface_shells": "0", - "ironing_flow": "15%", - "ironing_spacing": "0.25", - "ironing_speed": "15", - "ironing_type": "no ironing", - "reduce_infill_retraction": "1", - "filename_format": "{printer_model}_{input_filename_base}_{filament_type[0]}_{layer_height}_{print_time}.gcode", - "detect_overhang_wall": "1", - "overhang_1_4_speed": "0", - "overhang_2_4_speed": "50", - "overhang_3_4_speed": "30", - "overhang_4_4_speed": "20", - "inner_wall_line_width": "0.4", + "initial_layer_print_height": "0.24", + "initial_layer_line_width": "0.6", + "sparse_infill_line_width": "0.44", + "infill_wall_overlap": "20", + "ironing_speed": "100", + "inner_wall_line_width": "0.44", "wall_loops": "2", - "print_settings_id": "", - "raft_layers": "0", - "seam_position": "aligned", - "skirt_distance": "0.8", - "skirt_height": "1", - "skirt_loops": "1", - "minimum_sparse_infill_area": "15", - "internal_solid_infill_line_width": "0.45", - "spiral_mode": "0", - "standby_temperature_delta": "-5", - "enable_support": "0", - "resolution": "0.012", - "support_type": "tree(auto)", - "support_style": "default", - "support_on_build_plate_only": "0", - "support_top_z_distance": "0.235", - "support_bottom_z_distance": "0.235", - "support_filament": "0", - "support_line_width": "0.45", - "support_interface_loop_pattern": "0", - "support_interface_filament": "0", - "support_interface_top_layers": "5", - "support_interface_bottom_layers": "-1", - "support_interface_spacing": "0.2", - "support_bottom_interface_spacing": "0.2", - "support_interface_speed": "100%", - "support_interface_pattern": "grid", - "support_base_pattern": "rectilinear", - "support_base_pattern_spacing": "3.5", - "support_speed": "100", - "support_threshold_angle": "20", - "support_object_xy_distance": "0.35", - "tree_support_branch_angle": "40", - "tree_support_wall_count": "0", - "detect_thin_wall": "1", - "top_surface_pattern": "monotonic", - "top_surface_line_width": "0.38", + "support_top_z_distance": "0.20", + "support_line_width": "0.44", + "tree_support_tip_diameter": "0.44", + "support_speed": "60", + "top_surface_line_width": "0.44", "top_shell_layers": "4", - "top_shell_thickness": "1", - "initial_layer_speed": "55", - "initial_layer_infill_speed": "105", - "initial_layer_travel_speed": "60%", - "outer_wall_speed": "350", - "inner_wall_speed": "400", - "small_perimeter_speed": "50%", - "internal_solid_infill_speed": "200", - "top_surface_speed": "200", - "gap_infill_speed": "200", - "sparse_infill_speed": "500", - "accel_to_decel_enable": "1", - "accel_to_decel_factor": "25%", - "travel_speed": "1000", - "enable_prime_tower": "1", - "wipe_tower_no_sparse_layers": "0", - "prime_tower_width": "60", - "xy_hole_compensation": "0", - "xy_contour_compensation": "0", - "bridge_acceleration": "50%", - "seam_gap": "10%", - "precise_outer_wall": "1", - "wall_generator": "classic", - "gcode_label_objects": "1", - "slow_down_layers": "3", - "top_solid_infill_flow_ratio": "0.9", - "only_one_wall_top": "1", - "wall_loop_direction": "clockwise", - "top_bottom_infill_wall_overlap": "25%", - "filter_out_gap_fill": "0", - "detect_narrow_internal_solid_infill": "1", - "thick_bridges": "1", - "bridge_angle": "0", + "internal_solid_infill_line_width": "0.44", "compatible_printers": [ - "Sovol Zero 0.4 nozzle" + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle" ] } diff --git a/resources/profiles/Sovol/process/0.25mm Quality @Sovol Zero 1.0 nozzle.json b/resources/profiles/Sovol/process/0.25mm Quality @Sovol Zero 1.0 nozzle.json new file mode 100644 index 0000000000..60e8db168f --- /dev/null +++ b/resources/profiles/Sovol/process/0.25mm Quality @Sovol Zero 1.0 nozzle.json @@ -0,0 +1,79 @@ +{ + "type": "process", + "name": "0.25mm Quality @Sovol Zero 1.0 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "JUmnFOoEMJrbWCN9", + "instantiation": "true", + "layer_height": "0.25", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "30", + "internal_bridge_speed": "200", + "default_acceleration": "40000", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "1.05", + "line_width": "1.05", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.45", + "initial_layer_line_width": "1.5", + "infill_combination": "0", + "sparse_infill_line_width": "1.05", + "infill_wall_overlap": "18", + "interface_shells": "0", + "ironing_flow": "15%", + "ironing_spacing": "0.25", + "ironing_speed": "15", + "ironing_type": "no ironing", + "filename_format": "{input_filename_base}_{nozzle_diameter[0]}n_{filament_type[0]}_{layer_height}_{print_time}.gcode", + "detect_overhang_wall": "1", + "overhang_1_4_speed": "0", + "overhang_2_4_speed": "50", + "overhang_3_4_speed": "30", + "overhang_4_4_speed": "20", + "inner_wall_line_width": "1.1", + "wall_loops": "3", + "print_settings_id": "", + "raft_layers": "0", + "seam_position": "aligned", + "skirt_distance": "0.8", + "skirt_height": "1", + "skirt_loops": "1", + "minimum_sparse_infill_area": "15", + "internal_solid_infill_line_width": "1.05", + "spiral_mode": "0", + "standby_temperature_delta": "-5", + "enable_support": "0", + "resolution": "0.012", + "support_type": "tree(auto)", + "support_style": "snug", + "support_on_build_plate_only": "0", + "support_top_z_distance": "0.25", + "support_bottom_z_distance": "0.235", + "support_filament": "0", + "support_line_width": "1.05", + "tree_support_tip_diameter": "1.05", + "support_interface_loop_pattern": "0", + "support_interface_filament": "0", + "support_interface_top_layers": "5", + "support_interface_bottom_layers": "-1", + "support_interface_spacing": "0.2", + "support_bottom_interface_spacing": "0.2", + "support_interface_speed": "100%", + "support_interface_pattern": "grid", + "support_base_pattern": "rectilinear", + "support_base_pattern_spacing": "3.5", + "support_speed": "100", + "support_threshold_angle": "50", + "support_object_xy_distance": "0.35", + "tree_support_branch_angle": "40", + "top_surface_line_width": "1.05", + "top_shell_layers": "4", + "top_shell_thickness": "1", + "compatible_printers": [ + "Sovol Zero 1.0 nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle.json b/resources/profiles/Sovol/process/0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle.json new file mode 100644 index 0000000000..89e248b223 --- /dev/null +++ b/resources/profiles/Sovol/process/0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle.json @@ -0,0 +1,85 @@ +{ + "type": "process", + "setting_id": "oFrWcSxmad1b6sD4", + "name": "0.25mm SPEEDBENCHY @Sovol Zero 0.4 nozzle", + "from": "system", + "inherits": "fdm_process_zero", + "instantiation": "true", + "layer_height": "0.25", + "initial_layer_print_height": "0.25", + "line_width": "0.5", + "initial_layer_line_width": "0.5", + "outer_wall_line_width": "0.5", + "inner_wall_line_width": "0.5", + "top_surface_line_width": "0.55", + "internal_solid_infill_line_width": "0.55", + "sparse_infill_line_width": "0.55", + "support_line_width": "0.5", + "tree_support_tip_diameter": "0.5", + "wall_loops": "2", + "top_shell_layers": "3", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "sparse_infill_density": "10%", + "sparse_infill_pattern": "grid", + "bottom_solid_infill_flow_ratio": "1.2", + "top_solid_infill_flow_ratio": "1.4", + "bridge_flow": "1", + "internal_bridge_flow": "1", + "bridge_speed": "250", + "internal_bridge_speed": "250", + "bridge_acceleration": "100%", + "thick_bridges": "0", + "thick_internal_bridges": "0", + "brim_type": "outer_only", + "brim_width": "1", + "brim_object_gap": "0", + "filter_out_gap_fill": "13", + "gap_fill_target": "nowhere", + "min_width_top_surface": "200%", + "minimum_sparse_infill_area": "0", + "infill_direction": "0", + "solid_infill_direction": "0", + "infill_wall_overlap": "15%", + "reduce_crossing_wall": "0", + "skirt_loops": "0", + "only_one_wall_top": "1", + "slow_down_layers": "1", + "wall_direction": "cw", + "initial_layer_speed": "300", + "initial_layer_infill_speed": "300", + "initial_layer_travel_speed": "100%", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "100%", + "internal_solid_infill_speed": "400", + "top_surface_speed": "300", + "gap_infill_speed": "350", + "sparse_infill_speed": "500", + "support_speed": "100", + "travel_speed": "1200", + "default_acceleration": "40000", + "initial_layer_acceleration": "20000", + "outer_wall_acceleration": "40000", + "inner_wall_acceleration": "40000", + "top_surface_acceleration": "20000", + "internal_solid_infill_acceleration": "100%", + "travel_acceleration": "40000", + "accel_to_decel_factor": "50%", + "outer_wall_jerk": "20", + "inner_wall_jerk": "20", + "infill_jerk": "20", + "top_surface_jerk": "20", + "initial_layer_jerk": "20", + "travel_jerk": "20", + "overhang_1_4_speed": "160", + "overhang_2_4_speed": "100", + "overhang_3_4_speed": "60", + "overhang_4_4_speed": "40", + "support_top_z_distance": "0.235", + "support_bottom_z_distance": "0.235", + "compatible_printers": [ + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.28mm Fast @Sovol Zero 0.4 nozzle.json b/resources/profiles/Sovol/process/0.28mm Fast @Sovol Zero 0.4 nozzle.json new file mode 100644 index 0000000000..1ad361e3e2 --- /dev/null +++ b/resources/profiles/Sovol/process/0.28mm Fast @Sovol Zero 0.4 nozzle.json @@ -0,0 +1,39 @@ +{ + "type": "process", + "name": "0.28mm Fast @Sovol Zero 0.4 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "1VoML2LGDk4TJgLI", + "instantiation": "true", + "layer_height": "0.28", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_speed": "60", + "outer_wall_acceleration": "22000", + "top_surface_acceleration": "22000", + "outer_wall_line_width": "0.48", + "line_width": "0.48", + "sparse_infill_density": "10%", + "inner_wall_acceleration": "28000", + "initial_layer_print_height": "0.28", + "initial_layer_line_width": "0.6", + "sparse_infill_line_width": "0.48", + "inner_wall_line_width": "0.48", + "internal_solid_infill_line_width": "0.48", + "support_top_z_distance": "0.30", + "support_line_width": "0.48", + "tree_support_tip_diameter": "0.48", + "support_speed": "120", + "top_surface_line_width": "0.48", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "50%", + "internal_solid_infill_speed": "200", + "top_surface_speed": "200", + "gap_infill_speed": "200", + "sparse_infill_speed": "500", + "compatible_printers": [ + "Sovol Zero 0.4 nozzle", + "Sovol Zero 0.4 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.30mm Standard @Sovol Zero 0.6 nozzle.json b/resources/profiles/Sovol/process/0.30mm Standard @Sovol Zero 0.6 nozzle.json new file mode 100644 index 0000000000..4f1f487bdf --- /dev/null +++ b/resources/profiles/Sovol/process/0.30mm Standard @Sovol Zero 0.6 nozzle.json @@ -0,0 +1,35 @@ +{ + "type": "process", + "setting_id": "lTAs2USEP8D749K7", + "name": "0.30mm Standard @Sovol Zero 0.6 nozzle", + "from": "system", + "inherits": "fdm_process_zero", + "instantiation": "true", + "layer_height": "0.30", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "50", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.66", + "line_width": "0.66", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.36", + "initial_layer_line_width": "0.9", + "sparse_infill_line_width": "0.66", + "infill_wall_overlap": "20", + "inner_wall_line_width": "0.66", + "wall_loops": "2", + "support_top_z_distance": "0.30", + "support_line_width": "0.66", + "tree_support_tip_diameter": "0.66", + "support_speed": "100", + "top_surface_line_width": "0.66", + "top_shell_layers": "4", + "internal_solid_infill_line_width": "0.66", + "compatible_printers": [ + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.40mm Standard @Sovol Zero 0.8 nozzle.json b/resources/profiles/Sovol/process/0.40mm Standard @Sovol Zero 0.8 nozzle.json new file mode 100644 index 0000000000..7eeecf4ece --- /dev/null +++ b/resources/profiles/Sovol/process/0.40mm Standard @Sovol Zero 0.8 nozzle.json @@ -0,0 +1,42 @@ +{ + "type": "process", + "name": "0.40mm Standard @Sovol Zero 0.8 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "8x4CsuqroC9HOdAN", + "instantiation": "true", + "layer_height": "0.40", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "50", + "outer_wall_acceleration": "10000", + "top_surface_acceleration": "5000", + "outer_wall_line_width": "0.88", + "line_width": "0.88", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "12000", + "initial_layer_print_height": "0.48", + "initial_layer_line_width": "1.2", + "sparse_infill_line_width": "0.88", + "infill_wall_overlap": "20", + "inner_wall_line_width": "0.88", + "wall_loops": "2", + "support_top_z_distance": "0.40", + "support_line_width": "0.88", + "tree_support_tip_diameter": "0.88", + "support_speed": "100", + "top_surface_line_width": "0.88", + "top_shell_layers": "4", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "50%", + "internal_solid_infill_speed": "200", + "top_surface_speed": "200", + "gap_infill_speed": "200", + "sparse_infill_speed": "500", + "internal_solid_infill_line_width": "0.88", + "compatible_printers": [ + "Sovol Zero 0.8 nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.42mm Fast @Sovol Zero 0.6 nozzle.json b/resources/profiles/Sovol/process/0.42mm Fast @Sovol Zero 0.6 nozzle.json new file mode 100644 index 0000000000..75a9522c62 --- /dev/null +++ b/resources/profiles/Sovol/process/0.42mm Fast @Sovol Zero 0.6 nozzle.json @@ -0,0 +1,39 @@ +{ + "type": "process", + "name": "0.42mm Fast @Sovol Zero 0.6 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "illqJAnGiA2pY0hw", + "instantiation": "true", + "layer_height": "0.42", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_speed": "60", + "outer_wall_acceleration": "22000", + "top_surface_acceleration": "22000", + "outer_wall_line_width": "0.72", + "line_width": "0.72", + "sparse_infill_density": "10%", + "inner_wall_acceleration": "28000", + "initial_layer_print_height": "0.42", + "initial_layer_line_width": "0.9", + "sparse_infill_line_width": "0.72", + "inner_wall_line_width": "0.72", + "internal_solid_infill_line_width": "0.72", + "support_top_z_distance": "0.45", + "support_line_width": "0.72", + "tree_support_tip_diameter": "0.72", + "support_speed": "120", + "top_surface_line_width": "0.72", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "50%", + "internal_solid_infill_speed": "200", + "top_surface_speed": "200", + "gap_infill_speed": "200", + "sparse_infill_speed": "500", + "compatible_printers": [ + "Sovol Zero 0.6 nozzle", + "Sovol Zero 0.6 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.50mm Standard @Sovol Zero 1.0 nozzle.json b/resources/profiles/Sovol/process/0.50mm Standard @Sovol Zero 1.0 nozzle.json new file mode 100644 index 0000000000..c1446a6536 --- /dev/null +++ b/resources/profiles/Sovol/process/0.50mm Standard @Sovol Zero 1.0 nozzle.json @@ -0,0 +1,87 @@ +{ + "type": "process", + "name": "0.50mm Standard @Sovol Zero 1.0 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "E5K55O4RkMyYCAlt", + "instantiation": "true", + "layer_height": "0.50", + "bottom_shell_layers": "4", + "bottom_shell_thickness": "0.8", + "bridge_speed": "45", + "internal_bridge_speed": "200", + "default_acceleration": "22000", + "outer_wall_acceleration": "16000", + "top_surface_acceleration": "16000", + "outer_wall_line_width": "1.1", + "line_width": "1.1", + "sparse_infill_density": "15%", + "inner_wall_acceleration": "22000", + "initial_layer_print_height": "0.60", + "initial_layer_line_width": "1.5", + "infill_combination": "0", + "sparse_infill_line_width": "1.1", + "infill_wall_overlap": "20", + "interface_shells": "0", + "ironing_flow": "15%", + "ironing_spacing": "0.25", + "ironing_speed": "15", + "ironing_type": "no ironing", + "filename_format": "{input_filename_base}_{nozzle_diameter[0]}n_{filament_type[0]}_{layer_height}_{print_time}.gcode", + "detect_overhang_wall": "1", + "overhang_1_4_speed": "0", + "overhang_2_4_speed": "50", + "overhang_3_4_speed": "30", + "overhang_4_4_speed": "20", + "inner_wall_line_width": "1.1", + "wall_loops": "2", + "print_settings_id": "", + "raft_layers": "0", + "seam_position": "aligned", + "skirt_distance": "0.8", + "skirt_height": "1", + "skirt_loops": "1", + "minimum_sparse_infill_area": "15", + "internal_solid_infill_line_width": "1.1", + "spiral_mode": "0", + "standby_temperature_delta": "-5", + "enable_support": "0", + "resolution": "0.012", + "support_type": "tree(auto)", + "support_style": "snug", + "support_on_build_plate_only": "0", + "support_top_z_distance": "0.50", + "support_bottom_z_distance": "0.235", + "support_filament": "0", + "support_line_width": "1.1", + "tree_support_tip_diameter": "1.1", + "support_interface_loop_pattern": "0", + "support_interface_filament": "0", + "support_interface_top_layers": "5", + "support_interface_bottom_layers": "-1", + "support_interface_spacing": "0.2", + "support_bottom_interface_spacing": "0.2", + "support_interface_speed": "100%", + "support_interface_pattern": "grid", + "support_base_pattern": "rectilinear", + "support_base_pattern_spacing": "3.5", + "support_speed": "60", + "support_threshold_angle": "50", + "support_object_xy_distance": "0.35", + "tree_support_branch_angle": "40", + "top_surface_line_width": "1.1", + "top_shell_layers": "4", + "top_shell_thickness": "1", + "outer_wall_speed": "30", + "inner_wall_speed": "45", + "small_perimeter_speed": "45", + "internal_solid_infill_speed": "45", + "top_surface_speed": "35", + "gap_infill_speed": "35", + "sparse_infill_speed": "70", + "travel_acceleration": "38000", + "compatible_printers": [ + "Sovol Zero 1.0 nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.56mm Fast @Sovol Zero 0.8 nozzle.json b/resources/profiles/Sovol/process/0.56mm Fast @Sovol Zero 0.8 nozzle.json new file mode 100644 index 0000000000..3bef013a12 --- /dev/null +++ b/resources/profiles/Sovol/process/0.56mm Fast @Sovol Zero 0.8 nozzle.json @@ -0,0 +1,40 @@ +{ + "type": "process", + "name": "0.56mm Fast @Sovol Zero 0.8 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "g9PPRM4FLMwjMUfy", + "instantiation": "true", + "layer_height": "0.56", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_speed": "60", + "default_acceleration": "28000", + "outer_wall_acceleration": "22000", + "top_surface_acceleration": "22000", + "outer_wall_line_width": "0.96", + "line_width": "0.96", + "sparse_infill_density": "10%", + "inner_wall_acceleration": "28000", + "initial_layer_print_height": "0.56", + "initial_layer_line_width": "1.2", + "sparse_infill_line_width": "0.96", + "inner_wall_line_width": "0.96", + "internal_solid_infill_line_width": "0.96", + "support_top_z_distance": "0.60", + "support_line_width": "0.96", + "tree_support_tip_diameter": "0.96", + "top_surface_line_width": "0.96", + "top_shell_layers": "4", + "outer_wall_speed": "35", + "inner_wall_speed": "55", + "small_perimeter_speed": "60", + "internal_solid_infill_speed": "55", + "top_surface_speed": "40", + "gap_infill_speed": "40", + "sparse_infill_speed": "80", + "compatible_printers": [ + "Sovol Zero 0.8 nozzle", + "Sovol Zero 0.8 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/0.70mm Fast @Sovol Zero 1.0 nozzle.json b/resources/profiles/Sovol/process/0.70mm Fast @Sovol Zero 1.0 nozzle.json new file mode 100644 index 0000000000..39d5309914 --- /dev/null +++ b/resources/profiles/Sovol/process/0.70mm Fast @Sovol Zero 1.0 nozzle.json @@ -0,0 +1,33 @@ +{ + "type": "process", + "name": "0.70mm Fast @Sovol Zero 1.0 nozzle", + "inherits": "fdm_process_zero", + "from": "system", + "setting_id": "fW5KJBVndgQE868q", + "instantiation": "true", + "layer_height": "0.70", + "bottom_shell_layers": "3", + "bottom_shell_thickness": "0", + "bridge_speed": "60", + "outer_wall_acceleration": "22000", + "top_surface_acceleration": "22000", + "outer_wall_line_width": "1.2", + "line_width": "1.2", + "sparse_infill_density": "10%", + "inner_wall_acceleration": "28000", + "initial_layer_print_height": "0.70", + "initial_layer_line_width": "1.5", + "sparse_infill_line_width": "1.2", + "inner_wall_line_width": "1.2", + "internal_solid_infill_line_width": "1.2", + "support_top_z_distance": "0.75", + "support_line_width": "1.2", + "tree_support_tip_diameter": "1.2", + "support_speed": "120", + "top_surface_line_width": "1.2", + "top_shell_layers": "3", + "compatible_printers": [ + "Sovol Zero 1.0 nozzle", + "Sovol Zero 1.0 Hardened Steel nozzle" + ] +} diff --git a/resources/profiles/Sovol/process/fdm_process_zero.json b/resources/profiles/Sovol/process/fdm_process_zero.json new file mode 100644 index 0000000000..da4df2686e --- /dev/null +++ b/resources/profiles/Sovol/process/fdm_process_zero.json @@ -0,0 +1,73 @@ +{ + "type": "process", + "name": "fdm_process_zero", + "inherits": "fdm_process_common", + "from": "system", + "instantiation": "false", + "reduce_crossing_wall": "1", + "max_travel_detour_distance": "10", + "bottom_surface_pattern": "monotonic", + "bridge_flow": "1.2", + "internal_bridge_flow": "1.2", + "brim_type": "auto_brim", + "brim_width": "5", + "brim_object_gap": "0", + "print_sequence": "by layer", + "bridge_no_support": "0", + "draft_shield": "disabled", + "elefant_foot_compensation": "0.1", + "enable_arc_fitting": "0", + "exclude_object": "1", + "wall_infill_order": "inner wall/outer wall/infill", + "infill_direction": "45", + "sparse_infill_pattern": "grid", + "internal_solid_infill_acceleration": "50%", + "initial_layer_acceleration": "1000", + "travel_acceleration": "40000", + "outer_wall_jerk": "5", + "inner_wall_jerk": "5", + "infill_jerk": "5", + "top_surface_jerk": "5", + "initial_layer_jerk": "5", + "travel_jerk": "5", + "tree_support_wall_count": "0", + "reduce_infill_retraction": "1", + "detect_thin_wall": "1", + "top_surface_pattern": "monotonic", + "initial_layer_speed": "55", + "initial_layer_infill_speed": "105", + "initial_layer_travel_speed": "60%", + "outer_wall_speed": "350", + "inner_wall_speed": "400", + "small_perimeter_speed": "50%", + "internal_solid_infill_speed": "200", + "top_surface_speed": "200", + "gap_infill_speed": "200", + "sparse_infill_speed": "500", + "accel_to_decel_enable": "1", + "accel_to_decel_factor": "25%", + "travel_speed": "1000", + "enable_prime_tower": "0", + "wipe_tower_no_sparse_layers": "0", + "prime_tower_width": "60", + "xy_hole_compensation": "0", + "xy_contour_compensation": "0", + "bridge_acceleration": "50%", + "seam_gap": "10%", + "precise_outer_wall": "1", + "wall_generator": "classic", + "gcode_label_objects": "1", + "slow_down_layers": "3", + "top_solid_infill_flow_ratio": "0.9", + "only_one_wall_top": "1", + "top_bottom_infill_wall_overlap": "25%", + "filter_out_gap_fill": "0", + "detect_narrow_internal_solid_infill": "1", + "thick_bridges": "1", + "bridge_angle": "0", + "initial_layer_line_width": "150%", + "default_acceleration": "40000", + "compatible_printers": [], + "internal_bridge_speed": "150", + "filename_format": "{input_filename_base}_{nozzle_diameter[0]}n_{filament_type[0]}_{layer_height}_{print_time}.gcode" +} diff --git a/resources/profiles/Wanhao.json b/resources/profiles/Wanhao.json index 81afe3eff5..96290c7712 100644 --- a/resources/profiles/Wanhao.json +++ b/resources/profiles/Wanhao.json @@ -1,12 +1,60 @@ { "name": "Wanhao", - "version": "02.04.00.02", + "version": "02.04.00.03", "force_update": "0", "description": "Wanhao configurations", "machine_model_list": [ { "name": "Wanhao D12-300", "sub_path": "machine/Wanhao D12-300.json" + }, + { + "name": "Wanhao D9-300 MK1", + "sub_path": "machine/Wanhao D9-300 MK1.json" + }, + { + "name": "Wanhao D9-300 MK1 BLTouch kit", + "sub_path": "machine/Wanhao D9-300 MK1 BLTouch kit.json" + }, + { + "name": "Wanhao D9-300 MK2", + "sub_path": "machine/Wanhao D9-300 MK2.json" + }, + { + "name": "Wanhao D9-300 MK3", + "sub_path": "machine/Wanhao D9-300 MK3.json" + }, + { + "name": "Wanhao D9-400 MK1", + "sub_path": "machine/Wanhao D9-400 MK1.json" + }, + { + "name": "Wanhao D9-400 MK1 BLTouch kit", + "sub_path": "machine/Wanhao D9-400 MK1 BLTouch kit.json" + }, + { + "name": "Wanhao D9-400 MK2", + "sub_path": "machine/Wanhao D9-400 MK2.json" + }, + { + "name": "Wanhao D9-400 MK3", + "sub_path": "machine/Wanhao D9-400 MK3.json" + }, + { + "name": "Wanhao D9-500 MK1", + "sub_path": "machine/Wanhao D9-500 MK1.json" + }, + { + "name": "Wanhao D9-500 MK1 BLTouch kit", + "sub_path": "machine/Wanhao D9-500 MK1 BLTouch kit.json" + }, + { + "name": "Wanhao D9-500 MK2", + "sub_path": "machine/Wanhao D9-500 MK2.json" + }, + { + "name": "Wanhao D9-500 MK3", + "sub_path": "machine/Wanhao D9-500 MK3.json" } ], "process_list": [ @@ -33,6 +81,38 @@ { "name": "0.24mm Draft @Wanhao D12-300", "sub_path": "process/0.24mm Draft @Wanhao D12-300.json" + }, + { + "name": "fdm_process_wanhao_d9_common", + "sub_path": "process/fdm_process_wanhao_d9_common.json" + }, + { + "name": "0.12mm Fine @Wanhao D9", + "sub_path": "process/0.12mm Fine @Wanhao D9.json" + }, + { + "name": "0.20mm Standard @Wanhao D9", + "sub_path": "process/0.20mm Standard @Wanhao D9.json" + }, + { + "name": "0.28mm Draft @Wanhao D9", + "sub_path": "process/0.28mm Draft @Wanhao D9.json" + }, + { + "name": "fdm_process_wanhao_d9_mk1_common", + "sub_path": "process/fdm_process_wanhao_d9_mk1_common.json" + }, + { + "name": "0.12mm Fine @Wanhao D9 MK1", + "sub_path": "process/0.12mm Fine @Wanhao D9 MK1.json" + }, + { + "name": "0.20mm Standard @Wanhao D9 MK1", + "sub_path": "process/0.20mm Standard @Wanhao D9 MK1.json" + }, + { + "name": "0.28mm Draft @Wanhao D9 MK1", + "sub_path": "process/0.28mm Draft @Wanhao D9 MK1.json" } ], "filament_list": [], @@ -48,6 +128,62 @@ { "name": "Wanhao D12-300 0.4 nozzle", "sub_path": "machine/Wanhao D12-300 0.4 nozzle.json" + }, + { + "name": "fdm_wanhao_d9_common", + "sub_path": "machine/fdm_wanhao_d9_common.json" + }, + { + "name": "Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle", + "sub_path": "machine/Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-300 MK2 0.4 nozzle", + "sub_path": "machine/Wanhao D9-300 MK2 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-300 MK3 0.4 nozzle", + "sub_path": "machine/Wanhao D9-300 MK3 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle", + "sub_path": "machine/Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-400 MK2 0.4 nozzle", + "sub_path": "machine/Wanhao D9-400 MK2 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-400 MK3 0.4 nozzle", + "sub_path": "machine/Wanhao D9-400 MK3 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle", + "sub_path": "machine/Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-500 MK2 0.4 nozzle", + "sub_path": "machine/Wanhao D9-500 MK2 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-500 MK3 0.4 nozzle", + "sub_path": "machine/Wanhao D9-500 MK3 0.4 nozzle.json" + }, + { + "name": "fdm_wanhao_d9_mk1_common", + "sub_path": "machine/fdm_wanhao_d9_mk1_common.json" + }, + { + "name": "Wanhao D9-300 MK1 0.4 nozzle", + "sub_path": "machine/Wanhao D9-300 MK1 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-400 MK1 0.4 nozzle", + "sub_path": "machine/Wanhao D9-400 MK1 0.4 nozzle.json" + }, + { + "name": "Wanhao D9-500 MK1 0.4 nozzle", + "sub_path": "machine/Wanhao D9-500 MK1 0.4 nozzle.json" } ] } diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 0.4 nozzle.json new file mode 100644 index 0000000000..331a26e5ed --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-300 MK1 0.4 nozzle", + "inherits": "fdm_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "MXNRpMDaVXj2xUx0", + "instantiation": "true", + "printer_model": "Wanhao D9-300 MK1", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9 MK1" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle.json new file mode 100644 index 0000000000..249c8f8d19 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "rq6cPs92KgrUyZrn", + "instantiation": "true", + "printer_model": "Wanhao D9-300 MK1 BLTouch kit", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit.json new file mode 100644 index 0000000000..57a4eddbfa --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1 BLTouch kit.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-300 MK1 BLTouch kit", + "model_id": "Wanhao-D9-300-MK1u2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1.json new file mode 100644 index 0000000000..288a444644 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK1.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-300 MK1", + "model_id": "Wanhao-D9-300-MK1", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2 0.4 nozzle.json new file mode 100644 index 0000000000..5cc17c7aa2 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-300 MK2 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "w3Cin9es1Jfx0dmR", + "instantiation": "true", + "printer_model": "Wanhao D9-300 MK2", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2.json new file mode 100644 index 0000000000..4c9bcb2412 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK2.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-300 MK2", + "model_id": "Wanhao-D9-300-MK2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3 0.4 nozzle.json new file mode 100644 index 0000000000..19534f409e --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-300 MK3 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "acFQWZqlt4QSgBPv", + "instantiation": "true", + "printer_model": "Wanhao D9-300 MK3", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "300x0", + "300x300", + "0x300" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3.json b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3.json new file mode 100644 index 0000000000..07b31cf83f --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-300 MK3.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-300 MK3", + "model_id": "Wanhao-D9-300-MK3", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 0.4 nozzle.json new file mode 100644 index 0000000000..e08ee55eba --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-400 MK1 0.4 nozzle", + "inherits": "fdm_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "DGSNE3AbmlBBG5PY", + "instantiation": "true", + "printer_model": "Wanhao D9-400 MK1", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "400x0", + "400x400", + "0x400" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9 MK1" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle.json new file mode 100644 index 0000000000..0ce2980a14 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "WcKY7v24ewtzPfnU", + "instantiation": "true", + "printer_model": "Wanhao D9-400 MK1 BLTouch kit", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "400x0", + "400x400", + "0x400" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit.json new file mode 100644 index 0000000000..bb68bd3cf9 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1 BLTouch kit.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-400 MK1 BLTouch kit", + "model_id": "Wanhao-D9-400-MK1u2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1.json new file mode 100644 index 0000000000..f730632548 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK1.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-400 MK1", + "model_id": "Wanhao-D9-400-MK1", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2 0.4 nozzle.json new file mode 100644 index 0000000000..91976f712b --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-400 MK2 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "jn1FEJFeN7S2gEbD", + "instantiation": "true", + "printer_model": "Wanhao D9-400 MK2", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "400x0", + "400x400", + "0x400" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2.json new file mode 100644 index 0000000000..2785f480e4 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK2.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-400 MK2", + "model_id": "Wanhao-D9-400-MK2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3 0.4 nozzle.json new file mode 100644 index 0000000000..c7b44c9e7c --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-400 MK3 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "2kFyH1E3tqMWtDmG", + "instantiation": "true", + "printer_model": "Wanhao D9-400 MK3", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "400x0", + "400x400", + "0x400" + ], + "printable_height": "400", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3.json b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3.json new file mode 100644 index 0000000000..2e0b5143a1 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-400 MK3.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-400 MK3", + "model_id": "Wanhao-D9-400-MK3", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 0.4 nozzle.json new file mode 100644 index 0000000000..0f238c7d5c --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-500 MK1 0.4 nozzle", + "inherits": "fdm_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "nj0TfnDKHb4wFX1R", + "instantiation": "true", + "printer_model": "Wanhao D9-500 MK1", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "500x0", + "500x500", + "0x500" + ], + "printable_height": "500", + "default_print_profile": "0.20mm Standard @Wanhao D9 MK1" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle.json new file mode 100644 index 0000000000..25ce3bc9f6 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "Hmrnlh6pJzdO2dIJ", + "instantiation": "true", + "printer_model": "Wanhao D9-500 MK1 BLTouch kit", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "500x0", + "500x500", + "0x500" + ], + "printable_height": "500", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit.json new file mode 100644 index 0000000000..4b6eea98ca --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1 BLTouch kit.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-500 MK1 BLTouch kit", + "model_id": "Wanhao-D9-500-MK1u2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1.json new file mode 100644 index 0000000000..87a99fd65f --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK1.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-500 MK1", + "model_id": "Wanhao-D9-500-MK1", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2 0.4 nozzle.json new file mode 100644 index 0000000000..cabfea9cad --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-500 MK2 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "g2GSmhWJLFDwqzFI", + "instantiation": "true", + "printer_model": "Wanhao D9-500 MK2", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "500x0", + "500x500", + "0x500" + ], + "printable_height": "500", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2.json new file mode 100644 index 0000000000..0e8f9c1603 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK2.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-500 MK2", + "model_id": "Wanhao-D9-500-MK2", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3 0.4 nozzle.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3 0.4 nozzle.json new file mode 100644 index 0000000000..434a1cb830 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3 0.4 nozzle.json @@ -0,0 +1,20 @@ +{ + "type": "machine", + "name": "Wanhao D9-500 MK3 0.4 nozzle", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "setting_id": "F7l90Rgr8Ni0sQva", + "instantiation": "true", + "printer_model": "Wanhao D9-500 MK3", + "nozzle_diameter": [ + "0.4" + ], + "printable_area": [ + "0x0", + "500x0", + "500x500", + "0x500" + ], + "printable_height": "500", + "default_print_profile": "0.20mm Standard @Wanhao D9" +} diff --git a/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3.json b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3.json new file mode 100644 index 0000000000..91dc6cb5a5 --- /dev/null +++ b/resources/profiles/Wanhao/machine/Wanhao D9-500 MK3.json @@ -0,0 +1,9 @@ +{ + "type": "machine_model", + "name": "Wanhao D9-500 MK3", + "model_id": "Wanhao-D9-500-MK3", + "nozzle_diameter": "0.4", + "machine_tech": "FFF", + "family": "Wanhao", + "default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System" +} diff --git a/resources/profiles/Wanhao/machine/fdm_wanhao_d9_common.json b/resources/profiles/Wanhao/machine/fdm_wanhao_d9_common.json new file mode 100644 index 0000000000..563e50b943 --- /dev/null +++ b/resources/profiles/Wanhao/machine/fdm_wanhao_d9_common.json @@ -0,0 +1,108 @@ +{ + "type": "machine", + "name": "fdm_wanhao_d9_common", + "inherits": "fdm_wanhao_common", + "from": "system", + "instantiation": "false", + "gcode_flavor": "marlin2", + "use_relative_e_distances": "1", + "emit_machine_limits_to_gcode": "0", + "nozzle_diameter": [ + "0.4" + ], + "printer_variant": "0.4", + "max_layer_height": [ + "0.32" + ], + "min_layer_height": [ + "0.08" + ], + "machine_max_speed_x": [ + "300", + "300" + ], + "machine_max_speed_y": [ + "300", + "300" + ], + "machine_max_speed_z": [ + "5", + "5" + ], + "machine_max_speed_e": [ + "25", + "25" + ], + "machine_max_acceleration_x": [ + "3000", + "3000" + ], + "machine_max_acceleration_y": [ + "3000", + "3000" + ], + "machine_max_acceleration_z": [ + "100", + "100" + ], + "machine_max_acceleration_e": [ + "3000", + "3000" + ], + "machine_max_acceleration_extruding": [ + "1000", + "1000" + ], + "machine_max_acceleration_travel": [ + "1000", + "1000" + ], + "machine_max_acceleration_retracting": [ + "800", + "800" + ], + "machine_max_jerk_x": [ + "10", + "10" + ], + "machine_max_jerk_y": [ + "10", + "10" + ], + "machine_max_jerk_z": [ + "0.4", + "0.4" + ], + "machine_max_jerk_e": [ + "1", + "1" + ], + "retraction_length": [ + "2.0" + ], + "retraction_speed": [ + "25" + ], + "deretraction_speed": [ + "25" + ], + "retraction_minimum_travel": [ + "1" + ], + "z_hop": [ + "0" + ], + "wipe": [ + "0" + ], + "retract_when_changing_layer": [ + "1" + ], + "machine_start_gcode": "; Wanhao Duplicator 9 start G-code, for the Marlin 2.1 firmwares at github.com/Le-Syl21/WANHAO-Duplicator-9\nG21 ; millimetres\nG90 ; absolute coordinates\nM83 ; relative extrusion\nM140 S[bed_temperature_initial_layer_single] ; heat the bed\nM104 S150 ; warm the nozzle without letting it ooze\nG91 ; a print stopped by hand can leave the nozzle down on the bed\nG1 Z10 F300 ; so raise it before homing: a BLTouch needs 10 mm to deploy\nG90\nM280 P0 S160 ; BLTouch: clear an alarm and stow the pin\nG28 ; home all axes (this turns bed levelling off)\nM420 S1 ; turn the bed mesh saved from the screen back on\nG1 Z10 F300\nM104 S[nozzle_temperature_initial_layer] ; start the nozzle now, the bed is still heating\nM190 S[bed_temperature_initial_layer_single] ; wait for the bed\nM109 S[nozzle_temperature_initial_layer] ; and confirm the nozzle\nG92 E0\nG1 X15 Y20 Z0.3 F3000 ; start of the priming line\nG1 Y140 E9.0 F1200 ; draw a line of filament\nG1 X15.5 F3000\nG1 Y20 E9.0 F1200 ; and back, next to it\nG92 E0\nG1 X22 F6000 ; wipe sideways to snap the string\nG1 Z2 F300 ; lift, so the line does not stick to the nozzle", + "machine_end_gcode": "; Wanhao Duplicator 9 end G-code\nM104 S0 ; nozzle heater off\nM140 S0 ; bed heater off\nM107 ; fan off\nG91 ; relative moves\nG1 E-2 F1500 ; release the pressure in the nozzle\nG1 Z10 F300 ; lift the nozzle\nG90 ; absolute moves\nG1 X5 Y{print_bed_max[1] - 10} F3000 ; bring the bed to the front\nM84 X Y E ; motors off, Z keeps its position", + "layer_change_gcode": "G92 E0", + "machine_pause_gcode": "M600", + "default_filament_profile": [ + "Generic PLA @System" + ] +} diff --git a/resources/profiles/Wanhao/machine/fdm_wanhao_d9_mk1_common.json b/resources/profiles/Wanhao/machine/fdm_wanhao_d9_mk1_common.json new file mode 100644 index 0000000000..f6de484370 --- /dev/null +++ b/resources/profiles/Wanhao/machine/fdm_wanhao_d9_mk1_common.json @@ -0,0 +1,28 @@ +{ + "type": "machine", + "name": "fdm_wanhao_d9_mk1_common", + "inherits": "fdm_wanhao_d9_common", + "from": "system", + "instantiation": "false", + "machine_start_gcode": "; Wanhao Duplicator 9 start G-code, for the Marlin 2.1 firmwares at github.com/Le-Syl21/WANHAO-Duplicator-9\nG21 ; millimetres\nG90 ; absolute coordinates\nM83 ; relative extrusion\nM140 S[bed_temperature_initial_layer_single] ; heat the bed\nM104 S150 ; warm the nozzle without letting it ooze\nG91 ; a print stopped by hand can leave the nozzle down on the bed\nG1 Z10 F300 ; clear the bed before homing\nG90\nG28 ; home all axes (this turns bed levelling off)\nM420 S1 ; turn the bed mesh saved from the screen back on\nG1 Z10 F300\nM104 S[nozzle_temperature_initial_layer] ; start the nozzle now, the bed is still heating\nM190 S[bed_temperature_initial_layer_single] ; wait for the bed\nM109 S[nozzle_temperature_initial_layer] ; and confirm the nozzle\nG92 E0\nG1 X15 Y20 Z0.3 F3000 ; start of the priming line\nG1 Y140 E9.0 F1200 ; draw a line of filament\nG1 X15.5 F3000\nG1 Y20 E9.0 F1200 ; and back, next to it\nG92 E0\nG1 X22 F6000 ; wipe sideways to snap the string\nG1 Z2 F300 ; lift, so the line does not stick to the nozzle", + "machine_max_acceleration_x": [ + "500", + "500" + ], + "machine_max_acceleration_y": [ + "500", + "500" + ], + "machine_max_acceleration_e": [ + "500", + "500" + ], + "machine_max_acceleration_extruding": [ + "500", + "500" + ], + "machine_max_acceleration_travel": [ + "500", + "500" + ] +} diff --git a/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9 MK1.json b/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9 MK1.json new file mode 100644 index 0000000000..b5c5083042 --- /dev/null +++ b/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9 MK1.json @@ -0,0 +1,17 @@ +{ + "type": "process", + "name": "0.12mm Fine @Wanhao D9 MK1", + "inherits": "fdm_process_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "EClNN5UDWbLACTuz", + "instantiation": "true", + "layer_height": "0.12", + "initial_layer_print_height": "0.14", + "top_shell_layers": "4", + "bottom_shell_layers": "4", + "compatible_printers": [ + "Wanhao D9-300 MK1 0.4 nozzle", + "Wanhao D9-400 MK1 0.4 nozzle", + "Wanhao D9-500 MK1 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9.json b/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9.json new file mode 100644 index 0000000000..5ea13a0f1e --- /dev/null +++ b/resources/profiles/Wanhao/process/0.12mm Fine @Wanhao D9.json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.12mm Fine @Wanhao D9", + "inherits": "fdm_process_wanhao_d9_common", + "from": "system", + "setting_id": "0hvsKsPO55TlduZR", + "instantiation": "true", + "layer_height": "0.12", + "initial_layer_print_height": "0.14", + "top_shell_layers": "4", + "bottom_shell_layers": "4", + "compatible_printers": [ + "Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-300 MK2 0.4 nozzle", + "Wanhao D9-400 MK2 0.4 nozzle", + "Wanhao D9-500 MK2 0.4 nozzle", + "Wanhao D9-300 MK3 0.4 nozzle", + "Wanhao D9-400 MK3 0.4 nozzle", + "Wanhao D9-500 MK3 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9 MK1.json b/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9 MK1.json new file mode 100644 index 0000000000..834dd8b158 --- /dev/null +++ b/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9 MK1.json @@ -0,0 +1,17 @@ +{ + "type": "process", + "name": "0.20mm Standard @Wanhao D9 MK1", + "inherits": "fdm_process_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "CXNOj3tPeTUXx81B", + "instantiation": "true", + "layer_height": "0.20", + "initial_layer_print_height": "0.24", + "top_shell_layers": "3", + "bottom_shell_layers": "3", + "compatible_printers": [ + "Wanhao D9-300 MK1 0.4 nozzle", + "Wanhao D9-400 MK1 0.4 nozzle", + "Wanhao D9-500 MK1 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9.json b/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9.json new file mode 100644 index 0000000000..ee3fe49c29 --- /dev/null +++ b/resources/profiles/Wanhao/process/0.20mm Standard @Wanhao D9.json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.20mm Standard @Wanhao D9", + "inherits": "fdm_process_wanhao_d9_common", + "from": "system", + "setting_id": "UVXxknNxJswqaURQ", + "instantiation": "true", + "layer_height": "0.20", + "initial_layer_print_height": "0.24", + "top_shell_layers": "3", + "bottom_shell_layers": "3", + "compatible_printers": [ + "Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-300 MK2 0.4 nozzle", + "Wanhao D9-400 MK2 0.4 nozzle", + "Wanhao D9-500 MK2 0.4 nozzle", + "Wanhao D9-300 MK3 0.4 nozzle", + "Wanhao D9-400 MK3 0.4 nozzle", + "Wanhao D9-500 MK3 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9 MK1.json b/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9 MK1.json new file mode 100644 index 0000000000..e3ffe5895f --- /dev/null +++ b/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9 MK1.json @@ -0,0 +1,17 @@ +{ + "type": "process", + "name": "0.28mm Draft @Wanhao D9 MK1", + "inherits": "fdm_process_wanhao_d9_mk1_common", + "from": "system", + "setting_id": "omfIWwVnQVVxPrtb", + "instantiation": "true", + "layer_height": "0.28", + "initial_layer_print_height": "0.34", + "top_shell_layers": "3", + "bottom_shell_layers": "3", + "compatible_printers": [ + "Wanhao D9-300 MK1 0.4 nozzle", + "Wanhao D9-400 MK1 0.4 nozzle", + "Wanhao D9-500 MK1 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9.json b/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9.json new file mode 100644 index 0000000000..d9fd9e21e9 --- /dev/null +++ b/resources/profiles/Wanhao/process/0.28mm Draft @Wanhao D9.json @@ -0,0 +1,23 @@ +{ + "type": "process", + "name": "0.28mm Draft @Wanhao D9", + "inherits": "fdm_process_wanhao_d9_common", + "from": "system", + "setting_id": "FEJqq1ylXWqaBPR6", + "instantiation": "true", + "layer_height": "0.28", + "initial_layer_print_height": "0.34", + "top_shell_layers": "3", + "bottom_shell_layers": "3", + "compatible_printers": [ + "Wanhao D9-300 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-400 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-500 MK1 BLTouch kit 0.4 nozzle", + "Wanhao D9-300 MK2 0.4 nozzle", + "Wanhao D9-400 MK2 0.4 nozzle", + "Wanhao D9-500 MK2 0.4 nozzle", + "Wanhao D9-300 MK3 0.4 nozzle", + "Wanhao D9-400 MK3 0.4 nozzle", + "Wanhao D9-500 MK3 0.4 nozzle" + ] +} diff --git a/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_common.json b/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_common.json new file mode 100644 index 0000000000..457ba4ae2d --- /dev/null +++ b/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_common.json @@ -0,0 +1,38 @@ +{ + "type": "process", + "name": "fdm_process_wanhao_d9_common", + "inherits": "fdm_process_wanhao_common", + "from": "system", + "instantiation": "false", + "line_width": "0.4", + "initial_layer_line_width": "0.6", + "wall_loops": "2", + "infill_wall_overlap": "15%", + "sparse_infill_density": "15%", + "sparse_infill_pattern": "gyroid", + "outer_wall_speed": "25", + "inner_wall_speed": "40", + "sparse_infill_speed": "50", + "internal_solid_infill_speed": "50", + "top_surface_speed": "40", + "gap_infill_speed": "40", + "bridge_speed": "50", + "support_speed": "35", + "initial_layer_speed": "25", + "initial_layer_infill_speed": "25", + "travel_speed": "100", + "default_acceleration": "1000", + "outer_wall_acceleration": "1000", + "inner_wall_acceleration": "1000", + "top_surface_acceleration": "1000", + "sparse_infill_acceleration": "1000", + "initial_layer_acceleration": "1000", + "bridge_acceleration": "1000", + "travel_acceleration": "1000", + "default_jerk": "0", + "skirt_loops": "2", + "skirt_distance": "4", + "brim_type": "no_brim", + "enable_support": "0", + "enable_arc_fitting": "0" +} diff --git a/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_mk1_common.json b/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_mk1_common.json new file mode 100644 index 0000000000..3fb1c650db --- /dev/null +++ b/resources/profiles/Wanhao/process/fdm_process_wanhao_d9_mk1_common.json @@ -0,0 +1,15 @@ +{ + "type": "process", + "name": "fdm_process_wanhao_d9_mk1_common", + "inherits": "fdm_process_wanhao_d9_common", + "from": "system", + "instantiation": "false", + "default_acceleration": "500", + "outer_wall_acceleration": "500", + "inner_wall_acceleration": "500", + "top_surface_acceleration": "500", + "sparse_infill_acceleration": "500", + "initial_layer_acceleration": "500", + "bridge_acceleration": "500", + "travel_acceleration": "500" +} diff --git a/resources/web/dialog/SpeedDial/speeddial.js b/resources/web/dialog/SpeedDial/speeddial.js index c5caf0d423..03f315e39c 100644 --- a/resources/web/dialog/SpeedDial/speeddial.js +++ b/resources/web/dialog/SpeedDial/speeddial.js @@ -30,7 +30,7 @@ var USER_MODE = "simple"; var MODE_RANK = { simple: 0, advanced: 1, expert: 2, develop: 3 }; // Search ranking weights: every contiguous match must outrank every fuzzy one regardless of field, -// and title must outrank group, which outranks source. +// and title must outrank group/category, which outranks source. var SCORE_CONTIGUOUS = 100000; var SCORE_TITLE = 2000; var SCORE_GROUP = 1000; @@ -118,6 +118,18 @@ function sourceNorm(a) { a._sn = NormText(a.source || "", false); return a._sn; } +function pluginCategoryNorm(a) { + if (a._pcn === undefined) + a._pcn = a.kind === "plugin" ? NormText(T("sd_plugins", "Plugins"), false) : ""; + return a._pcn; +} +// Plugin type is searchable metadata, so typing "plugin" can find runnable plugin actions even +// when neither their capability nor plugin name contains that word. Keep both category forms. +function pluginTypeNorm(a) { + if (a._pn === undefined) + a._pn = a.kind === "plugin" ? NormText("plugin plugins", false) : ""; + return a._pn; +} // Search-only alias for the descriptive name when the title differs (e.g. "Reverse on even" vs // "Overhang reversal"). Never rendered, so no highlight ranges. function fullNorm(a) { @@ -152,7 +164,7 @@ function fieldMatchScore(norm, needle, wwRe) { // Split a query into normalized (folded+lowercased) whitespace-separated tokens. Empty for a blank // query. These drive the multi-token path: every token must match some field, but different tokens -// may match different fields (the title, the group, or the source breadcrumb). +// may match different fields (the title, group, source breadcrumb, or plugin kind). function queryTokens(query) { var norm = NormText(String(query || "").trim(), false); return norm ? norm.split(/\s+/).filter(Boolean) : []; @@ -170,14 +182,19 @@ function tokenMatch(a, token, wwRe) { var g = fieldMatchScore(groupNorm(a), token, wwRe); var s = fieldMatchScore(sourceNorm(a), token, wwRe); var f = fieldMatchScore(fullNorm(a), token, wwRe); - if (!t && !g && !s && !f) return null; + var p = fieldMatchScore(pluginCategoryNorm(a), token, wwRe); + var typeMatch = fieldMatchScore(pluginTypeNorm(a), token, wwRe); + if (!t && !g && !s && !f && !p && !typeMatch) return null; var score = Math.max( t ? (t.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_TITLE + t.score : -Infinity, g ? (g.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + g.score : -Infinity, s ? (s.contiguous ? SCORE_CONTIGUOUS : 0) + s.score : -Infinity, - f ? (f.contiguous ? SCORE_CONTIGUOUS : 0) + f.score : -Infinity + f ? (f.contiguous ? SCORE_CONTIGUOUS : 0) + f.score : -Infinity, + p ? (p.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + p.score : -Infinity, + typeMatch ? (typeMatch.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + typeMatch.score : -Infinity ); - return { score: score, title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null }; + return { score: score, title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null, + plugin: p ? p.ranges : null }; } // Merge per-field token ranges into sorted, coalesced ranges for highlighting. Overlapping or adjacent @@ -201,8 +218,8 @@ function mergeRanges(ranges) { } // Combine per-field scores into one value, or null when nothing matched. -// Ranking: contiguous > fuzzy, then title > group > source/full alias, then start/gaps. -function scoreFields(t, g, s, f) { +// Ranking: contiguous > fuzzy, then title > group/category > source/full alias, then start/gaps. +function scoreFields(t, g, s, f, p, typeMatch) { var best = null; function consider(m, weight) { if (!m) return; @@ -213,6 +230,8 @@ function scoreFields(t, g, s, f) { consider(g, SCORE_GROUP); consider(s, 0); consider(f, 0); + consider(p, SCORE_GROUP); + consider(typeMatch, SCORE_GROUP); return best; } @@ -226,7 +245,8 @@ function scoreFields(t, g, s, f) { // - tokens: every whitespace-separated word must match SOME field, but different words may match // different fields. This is what lets "speed acceleration inner" find "Inner wall" whose path is // "Process : Speed : Acceleration" (title + source breadcrumb together). -// full_label is searchable too but never highlighted, since it is not rendered. +// full_label and canonical plugin kind are searchable aliases. The visible category is also searchable +// so its own match can be highlighted in plugin rows. // A phrase match always outranks a distributed token match. function searchActions(actions, query) { var q = (query || "").trim(); @@ -251,26 +271,31 @@ function searchActions(actions, query) { var g = fieldMatchScore(groupNorm(a), searchNeedle, wwRe); var s = fieldMatchScore(sourceNorm(a), searchNeedle, wwRe); var f = fieldMatchScore(fullNorm(a), searchNeedle, wwRe); - var phrase = scoreFields(t, g, s, f); + var p = fieldMatchScore(pluginCategoryNorm(a), searchNeedle, wwRe); + var typeMatch = fieldMatchScore(pluginTypeNorm(a), searchNeedle, wwRe); + var phrase = scoreFields(t, g, s, f, p, typeMatch); var score, ranges; if (phrase !== null) { score = phrase + SCORE_PHRASE; - ranges = { title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null }; + ranges = { title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null, + plugin: p ? p.ranges : null }; } else { // Require every token; a token that matches nothing drops the action immediately. Ranges // from all matching tokens are merged per field so each matched word highlights. - var sum = 0, titleR = null, groupR = null, sourceR = null, all = true; - for (var k = 0; k < searchTokens.length; k++) { - var m = tokenMatch(a, searchTokens[k], searchTokenRes[k]); + var sum = 0, titleR = null, groupR = null, sourceR = null, pluginR = null, all = true; + for (var tokenIndex = 0; tokenIndex < searchTokens.length; tokenIndex++) { + var m = tokenMatch(a, searchTokens[tokenIndex], searchTokenRes[tokenIndex]); if (!m) { all = false; break; } sum += m.score; if (m.title) (titleR || (titleR = [])).push(m.title); if (m.group) (groupR || (groupR = [])).push(m.group); if (m.source) (sourceR || (sourceR = [])).push(m.source); + if (m.plugin) (pluginR || (pluginR = [])).push(m.plugin); } if (!all) continue; score = sum; - ranges = { title: mergeRanges(titleR), group: mergeRanges(groupR), source: mergeRanges(sourceR) }; + ranges = { title: mergeRanges(titleR), group: mergeRanges(groupR), source: mergeRanges(sourceR), + plugin: mergeRanges(pluginR) }; } // Ranges are per-field against the ACTUAL text drawn: title for the row-name, and group (or // source when group is empty) for the eyebrow - so highlight offsets stay aligned to the label. @@ -278,6 +303,7 @@ function searchActions(actions, query) { title: ranges.title, group: ranges.group, source: ranges.source, + plugin: ranges.plugin, useEyebrowGroup: !!(a.group) }; scored.push({ a: a, s: score }); @@ -390,7 +416,7 @@ function favDigitFromEvent(e) { function resultCountText(total, shown, query) { return (query || "").trim() ? - T("sd_result_count", "Showing %s of %s actions", shown, total) : + T("sd_result_count", "Showing %s actions", shown) : T("sd_result_count_all", "%s actions", total); } @@ -461,6 +487,12 @@ function actionCategory(a) { return cat || T("sd_other", "Other"); } +function actionEyebrow(a, typedQuery, isRecent) { + if (a && a.kind === "plugin" && (String(typedQuery || "").trim() || isRecent)) + return T("sd_plugins", "Plugins"); + return (a && (a.group || a.source)) || ""; +} + // Stable-bucket actions by category, then order the groups alphabetically. Within a group the incoming // (frecency) order is kept. Pure so the node-vm test can exercise grouping. function groupActions(list) { @@ -711,7 +743,7 @@ window.HandleStudio = function (payload) { builtKey = ""; if (qEl) { qEl.value = ""; - qEl.placeholder = T("sd_search_n", "Search %s actions", ACTIONS.length); + qEl.placeholder = T("sd_search", "Search actions"); syncClearButton(); } render({ resize: true, resetScroll: true }); @@ -931,12 +963,14 @@ function beginRow(item, i, mono, ariaLabel) { function renderActionRow(a, i) { var on = FAVS.indexOf(a.id) !== -1; var shell = beginRow(a, i, false, actionLabel(a, ACTIONS)); - var mi = matchIndex[a.id]; - // The eyebrow shows group when present, else source. Highlight with the ranges of whichever of the - // two the eyebrow actually renders (so a "Recent Projects"/"Object" header match lights up like a - // setting path does - the offsets are computed against the same string we are marking). - var eyebrow = a.group || a.source; - var eyebrowMatch = mi ? (mi.useEyebrowGroup ? mi.group : mi.source) : null; + var typedQuery = String(query || "").trim(); + var isRecent = !typedQuery && i < RECENTS.length && RECENTS[i].id === a.id; + var mi = typedQuery ? matchIndex[a.id] : null; + // Plugin search/recent rows show their category; other rows show their group or source breadcrumb. + // Use match ranges from the visible field so category and breadcrumb highlights stay aligned. + var showPluginCategory = a.kind === "plugin" && (typedQuery || isRecent); + var eyebrow = actionEyebrow(a, query, isRecent); + var eyebrowMatch = mi ? (showPluginCategory ? mi.plugin : (mi.useEyebrowGroup ? mi.group : mi.source)) : null; shell.left.insertBefore(markedText("row-eyebrow", eyebrow, eyebrowMatch), shell.line); shell.line.appendChild(markedText("row-name", a.title, mi ? mi.title : null)); var badge = modeBadge(a, USER_MODE); @@ -1376,7 +1410,7 @@ function exitPhase() { // It survives a second-phase exit (which never goes through exitPhase from the commands view), // so without a reset the cached empty-query key would skip the rebuild and leave stale content. builtKey = ""; - qEl.placeholder = T("sd_search_n", "Search %s actions", ACTIONS.length); + qEl.placeholder = T("sd_search", "Search actions"); render({ resize: true, resetScroll: true }); qEl.focus(); } diff --git a/resources/web/dialog/SpeedDial/speeddial.test.js b/resources/web/dialog/SpeedDial/speeddial.test.js index 03099df91b..1fda833801 100644 --- a/resources/web/dialog/SpeedDial/speeddial.test.js +++ b/resources/web/dialog/SpeedDial/speeddial.test.js @@ -82,6 +82,20 @@ assert.equal(ctx.searchActions(pool, "layer").length >= 2, true, assert.equal(ctx.searchActions(pool, "surface")[0].id, "c2", "a later-but-precise match still ranks by relevance, not by pool type"); +const pluginPool = [ + { id: "plugin-action", title: "Optimize G-code", source: "Gcode Optimizer", group: "", kind: "plugin" }, + { id: "command-action", title: "Open Preferences", source: "OrcaSlicer", group: "Commands", kind: "command" } +]; +assert.deepEqual(ctx.searchActions(pluginPool, "plugin").map(function (a) { return a.id; }), ["plugin-action"], + "the plugin kind makes runnable plugin actions searchable by plugin"); +assert.deepEqual(ctx.searchActions(pluginPool, "plugins").map(function (a) { return a.id; }), ["plugin-action"], + "the plural Plugins category also finds plugin actions"); +ctx.searchActions(pluginPool, "plugins"); +assert.deepEqual(ctx.matchIndex["plugin-action"].plugin, [[0, 7]], + "a category match highlights the visible Plugins label"); +assert.deepEqual(ctx.searchActions(pluginPool, "plugin optimize").map(function (a) { return a.id; }), ["plugin-action"], + "plugin kind can match one token while the action title matches another"); + // A perfect match (the needle as one contiguous run) outranks a fuzzy match of the same field - and a // contiguous GROUP/header hit ("Recent Projects") beats a scattered fuzzy TITLE hit ("Retraction Length"), // which is what the old flat title-bonus ranking got backwards. @@ -188,6 +202,12 @@ assert.equal(ctx.actionCategory({ id: "s", group: "", source: "Filament : Coolin "a Filament setting groups under Filament"); assert.equal(ctx.actionCategory({ id: "plugin_script_action:Foo:bar.py", group: "", source: "Gcode Optimizer", kind: "plugin" }), "Plugins", "every plugin shares one Plugins header"); +assert.equal(ctx.actionEyebrow({ group: "", source: "Gcode Optimizer", kind: "plugin" }, "plugin"), "Plugins", + "typed results show only the Plugins category"); +assert.equal(ctx.actionEyebrow({ group: "", source: "Gcode Optimizer", kind: "plugin" }, "", true), "Plugins", + "recent plugin actions show only the Plugins category"); +assert.equal(ctx.actionEyebrow({ group: "", source: "Gcode Optimizer", kind: "plugin" }, ""), "Gcode Optimizer", + "the unfiltered plugin section keeps the source name on non-recent rows"); assert.equal(ctx.actionCategory({ id: "x", group: "", source: "", kind: "command" }), "Other", "a category-less action falls back to Other"); @@ -436,4 +456,9 @@ assert.equal(ctx.stateFromPayload({}).tooltipExpanded, true, "expansion defaults assert.equal(ctx.stateFromPayload({ tooltip_expanded: false }).tooltipExpanded, false, "a collapsed payload is honored"); assert.equal(ctx.stateFromPayload({ tooltip_expanded: true }).tooltipExpanded, true, "an expanded payload is honored"); +// resultCountText: a search counts the shown matches only ("Showing N actions"); the total is used +// solely for the empty-query count. +assert.equal(ctx.resultCountText(100, 3, "lay"), "Showing 3 actions", "a search reports the shown match count only"); +assert.equal(ctx.resultCountText(100, 100, ""), "100 actions", "an empty query reports the total"); + console.log("ok"); diff --git a/scripts/orca_profile_tool.py b/scripts/orca_profile_tool.py index c41c2ba7fc..29863f8d1a 100755 --- a/scripts/orca_profile_tool.py +++ b/scripts/orca_profile_tool.py @@ -437,6 +437,7 @@ def resolve_filament_field(name, field, filaments, ofl_filaments, seen=None, in_ the same hop semantics as resolve_filament_id: own value, else walk `inherits` in the vendor map with OFL base-bundle fallback. Values are list options — the first element counts; "" when the chain never defines one. + Templates pulled in by `include` are not consulted: none states either field. """ if seen is None: seen = set() @@ -887,8 +888,8 @@ def _vendor_json_files(vendor_path): def check_preset_name_uniqueness(profiles_dir, vendor): """No two profiles in a bundle may share a type and a name, indexed or not. - The loader resolves "inherits" through a per-type map of the bundle's profiles - (PresetBundle.cpp load_subfiles), and std::map::emplace keeps the first + The loader resolves "inherits" and "include" through per-type maps of the bundle's + profiles (PresetBundle.cpp load_subfiles), and std::map::emplace keeps the first insertion: a second file claiming the name is silently dropped, and which one wins is nothing but index order. An unindexed twin counts too - it is one sub_path edit away from deciding that silently. @@ -1287,11 +1288,11 @@ def check_normalized(profiles_dir, vendor): Those two commands define a profile file's canonical shape - identifying keys first, keys the slicer no longer reads gone, filament options that are vectors - written as vectors - and a .json's canonical lists, ordered parents-first - so the loader resolves every "inherits" in one pass. Running them over a - contributed bundle has to be a no-op; where it would not be, the file that was - reviewed is not the file that ships, and the next maintainer to run normalize - carries an unrelated diff into their own change. + written as vectors - and a .json's canonical lists, ordered + dependencies-first so the loader resolves every "inherits" and "include" in one + pass. Running them over a contributed bundle has to be a no-op; where it would + not be, the file that was reviewed is not the file that ships, and the next + maintainer to run normalize carries an unrelated diff into their own change. It asks the normalize and update-index sections below rather than restating what they do, because a second definition of normal is free to drift from the one that @@ -2034,9 +2035,9 @@ def trim_profiles(profiles_dir=PROFILES_DIR, vendors=None, profile_types=None, cli_config.json carry no "type" - all of them stay. A file that cannot be parsed is reported and kept: never delete what could not be read. - An unindexed file that some surviving profile names in "inherits" is kept too, - and reported, UNLESS an indexed profile already carries that name: "inherits" is - resolved by preset name, so the indexed one is the parent every child actually + An unindexed file that some surviving profile names in "inherits" or "include" is + kept too, and reported, UNLESS an indexed profile already carries that name: both + are resolved by preset name, so the indexed one is the parent every child actually gets, and the unindexed file is a stale copy the loader never reaches. Where no indexed profile provides the name the inheriting preset really is broken, and deleting the file would destroy the only record of the settings it was written @@ -2069,7 +2070,7 @@ def trim_profiles(profiles_dir=PROFILES_DIR, vendors=None, profile_types=None, listed.add(posixpath.normpath(sub_path.replace("\\", "/"))) candidates = {} # path -> profile, for every unindexed preset - inherited = set() # every name the files that stay claim as a parent + inherited = set() # every name the files that stay claim as a parent or include provided = {} # name -> sub_path, for the profiles the loader can see for sub in subs: for path in _walk_json(os.path.join(vendor_dir, sub)): @@ -2084,8 +2085,7 @@ def trim_profiles(profiles_dir=PROFILES_DIR, vendors=None, profile_types=None, if not isinstance(profile, dict): continue if sub_path in listed or profile.get("type") not in PROFILE_TYPES: - if profile.get("inherits"): - inherited.add(profile["inherits"]) + inherited.update(profile_dependencies(profile)) if sub_path in listed and profile.get("name"): provided[profile["name"]] = sub_path continue @@ -2104,8 +2104,7 @@ def trim_profiles(profiles_dir=PROFILES_DIR, vendors=None, profile_types=None, for path, profile in rescued.items(): del candidates[path] kept[path] = profile - if profile.get("inherits"): - inherited.add(profile["inherits"]) + inherited.update(profile_dependencies(profile)) for path in sorted(kept): print_warning(f"{_rel(path, profiles_dir)}: not indexed by {vendor}.json but " @@ -2142,40 +2141,48 @@ def trim_profiles(profiles_dir=PROFILES_DIR, vendors=None, profile_types=None, # update-index # --------------------------------------------------------------------------- -def topological_sort(profiles): - """Order index entries parents-first, so the loader resolves inherits in one pass. +def profile_dependencies(profile): + """The names a profile needs loaded before it: its parent and every include.""" + include = profile.get("include") or [] + if isinstance(include, str): + include = [include] + return [name for name in [profile.get("inherits"), *include] if name] - Entries whose parent is not in the same section keep their own (sorted) order at - the end; the loader finds those parents through the base bundle instead. + +def topological_sort(profiles): + """Order index entries dependencies-first, so the loader resolves every + "inherits" and "include" in one pass. + + Entries that neither depend on nor are depended on by another in the same section + go at the end in name order; the loader finds their parents, if any, through the + base bundle instead. Every entry on a dependency cycle, which no order can satisfy, + goes there too. """ graph = defaultdict(list) in_degree = defaultdict(int) by_name = {p["name"]: p for p in profiles} all_names = set(by_name) - placed = set() for profile in profiles: - parent = profile.get("inherits") child = profile["name"] - if parent in all_names: - graph[parent].append(child) - in_degree[child] += 1 - in_degree.setdefault(parent, 0) - placed.add(child) - placed.add(parent) + for parent in profile_dependencies(profile): + if parent in all_names: + graph[parent].append(child) + in_degree[child] += 1 + in_degree.setdefault(parent, 0) queue = sorted(name for name, degree in in_degree.items() if degree == 0) result = [] while queue: current = queue.pop(0) result.append(by_name[current]) - placed.add(current) for child in sorted(graph[current]): in_degree[child] -= 1 if in_degree[child] == 0: queue.append(child) - result.extend(by_name[name] for name in sorted(all_names - placed)) + ordered = {p["name"] for p in result} + result.extend(by_name[name] for name in sorted(all_names - ordered)) return result @@ -2225,15 +2232,15 @@ def build_index_sections(profiles_dir, vendor, profile_types=None): "name": name, "sub_path": os.path.relpath(path, vendor_dir).replace(os.sep, "/"), } - if profile.get("inherits"): - entry["inherits"] = profile["inherits"] + for key in ("inherits", "include"): + if profile.get(key): + entry[key] = profile[key] by_name[name].append(entry["sub_path"]) entries.append(entry) - sorted_entries = topological_sort(entries) - for entry in sorted_entries: - entry.pop("inherits", None) # ordering input only, not part of the index - sections[profile_type + "_list"] = sorted_entries + # inherits/include were ordering input only; the index holds name and sub_path + sections[profile_type + "_list"] = [{"name": e["name"], "sub_path": e["sub_path"]} + for e in topological_sort(entries)] for rel, found in sorted(unplaceable.items()): problems.append(f'{rel}: type {found!r} is not one of {list(PROFILE_TYPES)}, so it ' @@ -2418,7 +2425,8 @@ def build_parser(): add("update-index", [vendor_opt, type_opt, dry_run_opt, profiles_opt], "regenerate the *_list sections of .json", "Rebuild the *_list sections of each .json from the files on disk,\n" - "ordered parents-first so the loader resolves inherits in one pass.\n" + "ordered dependencies-first so the loader resolves inherits and include\n" + "in one pass.\n" "\n" "A profile is indexed under the section its own \"type\" names, so run\n" "normalize first: it is what writes a missing type. Two files claiming one\n" diff --git a/scripts/tests/test_filament_id.py b/scripts/tests/test_filament_id.py index 001763fca9..fc62f5abc3 100644 --- a/scripts/tests/test_filament_id.py +++ b/scripts/tests/test_filament_id.py @@ -405,6 +405,20 @@ class TestTripleResolution(unittest.TestCase): self.assertEqual(afi.resolve_triple("MyPLA @P1", fmap, {}), ("MyVendor", "PLA", "MyPLA")) + def test_split_vendor_and_type_bases_resolve(self): + # A partial base is normal, not an error: vendor and type may live on + # different ancestors, with an intermediate supplying neither (the + # Snapmaker shape). The pair is complete at the instantiated preset. + recs = [ + self.rec("APLA @P1", inherits="mid"), + self.rec("mid", inherits="typebase"), + self.rec("typebase", filament_type=["PLA"], inherits="vendorbase"), + self.rec("vendorbase", filament_vendor=["AV"]), + ] + fmap = {r["name"]: r for r in recs} + self.assertEqual(afi.resolve_triple("APLA @P1", fmap, {}), + ("AV", "PLA", "APLA")) + # --------------------------------------------------------------------------- # checks on synthetic trees @@ -417,6 +431,24 @@ class TestChecks(OfCleanTreeCase): self.assertNotIn("[ERROR]", out) self.assertNotIn("[WARNING]", out) + def test_instantiated_preset_over_partial_bases_is_silent(self): + # Vendor and type split across two non-instantiated bases, an + # intermediate base with neither: base profiles are allowed to be + # partial. Only the instantiated preset must resolve both. + self.t.write_preset("VendorA", preset("XPLA vendorbase", instantiation=False, + filament_vendor="XV")) + self.t.write_preset("VendorA", preset("XPLA typebase", instantiation=False, + filament_type="PLA", + inherits="XPLA vendorbase")) + self.t.write_preset("VendorA", preset("XPLA mid", instantiation=False, + inherits="XPLA typebase")) + self.t.write_preset("VendorA", preset( + "XPLA @P1", inherits="XPLA mid", + filament_id=afi.generate_filament_id("XV", "PLA", "XPLA"), + compatible_printers=["P1"])) + errors, out = self.t.check() + self.assertEqual(errors, 0, out) + def test_check1_unknown_non_of_id(self): self.t.write_preset("VendorA", preset("BPLA @base", filament_id="BOGUS_9", instantiation=False, @@ -1523,6 +1555,19 @@ class TestRealTree(unittest.TestCase): self.assertEqual(analysis["missing_effective"], []) self.assertEqual(analysis["read_errors"], []) + def test_every_instantiated_filament_resolves_vendor_and_type(self): + # The property the web guide resolves at load: a partial base is fine as + # long as the instantiated preset ends up with both fields. Guards the + # split-base bundles (Snapmaker, Anker, SeeMeCNC). + analysis = afi.analyze_tree(REAL_PROFILES) + unresolved = [ + (vendor, rec["name"], rec["triple"][0], rec["triple"][1]) + for vendor, filaments in analysis["vendors"].items() + for rec in filaments.values() + if rec["instantiation"] and not (rec["triple"][0] and rec["triple"][1]) + ] + self.assertEqual(unresolved, []) + # --------------------------------------------------------------------------- # review-fix regressions # --------------------------------------------------------------------------- diff --git a/scripts/tests/test_profile_tool.py b/scripts/tests/test_profile_tool.py index f9a7634471..5359b1158b 100644 --- a/scripts/tests/test_profile_tool.py +++ b/scripts/tests/test_profile_tool.py @@ -411,6 +411,26 @@ class TestUpdateIndex(TreeCase): for entry in self.t.read_index("V")["filament_list"]: self.assertEqual(sorted(entry), ["name", "sub_path"]) + def test_include_targets_are_listed_before_their_users(self): + # The loader resolves include like inherits: in one pass over the list, so + # a template must be listed before every preset that includes it - even + # though a template has no parent of its own to order it by. + self.t.write("V", "machine/P.json", {"type": "machine", "name": "P", + "include": ["T start", "T end"]}) + self.t.write("V", "machine/T start.json", {"type": "machine", "name": "T start"}) + self.t.write("V", "machine/T end.json", {"type": "machine", "name": "T end"}) + self.t.write("V", "filament/F.json", {"type": "filament", "name": "F", + "inherits": "B", "include": "S"}) + self.t.write("V", "filament/B.json", {"type": "filament", "name": "B"}) + self.t.write("V", "filament/S.json", {"type": "filament", "name": "S"}) + rc, out = self.run_command("update-index") + self.assertEqual(rc, 0, out) + machines = [e["name"] for e in self.t.read_index("V")["machine_list"]] + self.assertEqual(machines, ["T end", "T start", "P"]) + filaments = [e["name"] for e in self.t.read_index("V")["filament_list"]] + self.assertLess(filaments.index("B"), filaments.index("F")) + self.assertLess(filaments.index("S"), filaments.index("F")) + def test_a_profile_with_no_usable_type_is_reported_not_dropped(self): self.t.write("V", "filament/A.json", {"type": "filament", "name": "A"}) self.t.write("V", "filament/B.json", {"name": "B"}) diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 57be0595cb..e7facbdbd8 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -5527,12 +5527,28 @@ int CLI::run(int argc, char **argv) //add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected, enable_wrapping_detect); - if (used_filament_set.size() > 0) + // Filament ids given on the command line size the tower for STL input. A project + // records its filament use per plate, so count there and keep its tower positions. + const int plate_count = partplate_list.get_plate_count(); + const bool from_project = used_filament_set.empty(); + std::vector plate_filament_counts(plate_count, static_cast(used_filament_set.size())); + if (from_project) + for (int plate_index = 0; plate_index < plate_count; ++plate_index) + plate_filament_counts[plate_index] = static_cast(partplate_list.get_plate(plate_index)->get_extruders_under_cli(true, m_print_config).size()); + // A project only gets a tower the slicer will print: the prime tower enabled, and not + // a by-object print unless a smooth timelapse needs it, as the per-plate arrange decides. + const bool project_tower_allowed = m_print_config.option("enable_prime_tower", true)->value && + (is_smooth_timelapse || !arrange_cfg.is_seq_print); + const auto plate_needs_wipe_tower = [from_project, project_tower_allowed, is_smooth_timelapse](int filament_count) { + if (!from_project) + return filament_count > 0; + return project_tower_allowed && (filament_count > 1 || (filament_count > 0 && is_smooth_timelapse)); + }; + const int max_filament_count = plate_count > 0 ? *std::max_element(plate_filament_counts.begin(), plate_filament_counts.end()) : 0; + + if (plate_needs_wipe_tower(max_filament_count)) { //prepare the wipe tower - int plate_count = partplate_list.get_plate_count(); - int extruder_size = used_filament_set.size(); - auto printer_structure_opt = m_print_config.option>("printer_structure"); // This margin only pre-adjusts the default away from the near edges; // estimate_wipe_tower_polygon below computes the real clamped position. @@ -5568,7 +5584,11 @@ int CLI::run(int argc, char **argv) for (int bedid = 0; bedid < MAX_PLATE_COUNT; bedid++) { int plate_index_valid = std::min(bedid, plate_count - 1); - if (bedid < plate_count) { + // Overflow beds may receive objects from any plate, so size them for the busiest one. + const int extruder_size = bedid < plate_count ? plate_filament_counts[bedid] : max_filament_count; + if (!plate_needs_wipe_tower(extruder_size)) + continue; + if (bedid < plate_count && !from_project) { wipe_x_option->set_at(&wt_x_opt, plate_index_valid, 0); wipe_y_option->set_at(&wt_y_opt, plate_index_valid, 0); } @@ -7024,6 +7044,12 @@ int CLI::run(int argc, char **argv) } } sliced_info.sliced_plates.push_back(sliced_plate_info); + } catch (const Slic3r::SlicingErrors &exs) { + const std::string message = print_fff ? print_fff->slicing_errors_message(exs) : std::string(exs.what()); + BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate " << index+1 << ": " << message; + boost::nowide::cerr << message << std::endl; + record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, message, sliced_info); + flush_and_exit(CLI_SLICING_ERROR); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "< +#include +#include "nlohmann/json.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace Slic3r; +namespace fs = boost::filesystem; +using nlohmann::json; + +namespace { + +// What a sub-file states about where its values come from, and what it sets. +struct Entry +{ + std::string inherits; + std::vector includes; + std::vector keys; +}; + +// Sub-files by name, per section of the vendor index: `inherits` and `include` +// both resolve within a section. +using Sections = std::map>; + +json read_json(const fs::path &path) +{ + boost::nowide::ifstream in(path.string()); + if (!in) + throw std::runtime_error("Cannot read " + path.string()); + return json::parse(in); +} + +Sections read_vendor(const fs::path &dir, const std::string &vendor) +{ + // Keys that name and place a sub-file rather than set a value. + static const std::set metadata = {"name", "type", "from", "instantiation", "inherits", "include"}; + Sections sections; + const json index = read_json(dir / (vendor + ".json")); + for (const char *section : {"process_list", "filament_list", "machine_list"}) { + const auto list = index.find(section); + if (list == index.end()) + continue; + for (const json &item : *list) { + const json file = read_json(dir / vendor / item.at("sub_path").get()); + // A file without a name goes by its name in the index. + Entry &entry = sections[section][file.value("name", item.at("name").get())]; + entry.inherits = file.value("inherits", ""); + if (const auto include = file.find("include"); include != file.end()) { + if (include->is_string()) + entry.includes.push_back(include->get()); + else + for (const json &name : *include) + entry.includes.push_back(name.get()); + } + for (auto it = file.begin(); it != file.end(); ++it) + if (metadata.count(it.key()) == 0) + entry.keys.push_back(it.key()); + } + } + return sections; +} + +const PresetCollection &presets_of(const PresetBundle &bundle, const std::string §ion) +{ + if (section == "process_list") + return bundle.prints; + if (section == "filament_list") + return bundle.filaments; + return bundle.printers; +} + +size_t dump(const PresetBundle &bundle, const Sections §ions, std::ostream &out) +{ + size_t dumped = 0; + for (const auto &[section, entries] : sections) { + std::vector presets; + for (const Preset &preset : presets_of(bundle, section).get_presets()) + if (preset.is_system) + presets.push_back(&preset); + std::sort(presets.begin(), presets.end(), [](const Preset *a, const Preset *b) { return a->name < b->name; }); + for (const Preset *preset : presets) { + // The preset and its ancestors, root first: the order their includes apply in. + std::vector lineage; + for (auto it = entries.find(preset->name); it != entries.end() && lineage.size() <= entries.size(); + it = entries.find(it->second.inherits)) + lineage.insert(lineage.begin(), &it->second); + std::string templates; + std::set keys; + for (const Entry *entry : lineage) + for (const std::string &name : entry->includes) { + templates += (templates.empty() ? "" : "; ") + name; + if (const auto it = entries.find(name); it != entries.end()) + keys.insert(it->second.keys.begin(), it->second.keys.end()); + } + if (templates.empty()) + continue; + const std::string prefix = section.substr(0, section.find('_')) + " | " + preset->name + " | "; + out << prefix << "include = " << templates << "\n"; + for (const std::string &key : keys) + out << prefix << key << " = " << (preset->config.has(key) ? preset->config.opt_serialize(key) : "") << "\n"; + ++dumped; + } + } + return dumped; +} + +// Orca: load the vendor as the app does, against the filament library when the +// directory has one: parsed from its JSON files or, with from_cache, from the +// preset cache the app reads on every launch after the first. +void load_vendor(PresetBundle &bundle, const std::string &dir, const std::string &vendor, bool from_cache) +{ + const auto rule = ForwardCompatibilitySubstitutionRule::EnableSilent; + PresetBundle library; + const PresetBundle *base = nullptr; + if (fs::is_regular_file(fs::path(dir) / (std::string(PresetBundle::ORCA_FILAMENT_LIBRARY) + ".json"))) { + library.load_vendor_configs_from_json(dir, PresetBundle::ORCA_FILAMENT_LIBRARY, PresetBundle::LoadSystem, rule, nullptr, false); + base = &library; + } + if (!from_cache) { + bundle.load_vendor_configs_from_json(dir, vendor, PresetBundle::LoadSystem, rule, base, false); + return; + } + // Parse once to write .opc beside the profile, then load that + // cache into a clean bundle. Any cache already there goes first, so that a + // failed write cannot leave it to be loaded; one that was not there before + // goes again afterwards. + const fs::path cache = fs::path(dir) / (vendor + ".opc"); + const bool had_cache = fs::exists(cache); + fs::remove(cache); + PresetBundle parsed; + parsed.set_is_validation_mode(true); // parse the JSON: validation never serves a cache + parsed.set_generate_vendor_caches(true); + parsed.load_vendor_configs_from_json(dir, vendor, PresetBundle::LoadSystem, rule, base); + const bool loaded = bundle.load_vendor_cache(cache.string(), vendor, parsed.vendors.at(vendor).config_version, base); + if (!had_cache) + fs::remove(cache); + if (!loaded) + throw std::runtime_error("The preset cache " + cache.string() + " was not written or was rejected"); +} + +} // namespace + +int main(int argc, char *argv[]) +{ + const char *usage = "Usage: profile_include_dump -p -o [-v ] [-l ] [-c]\n" + " -v vendor to dump, BBL if omitted\n" + " -l log level, 0 (fatal) to 5 (trace); 1 if omitted\n" + " -c load the presets from the vendor's preset cache, generated first, not the JSON\n"; + std::string dir, output, vendor = "BBL"; + unsigned log_level = 1; + bool from_cache = false; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "-c") { + from_cache = true; + continue; + } + if (i + 1 == argc) { + std::cerr << usage; + return 1; + } + const std::string value = argv[++i]; + if (arg == "-p") + dir = value; + else if (arg == "-o") + output = value; + else if (arg == "-v") + vendor = value; + else if (arg == "-l" && value.size() == 1 && value[0] >= '0' && value[0] <= '5') + log_level = unsigned(value[0] - '0'); + else { + std::cerr << usage; + return 1; + } + } + if (dir.empty() || output.empty() || !fs::is_directory(dir)) { + std::cerr << usage; + return 1; + } + set_logging_level(log_level); + + try { + PresetBundle bundle; + load_vendor(bundle, dir, vendor, from_cache); + boost::nowide::ofstream out(output); + if (!out) + throw std::runtime_error("Cannot write " + output); + const size_t dumped = dump(bundle, read_vendor(dir, vendor), out); + out.close(); + if (!out) + throw std::runtime_error("Failed writing " + output); + std::cerr << "Dumped " << dumped << " " << vendor << " presets that include a template\n"; + } catch (const std::exception &ex) { + std::cerr << ex.what() << "\n"; + return 1; + } + return 0; +} diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index c8d816b3a4..42d6970ad1 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -950,6 +950,9 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex } else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INHERITS)) { key_values.emplace(BBL_JSON_KEY_INHERITS, it.value()); + } + else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INCLUDES)) { + key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump()); } else if (boost::iequals(it.key(), ORCA_JSON_KEY_RENAMED_FROM)) { key_values.emplace(ORCA_JSON_KEY_RENAMED_FROM, it.value()); } else { diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 36955a19bd..bd45150a15 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -423,6 +423,74 @@ bool ExtrusionLoop::is_smooth(double angle_threshold, double min_arm_length) con return true; } +// The seam is inserted into the loop unless a vertex lies within the G-code resolution of it, so a +// loop can begin and end with a segment of a few micrometres. A plain loop stops there anyway; a +// scarf extrudes through both ends, and the planner nearly halts on a block that short. Drop the +// vertex next to the seam point instead, so the loop still starts and ends at the seam. A trimmed +// path loses its arc fitting; its geometry is unchanged, it just prints as line segments. +static void trim_seam_ends(ExtrusionPaths &paths, double tolerance) +{ + const auto shorter = [tolerance](const Point3 &a, const Point3 &b) { return (b - a).cast().norm() < tolerance; }; + + while (!paths.empty()) { + Points3 &points = paths.front().polyline.points; + if (points.size() < 2 || !shorter(points[0], points[1])) + break; + if (points.size() > 2) { + points.erase(points.begin() + 1); + paths.front().polyline.fitting_result.clear(); + } else if (paths.size() > 1) { + const Point3 seam = points.front(); + paths.erase(paths.begin()); + paths.front().polyline.points.front() = seam; + paths.front().polyline.fitting_result.clear(); + } else { + break; + } + } + + while (!paths.empty()) { + Points3 &points = paths.back().polyline.points; + if (points.size() < 2 || !shorter(points[points.size() - 2], points.back())) + break; + if (points.size() > 2) { + points.erase(points.end() - 2); + paths.back().polyline.fitting_result.clear(); + } else if (paths.size() > 1) { + const Point3 seam = points.back(); + paths.pop_back(); + paths.back().polyline.points.back() = seam; + paths.back().polyline.fitting_result.clear(); + } else { + break; + } + } +} + +// Split `polyline` where the scarf ramp ends. When the split would leave a remainder shorter +// than half a slope step before the next vertex, the ramp is extended to that vertex instead: +// a stub that short makes the motion planner slow down at the end of the ramp. Planners treat +// moves of a millimetre and more as ordinary, so the ramp never grows by more than that. +static void split_at_slope_end(const Polyline3 &polyline, double length, double slope_max_segment_length, Polyline3 &slope, Polyline3 &flat) +{ + const double snap_distance = std::min(0.5 * slope_max_segment_length, scale_(1.)); + double acc_length = 0.; + size_t line_idx = 0; + for (const Line3 &line : polyline.lines()) { + const double end_length = acc_length + line.length(); + if (end_length >= length) { + if (end_length - length < snap_distance) { + polyline.split_at_index(line_idx + 1, &slope, &flat); + return; + } + break; + } + acc_length = end_length; + ++line_idx; + } + polyline.split_at_length(length, &slope, &flat); +} + ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, double seam_gap, double slope_min_length, @@ -431,6 +499,14 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, ExtrusionLoopRole role) : ExtrusionLoop(role) { + // An eighth of a common line width: the path moves by less than that at the seam. + trim_seam_ends(original_paths, scale_(0.05)); + // The caller measured the loop before the trim; a scarf that covers the whole loop must still end at 1. + double trimmed_length = 0.; + for (const ExtrusionPath &path : original_paths) + trimmed_length += unscale_(path.length()); + slope_min_length = std::min(slope_min_length, trimmed_length); + // create slopes const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath &path, const Polyline3 &poly, double ratio_begin, double ratio_end) { if (poly.empty()) { return; } @@ -487,12 +563,13 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, // Split current path into slope and non-slope part Polyline3 slope_path; Polyline3 flat_path; - path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path); + split_at_slope_end(path->polyline, scale_(remaining_length), slope_max_segment_length, slope_path, flat_path); add_slop(*path, slope_path, start_ratio, 1); start_ratio = 1; - paths.emplace_back(std::move(flat_path), *path); + if (flat_path.size() > 1) + paths.emplace_back(std::move(flat_path), *path); remaining_length = 0; } else { remaining_length -= path_len; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1a1d1bcbff..40743dd5ca 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -8084,7 +8084,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, _mm3_per_mm *= filament_flow_ratio; if (path.role() == erTopSolidInfill) { - _mm3_per_mm *= m_config.top_solid_infill_flow_ratio; + _mm3_per_mm *= NOZZLE_CONFIG(top_solid_infill_flow_ratio); } else if (path.role() == erBottomSurface) { _mm3_per_mm *= m_config.bottom_solid_infill_flow_ratio; } else if (path.role() == erInternalBridgeInfill) { diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index c66518e29d..5b32d90ac6 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -62,6 +62,7 @@ #define ORCA_JSON_KEY_UPDATE_TIME "updated_time" #define ORCA_JSON_KEY_CREATED_TIME "created_time" #define BBL_JSON_KEY_INHERITS "inherits" +#define BBL_JSON_KEY_INCLUDES "include" #define BBL_JSON_KEY_INSTANTIATION "instantiation" #define BBL_JSON_KEY_NOZZLE_DIAMETER "nozzle_diameter" #define BBL_JSON_KEY_PRINTER_TECH "machine_tech" diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index dc731b63c4..c8fe00b17c 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -6598,7 +6598,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // now, or deserialized from the vendor's cache; the code is shared so a // cache-loaded bundle cannot come out different from a JSON-loaded one. // Resolves `inherits` against the presets loaded before this one -// (config_maps) or against base_bundle's filament library, flattens, validates +// (config_maps) or against base_bundle's filament library, layers each +// `include` (include_maps) under the preset's own keys, flattens, validates // and registers the preset. Returns the reason loading failed, empty on // success. std::string PresetBundle::load_vendor_preset( @@ -6607,9 +6608,10 @@ std::string PresetBundle::load_vendor_preset( const PresetBundle* base_bundle, LoadConfigBundleAttributes flags, ConfigSubstitutionContext& substitution_context, PresetsConfigSubstitutions& substitutions, - std::map& config_maps, std::map& filament_id_maps, + std::map& config_maps, std::map& include_maps, + std::map& filament_id_maps, PresetCollection* presets_collection, size_t& count, bool is_from_lib, - const std::set* retain_configs) + const std::set* retain_configs, const std::set* retain_includes) { const VendorProfile* current_vendor_profile = &this->vendors.at(vendor_name); const std::string subfile = path + "/" + vendor_name + "/" + entry.sub_path; @@ -6652,14 +6654,30 @@ std::string PresetBundle::load_vendor_preset( return reason; } } - else { - if (presets_collection->type() == Preset::TYPE_PRINTER) - default_config = &presets_collection->default_preset_for(entry.config_src).config; - else - default_config = &presets_collection->default_preset().config; - } + else + default_config = &presets_collection->default_preset_for(entry.config_src).config; config = *default_config; + // Layer each included preset's own keys over the parent, in the order listed; + // this preset's own keys go on top. + for (const std::string& name : entry.includes) { + auto it = include_maps.find(name); + if (it == include_maps.end()) { + ++m_errors; + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": can not find include " << name << " for " << preset_name; + continue; + } + config.apply(it->second); + } config.apply(entry.config_src); + // Record what a base states, its diff against the default, for the presets + // that include it. It is taken before extend_default_config_length pads every + // per-variant key to the base's variant count: the padded defaults would + // otherwise override the values each includer inherits. + if (entry.instantiation == "false" && (retain_includes == nullptr || retain_includes->count(preset_name) != 0)) { + DynamicPrintConfig included; + included.apply_only(config, config.diff(presets_collection->default_preset_for(config).config)); + include_maps.emplace(preset_name, std::move(included)); + } extend_default_config_length(config, true, *default_config); if (entry.instantiation == "false" && "Template" != vendor_name) { // Report configuration fields, which are misplaced into a wrong group. @@ -7118,6 +7136,15 @@ std::pair PresetBundle::load_vendor_configs_ } entry.name = key_values[BBL_JSON_KEY_NAME]; entry.description = key_values[BBL_JSON_KEY_DESCRIPTION]; + // A file that states no instantiation and is named as G-code, or has no + // name, is a template that is only there to be included. A nameless one + // goes by its name in the vendor index. + if (auto it = key_values.find(BBL_JSON_KEY_INSTANTIATION); + (it == key_values.end() || it->second.empty()) && (entry.name.empty() || entry.name.find("gcode") != std::string::npos)) { + key_values[BBL_JSON_KEY_INSTANTIATION] = "false"; + if (entry.name.empty()) + entry.name = subfile_iter.first; + } if(key_values.find(BBL_JSON_KEY_INSTANTIATION) == key_values.end()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Missing instantiation attribute for " << entry.name; @@ -7146,6 +7173,20 @@ std::pair PresetBundle::load_vendor_configs_ return reason; } } + if (auto it = key_values.find(BBL_JSON_KEY_INCLUDES); it != key_values.end()) { + // An array of names, or one bare name; load_from_json kept the JSON text. + nlohmann::json includes = nlohmann::json::parse(it->second); + if (!includes.is_array()) + includes = nlohmann::json::array({std::move(includes)}); + for (const auto& name : includes) { + if (name.is_string()) + entry.includes.push_back(name.get()); + else { + ++m_errors; + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": invalid include " << name.dump() << " for " << entry.name; + } + } + } if (key_values.find(ORCA_JSON_KEY_RENAMED_FROM) != key_values.end()) { if (!unescape_strings_cstyle(key_values[ORCA_JSON_KEY_RENAMED_FROM], entry.renamed_from)) { BOOST_LOG_TRIVIAL(error) << "Error in a Config \"" << dir << "\": The preset \"" << entry.name @@ -7162,7 +7203,7 @@ std::pair PresetBundle::load_vendor_configs_ return reason; }; - std::map configs; + std::map configs, include_maps; std::map filament_id_maps; // Orca: whether to (re)write the vendor's cache after this parse, leaving it // in step with the profile so the next run reads it instead. It is written @@ -7179,6 +7220,7 @@ std::pair PresetBundle::load_vendor_configs_ auto load_subfiles = [&](std::vector>& subfiles, std::vector& entries, const char* kind, bool is_from_lib = false) { configs.clear(); + include_maps.clear(); filament_id_maps.clear(); for (auto& subfile : subfiles) { CachedPreset entry; @@ -7186,8 +7228,8 @@ std::pair PresetBundle::load_vendor_configs_ if (reason.empty()) { const int errors_before_install = m_errors; reason = load_vendor_preset(entry, dir, vendor_name, base_bundle, flags, - substitution_context, substitutions, configs, filament_id_maps, presets, - presets_loaded, is_from_lib); + substitution_context, substitutions, configs, include_maps, filament_id_maps, + presets, presets_loaded, is_from_lib); install_errors += m_errors - errors_before_install; } if (!reason.empty()) { @@ -7982,26 +8024,29 @@ bool PresetBundle::load_vendor_cache(const std::string& cache_path, const std::s // parsed), so no substitutions are reported, as before. ConfigSubstitutionContext substitution_context { ForwardCompatibilitySubstitutionRule::EnableSilent }; PresetsConfigSubstitutions substitutions; - std::map configs; + std::map configs, include_maps; std::map filament_id_maps; const std::string path = boost::filesystem::path(cache_path).parent_path().string(); size_t count = 0; auto install_entries = [&](const std::vector& entries, PresetCollection* presets, bool is_from_lib) { configs.clear(); + include_maps.clear(); filament_id_maps.clear(); - // Only configs of presets that other entries inherit are ever looked - // up again; registering just those skips one full config copy for - // every leaf preset. The library's filaments are all retained — they - // become the m_config_maps other vendors resolve against. - std::set inherited; - for (const CachedPreset& entry : entries) + // Only configs of presets that other entries inherit or include are + // ever looked up again; registering just those skips one full config + // copy for every leaf preset. The library's filaments are all retained + // — they become the m_config_maps other vendors resolve against. + std::set inherited, included; + for (const CachedPreset& entry : entries) { if (! entry.inherits.empty()) inherited.insert(entry.inherits); + included.insert(entry.includes.begin(), entry.includes.end()); + } const std::set* retain_configs = is_from_lib ? nullptr : &inherited; for (const CachedPreset& entry : entries) { const std::string reason = load_vendor_preset(entry, path, vendor_name, base_bundle, LoadConfigBundleAttribute::LoadSystem, substitution_context, substitutions, - configs, filament_id_maps, presets, count, is_from_lib, retain_configs); + configs, include_maps, filament_id_maps, presets, count, is_from_lib, retain_configs, &included); if (! reason.empty()) throw std::runtime_error("entry " + entry.name + " failed to install: " + reason); } diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 5a8f34e706..741f6069a4 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -634,22 +634,24 @@ private: // load_vendor_configs_from_json reads a cache. bool load_vendor_cache(const boost::filesystem::path& dir, const std::string& vendor_name, const PresetBundle* base_bundle); - // Load one source-form preset entry into this bundle: resolve `inherits`, - // flatten, validate and register the preset. Returns the reason loading - // failed, empty on success. See the definition for the sharing contract - // between the JSON parse and the cache load. - // retain_configs, when non-null, names the only presets registered into - // config_maps (a full config copy each). The cache load passes the names its - // entries inherit — the only ones ever looked up again; the JSON parse - // retains all, not knowing what later subfiles inherit. + // Load one source-form preset entry into this bundle: resolve `inherits` + // and `include`, flatten, validate and register the preset. Returns the + // reason loading failed, empty on success. See the definition for the + // sharing contract between the JSON parse and the cache load. + // retain_configs / retain_includes, when non-null, name the only presets + // registered into config_maps / include_maps (a config copy each). The + // cache load passes the names its entries inherit / include — the only + // ones ever looked up again; the JSON parse retains all, not knowing what + // later subfiles name. std::string load_vendor_preset(const CachedPreset& entry, const std::string& path, const std::string& vendor_name, const PresetBundle* base_bundle, LoadConfigBundleAttributes flags, ConfigSubstitutionContext& substitution_context, PresetsConfigSubstitutions& substitutions, - std::map& config_maps, std::map& filament_id_maps, + std::map& config_maps, std::map& include_maps, + std::map& filament_id_maps, PresetCollection* presets_collection, size_t& count, bool is_from_lib, - const std::set* retain_configs = nullptr); + const std::set* retain_configs = nullptr, const std::set* retain_includes = nullptr); // Clear every collection's m_printer_hold_alias, which reset() leaves alone. void clear_printer_hold_aliases(); diff --git a/src/libslic3r/PresetCacheFormat.cpp b/src/libslic3r/PresetCacheFormat.cpp index accebba7b4..1eca878fe2 100644 --- a/src/libslic3r/PresetCacheFormat.cpp +++ b/src/libslic3r/PresetCacheFormat.cpp @@ -254,7 +254,7 @@ constexpr uint32_t CACHE_MAGIC = 0x4F52435A; // "ORCZ" // save_entries below), or a change to the cache's own layout or the // meaning of its stamps. Option-schema drift is NOT such a change — the // dictionary handles it, which is why this no longer moves every release. -constexpr uint32_t CACHE_VERSION = 1; +constexpr uint32_t CACHE_VERSION = 2; // A stamp-string read that refuses an absurd length before allocating anything. // The stamps are read from files named from the outside (peek_version is @@ -325,7 +325,7 @@ void visit_entry(Archive& ar, Entry& e, ConfigFn&& config) { ar(e.name, e.sub_path); config(); - ar(e.inherits, e.description, e.instantiation, e.setting_id, e.filament_id, e.renamed_from); + ar(e.inherits, e.includes, e.description, e.instantiation, e.setting_id, e.filament_id, e.renamed_from); } // The count comes from a file that has already passed magic and CRC, but a diff --git a/src/libslic3r/PresetCacheFormat.hpp b/src/libslic3r/PresetCacheFormat.hpp index b200ec9911..2c90c07882 100644 --- a/src/libslic3r/PresetCacheFormat.hpp +++ b/src/libslic3r/PresetCacheFormat.hpp @@ -107,12 +107,13 @@ void load_config(cereal::BinaryInputArchive& ar, DynamicPrintConfig& config, con // comes after. void skip_config(cereal::BinaryInputArchive& ar, const CacheDictionary& dict); -// One preset as its JSON subfile states it: the config diff, the name of the -// preset it inherits, and the parse metadata — everything the parse phase of -// load_vendor_configs_from_json extracts and nothing it derives. Inheritance -// is resolved when the entry is installed, against whatever filament library -// is loaded then, so a cache carries no other vendor's values and no other -// vendor's update can make it stale. +// One preset as its JSON subfile states it: the config diff, the names of the +// preset it inherits and the presets it includes, and the parse metadata — +// everything the parse phase of load_vendor_configs_from_json extracts and +// nothing it derives. Inheritance and includes are resolved when the entry is +// installed, against whatever filament library is loaded then, so a cache +// carries no other vendor's values and no other vendor's update can make it +// stale. // Written and read by visit_entry in PresetCacheFormat.cpp, which lists every // field below in this order — once, for the save, the load and the name peek alike. struct CachedPreset @@ -121,6 +122,7 @@ struct CachedPreset std::string sub_path; // path under the vendor's directory DynamicPrintConfig config_src; // the preset's own diff, nothing inherited std::string inherits; + std::vector includes; // layered under config_src, in this order std::string description; std::string instantiation; // "true"/"false" as stated; anything else was already counted as a parse error std::string setting_id; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 5df69b7335..477c2c74ca 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1705,6 +1705,25 @@ StringObjectException Print::check_multi_filament_valid(const Print& print) // Precondition: Print::validate() requires the Print::apply() to be called its invocation. //BBS: refine seq-print validation logic +// The exception's own message is just "Errors"; the detail is in the per-object errors, +// whose object id is the PrintObject's. +std::string Print::slicing_errors_message(const SlicingErrors &errors) const +{ + std::string message; + for (const SlicingError &error : errors.errors_) { + std::string object_name; + for (const PrintObject *object : m_objects) + if (object->id().id == error.objectId()) { + object_name = object->model_object()->name; + break; + } + if (!message.empty()) + message += "\n"; + message += object_name.empty() ? std::string(error.what()) : object_name + ": " + error.what(); + } + return message; +} + StringObjectException Print::validate(std::vector *warnings, Polygons* collison_polygons, std::vector>* height_polygons) const { auto add_warning = [warnings](StringObjectException w) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9c1782e047..12be35ea3c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -30,6 +30,8 @@ namespace Slic3r { +class SlicingErrors; + class GCode; class Layer; class ModelObject; @@ -967,6 +969,8 @@ public: // Returns an empty string if valid, otherwise returns an error message. StringObjectException validate(std::vector *warnings = nullptr, Polygons* collison_polygons = nullptr, std::vector>* height_polygons = nullptr) const override; + // The per-object messages of a SlicingErrors, each prefixed with its object's name. + std::string slicing_errors_message(const SlicingErrors &errors) const; double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index bf7a651dcc..9f317d21af 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1522,7 +1522,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(1)); - def = this->add("top_solid_infill_flow_ratio", coFloat); + def = this->add("top_solid_infill_flow_ratio", coFloats); def->label = L("Top surface flow ratio"); def->category = L("Advanced"); def->tooltip = L("This factor affects the amount of material for top solid infill. " @@ -1531,7 +1531,8 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->max = 2; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(1)); + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{1}); def = this->add("bottom_solid_infill_flow_ratio", coFloat); def->label = L("Bottom surface flow ratio"); @@ -9127,6 +9128,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va value = "tree(auto)"; } else if (opt_key == "support_base_pattern" && value == "none") { value = "hollow"; + } else if (opt_key == "tree_support_wall_count" && value == "-1") { + value = "0"; } else if (opt_key == "different_settings_to_system") { std::string copy_value = value; copy_value.erase(std::remove(copy_value.begin(), copy_value.end(), '\"'), copy_value.end()); // remove '"' in string @@ -9395,7 +9398,8 @@ std::set print_options_with_variant = { "initial_layer_travel_jerk", "default_junction_deviation", "print_extruder_id", //coInts - "print_extruder_variant" //coStrings + "print_extruder_variant", //coStrings + "top_solid_infill_flow_ratio" }; std::set filament_options_with_variant = { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index eb03c8798e..9a485ff271 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1403,7 +1403,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatsNullable, small_perimeter_threshold)) ((ConfigOptionFloatsOrPercentsNullable, small_support_perimeter_speed)) ((ConfigOptionFloatsNullable, small_support_perimeter_threshold)) - ((ConfigOptionFloat, top_solid_infill_flow_ratio)) + ((ConfigOptionFloatsNullable, top_solid_infill_flow_ratio)) ((ConfigOptionFloat, bottom_solid_infill_flow_ratio)) ((ConfigOptionFloatOrPercent, infill_anchor)) ((ConfigOptionFloatOrPercent, infill_anchor_max)) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7ac307c87b..584fc2a2aa 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -286,6 +286,8 @@ set(SLIC3R_GUI_SOURCES GUI/FilamentMapDialog.hpp GUI/IconManager.cpp GUI/IconManager.hpp + GUI/IdleScheduler.cpp + GUI/IdleScheduler.hpp GUI/ImageGrid.cpp GUI/ImageGrid.h GUI/ImGuiWrapper.cpp @@ -347,6 +349,10 @@ set(SLIC3R_GUI_SOURCES GUI/KBShortcutsDialog.hpp GUI/KeyChord.cpp GUI/KeyChord.hpp + GUI/Lazy.cpp + GUI/Lazy.hpp + GUI/LazyPage.cpp + GUI/LazyPage.hpp GUI/LibVGCode/LibVGCodeWrapper.hpp GUI/LibVGCode/LibVGCodeWrapper.cpp GUI/LinuxDisplayBackend.cpp @@ -433,6 +439,7 @@ set(SLIC3R_GUI_SOURCES GUI/Plater.hpp GUI/PlateSettingsDialog.cpp GUI/PlateSettingsDialog.hpp + GUI/PrebuildQueue.hpp GUI/Preferences.cpp GUI/Preferences.hpp GUI/PresetBundleDialog.cpp @@ -512,6 +519,7 @@ set(SLIC3R_GUI_SOURCES GUI/SliceInfoPanel.hpp GUI/SlicingProgressNotification.cpp GUI/SlicingProgressNotification.hpp + GUI/StagedBuild.hpp GUI/StatusPanel.cpp GUI/StatusPanel.hpp GUI/StepMeshDialog.cpp diff --git a/src/slic3r/GUI/Auxiliary.cpp b/src/slic3r/GUI/Auxiliary.cpp index 13ca173eb6..14b8d238a2 100644 --- a/src/slic3r/GUI/Auxiliary.cpp +++ b/src/slic3r/GUI/Auxiliary.cpp @@ -863,17 +863,27 @@ void AuxiliaryPanel::init_tabpanel() m_tabpanel->SetBackgroundColour(wxColour("#FEFFFF")); m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [](wxBookCtrlEvent &e) { /* Event handling */ }); - m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER); - m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE); - m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS); - m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE); - m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS); - - m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true); - m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false); - m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false); - m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false); - m_tabpanel->AddPage(m_others_panel, _L("Others"), false); + add_build_step([this] { + m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER); + m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true); + }); + add_build_step([this] { + m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE); + m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false); + }); + add_build_step([this] { + m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS); + m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false); + }); + add_build_step([this] { + m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE); + m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false); + }); + add_build_step([this] { + m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS); + m_tabpanel->AddPage(m_others_panel, _L("Others"), false); + Layout(); + }); } wxWindow *AuxiliaryPanel::create_side_tools() diff --git a/src/slic3r/GUI/Auxiliary.hpp b/src/slic3r/GUI/Auxiliary.hpp index 0bced37115..76e6adf97b 100644 --- a/src/slic3r/GUI/Auxiliary.hpp +++ b/src/slic3r/GUI/Auxiliary.hpp @@ -46,6 +46,7 @@ #include "slic3r/GUI/UpgradePanel.hpp" #include "slic3r/GUI/AmsWidgets.hpp" #include "Widgets/SideTools.hpp" +#include "StagedBuild.hpp" #define AUFILE_GREY700 wxColour(107, 107, 107) #define AUFILE_GREY500 wxColour(158, 158, 158) @@ -194,7 +195,7 @@ public: }; -class AuxiliaryPanel : public wxPanel +class AuxiliaryPanel : public wxPanel, public StagedBuild { private: Tabbook *m_tabpanel = {nullptr}; diff --git a/src/slic3r/GUI/CAD/DesignPanel.hpp b/src/slic3r/GUI/CAD/DesignPanel.hpp index 88b445534d..16f0a2722c 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.hpp +++ b/src/slic3r/GUI/CAD/DesignPanel.hpp @@ -12,6 +12,7 @@ #include "libslic3r/CAD/CadDocument.hpp" #include "slic3r/GUI/CAD/DesignInteraction.hpp" // CadLevel: what one Esc press means +#include "slic3r/GUI/Lazy.hpp" class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here class StaticBox; // Orca rounded card frame (Widgets/StaticBox.hpp) @@ -46,7 +47,7 @@ class DesignCanvas; // Design (CAD) tab: a sketch-first, Onshape-style form-driven CAD panel. // Sketch and Extrude are independent tools: the user creates a Sketch first, // then selects it and Extrudes to produce a solid. -class DesignPanel : public wxPanel +class DesignPanel : public wxPanel, public LazyInstance { public: explicit DesignPanel(wxWindow* parent); diff --git a/src/slic3r/GUI/CAD/McpControl.cpp b/src/slic3r/GUI/CAD/McpControl.cpp index 8e34e2aac8..0ad05667c3 100644 --- a/src/slic3r/GUI/CAD/McpControl.cpp +++ b/src/slic3r/GUI/CAD/McpControl.cpp @@ -1989,11 +1989,10 @@ json action_set_feature_expr(DesignPanel* panel, const json& params) // Dispatch one parsed request ON THE MAIN THREAD. Returns a JSON-RPC reply string. std::string handle_on_main(const std::string& method, const json& params, const json& id) { - MainFrame* mf = wxGetApp().mainframe; // The panel is built on first use, and in a headless session nobody clicks the tab that // would build it -- so build it here rather than refusing. Safe: this runs on the main // thread (see the CallAfter that dispatches us). - DesignPanel* panel = mf ? mf->ensure_design_panel() : nullptr; + DesignPanel* panel = DesignPanel::ensure(); if (!panel) return rpc_error(id, -32001, "Design panel not ready"); diff --git a/src/slic3r/GUI/CalibrationPanel.cpp b/src/slic3r/GUI/CalibrationPanel.cpp index bdc79c1c8e..fca9fa2be9 100644 --- a/src/slic3r/GUI/CalibrationPanel.cpp +++ b/src/slic3r/GUI/CalibrationPanel.cpp @@ -491,14 +491,15 @@ void CalibrationPanel::init_tabpanel() { selected); } + for (int i = 0; i < (int)CALI_MODE_COUNT; i++) + add_build_steps_of(*m_cali_panels[i]); + // ORCA use standard paddings and keep arrow icon for consistent look between sidebars //for (int i = 0; i < (int)CALI_MODE_COUNT; i++) // m_tabpanel->SetPageImage(i, ""); //auto padding_size = m_tabpanel->GetBtnsListCtrl()->GetPaddingSize(0); //m_tabpanel->GetBtnsListCtrl()->SetPaddingSize({ FromDIP(15), padding_size.y }); - - m_initialized = true; } void CalibrationPanel::init_timer() @@ -534,6 +535,8 @@ void CalibrationPanel::update_print_error_info(int code, std::string msg, std::s } void CalibrationPanel::update_all() { + // Every wizard's pages exist once the last build step has run. + if (!built()) return; NetworkAgent* m_agent = wxGetApp().getAgent(); Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); @@ -597,7 +600,7 @@ void CalibrationPanel::update_all() { void CalibrationPanel::show_status(int status) { - if (!m_initialized) return; + if (!built()) return; if (last_status == status)return; last_status = status; diff --git a/src/slic3r/GUI/CalibrationPanel.hpp b/src/slic3r/GUI/CalibrationPanel.hpp index 1f6b097af9..960c5eed37 100644 --- a/src/slic3r/GUI/CalibrationPanel.hpp +++ b/src/slic3r/GUI/CalibrationPanel.hpp @@ -3,6 +3,7 @@ #include "CalibrationWizard.hpp" #include "Tabbook.hpp" +#include "Lazy.hpp" //#include "Widgets/SideTools.hpp" namespace Slic3r { namespace GUI { @@ -88,7 +89,7 @@ private: }; -class CalibrationPanel : public wxPanel +class CalibrationPanel : public wxPanel, public StagedBuild, public LazyInstance { public: CalibrationPanel(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL); @@ -109,7 +110,6 @@ protected: int last_status; - bool m_initialized { false }; std::string last_conn_type = "undedefined"; MachineObject* obj{ nullptr }; MachineObject* last_obj { nullptr }; diff --git a/src/slic3r/GUI/CalibrationWizard.cpp b/src/slic3r/GUI/CalibrationWizard.cpp index f80562578d..fa42143f06 100644 --- a/src/slic3r/GUI/CalibrationWizard.cpp +++ b/src/slic3r/GUI/CalibrationWizard.cpp @@ -130,6 +130,15 @@ CalibrationWizard::~CalibrationWizard() ; } +void CalibrationWizard::add_page_step(CalibrationWizardPageStep*& step, std::function make) +{ + add_build_step([this, &step, make = std::move(make)] { + step = new CalibrationWizardPageStep(make()); + m_all_pages_sizer->Add(step->page, 1, wxEXPAND | wxALL, FromDIP(25)); + step->page->Hide(); + }); +} + void CalibrationWizard::on_cali_job_finished(wxCommandEvent& event) { this->on_cali_job_finished(event.GetString()); @@ -520,33 +529,28 @@ void PressureAdvanceWizard::on_cali_job_finished(wxString evt_data) void PressureAdvanceWizard::create_pages() { - start_step = new CalibrationWizardPageStep(new CalibrationPAStartPage(m_scrolledWindow)); - preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false)); - cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode)); - save_step = new CalibrationWizardPageStep(new CalibrationPASavePage(m_scrolledWindow)); + add_page_step(start_step, [this] { return new CalibrationPAStartPage(m_scrolledWindow); }); + add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); }); + add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); }); + add_page_step(save_step, [this] { return new CalibrationPASavePage(m_scrolledWindow); }); - m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); + add_build_step([this] { + m_page_steps.push_back(start_step); + m_page_steps.push_back(preset_step); + m_page_steps.push_back(cali_step); + m_page_steps.push_back(save_step); + for (int i = 0; i < m_page_steps.size() -1; i++) { + m_page_steps[i]->chain(m_page_steps[i+1]); + } - m_page_steps.push_back(start_step); - m_page_steps.push_back(preset_step); - m_page_steps.push_back(cali_step); - m_page_steps.push_back(save_step); + for (int i = 0; i < m_page_steps.size(); i++) { + m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this); + } - for (int i = 0; i < m_page_steps.size() -1; i++) { - m_page_steps[i]->chain(m_page_steps[i+1]); - } - - for (int i = 0; i < m_page_steps.size(); i++) { - m_page_steps[i]->page->Hide(); - m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this); - } - - if (!m_page_steps.empty()) - show_step(m_page_steps.front()); + if (!m_page_steps.empty()) + show_step(m_page_steps.front()); + }); } void PressureAdvanceWizard::on_cali_action(wxCommandEvent& evt) @@ -1053,59 +1057,46 @@ FlowRateWizard::FlowRateWizard(wxWindow* parent, wxWindowID id, const wxPoint& p void FlowRateWizard::create_pages() { - start_step = new CalibrationWizardPageStep(new CalibrationFlowRateStartPage(m_scrolledWindow)); - preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false)); + add_page_step(start_step, [this] { return new CalibrationFlowRateStartPage(m_scrolledWindow); }); + add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); }); // manual - cali_coarse_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI)); - coarse_save_step = new CalibrationWizardPageStep(new CalibrationFlowCoarseSavePage(m_scrolledWindow)); - cali_fine_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI)); - fine_save_step = new CalibrationWizardPageStep(new CalibrationFlowFineSavePage(m_scrolledWindow)); + add_page_step(cali_coarse_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI); }); + add_page_step(coarse_save_step, [this] { return new CalibrationFlowCoarseSavePage(m_scrolledWindow); }); + add_page_step(cali_fine_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI); }); + add_page_step(fine_save_step, [this] { return new CalibrationFlowFineSavePage(m_scrolledWindow); }); // auto - cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode)); - save_step = new CalibrationWizardPageStep(new CalibrationFlowX1SavePage(m_scrolledWindow)); + add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); }); + add_page_step(save_step, [this] { return new CalibrationFlowX1SavePage(m_scrolledWindow); }); - m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(cali_coarse_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(coarse_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(cali_fine_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(fine_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); + add_build_step([this] { + m_page_steps.push_back(start_step); + m_page_steps.push_back(preset_step); + m_page_steps.push_back(cali_coarse_step); + m_page_steps.push_back(coarse_save_step); + m_page_steps.push_back(cali_fine_step); + m_page_steps.push_back(fine_save_step); - m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); - m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25)); + //m_page_steps.push_back(cali_step); + //m_page_steps.push_back(save_step); - m_page_steps.push_back(start_step); - m_page_steps.push_back(preset_step); - m_page_steps.push_back(cali_coarse_step); - m_page_steps.push_back(coarse_save_step); - m_page_steps.push_back(cali_fine_step); - m_page_steps.push_back(fine_save_step); + for (int i = 0; i < m_page_steps.size() - 1; i++) { + m_page_steps[i]->chain(m_page_steps[i + 1]); + } - //m_page_steps.push_back(cali_step); - //m_page_steps.push_back(save_step); + for (int i = 0; i < m_page_steps.size(); i++) { + m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); + } - for (int i = 0; i < m_page_steps.size() - 1; i++) { - m_page_steps[i]->chain(m_page_steps[i + 1]); - } + cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); + save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); - // hide all pages - cali_step->page->Hide(); - save_step->page->Hide(); - for (int i = 0; i < m_page_steps.size(); i++) { - m_page_steps[i]->page->Hide(); - m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); - } + if (!m_page_steps.empty()) + show_step(m_page_steps.front()); - - cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); - save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this); - - if (!m_page_steps.empty()) - show_step(m_page_steps.front()); - - set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL); + set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL); + }); } void FlowRateWizard::on_cali_action(wxCommandEvent& evt) diff --git a/src/slic3r/GUI/CalibrationWizard.hpp b/src/slic3r/GUI/CalibrationWizard.hpp index d72d5fcb7b..89c515eb80 100644 --- a/src/slic3r/GUI/CalibrationWizard.hpp +++ b/src/slic3r/GUI/CalibrationWizard.hpp @@ -9,6 +9,7 @@ #include "CalibrationWizardPresetPage.hpp" #include "CalibrationWizardCaliPage.hpp" #include "CalibrationWizardSavePage.hpp" +#include "StagedBuild.hpp" namespace Slic3r { namespace GUI { @@ -36,7 +37,7 @@ struct ConfigIndexValue int index{0}; }; -class CalibrationWizard : public wxPanel { +class CalibrationWizard : public wxPanel, public StagedBuild { public: CalibrationWizard(wxWindow* parent, CalibMode mode, wxWindowID id = wxID_ANY, @@ -79,6 +80,9 @@ public: protected: void on_cali_go_home(); + // Queues a page as a build step, created hidden and added to the pages sizer. + void add_page_step(CalibrationWizardPageStep*& step, std::function make); + protected: /* wx widgets*/ wxScrolledWindow* m_scrolledWindow; diff --git a/src/slic3r/GUI/CalibrationWizardCaliPage.cpp b/src/slic3r/GUI/CalibrationWizardCaliPage.cpp index 152f46f37c..2dc766dca3 100644 --- a/src/slic3r/GUI/CalibrationWizardCaliPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardCaliPage.cpp @@ -192,8 +192,10 @@ void CalibrationCaliPage::update(MachineObject* obj) set_cali_img(); } + // A calibration can run before the Device tab is ever opened, and only its status + // panel shows a print error. if (obj->print_error > 0) { - StatusPanel* status_panel = Slic3r::GUI::wxGetApp().mainframe->m_monitor->get_status_panel(); + StatusPanel* status_panel = MonitorPanel::ensure()->get_status_panel(); status_panel->obj = obj; status_panel->update_error_message(); } diff --git a/src/slic3r/GUI/DeviceErrorDialog.cpp b/src/slic3r/GUI/DeviceErrorDialog.cpp index 2a55d77d24..9790db67bd 100644 --- a/src/slic3r/GUI/DeviceErrorDialog.cpp +++ b/src/slic3r/GUI/DeviceErrorDialog.cpp @@ -538,7 +538,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id) break; } case DeviceErrorDialog::CHECK_ASSISTANT: { - wxGetApp().mainframe->m_monitor->jump_to_HMS(); // go to assistant page + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->jump_to_HMS(); // go to assistant page break; } case DeviceErrorDialog::FILAMENT_EXTRUDED: { @@ -568,7 +569,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id) } case DeviceErrorDialog::JUMP_TO_LIVEVIEW: { Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor(); - Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_LiveView(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->jump_to_LiveView(); break; } case DeviceErrorDialog::NO_REMINDER_NEXT_TIME: { @@ -618,7 +620,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id) } case DeviceErrorDialog::OK_JUMP_RACK: { Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor(); - Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->jump_to_Rack(); break; } case DeviceErrorDialog::ABORT: { diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp index 44394866c8..00d4c3e809 100644 --- a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp @@ -934,7 +934,8 @@ void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt) dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true); if (dlg.ShowModal() == wxID_OK) { - wxGetApp().mainframe->m_monitor->jump_to_Upgrade(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->jump_to_Upgrade(); }; } } diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp index fac14e31d9..eae377c147 100644 --- a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp @@ -344,7 +344,8 @@ void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event) if (dlg.ShowModal() == wxID_OK) { - wxGetApp().mainframe->m_monitor->jump_to_Upgrade(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->jump_to_Upgrade(); wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId()); evt.SetEventObject(this); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 887d13aedc..0b16212567 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -20,6 +20,7 @@ #include "libslic3r/AppConfig.hpp" #include "3DScene.hpp" #include "BackgroundSlicingProcess.hpp" +#include "CameraUtils.hpp" #include "GLShader.hpp" #include "GUI.hpp" #include "Tab.hpp" @@ -117,6 +118,36 @@ void GLCanvas3D::load_render_colors() namespace Slic3r { namespace GUI { +static void pan_camera(Camera& camera, const Vec2d& screen_delta, const Vec3d& anchor) +{ + // Orca: Derive world-units-per-pixel from the projection which produced the visible frame. + // Perspective additionally scales with the eye-space depth of the point being dragged. + const auto& viewport = camera.get_viewport(); + const auto& projection = camera.get_projection_matrix().matrix(); + const double depth_scale = camera.get_type() == Camera::EType::Perspective ? + (anchor - camera.get_position()).dot(camera.get_dir_forward()) : 1.0; + const double projection_x = projection(0, 0) * viewport[2]; + const double projection_y = projection(1, 1) * viewport[3]; + + // Orca: X/Y projection coefficients already include zoom. Using them directly avoids a + // project/unproject round-trip through window depth, whose precision depends on the scene frustum. + if (viewport[2] > 0 && viewport[3] > 0 && anchor.allFinite() && depth_scale > EPSILON && + std::abs(projection_x) > EPSILON && std::abs(projection_y) > EPSILON) { + const Vec3d displacement = 2.0 * depth_scale * + (screen_delta.y() / projection_y * camera.get_dir_up() - + screen_delta.x() / projection_x * camera.get_dir_right()); + if (displacement.allFinite()) { + camera.translate(displacement); + return; + } + } + + // Orca: Preserve the former target-plane behavior if the projection or anchor is invalid. + // Screen Y grows downward, and the camera moves opposite to the drag. + camera.translate(camera.get_inv_zoom() * + (screen_delta.y() * camera.get_dir_up() - screen_delta.x() * camera.get_dir_right())); +} + #ifdef __WXGTK3__ // wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support. RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {} @@ -2206,15 +2237,14 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size) _render_background(); //BBS add partplater rendering logic - bool only_current = false, only_body = false, no_partplate = false; + bool only_current = false, only_body = false; + const bool show_bed = is_bed_visible(); bool show_grid = true; GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type(); if (!m_main_toolbar.is_enabled()) { //only_body = true; only_current = true; } - else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmSegmentation) || (gizmo_type == GLGizmosManager::FuzzySkin)) - no_partplate = true; else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward()) show_grid = false; if (m_axes_at_bed_center) @@ -2228,11 +2258,11 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size) if (m_canvas_type == ECanvasType::CanvasView3D) { // m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline // floating would read as a rendering fault rather than a deliberate view option. - if (!no_partplate && m_show_bed) + if (show_bed) _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes); - if (!no_partplate && m_show_bed) //BBS: add outline logic + if (show_bed) //BBS: add outline logic _render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid); - if (m_axes_at_bed_center && m_show_bed && !no_partplate) + if (m_axes_at_bed_center && show_bed) // Design tab: replace the plate's corner-origin grid with the origin-centred CAD grid. _render_cad_grid(camera.get_view_matrix(), camera.get_projection_matrix()); @@ -2324,6 +2354,13 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data, render_thumbnail(thumbnail_data, w, h, thumbnail_params, model_objects, m_volumes, camera_type, camera_view_angle_type, for_picking, ban_light); } +bool GLCanvas3D::_set_shown_canvas_current() +{ + // Thumbnails also render outside render(), where another library's GL context (e.g. WebKitGTK's) can be current. + // Inside render(), the shown canvas is the one already bound. + return wxGetApp().plater()->get_current_canvas3D()->_set_current(); +} + void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data, unsigned int w, unsigned int h, @@ -2335,6 +2372,9 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data, bool for_picking, bool ban_light) { + if (!_set_shown_canvas_current()) + return; + GLShaderProgram* shader = nullptr; if (for_picking) shader = wxGetApp().get_shader("flat"); @@ -2373,6 +2413,9 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_d bool for_picking, bool ban_light) { + if (!_set_shown_canvas_current()) + return; + GLShaderProgram *shader = wxGetApp().get_shader("thumbnail"); switch (OpenGLManager::get_framebuffers_type()) { case OpenGLManager::EFramebufferType::Arb: { @@ -3281,6 +3324,9 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this); m_canvas->Unbind(wxEVT_GESTURE_ZOOM, &GLCanvas3D::on_gesture, this); m_canvas->Unbind(wxEVT_GESTURE_ROTATE, &GLCanvas3D::on_gesture, this); +#if __WXOSX__ + initGestures(m_canvas->GetHandle(), nullptr); +#endif } } @@ -4029,27 +4075,38 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt) auto & camera = wxGetApp().plater()->get_camera(); if (evt.GetEventType() == wxEVT_GESTURE_PAN) { - auto p = evt.GetPosition(); + // Orca: Gesture coordinates must use framebuffer pixels, and one stable world-space + // anchor must be retained for the complete gesture to prevent perspective drift. + const auto p = evt.GetPosition(); auto d = static_cast(evt).GetDelta(); - float z = 0; - const Vec3d &p2 = _mouse_to_3d({p.x, p.y}, &z); - const Vec3d &p1 = _mouse_to_3d({p.x - d.x, p.y - d.y}, &z); - camera.set_target(camera.get_target() + p1 - p2); + Vec2d screen_position(p.x, p.y); + Vec2d screen_delta(d.x, d.y); + apply_retina_scale(screen_position); + apply_retina_scale(screen_delta); + if (evt.IsGestureStart() || !m_gesture_pan_anchor.has_value()) + m_gesture_pan_anchor = get_camera_pan_anchor(camera, ECameraNavigationType::Gesture, + screen_position - screen_delta); + pan_camera(camera, screen_delta, *m_gesture_pan_anchor); + if (evt.IsGestureEnd()) + m_gesture_pan_anchor.reset(); } else if (evt.GetEventType() == wxEVT_GESTURE_ZOOM) { static float zoom_start = 1; if (evt.IsGestureStart()) zoom_start = camera.get_zoom(); camera.set_zoom(zoom_start * static_cast(evt).GetZoomFactor()); } else if (evt.GetEventType() == wxEVT_GESTURE_ROTATE) { - PartPlate* plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + // Orca: Rotation starts a different navigation operation, so a previous pan anchor + // must not be reused; rotation and pan fallbacks share the same navigation pivot. + m_gesture_pan_anchor.reset(); bool rotate_limit = current_printer_technology() != ptSLA; static double last_rotate = 0; if (evt.IsGestureStart()) last_rotate = 0; auto rotate = static_cast(evt).GetRotationAngle() - last_rotate; last_rotate += rotate; - if (plate) - camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center()); + const std::optional rotate_target = get_camera_orbit_target(ECameraNavigationType::Gesture); + if (rotate_target.has_value()) + camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, *rotate_target); else camera.rotate_on_sphere(-rotate, 0, rotate_limit); camera.auto_type(Camera::EType::Perspective); @@ -4339,6 +4396,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) post_event(SimpleEvent(EVT_GLCANVAS_SWITCH_TO_GLOBAL)); } else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown()) { + // Orca: Retain the click position even if the first motion event crosses a surface edge. + m_mouse.set_start_position_2D_as_invalid(); + m_mouse.drag.start_position_2D = pos; + //BBS: add orient deactivate logic if (!m_gizmos.on_mouse(evt)) { if (_deactivate_arrange_menu() || _deactivate_orient_menu()) @@ -4532,6 +4593,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } // do not process the dragging if the left mouse was set down in another canvas else if (is_camera_rotate(evt, button_mappings)) { + // Orca: Rotation and panning use different drag coordinates and cached anchors. + // Clear the pan state before processing rotation or switching buttons mid-drag. + m_mouse.set_start_position_2D_as_invalid(); if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds m_canvas->CaptureMouse(); @@ -4549,12 +4613,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports || m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmSegmentation || m_gizmos.get_current_type() == GLGizmosManager::FuzzySkin) { - Vec3d rotate_target = Vec3d::Zero(); - if (!m_selection.is_empty()) - rotate_target = m_selection.get_bounding_box().center(); + // Orca: Reuse the centralized pivot policy for scene-oriented tools. + const std::optional rotate_target = get_camera_orbit_target(ECameraNavigationType::Mouse); + if (rotate_target.has_value()) + camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, *rotate_target); else - rotate_target = volumes_bounding_box().center(); - camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, rotate_target); + camera.rotate_on_sphere(rot.x(), rot.y(), false); } else { if (wxGetApp().app_config->get_bool("use_free_camera")) @@ -4578,28 +4642,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, m_rotation_center); } else { - Vec3d rotate_target = Vec3d::Zero(); - if (m_canvas_type == ECanvasType::CanvasPreview) { - PartPlate *plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); - if (plate) - rotate_target = plate->get_bounding_box().center(); - } - else { - if (!m_selection.is_empty()) - rotate_target = m_selection.get_bounding_box().center(); - else { - // Rotate around the center of objects on current plate - auto bbox = volumes_bounding_box(true); - if (!bbox.defined) { - // Rotate around current plate center if current plate is empty - bbox = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_bounding_box(); - } - rotate_target = bbox.center(); - } - } - - if (!rotate_target.isZero()) - camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target); + // Orca: Keep regular mouse orbit and perspective-pan fallback centered + // on the same selection, active-plate, or scene reference. + const std::optional rotate_target = get_camera_orbit_target(ECameraNavigationType::Mouse); + if (rotate_target.has_value()) + camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, *rotate_target); else camera.rotate_on_sphere(rot.x(), rot.y(), rotate_limit); } @@ -4615,16 +4662,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); } else if (is_camera_pan(evt, button_mappings)) { + // Orca: Pan uses screen coordinates and must not inherit the rotation start point. + m_mouse.set_start_position_3D_as_invalid(); if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds m_canvas->CaptureMouse(); // if dragging with right button or if button functions swapped and dragging with left button over blank area then pan if (m_mouse.is_start_position_2D_defined()) { - // get point in model space at Z = 0 - float z = 0.0f; - const Vec3d& cur_pos = _mouse_to_3d(pos, &z); - Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); Camera& camera = wxGetApp().plater()->get_camera(); if (this->m_canvas_type != ECanvasType::CanvasAssembleView) { // Orca: Use a constrained camera when navigating the 3D scene with a regular mouse, if the free camera is not selected @@ -4636,7 +4681,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) camera.recover_from_free_camera(); } - camera.set_target(camera.get_target() + orig - cur_pos); + // Orca: Cache the surface under the initial click and apply every incremental + // cursor delta at that depth, so perspective zoom and camera angle stay exact. + const Vec2d screen_delta = + pos.cast() - m_mouse.drag.start_position_2D.cast(); + if (!m_mouse.drag.camera_pan_anchor.has_value()) + m_mouse.drag.camera_pan_anchor = get_camera_pan_anchor(camera, ECameraNavigationType::Mouse, + m_mouse.drag.start_position_2D.cast()); + pan_camera(camera, screen_delta, *m_mouse.drag.camera_pan_anchor); m_dirty = true; m_mouse.ignore_right_up = true; // will be reset on button up event even if not right button is pressed } @@ -7329,11 +7381,8 @@ void GLCanvas3D::_picking_pass() m_hover_volume_idxs.clear(); m_hover_plate_idxs.clear(); - // Orca: ignore clipping plane if not applying - GLGizmoBase *current_gizmo = m_gizmos.get_current(); - const ClippingPlane clipping_plane = ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() : - ClippingPlane::ClipsNothing()) - .inverted_normal(); + // Orca: Picking and camera navigation must interpret the active gizmo clipping plane identically. + const ClippingPlane clipping_plane = get_raycaster_clipping_plane(); const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane); if (hit.is_valid()) { switch (hit.type) @@ -10627,6 +10676,129 @@ Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) return mouse_ray(mouse_pos).intersect_plane(0.0); } +ClippingPlane GLCanvas3D::get_raycaster_clipping_plane() const +{ + // Orca: Ignore the gizmo clipping plane when the active tool does not apply it, and + // invert the result into the convention expected by SceneRaycaster. + GLGizmoBase* current_gizmo = m_gizmos.get_current(); + return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() : + ClippingPlane::ClipsNothing()) + .inverted_normal(); +} + +std::optional GLCanvas3D::get_camera_orbit_target(ECameraNavigationType navigation_type) const +{ + // Orca: Centralize the pre-existing pivot rules so orbiting and pan fallback cannot + // choose different reference depths for the same canvas and active tool. + PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + if (navigation_type == ECameraNavigationType::Gesture) + return current_plate == nullptr ? std::nullopt : + std::make_optional(current_plate->get_bounding_box().center()); + + const GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type(); + const bool use_scene_target = m_canvas_type == ECanvasType::CanvasAssembleView || + gizmo_type == GLGizmosManager::FdmSupports || gizmo_type == GLGizmosManager::Seam || + gizmo_type == GLGizmosManager::MmSegmentation || gizmo_type == GLGizmosManager::FuzzySkin; + if (use_scene_target) { + if (!m_selection.is_empty()) + return m_selection.get_bounding_box().center(); + + // Orca: Preserve the world-origin fallback used by orbit in an empty scene. + return volumes_bounding_box().center(); + } + + // Orca: Free-camera rotation uses Camera::m_target rather than a plate or selection pivot. + if (wxGetApp().app_config->get_bool("use_free_camera")) + return std::nullopt; + + Vec3d target = Vec3d::Zero(); + if (m_canvas_type == ECanvasType::CanvasPreview) { + if (current_plate != nullptr) + target = current_plate->get_bounding_box().center(); + } else if (!m_selection.is_empty()) { + target = m_selection.get_bounding_box().center(); + } else { + // Orca: Match regular mouse orbit: objects on the active plate, then the plate itself. + BoundingBoxf3 bbox = volumes_bounding_box(true); + if (!bbox.defined && current_plate != nullptr) + bbox = current_plate->get_bounding_box(); + if (bbox.defined) + target = bbox.center(); + } + + // Orca: Preserve the existing zero sentinel used by regular mouse orbit. + return target.isZero() ? std::nullopt : std::make_optional(target); +} + +bool GLCanvas3D::is_bed_visible() const +{ + if (m_canvas_type == ECanvasType::CanvasPreview) + return m_render_preview; + if (m_canvas_type != ECanvasType::CanvasView3D || !m_show_bed) + return false; + if (!m_main_toolbar.is_enabled()) + return true; + + const auto type = m_gizmos.get_current_type(); + return type != GLGizmosManager::FdmSupports && type != GLGizmosManager::Seam && + type != GLGizmosManager::MmSegmentation && type != GLGizmosManager::FuzzySkin; +} + +Vec3d GLCanvas3D::get_camera_pan_anchor(Camera& camera, ECameraNavigationType navigation_type, + const Vec2d& screen_position) const +{ + // Orthographic panning has the same scale at every depth, so no raycast is needed. + if (camera.get_type() != Camera::EType::Perspective) + return camera.get_target(); + + // Orca: Reject non-finite anchors and points behind the camera before their depth is + // allowed to scale a perspective pan. + const Vec3d camera_position = camera.get_position(); + const Vec3d camera_forward = camera.get_dir_forward(); + const auto is_valid_anchor = [&camera_position, &camera_forward](const Vec3d& anchor) { + return anchor.allFinite() && (anchor - camera_position).dot(camera_forward) > EPSILON; + }; + + // Orca: Prefer the nearest visible bed or volume surface and exclude gizmos and + // selected-volume picking priority from navigation depth selection. + const ClippingPlane clipping_plane = get_raycaster_clipping_plane(); + const bool bed_visible = is_bed_visible(); + const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(screen_position, camera, &clipping_plane, + bed_visible ? SceneRaycaster::EHitMode::SceneOnly : SceneRaycaster::EHitMode::VolumesOnly); + if (hit.is_valid()) { + const Vec3d hit_position = hit.position.cast(); + if (is_valid_anchor(hit_position)) + return hit_position; + } + + // Orca: When the cursor is just outside the visible plate, use the point under it on the active + // plate plane. Using the plate center here would give it a different perspective depth. + PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + // Orca: An almost edge-on perspective makes intersection depth extremely sensitive to + // the cursor's vertical position. Use the stable orbit depth around horizontal views. + static constexpr double min_plate_plane_forward_z = 0.05; + if (bed_visible && std::abs(camera_forward.z()) >= min_plate_plane_forward_z && + current_plate != nullptr && current_plate->get_bounding_box().defined) { + Vec3d ray_origin; + Vec3d ray_direction; + CameraUtils::ray_from_screen_pos(camera, screen_position, ray_origin, ray_direction); + const double z_direction = ray_direction.z(); + if (ray_origin.allFinite() && ray_direction.allFinite() && std::abs(z_direction) > EPSILON) { + const double plate_z = current_plate->get_bounding_box().center().z(); + const double distance = (plate_z - ray_origin.z()) / z_direction; + const Vec3d plate_position = ray_origin + distance * ray_direction; + const double eye_depth = (plate_position - camera_position).dot(camera_forward); + if (distance >= 0.0 && is_valid_anchor(plate_position) && + eye_depth >= camera.get_near_z() && eye_depth <= camera.get_far_z()) + return plate_position; + } + } + + // Orca: Near-horizontal rays and points outside the scene depth use the orbit reference point. + const std::optional orbit_target = get_camera_orbit_target(navigation_type); + return orbit_target.has_value() && is_valid_anchor(*orbit_target) ? *orbit_target : camera.get_target(); +} + // While it looks like we can call // this->reload_scene(true, true) // the two functions are quite different: diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 75820694b0..663b72a1d3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -338,6 +338,8 @@ class GLCanvas3D int move_volume_idx{ -1 }; bool move_requires_threshold{ false }; Point move_start_threshold_position_2D{ Invalid_2D_Point }; + // Orca: Keep the world-space point selected at the start of a mouse pan. + std::optional camera_pan_anchor; }; bool dragging{ false }; @@ -346,7 +348,12 @@ class GLCanvas3D Drag drag; bool ignore_right_up; - void set_start_position_2D_as_invalid() { drag.start_position_2D = Drag::Invalid_2D_Point; } + // Orca: The screen-space start and world-space anchor describe the same pan session. + // Invalidating one must invalidate the other so a new drag cannot reuse stale depth. + void set_start_position_2D_as_invalid() { + drag.start_position_2D = Drag::Invalid_2D_Point; + drag.camera_pan_anchor.reset(); + } void set_start_position_3D_as_invalid() { drag.start_position_3D = Drag::Invalid_3D_Point; } void set_move_start_threshold_position_2D_as_invalid() { drag.move_start_threshold_position_2D = Drag::Invalid_2D_Point; } @@ -549,6 +556,8 @@ private: bool m_fps_overlay_tick{ false }; LayersEditing m_layers_editing; Mouse m_mouse; + // Orca: Gesture pans have their own lifecycle and stable world-space anchor. + std::optional m_gesture_pan_anchor; GLGizmosManager m_gizmos; //BBS: GUI refactor: GLToolbar mutable GLToolbar m_main_toolbar; @@ -1311,6 +1320,7 @@ private: bool _init_collapse_toolbar(); bool _set_current(); + bool _set_shown_canvas_current(); void _resize(unsigned int w, unsigned int h); //BBS: add part plate related logic @@ -1406,6 +1416,20 @@ private: // Convert the screen space coordinate to world coordinate on the bed. Vec3d _mouse_to_bed_3d(const Point& mouse_pos); + // Orca: Navigation type selects the legacy pivot policy used when no visible surface is hit. + enum class ECameraNavigationType : unsigned char + { + Mouse, + Gesture + }; + + // Orca: These helpers keep clipping, orbit pivots, and perspective-pan depth selection consistent. + ClippingPlane get_raycaster_clipping_plane() const; + bool is_bed_visible() const; + std::optional get_camera_orbit_target(ECameraNavigationType navigation_type) const; + Vec3d get_camera_pan_anchor(Camera& camera, ECameraNavigationType navigation_type, + const Vec2d& screen_position) const; + void _start_timer() { m_timer.Start(100, wxTIMER_CONTINUOUS); } void _stop_timer() { m_timer.Stop(); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 29bab18aeb..1a153ed4fb 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -863,8 +863,11 @@ void GUI_App::post_init() mainframe->Freeze(); #endif plater_->canvas3D()->enable_render(false); - mainframe->select_tab(TAB_ID_PREPARE); + mainframe->select_prepare_for_gl_init(); plater_->select_view_3D("3D"); + // The first render happens before the queued new_project() sets the same view. + plater_->get_camera().select_view("topfront"); + plater_->get_camera().requires_zoom_to_bed = true; //BBS init the opengl resource here if (!plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen() || !plater_->canvas3D()->make_current_for_postinit()) { @@ -900,10 +903,10 @@ void GUI_App::post_init() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test"; } } - if (is_editor()) - mainframe->select_tab(TAB_ID_HOME); - if (app_config->get("default_page") == "1") + if (starts_on_prepare()) mainframe->select_tab(TAB_ID_PREPARE); + else if (is_editor()) + mainframe->select_tab(TAB_ID_HOME); #ifndef __linux__ mainframe->Thaw(); #endif @@ -912,6 +915,7 @@ void GUI_App::post_init() plater_->trigger_restore_project(1); //#endif + mainframe->prebuild_pages_when_idle(); //BBS: remove GCodeViewer as seperate APP logic /*if (this->init_params->start_as_gcodeviewer) { @@ -1926,9 +1930,9 @@ bool GUI_App::hot_reload_network_plugin() m_device_manager->add_user_subscribe(); } - if (mainframe && mainframe->m_monitor) { - mainframe->m_monitor->update_network_version_footer(); - mainframe->m_monitor->set_default(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) { + monitor->update_network_version_footer(); + monitor->set_default(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": reset monitor panel"; } @@ -3419,14 +3423,16 @@ bool GUI_App::on_init_inner() } BOOST_LOG_TRIVIAL(info) << "create the main window"; mainframe = new MainFrame(); - // hide settings tabs after first Layout if (is_editor()) { - mainframe->select_tab(TAB_ID_HOME); + if (starts_on_prepare()) { + mainframe->select_tab(TAB_ID_PREPARE); + } else { + mainframe->select_tab(TAB_ID_HOME); + } } sidebar().obj_list()->init(); //sidebar().aux_list()->init_auxiliary(); - mainframe->m_project->init_auxiliary(); // update_mode(); // !!! do that later SetTopWindow(mainframe); @@ -4137,13 +4143,13 @@ void GUI_App::select_machine(const std::string& agent_id) // Use MonitorPanel::select_machine() to trigger full selection flow // This reuses existing logic for machine switching (UI updates, callbacks, etc.) - if (mainframe && mainframe->m_monitor) { - mainframe->m_monitor->select_machine(dev_id); + if (MonitorPanel* monitor = MonitorPanel::if_built()) { + monitor->select_machine(dev_id); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": triggered select_machine for dev_id=" << dev_id; - } else { - // Fallback if MonitorPanel not available - m_device_manager->set_selected_machine(dev_id); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fallback set_selected_machine dev_id=" << dev_id; + } else if (m_device_manager->set_selected_machine(dev_id)) { + // The Device tab's own state is set when the tab is built. + MonitorPanel::on_machine_selected(m_device_manager->get_selected_machine()); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": set_selected_machine dev_id=" << dev_id; } } @@ -4667,6 +4673,7 @@ void GUI_App::recreate_GUI(const wxString &msg_name) //BBS: trigger restore project logic here, and skip confirm plater_->trigger_restore_project(1); + mainframe->prebuild_pages_when_idle(); // #ys_FIXME_delete_after_testing Do we still need this ? // CallAfter([]() { @@ -5008,7 +5015,8 @@ void GUI_App::get_login_info(const std::string& provider/* = ORCA_CLOUD_PROVIDER wxString strJS = wxString::Format("window.postMessage(%s)", from_u8(logout_cmd)); GUI::wxGetApp().run_script(strJS); } - mainframe->m_webview->SetLoginPanelVisibility(true); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SetLoginPanelVisibility(true); } } @@ -5140,9 +5148,9 @@ std::string GUI_App::handle_web_request(std::string cmd) "homepage_bambu_login_or_register", }; if (app_config->get_stealth_mode() && stealth_blocked_info_commands.count(command_str)) { - CallAfter([this] { - if (mainframe && mainframe->m_webview) - mainframe->m_webview->SendCloudProvidersInfo(); + CallAfter([] { + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendCloudProvidersInfo(); }); return ""; } @@ -5156,8 +5164,8 @@ std::string GUI_App::handle_web_request(std::string cmd) if (dlg.ShowModal() == wxID_OK) { app_config->set_bool("stealth_mode", false); app_config->save(); - if (mainframe && mainframe->m_webview) - mainframe->m_webview->SendCloudProvidersInfo(); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendCloudProvidersInfo(); // Continue with login if (command_str == "homepage_login_or_register") this->request_login(true); @@ -5238,8 +5246,8 @@ std::string GUI_App::handle_web_request(std::string cmd) } else if (command_str.compare("get_recent_projects") == 0) { if (mainframe) { - if (mainframe->m_webview) { - mainframe->m_webview->SendRecentList(INT_MAX); + if (WebViewPanel* home = WebViewPanel::if_built()) { + home->SendRecentList(INT_MAX); } } } @@ -7772,8 +7780,8 @@ void GUI_App::on_stealth_mode_enter() BOOST_LOG_TRIVIAL(info) << "logout: on_stealth_mode_enter"; request_user_logout(ORCA_CLOUD_PROVIDER); request_user_logout(BBL_CLOUD_PROVIDER); - if (mainframe && mainframe->m_webview) { - mainframe->m_webview->SendCloudProvidersInfo(); + if (WebViewPanel* home = WebViewPanel::if_built()) { + home->SendCloudProvidersInfo(); } } @@ -8183,6 +8191,24 @@ ConfigOptionMode GUI_App::get_saved_mode() return saved_mode_from_string(app_config->get("user_mode")); } +bool GUI_App::starts_on_prepare() const +{ + return app_config->get("default_page") == "1"; +} + +int GUI_App::input_idle_ms() const +{ + return int(std::chrono::duration_cast(std::chrono::steady_clock::now() - m_last_input).count()); +} + +// Every wxCommandEvent claims the user-input category, so only real mouse and key events count. +int GUI_App::FilterEvent(wxEvent& event) +{ + if (!event.IsCommandEvent() && (event.GetEventCategory() & wxEVT_CATEGORY_USER_INPUT)) + m_last_input = std::chrono::steady_clock::now(); + return Event_Skip; +} + ConfigOptionMode GUI_App::get_mode() { return app_config->get_bool("developer_mode") ? comDevelop : get_saved_mode(); @@ -8234,9 +8260,10 @@ void GUI_App::update_mode() mainframe->m_param_panel->update_mode(); if (mainframe->m_param_dialog) mainframe->m_param_dialog->panel()->update_mode(); - if (mainframe->m_printer_view) - mainframe->m_printer_view->update_mode(); - mainframe->m_webview->update_mode(); + if (PrinterWebView* view = PrinterWebView::if_built()) + view->update_mode(); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->update_mode(); #ifdef _MSW_DARK_MODE if (!wxGetApp().tabs_as_menu()) @@ -8254,9 +8281,10 @@ void GUI_App::update_mode() } void GUI_App::update_internal_development() { - mainframe->m_webview->update_mode(); - if (mainframe->m_printer_view) - mainframe->m_printer_view->update_mode(); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->update_mode(); + if (PrinterWebView* view = PrinterWebView::if_built()) + view->update_mode(); } void GUI_App::show_ip_address_enter_dialog(wxString title) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 2d55c5e96c..e888a52b06 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_GUI_App_hpp_ #define slic3r_GUI_App_hpp_ +#include #include #include #include @@ -248,6 +249,7 @@ private: bool m_app_conf_exists{ false }; EAppMode m_app_mode{ EAppMode::Editor }; bool m_is_recreating_gui{ false }; + std::chrono::steady_clock::time_point m_last_input{ std::chrono::steady_clock::now() }; #ifdef __linux__ bool m_opengl_initialized{ false }; #endif @@ -387,6 +389,11 @@ public: bool is_editor() const { return m_app_mode == EAppMode::Editor; } bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; } bool is_recreating_gui() const { return m_is_recreating_gui; } + // Milliseconds since the last mouse or keyboard event the app processed. + int input_idle_ms() const; + int FilterEvent(wxEvent& event) override; + // The Preferences "Default page" choice, stored as its index: 0 Home, 1 Prepare. + bool starts_on_prepare() const; std::string logo_name() const { return is_editor() ? "OrcaSlicer" : "OrcaSlicer-gcodeviewer"; } bool is_closing() const { return m_is_closing.load(std::memory_order_acquire); } diff --git a/src/slic3r/GUI/HMSPanel.cpp b/src/slic3r/GUI/HMSPanel.cpp index cd3d35bfc8..c2a8314bd5 100644 --- a/src/slic3r/GUI/HMSPanel.cpp +++ b/src/slic3r/GUI/HMSPanel.cpp @@ -122,7 +122,8 @@ HMSNotifyItem::HMSNotifyItem(const std::string& dev_id, wxWindow *parent, DevHMS m_hms_content->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& e) { wxCommandEvent evt(EVT_ALREADY_READ_HMS); evt.SetString(long_error_code); - wxPostEvent(wxGetApp().mainframe->m_monitor, evt); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + wxPostEvent(monitor, evt); if (!m_url.empty()) wxLaunchDefaultBrowser(m_url); }); diff --git a/src/slic3r/GUI/IdleScheduler.cpp b/src/slic3r/GUI/IdleScheduler.cpp new file mode 100644 index 0000000000..2b71a9f8e6 --- /dev/null +++ b/src/slic3r/GUI/IdleScheduler.cpp @@ -0,0 +1,100 @@ +#include "IdleScheduler.hpp" + +#include + +#include + +#include "libslic3r/Utils.hpp" + +#ifdef _WIN32 +#include +#endif + +namespace Slic3r { namespace GUI { + +namespace { + +// The timer's period while waiting for the user to go quiet. +constexpr int tick_ms = 250; +// Input-free time before a slice may start, about a double-click interval, so a user +// mid-gesture is left alone. +constexpr int quiet_ms = 500; +// Budget of one slice. The next slice is a timer message, so paint, timers and input +// queued meanwhile are handled first; a click waits at most a slice plus the unit that +// overran it. +constexpr int slice_ms = 40; +// On GTK a due timer runs ahead of repaints and posted events, so the next slice waits a few ms. +#ifdef __WXGTK__ +constexpr int next_slice_ms = 5; +#else +constexpr int next_slice_ms = 0; +#endif + +// True when unhandled keyboard, button, touch or pen input is queued; only Windows can ask. +bool input_pending() +{ +#ifdef _WIN32 + // Windows synthesizes a mouse move whenever a window appears under the cursor, so those + // do not count. + return (GetQueueStatus(QS_INPUT & ~QS_MOUSEMOVE) >> 16) != 0; +#else + return false; +#endif +} + +} // namespace + +IdleScheduler::IdleScheduler(std::function input_idle_ms) : m_input_idle_ms(std::move(input_idle_ms)) +{ + m_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) { tick(); }); +} + +void IdleScheduler::start() +{ + if (!m_timer.IsRunning()) + m_timer.Start(tick_ms); +} + +void IdleScheduler::stop() +{ + m_timer.Stop(); +} + +void IdleScheduler::tick() +{ + // A unit that pumps the event loop lets the timer fire inside its own slice. + if (m_in_slice) + return; + if (!m_queue.pending()) { + stop(); + return; + } + if (m_input_idle_ms() < quiet_ms || input_pending()) { + start(); + return; + } + const auto now_ms = [] { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + }; + const char* const fn = __FUNCTION__; + m_in_slice = true; + PrebuildQueue::Slice slice; + { + ScopeGuard in_slice([this] { m_in_slice = false; }); + slice = m_queue.run_slice(slice_ms, now_ms, input_pending, [fn](const std::string& name, long long ms) { + BOOST_LOG_TRIVIAL(debug) << fn << ": " << name << ": unit took " << ms << " ms"; + }); + } + if (slice.completed) + BOOST_LOG_TRIVIAL(info) << fn << ": task complete: " << slice.name << ", " << slice.units << " unit(s) in " << slice.ms << " ms this slice"; + else + BOOST_LOG_TRIVIAL(debug) << fn << ": " << slice.name << ": " << slice.units << " unit(s) in " << slice.ms << " ms, yielding"; + if (!slice.remaining) + stop(); + else if (input_pending()) + m_timer.Start(tick_ms); + else + m_timer.StartOnce(next_slice_ms); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/IdleScheduler.hpp b/src/slic3r/GUI/IdleScheduler.hpp new file mode 100644 index 0000000000..7c31e9414c --- /dev/null +++ b/src/slic3r/GUI/IdleScheduler.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include +#include + +#include "PrebuildQueue.hpp" + +namespace Slic3r { namespace GUI { + +// Runs the queue's units while the user is idle. Once the user has been idle long enough, +// slices run one per timer message until the work is done or input arrives, so the event +// loop handles what it has between slices. Main thread only. +class IdleScheduler +{ +public: + // input_idle_ms reports how long ago the user last touched the mouse or keyboard. + explicit IdleScheduler(std::function input_idle_ms); + + void add(LazyBase& task) { m_queue.add(task); } + void clear() { m_queue.clear(); } + // The task names in queue order, for a log line. + std::string names() const { return m_queue.names(); } + + // Arms the timer; it stops itself once no task is pending, so call again when a task + // appears. + void start(); + void stop(); + +private: + void tick(); + + PrebuildQueue m_queue; + wxTimer m_timer; + std::function m_input_idle_ms; + bool m_in_slice{ false }; +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Lazy.cpp b/src/slic3r/GUI/Lazy.cpp new file mode 100644 index 0000000000..2a1a74510a --- /dev/null +++ b/src/slic3r/GUI/Lazy.cpp @@ -0,0 +1,27 @@ +#include "Lazy.hpp" + +#include +#include +#include + +namespace Slic3r { namespace GUI { + +LazyBase::OnDemandBuild::OnDemandBuild(const LazyBase& lazy) : m_lazy(lazy), m_started(std::chrono::steady_clock::now()) +{ + // The unit tests have no app. + if (wxTheApp != nullptr) + m_busy = std::make_unique(); +} + +LazyBase::OnDemandBuild::~OnDemandBuild() +{ + BOOST_LOG_TRIVIAL(info) << "Lazy::ensure: built " << m_lazy.name() << " on demand, " << m_units << " unit(s) in " + << std::chrono::duration_cast(std::chrono::steady_clock::now() - m_started).count() << " ms"; +} + +void LazyBase::log_null_factory(const std::string& name) +{ + BOOST_LOG_TRIVIAL(error) << "Lazy::build_step: the factory for " << name << " returned null"; +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Lazy.hpp b/src/slic3r/GUI/Lazy.hpp new file mode 100644 index 0000000000..33e092d887 --- /dev/null +++ b/src/slic3r/GUI/Lazy.hpp @@ -0,0 +1,198 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "StagedBuild.hpp" + +class wxBusyCursor; + +// Deferred construction of one object. Lazy holds the factory and builds the object on +// demand or, unit by unit, from an IdleScheduler; LazyBase is what the scheduler sees of +// it; LazyInstance gives a type with one such object app-wide static access to it. +// docs/HLSD/deferred-page-construction.md covers the design. + +namespace Slic3r { namespace GUI { + +template class LazyInstance; + +// A prebuild task: what PrebuildQueue sees of a Lazy. A type with its own unit sequence +// implements it directly. +class LazyBase +{ +public: + virtual ~LazyBase() = default; + + // Labels the object in logs. + virtual const std::string& name() const = 0; + virtual bool built() const = 0; + // Whether the idle prebuild has work here. + virtual bool pending() const { return !built(); } + // Runs one unit and returns true while more remain. + virtual bool build_step() = 0; + // Position in the idle queue: lower builds first, negative is never prebuilt. + virtual int prebuild_order() const = 0; + +protected: + // Busy cursor around a build the user is waiting on, and a log line after it. + class OnDemandBuild + { + public: + explicit OnDemandBuild(const LazyBase& lazy); + ~OnDemandBuild(); + void unit() { ++m_units; } + + private: + const LazyBase& m_lazy; + std::unique_ptr m_busy; + std::chrono::steady_clock::time_point m_started; + int m_units{ 0 }; + }; + + static void log_null_factory(const std::string& name); +}; + +// Holds an object a factory makes on the first build_step() or ensure(), followed by one +// StagedBuild step per unit if the type has them, and keeps callbacks until the object is +// complete. The object's parent owns it, not the holder. A LazyInstance type is registered +// for its statics; any other type is reached only through the holder. +template +class Lazy : public LazyBase +{ +public: + using Factory = std::function; + + Lazy(std::string name, int order, Factory make) : m_name(std::move(name)), m_order(order), m_make(std::move(make)) + { + if constexpr (registered()) + LazyInstance::s_lazy = this; + } + ~Lazy() override + { + if constexpr (registered()) + if (LazyInstance::s_lazy == this) + LazyInstance::s_lazy = nullptr; + } + Lazy(const Lazy&) = delete; + Lazy& operator=(const Lazy&) = delete; + + // Null until completely built. + T* get() const { return built() ? m_object : nullptr; } + + // Builds whatever is left now and returns the object, null if the factory returned null + // or a nested call finds the object mid-build. + T* ensure() + { + if (!built() && !m_building) { + OnDemandBuild build(*this); + do + build.unit(); + while (build_step()); + } + return get(); + } + + // Runs fn on the object now if it is built, otherwise once it is. + void when_built(std::function fn) + { + if (built()) + fn(*m_object); + else + m_deferred.push_back(std::move(fn)); + } + + const std::string& name() const override { return m_name; } + // Read by a worker thread through the statics; the last write in build_step() releases it. + bool built() const override { return m_complete.load(std::memory_order_acquire); } + bool pending() const override { return !built() && !m_failed; } + int prebuild_order() const override { return m_order; } + + // The factory is the first unit, then one StagedBuild step each. A unit that pumps the + // event loop cannot re-enter; a nested call does nothing. + bool build_step() override + { + if (built() || m_building || m_failed) + return false; + m_building = true; + try { + if (m_object == nullptr) + m_object = m_make(); + else + step(); + } catch (...) { + m_building = false; + throw; + } + m_building = false; + if (m_object == nullptr) { + m_failed = true; + log_null_factory(m_name); + return false; + } + if (steps_remain()) + return true; + m_complete.store(true, std::memory_order_release); + for (auto& fn : m_deferred) + fn(*m_object); + m_deferred.clear(); + return false; + } + +private: + static constexpr bool registered() { return std::is_base_of_v, T>; } + static constexpr bool staged() { return std::is_base_of_v; } + + bool steps_remain() const + { + if constexpr (staged()) + return !m_object->built(); + else + return false; + } + void step() + { + if constexpr (staged()) + m_object->build_step(); + } + + std::string m_name; + int m_order; + Factory m_make; + T* m_object{ nullptr }; + std::atomic m_complete{ false }; + bool m_building{ false }; + bool m_failed{ false }; + std::vector> m_deferred; +}; + +// Mixin for a type with one lazily built instance in the app. The statics reach that +// instance through its Lazy holder (a recreated MainFrame's holder replaces the old +// frame's). +template +class LazyInstance +{ +public: + // Null until completely built. + static Self* if_built() { return s_lazy ? s_lazy->get() : nullptr; } + // Builds the object if needed; null while no holder exists or the holder has no + // object. + static Self* ensure() { return s_lazy ? s_lazy->ensure() : nullptr; } + // Runs fn on the object now if it is built, otherwise once it is. + static void when_built(std::function fn) + { + if (s_lazy) + s_lazy->when_built(std::move(fn)); + } + +private: + friend class Lazy; + inline static Lazy* s_lazy{ nullptr }; +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/LazyPage.cpp b/src/slic3r/GUI/LazyPage.cpp new file mode 100644 index 0000000000..d14947bd80 --- /dev/null +++ b/src/slic3r/GUI/LazyPage.cpp @@ -0,0 +1,14 @@ +#include "LazyPage.hpp" + +#include "GUI_App.hpp" + +namespace Slic3r { namespace GUI { + +void apply_dark_ui_to_lazy_panel(wxWindow* panel) +{ +#ifdef _MSW_DARK_MODE + wxGetApp().UpdateDarkUIWin(panel); +#endif +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/LazyPage.hpp b/src/slic3r/GUI/LazyPage.hpp new file mode 100644 index 0000000000..fc85f69285 --- /dev/null +++ b/src/slic3r/GUI/LazyPage.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "Lazy.hpp" + +namespace Slic3r { namespace GUI { + +// Applies the app's dark-mode pass to a panel built after the frame's own pass ran. +void apply_dark_ui_to_lazy_panel(wxWindow* panel); + +// A page whose panel is built the first time the page is shown, or earlier by an +// IdleScheduler; nothing builds while the frame is still hidden. The Lazy side holds the +// panel and, for a LazyInstance panel type, registers it for the type's statics. +template +class LazyPage : public wxPanel, public Lazy +{ +public: + using Factory = std::function; + + LazyPage(wxWindow* parent, std::string name, int order, Factory make = [](wxWindow* parent) { return new Panel(parent); }) + : wxPanel(parent), Lazy(std::move(name), order, [this, make = std::move(make)] { + Panel* panel = make(this); + GetSizer()->Add(panel, 1, wxEXPAND); + // Hidden while its page is, as an inserted page would be. + if (!IsShown()) + panel->Hide(); + return panel; + }) + { + SetSizer(new wxBoxSizer(wxVERTICAL)); + // Shown by the book when its tab is selected. + Hide(); + this->when_built([this](Panel& panel) { + apply_dark_ui_to_lazy_panel(&panel); + Layout(); + }); + } + + // The parent book currently lists this page. + bool in_book() const + { + auto* book = dynamic_cast(GetParent()); + return book != nullptr && book->FindPage(this) != wxNOT_FOUND; + } + + // Pending only while the tab is in the book. + bool pending() const override { return in_book() && Lazy::pending(); } + + // Forwarded so the panel's own Show() override stays its activation hook; wx calls this + // virtual from the book's ShowWithEffect() only for wxSHOW_EFFECT_NONE, the default. + bool Show(bool show = true) override + { + const bool changed = wxPanel::Show(show); + if (show) { + // The book shows its first page as it is inserted, before startup has chosen the + // start page, so a hidden frame builds nothing; MainFrame::Show() completes it. + if (this->built() || wxGetTopLevelParent(this)->IsShown()) { + if (Panel* panel = this->ensure()) + panel->Show(true); + // The sizer skipped the panel while the book kept it hidden. + Layout(); + } + } else if (Panel* panel = this->get()) { + panel->Show(false); + } + return changed; + } +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ca0ef95a76..ec4428a32a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -317,7 +317,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ // BBS , m_recent_projects(18) , m_settings_dialog(this) - , diff_dialog(this) + , m_idle([] { return wxGetApp().input_idle_ms(); }) + , m_diff_dialog("compare_presets", 100, [this] { return make_diff_dialog(); }) { #ifdef __WXOSX__ set_miniaturizable(GetHandle()); @@ -737,9 +738,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ wxGetApp().persist_window_geometry(this, true); wxGetApp().persist_window_geometry(&m_settings_dialog, true); - // bind events from DiffDlg - - bind_diff_dialog(); } bool MainFrame::handle_global_shortcut(const KeyChord& chord) @@ -832,8 +830,10 @@ bool MainFrame::handle_global_shortcut(const KeyChord& chord) return true; } -void MainFrame::bind_diff_dialog() +DiffPresetDialog* MainFrame::make_diff_dialog() { + auto* dialog = new DiffPresetDialog(this); + auto get_tab = [](Preset::Type type) { Tab* null_tab = nullptr; for (Tab* tab : wxGetApp().tabs_list) @@ -842,23 +842,24 @@ void MainFrame::bind_diff_dialog() return null_tab; }; - auto transfer = [this, get_tab](Preset::Type type) { - get_tab(type)->transfer_options(diff_dialog.get_left_preset_name(type), - diff_dialog.get_right_preset_name(type), - diff_dialog.get_selected_options(type)); + auto transfer = [dialog, get_tab](Preset::Type type) { + get_tab(type)->transfer_options(dialog->get_left_preset_name(type), + dialog->get_right_preset_name(type), + dialog->get_selected_options(type)); }; - auto process_options = [this](std::function process) { - const Preset::Type diff_dlg_type = diff_dialog.view_type(); + auto process_options = [dialog](std::function process) { + const Preset::Type diff_dlg_type = dialog->view_type(); if (diff_dlg_type == Preset::TYPE_INVALID) { - for (const Preset::Type& type : diff_dialog.types_list() ) + for (const Preset::Type& type : dialog->types_list() ) process(type); } else process(diff_dlg_type); }; - diff_dialog.Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); }); + dialog->Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); }); + return dialog; } @@ -1186,8 +1187,9 @@ void MainFrame::update_edge_panels() void MainFrame::shutdown() { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter"; - if (m_project != nullptr) - m_project->shutdown(); + m_idle.stop(); + if (ProjectPanel* project = ProjectPanel::if_built()) + project->shutdown(); m_plugin_pages.shutdown(); if (m_plater != nullptr) m_plater->remove_dock_panes(); @@ -1314,19 +1316,6 @@ void MainFrame::show_option(bool show) } } -#ifdef SLIC3R_CAD -DesignPanel* MainFrame::ensure_design_panel() -{ - if (m_design_panel == nullptr && m_design_page != nullptr) { - wxBusyCursor busy; - m_design_panel = new DesignPanel(m_design_page); - m_design_page->GetSizer()->Add(m_design_panel, 1, wxEXPAND); - m_design_page->Layout(); - } - return m_design_panel; -} -#endif - void MainFrame::init_tabpanel() { // wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on // Windows 10 with multiple high resolution displays connected. @@ -1369,23 +1358,20 @@ void MainFrame::init_tabpanel() { // m_param_panel->OnActivate(); #ifdef SLIC3R_CAD else if (m_design_page != nullptr && panel == m_design_page) { - // Built on first activation, never at startup: the panel creates several hundred - // controls and its own GL canvas, which a user who does not open the tab should - // not pay for. - ensure_design_panel(); // Re-sync the Design bed to the active printer: the panel is built before the // printer profile is fully applied, so its bed must refresh on activation or the // grid (true bed) spills past the stale default bed quad. - m_design_panel->on_tab_shown(); + DesignPanel::ensure()->on_tab_shown(); } #endif - else if (panel == m_monitor) { + else if (panel == m_monitor_page) { //monitor } #ifdef SLIC3R_CAD // Any page that is not Design takes the Design status line down with it — see // DesignPanel::on_tab_hidden for why the popup does not follow the page on its own. - if (m_design_panel != nullptr && panel != m_design_page) m_design_panel->on_tab_hidden(); + if (DesignPanel* design = DesignPanel::if_built(); design != nullptr && panel != m_design_page) + design->on_tab_hidden(); #endif #ifndef __APPLE__ if (m_last_selected_tab == TAB_ID_PREPARE) { @@ -1401,13 +1387,14 @@ void MainFrame::init_tabpanel() { }); if (wxGetApp().is_editor()) { - m_webview = new WebViewPanel(m_tabpanel); + m_home_page = new LazyPage(m_tabpanel, TAB_ID_HOME, 10); + m_lazy_pages.push_back(m_home_page); Bind(EVT_LOAD_URL, [this](wxCommandEvent &evt) { wxString url = evt.GetString(); select_tab(TAB_ID_HOME); - m_webview->load_url(url); + WebViewPanel::ensure()->load_url(url); }); - m_tabpanel->AddPage(TAB_ID_HOME, m_webview, "", "tab_home_active"); + m_tabpanel->AddPage(TAB_ID_HOME, m_home_page, "", "tab_home_active"); m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); } @@ -1418,15 +1405,13 @@ void MainFrame::init_tabpanel() { wxGetApp().plater_ = m_plater; #ifdef SLIC3R_CAD - // Stand-in page for the Design tab. The real DesignPanel is built into it the first time - // the tab is selected (see the page-changed handler above), so nothing it constructs sits - // on the startup path. The experimental feature is off by default, and when it is off the - // page is never created, so the tab does not appear at all (the preference takes effect on - // the next start, like the other feature toggles). + // The experimental feature is off by default, and when it is off the page is never + // created, so the tab does not appear at all (the preference takes effect on the next + // start, like the other feature toggles). if (wxGetApp().is_enable_cad_feature()) { - m_design_page = new wxPanel(this); - m_design_page->SetSizer(new wxBoxSizer(wxVERTICAL)); - m_design_page->Hide(); + // Experimental and heavy enough that building it unasked would cost more than it saves. + m_design_page = new LazyPage(this, TAB_ID_DESIGN, -1); + m_lazy_pages.push_back(m_design_page); start_mcp_control_if_enabled(); // opens the MCP socket iff ORCA_CAD_MCP is set } #endif @@ -1434,33 +1419,41 @@ void MainFrame::init_tabpanel() { create_preset_tabs(); //BBS add pages - m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_monitor->SetBackgroundColour(*wxWHITE); - m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor, _L("Device"), "tab_monitor_active"); + m_monitor_page = new LazyPage(m_tabpanel, TAB_ID_MONITOR, 20); + m_lazy_pages.push_back(m_monitor_page); + m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor_page, _L("Device"), "tab_monitor_active"); - m_printer_view = new PrinterWebView(m_tabpanel); + m_printer_view_page = new LazyPage(m_tabpanel, TAB_ID_MONITOR_WEB, 50, [this](wxWindow* parent) { + auto* view = new PrinterWebView(parent); + if (!m_printer_url.empty()) + view->load_url(m_printer_url, m_printer_api_key); + return view; + }); + m_lazy_pages.push_back(m_printer_view_page); Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) { - wxString url = evt.GetString(); - wxString key = evt.GetAPIkey(); //select_tab(MainFrame::tpMonitor); - m_printer_view->load_url(url, key); + m_printer_url = evt.GetString(); + m_printer_api_key = evt.GetAPIkey(); + if (PrinterWebView* view = PrinterWebView::if_built()) + view->load_url(m_printer_url, m_printer_api_key); }); - m_printer_view->Hide(); + m_multi_machine_page = new LazyPage(m_tabpanel, TAB_ID_MULTI_DEVICE, 40); + m_lazy_pages.push_back(m_multi_machine_page); if (wxGetApp().is_enable_multi_machine()) { - m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_multi_machine->SetBackgroundColour(*wxWHITE); // TODO: change the bitmap - m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active"); + m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active"); } - m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_project->SetBackgroundColour(*wxWHITE); - m_tabpanel->AddPage(TAB_ID_PROJECT, m_project, _L("Project"), "tab_auxiliary_active"); + m_project_page = new LazyPage(m_tabpanel, TAB_ID_PROJECT, 60); + m_lazy_pages.push_back(m_project_page); + m_tabpanel->AddPage(TAB_ID_PROJECT, m_project_page, _L("Project"), "tab_auxiliary_active"); - m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_calibration->SetBackgroundColour(*wxWHITE); - m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), "tab_calibration_active"); + // show_device() removes this tab for printers without the Bambu device tab, and the page + // then never builds its panel. + m_calibration_page = new LazyPage(m_tabpanel, TAB_ID_CALIBRATION, 30); + m_lazy_pages.push_back(m_calibration_page); + m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration_page, _L("Calibration"), "tab_calibration_active"); // Plugin pages are appended after the built-in tabs; their ids are namespaced // (plugin..) so they can't collide with the built-in TAB_ID_* constants. @@ -1495,67 +1488,44 @@ void MainFrame::show_device(bool should_use_native) { // Remove the extra page before switching to any layout that shouldn't have it. if (!want_web_device_tab) { if ((idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR_WEB)) != wxNOT_FOUND) { - m_printer_view->Show(false); + m_printer_view_page->Show(false); m_tabpanel->RemovePage(idx); } } if (use_printer_agents) { - if (!m_monitor) { - m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_monitor->SetBackgroundColour(*wxWHITE); - } - - if (m_tabpanel->FindPage(m_monitor) == wxNOT_FOUND) { - if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) { - m_printer_view->Show(false); + if (!m_monitor_page->in_book()) { + if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) { + m_printer_view_page->Show(false); m_tabpanel->RemovePage(idx); } - m_monitor->Show(false); - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor, + m_monitor_page->Show(false); + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page, _L("Device"), "tab_monitor_active"); } - if (m_printer_view == nullptr) { - m_printer_view = new PrinterWebView(m_tabpanel); - Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) { - wxString url = evt.GetString(); - wxString key = evt.GetAPIkey(); - // select_tab(MainFrame::tpMonitor); - m_printer_view->load_url(url, key); - }); - } - if (wxGetApp().is_enable_multi_machine()) { - if (!m_multi_machine) { - m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_multi_machine->SetBackgroundColour(*wxWHITE); - } // TODO: change the bitmap - if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) { - m_multi_machine->Show(false); + if (!m_multi_machine_page->in_book()) { + m_multi_machine_page->Show(false); // Past the web Device tab when it is already there, so enabling multi-machine // later can't wedge this page between the two Device tabs. m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR_WEB, TAB_ID_MONITOR}), - TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active"); + TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active"); } } - if (!m_calibration) { - m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_calibration->SetBackgroundColour(*wxWHITE); - } - if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) { - m_calibration->Show(false); - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration, + if (!m_calibration_page->in_book()) { + m_calibration_page->Show(false); + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page, _L("Calibration"), "tab_calibration_active"); } if (want_web_device_tab) { - if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) { - m_printer_view->Show(false); + if ((idx = m_tabpanel->FindPage(m_printer_view_page)) == wxNOT_FOUND) { + m_printer_view_page->Show(false); // Immediately right of the native Device tab, not at the end of the tab bar. m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MONITOR_WEB, - m_printer_view, _L("Device (Web)"), "tab_monitor_active"); + m_printer_view_page, _L("Device (Web)"), "tab_monitor_active"); } else { m_tabpanel->SetPageText(idx, _L("Device (Web)")); } @@ -1567,48 +1537,37 @@ void MainFrame::show_device(bool should_use_native) { fit_tab_labels(); // ORCA on printer change m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above - + if (m_prebuild_started) + m_idle.start(); return; } if (should_use_native) { - if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND) { + if (m_monitor_page->in_book()) { fit_tab_labels(); // ORCA on printer change - same button layout return; } // Remove printer view - if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) { - m_printer_view->Show(false); + if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) { + m_printer_view_page->Show(false); m_tabpanel->RemovePage(idx); } - // Create/insert monitor page - if (!m_monitor) { - m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_monitor->SetBackgroundColour(*wxWHITE); - } - m_monitor->Show(false); - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor, + // Insert monitor page + m_monitor_page->Show(false); + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page, _L("Device"), "tab_monitor_active"); if (wxGetApp().is_enable_multi_machine()) { - if (!m_multi_machine) { - m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_multi_machine->SetBackgroundColour(*wxWHITE); - } // TODO: change the bitmap - m_multi_machine->Show(false); - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine, + m_multi_machine_page->Show(false); + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active"); } - if (!m_calibration) { - m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_calibration->SetBackgroundColour(*wxWHITE); - } - m_calibration->Show(false); + m_calibration_page->Show(false); // Last of the built-in tabs, but plugin tabs already sit past it — anchor rather than // append, so its position doesn't depend on the relayout() below running afterwards. - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration, + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page, _L("Calibration"), "tab_calibration_active"); #ifdef _MSW_DARK_MODE @@ -1616,37 +1575,30 @@ void MainFrame::show_device(bool should_use_native) { #endif // _MSW_DARK_MODE } else { - if (m_tabpanel->FindPage(m_printer_view) != wxNOT_FOUND) { + if (m_printer_view_page->in_book()) { fit_tab_labels(); // ORCA on printer change - same button layout return; } - if ((idx = m_tabpanel->FindPage(m_calibration)) != wxNOT_FOUND) { - m_calibration->Show(false); + if ((idx = m_tabpanel->FindPage(m_calibration_page)) != wxNOT_FOUND) { + m_calibration_page->Show(false); m_tabpanel->RemovePage(idx); } - if ((idx = m_tabpanel->FindPage(m_multi_machine)) != wxNOT_FOUND) { - m_multi_machine->Show(false); + if ((idx = m_tabpanel->FindPage(m_multi_machine_page)) != wxNOT_FOUND) { + m_multi_machine_page->Show(false); m_tabpanel->RemovePage(idx); } - if ((idx = m_tabpanel->FindPage(m_monitor)) != wxNOT_FOUND) { - m_monitor->Show(false); + if ((idx = m_tabpanel->FindPage(m_monitor_page)) != wxNOT_FOUND) { + m_monitor_page->Show(false); m_tabpanel->RemovePage(idx); } - if (m_printer_view == nullptr) { - m_printer_view = new PrinterWebView(m_tabpanel); - Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) { - wxString url = evt.GetString(); - wxString key = evt.GetAPIkey(); - // select_tab(MainFrame::tpMonitor); - m_printer_view->load_url(url, key); - }); - } - m_printer_view->Show(false); - m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view, + m_printer_view_page->Show(false); + m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view_page, _L("Device"), "tab_monitor_active"); } fit_tab_labels(); // ORCA on printer change m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above + if (m_prebuild_started) + m_idle.start(); } bool MainFrame::is_prepare_or_preview_tab() const @@ -2692,13 +2644,11 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) //BBS GUI refactor: remove unused layout new/dlg //if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog m_param_panel->msw_rescale(); - m_project->msw_rescale(); - if(m_monitor) - m_monitor->msw_rescale(); - if(m_multi_machine) - m_multi_machine->msw_rescale(); - if(m_calibration) - m_calibration->msw_rescale(); + // A panel mid-build gets the pass once it is complete. + ProjectPanel::when_built([](ProjectPanel& project) { project.msw_rescale(); }); + MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.msw_rescale(); }); + MultiMachinePage::when_built([](MultiMachinePage& multi_machine) { multi_machine.msw_rescale(); }); + CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.msw_rescale(); }); // BBS #if 0 @@ -2752,7 +2702,8 @@ void MainFrame::on_sys_color_changed() #endif #endif - diff_dialog.on_sys_color_changed(); + if (DiffPresetDialog* dialog = DiffPresetDialog::if_built()) + dialog->on_sys_color_changed(); // BBS m_tabpanel->Rescale(); @@ -2760,10 +2711,8 @@ void MainFrame::on_sys_color_changed() // update Plater wxGetApp().plater()->sys_color_changed(); - if(m_monitor) - m_monitor->on_sys_color_changed(); - if(m_calibration) - m_calibration->on_sys_color_changed(); + MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.on_sys_color_changed(); }); + CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.on_sys_color_changed(); }); // update Tabs for (auto tab : wxGetApp().tabs_list) tab->sys_color_changed(); @@ -3696,7 +3645,8 @@ void MainFrame::set_max_recent_count(int max) } wxGetApp().app_config->set_recent_projects(recent_projects); wxGetApp().app_config->save(); - m_webview->SendRecentList(-1); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendRecentList(-1); } } @@ -4040,23 +3990,56 @@ void MainFrame::select_tab(wxPanel* panel) select_tab(page_name); } +// Selects the Prepare page without the page-changed event, so the GL canvas is on screen +// and nothing else is built for the pass. +void MainFrame::select_prepare_for_gl_init() +{ + m_tabpanel->ChangeSelection(m_tabpanel->FindPageByName(TAB_ID_PREPARE)); +} + +// The book shows its first page as it is inserted, while the frame is hidden and nothing +// may build; the first show completes that page. +bool MainFrame::Show(bool show) +{ + const bool changed = DPIFrame::Show(show); + if (show && changed && m_tabpanel != nullptr) + if (wxWindow* page = m_tabpanel->GetCurrentPage()) + page->Show(true); + return changed; +} + +// A page out of the book stays registered and is passed over; a negative order is never +// registered. +void MainFrame::prebuild_pages_when_idle() +{ + m_idle.clear(); + if (m_param_panel) + m_idle.add(m_param_panel->settings_page_prebuild()); + for (LazyBase* page : m_lazy_pages) + if (page->prebuild_order() >= 0) + m_idle.add(*page); + m_idle.add(m_diff_dialog); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": prebuild queue: " << m_idle.names(); + m_idle.start(); + m_prebuild_started = true; +} + //BBS void MainFrame::jump_to_monitor(std::string dev_id) { - if(!m_monitor) - return; m_tabpanel->SelectPageByName(TAB_ID_MONITOR); if (!dev_id.empty()) { - ((MonitorPanel*)m_monitor)->select_machine(dev_id); + MonitorPanel::ensure()->select_machine(dev_id); } } void MainFrame::jump_to_multipage() { - if(!m_multi_machine) + if (!m_multi_machine_page->in_book()) return; m_tabpanel->SelectPageByName(TAB_ID_MULTI_DEVICE); - ((MultiMachinePage*)m_multi_machine)->jump_to_send_page(); + if (MultiMachinePage* page = m_multi_machine_page->ensure()) + page->jump_to_send_page(); } @@ -4105,8 +4088,8 @@ void MainFrame::request_select_tab(const wxString& id) } int MainFrame::get_calibration_curr_tab() { - if (m_calibration) - return m_calibration->get_tabpanel()->GetSelection(); + if (CalibrationPanel* calibration = CalibrationPanel::if_built()) + return calibration->get_tabpanel()->GetSelection(); return -1; } @@ -4215,7 +4198,8 @@ void MainFrame::add_to_recent_projects(const wxString& filename) recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); } wxGetApp().app_config->set_recent_projects(recent_projects); - m_webview->SendRecentList(0); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendRecentList(0); } } @@ -4331,7 +4315,8 @@ void MainFrame::open_recent_project(size_t file_id, wxString const & filename) recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); } wxGetApp().app_config->set_recent_projects(recent_projects); - m_webview->SendRecentList(-1); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendRecentList(-1); } } } @@ -4354,7 +4339,8 @@ void MainFrame::remove_recent_project(size_t file_id, wxString const &filename) recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); } wxGetApp().app_config->set_recent_projects(recent_projects); - m_webview->SendRecentList(-1); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendRecentList(-1); } void MainFrame::load_url(wxString url) @@ -4408,14 +4394,14 @@ bool MainFrame::is_printer_view() const { return m_tabpanel->GetSelectedPageName void MainFrame::refresh_plugin_tips() { - if (m_webview != nullptr) - m_webview->ShowNetpluginTip(); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->ShowNetpluginTip(); } void MainFrame::RunScript(wxString js) { - if (m_webview != nullptr) - m_webview->RunScript(js); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->RunScript(js); } void MainFrame::technology_changed() @@ -4524,7 +4510,8 @@ void MainFrame::update_side_preset_ui() //take off multi machine - if(m_multi_machine){m_multi_machine->clear_page();} + if (MultiMachinePage* multi_machine = MultiMachinePage::if_built()) + multi_machine->clear_page(); } void MainFrame::on_select_default_preset(SimpleEvent& evt) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 66f3b31263..f580de57e0 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -26,6 +26,8 @@ #include "Widgets/SideButton.hpp" #include "Widgets/SideMenuPopup.hpp" #include "FilamentGroupPopup.hpp" +#include "LazyPage.hpp" +#include "IdleScheduler.hpp" #include @@ -136,6 +138,13 @@ class MainFrame : public DPIFrame #endif bool m_loaded {false}; wxTimer* m_reset_title_text_colour_timer{ nullptr }; + IdleScheduler m_idle; + bool m_prebuild_started{ false }; + // Every LazyPage, in and out of the book; prebuild_pages_when_idle() registers them. + std::vector m_lazy_pages; + // The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built. + wxString m_printer_url; + wxString m_printer_api_key; wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; @@ -180,7 +189,7 @@ class MainFrame : public DPIFrame bool can_delete() const; bool can_delete_all() const; bool can_reslice() const; - void bind_diff_dialog(); + DiffPresetDialog* make_diff_dialog(); // BBS wxBoxSizer* create_side_tools(); @@ -377,6 +386,12 @@ public: void select_tab(wxPanel* panel); void select_tab(const wxString& id = wxString()); void request_select_tab(const wxString& id); + // post_init() needs the plater's canvas on screen to initialize OpenGL; this pass does not + // build the settings page. + void select_prepare_for_gl_init(); + // Builds the lazy tab pages while the user is idle; post_init() calls it once. + void prebuild_pages_when_idle(); + bool Show(bool show = true) override; int get_calibration_curr_tab(); void select_view(const std::string& direction); void update_shortcut_labels(); @@ -435,27 +450,21 @@ public: BBLTopbar* m_topbar{ nullptr }; PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } Plater* m_plater { nullptr }; + // Lazy pages, created once and kept for the frame's life; their panels are reached + // through LazyInstance's statics, and show_device() only moves pages in and out of the book. #ifdef SLIC3R_CAD - // The tab page is the placeholder; m_design_panel stays null until the tab is first - // selected, so everything the Design panel builds stays off the startup path. - wxPanel* m_design_page { nullptr }; - DesignPanel* m_design_panel { nullptr }; - // Builds the Design panel if it does not exist yet and returns it (null only before the - // placeholder page itself exists). Main thread only -- it creates wx controls. Both the - // tab activation and the MCP socket go through this: the socket is driven headlessly, - // with nobody to click the tab, and without this every verb would answer "not ready". - DesignPanel* ensure_design_panel(); + LazyPage* m_design_page { nullptr }; #endif //BBS: GUI refactor - MonitorPanel* m_monitor{ nullptr }; + LazyPage* m_monitor_page{ nullptr }; //AuxiliaryPanel* m_auxiliary{ nullptr }; - MultiMachinePage* m_multi_machine{ nullptr }; - ProjectPanel* m_project{ nullptr }; + LazyPage* m_multi_machine_page{ nullptr }; + LazyPage* m_project_page{ nullptr }; - CalibrationPanel* m_calibration{ nullptr }; - WebViewPanel* m_webview { nullptr }; - PrinterWebView* m_printer_view{nullptr}; + LazyPage* m_calibration_page{ nullptr }; + LazyPage* m_home_page { nullptr }; + LazyPage* m_printer_view_page{nullptr}; PluginPages m_plugin_pages; wxLogWindow* m_log_window { nullptr }; // BBS @@ -466,7 +475,8 @@ public: ParamsDialog* m_param_dialog{ nullptr }; //BBS SettingsDialog m_settings_dialog; - DiffPresetDialog diff_dialog; + // The Compare presets dialog, built on first use or at idle through its holder. + Lazy m_diff_dialog; wxWindow* m_plater_page{ nullptr }; PrintHostQueueDialog* m_printhost_queue_dlg; diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index 1a6d969988..7352ee3dd7 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -103,6 +103,7 @@ MonitorPanel::MonitorPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, : wxPanel(parent, id, pos, size, style), m_select_machine(SelectMachinePopup(this)) { + SetBackgroundColour(*wxWHITE); #ifdef __WINDOWS__ SetDoubleBuffered(true); #endif //__WINDOWS__ @@ -180,31 +181,36 @@ void MonitorPanel::init_tabpanel() auto title = m_tabpanel->GetPageText(m_tabpanel->GetSelection()); m_media_file_panel->SwitchStorage(title == _L("Storage")); } - page->SetFocus(); + // The first page is selected while the panel is built off screen. + if (page->IsShownOnScreen()) + page->SetFocus(); update_all(); }, m_tabpanel->GetId()); - //m_status_add_machine_panel = new AddMachinePanel(m_tabpanel); - m_status_info_panel = new StatusPanel(m_tabpanel); + m_status_info_panel = new StatusPanel(m_tabpanel); + // Queued before the page is added, since adding it selects it and runs the handler above, + // where built() must already be false. + add_build_steps_of(*m_status_info_panel); m_tabpanel->AddPage(m_status_info_panel, _L("Status"), true); + add_build_step([this] { + m_media_file_panel = new MediaFilePanel(m_tabpanel); + m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false); + }); + add_build_step([this] { + m_upgrade_panel = new UpgradePanel(m_tabpanel); + m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false); + }); + add_build_step([this] { + m_hms_panel = new HMSPanel(m_tabpanel); + m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false); - m_media_file_panel = new MediaFilePanel(m_tabpanel); - m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false); - //m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), false); + std::string network_ver = Slic3r::NetworkAgent::get_version(); + if (!network_ver.empty()) { + m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver)); + } - m_upgrade_panel = new UpgradePanel(m_tabpanel); - m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false); - - m_hms_panel = new HMSPanel(m_tabpanel); - m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false); - - std::string network_ver = Slic3r::NetworkAgent::get_version(); - if (!network_ver.empty()) { - m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver)); - } - - m_initialized = true; - show_status((int)MonitorStatus::MONITOR_NO_PRINTER); + show_status((int)MonitorStatus::MONITOR_NO_PRINTER); + }); } void MonitorPanel::set_default() @@ -288,19 +294,23 @@ void MonitorPanel::on_select_printer(wxCommandEvent& event) set_default(); update_all(); - MachineObject *obj_ = dev->get_selected_machine(); - if (obj_) { - obj_->last_cali_version = -1; - obj_->reset_pa_cali_history_result(); - obj_->reset_pa_cali_result(); - Sidebar &sidebar = GUI::wxGetApp().sidebar(); - sidebar.update_sync_status(obj_); - sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj_)); - } + on_machine_selected(dev->get_selected_machine()); Layout(); } +void MonitorPanel::on_machine_selected(MachineObject* obj) +{ + if (obj == nullptr) + return; + obj->last_cali_version = -1; + obj->reset_pa_cali_history_result(); + obj->reset_pa_cali_result(); + Sidebar& sidebar = GUI::wxGetApp().sidebar(); + sidebar.update_sync_status(obj); + sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj)); +} + void MonitorPanel::on_printer_clicked(wxMouseEvent &event) { auto mouse_pos = ClientToScreen(event.GetPosition()); @@ -331,7 +341,8 @@ void MonitorPanel::on_size(wxSizeEvent &event) void MonitorPanel::update_all() { - if (!m_initialized) + // Every page exists once the last build step has run. + if (!built()) return; NetworkAgent* m_agent = wxGetApp().getAgent(); @@ -412,16 +423,12 @@ void MonitorPanel::update_hms_tag() bool MonitorPanel::Show(bool show) { -#ifdef __APPLE__ - // Notebook::InsertPage() hides every page it appends, so this also runs while MainFrame is - // still constructing, before GUI_App::mainframe is assigned. Same guard as Plater::Show(). - if (wxGetApp().mainframe) - wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize()); -#endif - NetworkAgent* m_agent = wxGetApp().getAgent(); DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (show) { +#ifdef __APPLE__ + wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize()); +#endif start_update(); update_network_version_footer(); @@ -448,7 +455,7 @@ bool MonitorPanel::Show(bool show) void MonitorPanel::show_status(int status) { - if (!m_initialized) return; + if (!built()) return; if (last_status == status)return; if ((last_status & (int)MonitorStatus::MONITOR_CONNECTING) != 0) { NetworkAgent* agent = wxGetApp().getAgent(); diff --git a/src/slic3r/GUI/Monitor.hpp b/src/slic3r/GUI/Monitor.hpp index 9252b04559..94474f73b0 100644 --- a/src/slic3r/GUI/Monitor.hpp +++ b/src/slic3r/GUI/Monitor.hpp @@ -49,6 +49,7 @@ #include "slic3r/GUI/AmsWidgets.hpp" #include "Widgets/SideTools.hpp" #include "SelectMachinePop.hpp" +#include "Lazy.hpp" namespace Slic3r { namespace GUI { @@ -72,16 +73,16 @@ public: void msw_rescale(); }; -class MonitorPanel : public wxPanel +class MonitorPanel : public wxPanel, public StagedBuild, public LazyInstance { private: Tabbook* m_tabpanel{ nullptr }; wxSizer* m_main_sizer{ nullptr }; - StatusPanel* m_status_info_panel; - MediaFilePanel* m_media_file_panel; - UpgradePanel* m_upgrade_panel; - HMSPanel* m_hms_panel; + StatusPanel* m_status_info_panel{ nullptr }; + MediaFilePanel* m_media_file_panel{ nullptr }; + UpgradePanel* m_upgrade_panel{ nullptr }; + HMSPanel* m_hms_panel{ nullptr }; /* side tools */ SideTools* m_side_tools{nullptr}; @@ -96,7 +97,6 @@ private: wxBitmap m_arrow_img; int last_status; - bool m_initialized { false }; bool update_flag{false}; wxTimer* m_refresh_timer = nullptr; @@ -125,6 +125,8 @@ public: StatusPanel* get_status_panel() {return m_status_info_panel;}; void select_machine(std::string machine_sn); + // Resets the selected printer's calibration results and syncs the sidebar, without the Device tab. + static void on_machine_selected(MachineObject* obj); void on_timer(wxTimerEvent& event); void on_select_printer(wxCommandEvent& event); void on_printer_clicked(wxMouseEvent &event); diff --git a/src/slic3r/GUI/MultiMachineManagerPage.cpp b/src/slic3r/GUI/MultiMachineManagerPage.cpp index 792c23fb13..d5f9cbce8a 100644 --- a/src/slic3r/GUI/MultiMachineManagerPage.cpp +++ b/src/slic3r/GUI/MultiMachineManagerPage.cpp @@ -21,8 +21,9 @@ MultiMachineItem::MultiMachineItem(wxWindow* parent, MachineObject* obj) Bind(wxEVT_MOTION, &MultiMachineItem::OnMove, this); Bind(EVT_MULTI_DEVICE_VIEW, [obj](auto& e) { wxGetApp().mainframe->jump_to_monitor(obj->get_dev_id()); - if (wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()) { - wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play(); + MonitorPanel* monitor = MonitorPanel::if_built(); + if (monitor && monitor->get_status_panel()->get_media_play_ctrl()) { + monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play(); } }); wxGetApp().UpdateDarkUIWin(this); diff --git a/src/slic3r/GUI/MultiMachinePage.cpp b/src/slic3r/GUI/MultiMachinePage.cpp index dd67e099a0..e48e402405 100644 --- a/src/slic3r/GUI/MultiMachinePage.cpp +++ b/src/slic3r/GUI/MultiMachinePage.cpp @@ -11,6 +11,7 @@ namespace GUI { MultiMachinePage::MultiMachinePage(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) : wxPanel(parent, id, pos, size, style) { + SetBackgroundColour(*wxWHITE); init_tabpanel(); m_main_sizer = new wxBoxSizer(wxHORIZONTAL); m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxLEFT, 0); diff --git a/src/slic3r/GUI/MultiMachinePage.hpp b/src/slic3r/GUI/MultiMachinePage.hpp index 724eeed2a9..14d531e8ab 100644 --- a/src/slic3r/GUI/MultiMachinePage.hpp +++ b/src/slic3r/GUI/MultiMachinePage.hpp @@ -7,6 +7,7 @@ #include "MultiTaskManagerPage.hpp" #include "MultiMachineManagerPage.hpp" #include "Tabbook.hpp" +#include "Lazy.hpp" #include "wx/button.h" @@ -19,7 +20,7 @@ namespace GUI { #define PICK_LEFT_DEV_STATUS 250 #define PICK_DEVICE_MAX 6 -class MultiMachinePage : public wxPanel +class MultiMachinePage : public wxPanel, public LazyInstance { private: wxTimer* m_refresh_timer = nullptr; diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index 5a49df1280..b4fd81706a 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -290,7 +290,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c m_compare_btn = new ScalableButton(m_top_panel, wxID_ANY, "compare", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); m_compare_btn->SetToolTip(_L("Compare presets")); - m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { wxGetApp().mainframe->diff_dialog.show(); })); + m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { DiffPresetDialog::ensure()->show(); })); m_setting_btn = new ScalableButton(m_top_panel, wxID_ANY, "table", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); m_setting_btn->SetToolTip(_L("View all object's settings")); @@ -550,18 +550,36 @@ void ParamsPanel::clear_page() void ParamsPanel::OnActivate() { if (m_current_tab == NULL) - { - //the first time - BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print"); - // BBS: open/close tab - //m_current_tab = m_tab_print; - set_active_tab(m_tab_print ? m_tab_print : m_tab_filament); - } + select_default_tab(); Tab* cur_tab = dynamic_cast (m_current_tab); if (cur_tab) cur_tab->OnActivate(); } +void ParamsPanel::select_default_tab() +{ + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print"); + // BBS: open/close tab + //m_current_tab = m_tab_print; + set_active_tab(m_tab_print ? m_tab_print : m_tab_filament); +} + +bool ParamsPanel::SettingsPagePrebuild::built() const +{ + Tab* tab = dynamic_cast(m_panel.m_current_tab); + return tab != nullptr && !tab->page_build_pending(); +} + +bool ParamsPanel::SettingsPagePrebuild::build_step() +{ + if (m_panel.m_current_tab == nullptr) { + m_panel.select_default_tab(); + return !built(); + } + Tab* tab = dynamic_cast(m_panel.m_current_tab); + return tab != nullptr && tab->page_build_step(); +} + void ParamsPanel::OnToggled(wxCommandEvent& event) { if (m_mode_region && m_mode_region->GetId() == event.GetId()) { diff --git a/src/slic3r/GUI/ParamsPanel.hpp b/src/slic3r/GUI/ParamsPanel.hpp index 91bf3d2a7e..9f9264b480 100644 --- a/src/slic3r/GUI/ParamsPanel.hpp +++ b/src/slic3r/GUI/ParamsPanel.hpp @@ -30,6 +30,7 @@ #include "wxExtensions.hpp" #include "GUI_Utils.hpp" #include "Widgets/Button.hpp" +#include "Lazy.hpp" class ModeSwitchButton; class SwitchButton; @@ -121,6 +122,22 @@ class ParamsPanel : public wxPanel wxPanel* m_current_tab { nullptr }; + // Builds the selected page's option groups at idle; while no tab is selected yet, + // its first unit selects the default one. + class SettingsPagePrebuild : public LazyBase + { + public: + explicit SettingsPagePrebuild(ParamsPanel& panel) : m_panel(panel) {} + const std::string& name() const override { return m_name; } + bool built() const override; + bool build_step() override; + int prebuild_order() const override { return 0; } + + private: + ParamsPanel& m_panel; + std::string m_name{ "settings_page" }; + } m_settings_page_prebuild{ *this }; + bool m_has_object_config { false }; struct Highlighter @@ -149,6 +166,10 @@ class ParamsPanel : public wxPanel //clear the right page void clear_page(); void OnActivate(); + // The print tab, or the filament tab without one. + void select_default_tab(); + // The settings page's prebuild task. + LazyBase& settings_page_prebuild() { return m_settings_page_prebuild; } void set_active_tab(wxPanel*tab); bool is_active_and_shown_tab(wxPanel*tab); void update_mode(); diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 3493506228..e90b21e0ff 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -3555,8 +3555,11 @@ void PartPlate::render(const Transform3d& view_matrix, const Transform3d& projec shader->stop_using(); } - if (wxGetApp().show_plate_gridlines() && show_grid) + if (wxGetApp().show_plate_gridlines() && show_grid) { + glsafe(::glDepthMask(bottom ? GL_TRUE : GL_FALSE)); render_grid(bottom); + glsafe(::glDepthMask(GL_TRUE)); + } if (!hide_chrome && !bottom && m_selected && !force_background_color) { if (m_partplate_list) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d4386fbe93..607ec64174 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -7033,7 +7033,7 @@ struct Plater::priv void remove(size_t obj_idx); bool delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); //BBS void delete_all_objects_from_model(); - void reset(bool apply_presets_change = false); + void reset(bool apply_presets_change = false, bool reload_presets = true); void center_selection(); void drop_selection(); void mirror(Axis axis); @@ -7891,7 +7891,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) if (this->q->get_project_filename().IsEmpty() && this->q->is_empty_project()) { int skip_confirm = e.GetInt(); - this->q->new_project(skip_confirm, true); + // Skips the preset reload; trigger_restore_project()'s callers load the presets first. + this->q->new_project(skip_confirm, true, wxString(), false); } }); //wxPostEvent(this->q, wxCommandEvent{EVT_RESTORE_PROJECT}); @@ -10356,7 +10357,7 @@ void Plater::priv::delete_all_objects_from_model() model.plates_custom_gcodes.clear(); } -void Plater::priv::reset(bool apply_presets_change) +void Plater::priv::reset(bool apply_presets_change, bool reload_presets) { // TakeSnapshot below and load_current_presets() further down each re-evaluate the // aggregate dirty flag against a baseline that hasn't been reset yet, so they can toggle @@ -10411,8 +10412,8 @@ void Plater::priv::reset(bool apply_presets_change) // Same reason, one level up: the Design tab keeps the editable document, not the Model, so // clearing the recipe alone leaves the tab showing the previous project's feature tree — // and its next edit syncs that tree straight back into the new project. - if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr) - wxGetApp().mainframe->m_design_panel->clear_document(); + if (DesignPanel* design = DesignPanel::if_built()) + design->clear_document(); #endif assemble_view->get_canvas3d()->reset_explosion_ratio(); update(); @@ -10431,7 +10432,7 @@ void Plater::priv::reset(bool apply_presets_change) wxGetApp().preset_bundle->reset_project_embedded_presets(); if (apply_presets_change) wxGetApp().apply_keeped_preset_modifications(); - else + else if (reload_presets) wxGetApp().load_current_presets(false, false); //BBS @@ -13183,17 +13184,17 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e) // Pointer test, not a name lookup: in printer-agents mode this page is TAB_ID_MONITOR_WEB // while the native Device tab holds TAB_ID_MONITOR, and in legacy-web mode it holds // TAB_ID_MONITOR itself. - const bool selecting_web_device_tab = main_frame->m_printer_view && - main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view; + const bool selecting_web_device_tab = main_frame->m_printer_view_page && + main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view_page; if (selecting_web_device_tab) { // Use the selected discovered machine when the preset has no host. main_frame->load_printer_url(); } else if (new_name == TAB_ID_MONITOR && wxGetApp().preset_bundle != nullptr) { auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config; wxString url = from_u8(PrintHost::get_print_host_webui(&cfg)); - if (main_frame->m_printer_view && url.empty()) { + if (PrinterWebView* view = PrinterWebView::if_built(); view != nullptr && url.empty()) { // It's missing_connection page, reload so that we can replay the gif image - main_frame->m_printer_view->reload(); + view->reload(); } } } @@ -13924,8 +13925,8 @@ void Plater::priv::unbind_canvas_event_handlers() // The Design tab's viewport is a fourth GLCanvas3D on the same shared GL context, owned by // MainFrame rather than by us — same reach as reset() uses for clear_document(). Null until // the tab has been opened once, so most sessions skip it. - if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr) - wxGetApp().mainframe->m_design_panel->unbind_canvas_event_handlers(); + if (DesignPanel* design = DesignPanel::if_built()) + design->unbind_canvas_event_handlers(); #endif } @@ -13938,8 +13939,8 @@ void Plater::priv::reset_canvas_volumes() preview->get_canvas3d()->reset_volumes(); #ifdef SLIC3R_CAD - if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr) - wxGetApp().mainframe->m_design_panel->reset_canvas_volumes(); + if (DesignPanel* design = DesignPanel::if_built()) + design->reset_canvas_volumes(); #endif } @@ -15351,7 +15352,7 @@ Print& Plater::fff_print() { return p->fff_print; } const SLAPrint& Plater::sla_print() const { return p->sla_print; } SLAPrint& Plater::sla_print() { return p->sla_print; } -int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_name) +int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_name, bool reload_presets) { model().calib_pa_pattern.reset(nullptr); model().plates_custom_gcodes.clear(); @@ -15399,7 +15400,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_ // completes, so hold notifications until then to avoid firing on transient flips. ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(p->dirty_state); - reset(transfer_preset_changes); + reset(transfer_preset_changes, reload_presets); reset_project_dirty_after_save(); reset_project_dirty_initial_presets(); wxGetApp().update_saved_preset_from_current_preset(); @@ -16409,7 +16410,9 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i _obj->config.set_key_value("internal_solid_infill_line_width", new ConfigOptionFloatOrPercent(nozzle_diameter * 1.2f, false)); // ORCA: use the pattern parameter _obj->config.set_key_value("top_surface_pattern", new ConfigOptionEnum(pattern)); - _obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f)); + const auto *top_solid_flow = dynamic_cast(_obj->config.option("top_solid_infill_flow_ratio")); + _obj->config.set_key_value("top_solid_infill_flow_ratio", + new ConfigOptionFloatsNullable(top_solid_flow ? top_solid_flow->size() : 1, 1.0f)); _obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45)); _obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135)); _obj->config.set_key_value("center_of_surface_pattern", new ConfigOptionEnum(CenterOfSurfacePattern::Each_Surface)); @@ -17984,7 +17987,7 @@ void Plater::deselect_all() { p->deselect_all(); } void Plater::exit_gizmo() { p->exit_gizmo(); } void Plater::remove(size_t obj_idx) { p->remove(obj_idx); } -void Plater::reset(bool apply_presets_change) { p->reset(apply_presets_change); } +void Plater::reset(bool apply_presets_change, bool reload_presets) { p->reset(apply_presets_change, reload_presets); } void Plater::reset_with_confirm() { if (p->model.objects.empty() || MessageDialog(static_cast(this), _L("All objects will be removed, continue?"), @@ -20001,7 +20004,7 @@ int Plater::export_config_3mf(int plate_idx, Export3mfProgressFn proFn) void Plater::send_calibration_job_finished(wxCommandEvent & evt) { p->main_frame->request_select_tab(TAB_ID_CALIBRATION); - auto calibration_panel = p->main_frame->m_calibration; + CalibrationPanel* calibration_panel = CalibrationPanel::ensure(); if (calibration_panel) { auto curr_wizard = static_cast(calibration_panel->get_tabpanel()->GetPage(evt.GetInt())); wxCommandEvent event(EVT_CALIBRATION_JOB_FINISHED); @@ -20033,8 +20036,8 @@ void Plater::print_job_finished(wxCommandEvent &evt) dev->set_selected_machine(evt.GetString().ToStdString()); p->main_frame->request_select_tab(TAB_ID_MONITOR); - //jump to monitor and select device status panel - MonitorPanel* curr_monitor = p->main_frame->m_monitor; + // Selects the status page on a built Device tab; one built by the switch starts there. + MonitorPanel* curr_monitor = MonitorPanel::if_built(); if(curr_monitor) curr_monitor->get_tabpanel()->ChangeSelection(MonitorPanel::PrinterTab::PT_STATUS); } @@ -20774,8 +20777,8 @@ void Plater::update_print_error_info(int code, std::string msg, std::string extr if (p->m_send_to_sdcard_dlg) { p->m_send_to_sdcard_dlg->update_print_error_info(code, msg, extra); } - if (p->main_frame->m_calibration) - p->main_frame->m_calibration->update_print_error_info(code, msg, extra); + if (CalibrationPanel* calibration = CalibrationPanel::if_built()) + calibration->update_print_error_info(code, msg, extra); } wxString Plater::get_project_filename(const wxString& extension) const @@ -21068,7 +21071,8 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar { printer_name.Replace("Bambu Lab", "", false); wxString content; - bool device_page = (wxGetApp().mainframe == nullptr) && (wxGetApp().mainframe->m_monitor->IsShown()); + MainFrame* frame = wxGetApp().mainframe; + bool device_page = frame != nullptr && frame->m_monitor_page->in_book(); if (type == PrinterWarningType::NOT_CONNECTED) { if (device_page) { content = wxString::Format(_L("Printer not connected. Please go to the device page to connect %s before syncing."), diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index b30f0298ee..90d0e5e673 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -339,7 +339,7 @@ public: // Helper: returns config indices where filament_is_mixed == false std::vector physical_filament_config_indices() const; - int new_project(bool skip_confirm = false, bool silent = false, const wxString& project_name = wxString()); + int new_project(bool skip_confirm = false, bool silent = false, const wxString& project_name = wxString(), bool reload_presets = true); // BBS: save & backup void load_project(wxString const & filename = "", wxString const & originfile = "-"); int save_project(bool saveAs = false); @@ -499,7 +499,7 @@ public: void deselect_all(); void exit_gizmo(); void remove(size_t obj_idx); - void reset(bool apply_presets_change = false); + void reset(bool apply_presets_change = false, bool reload_presets = true); void reset_with_confirm(); //BBS: return int for various result int close_with_confirm(std::function second_check = nullptr); // BBS close project diff --git a/src/slic3r/GUI/PrebuildQueue.hpp b/src/slic3r/GUI/PrebuildQueue.hpp new file mode 100644 index 0000000000..c013f53865 --- /dev/null +++ b/src/slic3r/GUI/PrebuildQueue.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include + +#include "Lazy.hpp" + +namespace Slic3r { namespace GUI { + +// Holders to build at idle, ordered by prebuild_order(), run one slice at a time. The +// holders outlive the queue. Main thread only. +class PrebuildQueue +{ +public: + struct Slice + { + std::string name; // the task that ran; empty when nothing was pending + int units{ 0 }; // units run + long long ms{ 0 }; // time spent + bool completed{ false }; // the task has no more units + bool remaining{ false }; // some task is still pending + }; + + // Lower order runs first, equal order in the order added. + void add(LazyBase& task) + { + auto after = std::find_if(m_tasks.begin(), m_tasks.end(), [&](const LazyBase* t) { return t->prebuild_order() > task.prebuild_order(); }); + m_tasks.insert(after, &task); + } + + void clear() { m_tasks.clear(); } + + bool pending() const + { + return std::any_of(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); }); + } + + // The task names in queue order, comma separated. + std::string names() const + { + std::string out; + for (const LazyBase* t : m_tasks) + out += (out.empty() ? "" : ", ") + t->name(); + return out; + } + + // Runs units of the first pending task until it completes, budget_ms of now_ms() have + // passed, or interrupt() is true after a unit; on_unit gets the task name and each + // unit's duration. A task with no work is passed over and stays in the queue, so one + // whose work returns is pending again. + Slice run_slice(int budget_ms, + const std::function& now_ms, + const std::function& interrupt, + const std::function& on_unit = {}) + { + Slice slice; + auto next = std::find_if(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); }); + if (next == m_tasks.end()) + return slice; + // The pointer is copied out, since a unit may add tasks and reallocate m_tasks. + LazyBase* const task = *next; + slice.name = task->name(); + const long long started = now_ms(); + for (;;) { + const long long unit_started = now_ms(); + const bool more = task->build_step(); + const long long now = now_ms(); + ++slice.units; + if (on_unit) + on_unit(slice.name, now - unit_started); + slice.ms = now - started; + if (!more) { + slice.completed = true; + break; + } + if (slice.ms >= budget_ms || interrupt()) + break; + } + slice.remaining = pending(); + return slice; + } + +private: + std::vector m_tasks; +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 001866bfd9..fd5012b5ef 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1044,8 +1044,8 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too if (m_bambu_cloud_checkbox) m_bambu_cloud_checkbox->Enable(!enabled); } else if (param == "hide_login_side_panel") { - if (wxGetApp().mainframe && wxGetApp().mainframe->m_webview) { - wxGetApp().mainframe->m_webview->SendCloudProvidersInfo(); + if (WebViewPanel* home = WebViewPanel::if_built()) { + home->SendCloudProvidersInfo(); } } // ORCA: apply the preview dimming change immediately to the currently loaded preview @@ -1254,9 +1254,8 @@ wxBoxSizer *PreferencesDialog::create_item_bambu_cloud(wxString title, wxString app_config->save(); // Update homepage visibility immediately - auto *mainframe = wxGetApp().mainframe; - if (mainframe && mainframe->m_webview) - mainframe->m_webview->SendCloudProvidersInfo(); + if (WebViewPanel* home = WebViewPanel::if_built()) + home->SendCloudProvidersInfo(); }); m_sizer->Add(cb, 0, wxALIGN_CENTER); diff --git a/src/slic3r/GUI/PrinterWebView.hpp b/src/slic3r/GUI/PrinterWebView.hpp index 8a62ce70a2..1ab1e17932 100644 --- a/src/slic3r/GUI/PrinterWebView.hpp +++ b/src/slic3r/GUI/PrinterWebView.hpp @@ -26,6 +26,7 @@ #include "wx/textctrl.h" #include #include +#include "Lazy.hpp" namespace Slic3r { @@ -34,7 +35,7 @@ namespace GUI { class PrinterWebViewHandler; -class PrinterWebView : public wxPanel { +class PrinterWebView : public wxPanel, public LazyInstance { public: PrinterWebView(wxWindow *parent); virtual ~PrinterWebView(); diff --git a/src/slic3r/GUI/Project.cpp b/src/slic3r/GUI/Project.cpp index 6d1eb0e180..8d87939afc 100644 --- a/src/slic3r/GUI/Project.cpp +++ b/src/slic3r/GUI/Project.cpp @@ -44,6 +44,7 @@ const std::vector license_list = { ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style) { + SetBackgroundColour(*wxWHITE); m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir())); wxString strlang = wxGetApp().current_language_code_safe(); if (strlang != "") @@ -67,6 +68,7 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, m_auxiliary = new AuxiliaryPanel(this); m_auxiliary->Hide(); main_sizer->Add(m_auxiliary, wxSizerFlags().Expand().Proportion(1)); + add_build_steps_of(*m_auxiliary); Bind(EVT_AUXILIARY_DONE, [this](wxCommandEvent& e) { update_model_data();}); SetSizer(main_sizer); diff --git a/src/slic3r/GUI/Project.hpp b/src/slic3r/GUI/Project.hpp index a590212cb7..a22bc44430 100644 --- a/src/slic3r/GUI/Project.hpp +++ b/src/slic3r/GUI/Project.hpp @@ -35,6 +35,7 @@ #include "libslic3r/ProjectTask.hpp" #include "wxExtensions.hpp" #include "Auxiliary.hpp" +#include "Lazy.hpp" #define AUFILE_GREY700 wxColour(107, 107, 107) #define AUFILE_GREY500 wxColour(158, 158, 158) @@ -59,7 +60,7 @@ struct project_file{ std::string size; }; -class ProjectPanel : public wxPanel +class ProjectPanel : public wxPanel, public StagedBuild, public LazyInstance { private: std::atomic m_web_init_completed{false}; @@ -90,7 +91,6 @@ public: void msw_rescale(); void update_model_data(); void clear_model_info(); - void init_auxiliary() { m_auxiliary->init_auxiliary(); } bool Show(bool show); void OnScriptMessage(wxWebViewEvent& evt); diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 64a091211b..408e910ff8 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -99,8 +99,11 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr item) } } -SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const +SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, + const ClippingPlane* clipping_plane, SceneRaycaster::EHitMode mode) const { + // Orca: Picking may favor an already selected volume for interaction, while camera + // navigation must always use the geometrically closest visible scene surface. // helper class used to return currently selected volume as hit when overlapping with other volumes // to allow the user to click and drag on a selected volume class VolumeKeeper @@ -110,7 +113,11 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came bool m_selected_volume_already_found{ false }; public: - VolumeKeeper() { + explicit VolumeKeeper(bool enabled) { + // Orca: Disable selected-volume bias for navigation raycasts. + if (!enabled) + return; + const Selection& selection = wxGetApp().plater()->get_selection(); if (selection.is_single_volume() || selection.is_single_modifier()) { const GLVolume* volume = selection.get_first_volume(); @@ -135,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came } }; - VolumeKeeper volume_keeper; + VolumeKeeper volume_keeper(mode == EHitMode::Picking); double closest_hit_squared_distance = std::numeric_limits::max(); auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) { @@ -154,7 +161,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came HitResult ret; - auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { + auto test_raycasters = [this, is_closest, clipping_plane, mode, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) { const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr; const std::vector>* raycasters = get_raycasters(type); const Vec3f camera_forward = camera.get_dir_forward().cast(); @@ -163,12 +170,22 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came if (!item->is_active()) continue; + // Each plate's component 0 is its surface; the remaining Bed IDs are controls. + // Keep controls clickable, but do not use them as camera-pan anchors. + if (mode != EHitMode::Picking && type == EType::Bed && + decode_id(type, item->get_id()) % PartPlate::GRABBER_COUNT != 0) + continue; + current_hit.raycaster_id = item->get_id(); const Transform3d& trafo = item->get_transform(); if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { current_hit.position = (trafo * current_hit.position.cast()).cast(); current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast()).normalized().cast(); - if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) { + // Orca: Perspective rays away from the viewport center are not parallel to camera_forward. + // Keep picking's legacy policy, but accept every front-facing navigation surface. + const Vec3f view_direction = mode != EHitMode::Picking && camera.get_type() == Camera::EType::Perspective ? + Vec3f((current_hit.position.cast() - camera.get_position()).cast()) : camera_forward; + if (item->use_back_faces() || current_hit.normal.dot(view_direction) < 0.0f) { if (is_closest(camera, current_hit.position)) { if (volume_keeper.is_active()) { if (volume_keeper.check_hit_result(current_hit)) @@ -182,14 +199,18 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came } }; - if (!m_gizmos.empty()) - test_raycasters(EType::Gizmo, mouse_pos, camera, ret); + // Orca: Gizmo geometry is an interaction target, not a valid depth anchor for camera movement. + if (mode == EHitMode::Picking) { + if (!m_gizmos.empty()) + test_raycasters(EType::Gizmo, mouse_pos, camera, ret); - if (!m_fallback_gizmos.empty() && !ret.is_valid()) - test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret); + if (!m_fallback_gizmos.empty() && !ret.is_valid()) + test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret); + } if (!m_gizmos_on_top || !ret.is_valid()) { - if (camera.is_looking_downward() && !m_bed.empty()) + // Orca: In perspective the bottom of the viewport can see the bed even at a horizontal view. + if ((mode == EHitMode::SceneOnly || (mode == EHitMode::Picking && camera.is_looking_downward())) && !m_bed.empty()) test_raycasters(EType::Bed, mouse_pos, camera, ret); if (!m_volumes.empty()) test_raycasters(EType::Volume, mouse_pos, camera, ret); diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 99152712dd..9aa527abee 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -57,6 +57,15 @@ public: FallbackGizmo = 2000000 }; + // Orca: Navigation needs the closest visible scene surface, without picking-specific + // gizmo and selected-volume priority. + enum class EHitMode : unsigned char + { + Picking, + SceneOnly, + VolumesOnly // Navigation when the bed is hidden. + }; + struct HitResult { EType type{ EType::None }; @@ -97,7 +106,8 @@ public: void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; } - HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const; + HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr, + EHitMode mode = EHitMode::Picking) const; #if ENABLE_RAYCAST_PICKING_DEBUG void render_hit(const Camera& camera); diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 629aef7296..a9767d6c72 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -3470,7 +3470,7 @@ void SelectMachineDialog::navigate_to_timelapse_page() main_frame->jump_to_monitor(); // then switch to Storage (Media) tab inside Monitor - auto* monitor = dynamic_cast(main_frame->m_monitor); + MonitorPanel* monitor = MonitorPanel::if_built(); if (monitor) { auto* tabpanel = monitor->get_tabpanel(); if (tabpanel) { diff --git a/src/slic3r/GUI/SpeedDialDialog.cpp b/src/slic3r/GUI/SpeedDialDialog.cpp index 7be8bee123..1d81021a60 100644 --- a/src/slic3r/GUI/SpeedDialDialog.cpp +++ b/src/slic3r/GUI/SpeedDialDialog.cpp @@ -9,6 +9,8 @@ #include "Plater.hpp" #include "Widgets/WebViewHostDialog.hpp" +#include "slic3r/Utils/MacDarkMode.hpp" + #include #include @@ -82,39 +84,38 @@ nlohmann::json speed_dial_ui_strings() {"shortcut_alt", alt}, {"shortcut_ctrl", ctrl}, - {"sd_search", _u8L("Search actions")}, - {"sd_clear", _u8L("Clear")}, - {"sd_search_n", _u8L("Search %s actions")}, - {"sd_recent", _u8L("Recent")}, - {"sd_plugins", _u8L("Plugins")}, - {"sd_other", _u8L("Other")}, - {"sd_no_match_total", _u8L("No actions match (Total: %s)")}, - {"sd_no_actions", _u8L("No actions yet")}, - {"sd_no_tabs_match", _u8L("No tabs match")}, - {"sd_no_tabs", _u8L("No tabs")}, - {"sd_result_count", _u8L("Showing %s of %s actions")}, + {"sd_search", _u8L("Search actions")}, + {"sd_clear", _u8L("Clear")}, + {"sd_recent", _u8L("Recent")}, + {"sd_plugins", _u8L("Plugins")}, + {"sd_other", _u8L("Other")}, + {"sd_no_match_total", _u8L("No actions match (Total: %s)")}, + {"sd_no_actions", _u8L("No actions yet")}, + {"sd_no_tabs_match", _u8L("No tabs match")}, + {"sd_no_tabs", _u8L("No tabs")}, + {"sd_result_count", _u8L("Showing %s actions")}, {"sd_result_count_all", _u8L("%s actions")}, - {"sd_tab_count", _u8L("%s tabs")}, + {"sd_tab_count", _u8L("%s tabs")}, {"sd_tab_match_count", _u8L("%s matches")}, - {"sd_favs_full", _u8L("Favourites are full (%s max)")}, - {"sd_go_to_pct", _u8L("Go to %s%% of the layer range")}, - {"sd_enter_pct", _u8L("Enter a layer percentage (0-100)")}, - {"sd_go_layer_ph", _u8L("Go to layer %% (0-100)")}, - {"sd_go_tab_ph", _u8L("Go to tab")}, - {"sd_fav_slot", _u8L("Favourite %s (%s)")}, - {"sd_pin_fav", _u8L("Pin to favourites (%s)")}, - {"sd_unpin_fav", _u8L("Unpin from favourites (%s)")}, - {"sd_remove_fav", _u8L("Remove from favourites")}, - {"sd_move_left", _u8L("Move left")}, - {"sd_move_right", _u8L("Move right")}, - {"sd_unpin", _u8L("Unpin")}, - {"sd_mode_advanced", _u8L("Advanced")}, - {"sd_mode_expert", _u8L("Expert")}, - {"sd_mode_develop", _u8L("Developer")}, - {"sd_wiki_f1", _u8L("Wiki (F1)")}, - {"sd_no_wiki", _u8L("No wiki page for this action")}, - {"sd_show_details", _u8L("Show details")}, - {"sd_hide_details", _u8L("Hide details")}, + {"sd_favs_full", _u8L("Favourites are full (%s max)")}, + {"sd_go_to_pct", _u8L("Go to %s%% of the layer range")}, + {"sd_enter_pct", _u8L("Enter a layer percentage (0-100)")}, + {"sd_go_layer_ph", _u8L("Go to layer %% (0-100)")}, + {"sd_go_tab_ph", _u8L("Go to tab")}, + {"sd_fav_slot", _u8L("Favourite %s (%s)")}, + {"sd_pin_fav", _u8L("Pin to favourites (%s)")}, + {"sd_unpin_fav", _u8L("Unpin from favourites (%s)")}, + {"sd_remove_fav", _u8L("Remove from favourites")}, + {"sd_move_left", _u8L("Move left")}, + {"sd_move_right", _u8L("Move right")}, + {"sd_unpin", _u8L("Unpin")}, + {"sd_mode_advanced", _u8L("Advanced")}, + {"sd_mode_expert", _u8L("Expert")}, + {"sd_mode_develop", _u8L("Developer")}, + {"sd_wiki_f1", _u8L("Wiki (F1)")}, + {"sd_no_wiki", _u8L("No wiki page for this action")}, + {"sd_show_details", _u8L("Show details")}, + {"sd_hide_details", _u8L("Hide details")}, }; } @@ -178,6 +179,7 @@ void SpeedDialWebDialog::request_show() if (IsShown()) { Raise(); focus_webview(browser(), m_page_ready); + repaint_webview(); return; } @@ -189,6 +191,7 @@ void SpeedDialWebDialog::request_show() // Grab focus now and again on wxEVT_ACTIVATE; grabbing directly on the WebKit widget is // what makes typing reach the search field immediately on open. focus_webview(browser(), m_page_ready); + repaint_webview(); } void SpeedDialWebDialog::on_script_message(const nlohmann::json& payload) @@ -216,7 +219,7 @@ void SpeedDialWebDialog::handle_web_command(const nlohmann::json& payload) // set_favourite() refuses once the bar hits kFavLimit; tell the page so it can undo the // pin and show a "favourites are full" hint instead of silently losing the favourite. const std::string fav_id = payload.value("id", ""); - const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false)); + const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false)); if (!ok) call_web_handler({{"command", "favourite_full"}, {"limit", (int) ActionRegistry::kFavLimit}, {"id", fav_id}}); } else if (command == "reorder_favourites") { @@ -265,14 +268,36 @@ void SpeedDialWebDialog::resize_to_content(int height) Layout(); #ifdef __WXOSX__ // WKWebView can lag the dialog's new client size; force the viewport to match so the page is - // never painted (and clipped by the rounded layer) below the footer. - if (wxWebView* wv = browser()) { - const wxSize client = GetClientSize(); - if (wv->GetSize() != client) - wv->SetSize(client); - } + // never painted (and clipped by the rounded layer) below the footer. Unconditional: on a + // re-open the size is often unchanged, and skipping the sync leaves the fresh render unpainted. + if (wxWebView* wv = browser()) + wv->SetSize(GetClientSize()); #endif apply_rounded_shape(); + // A re-open re-renders at (usually) the same size, so nothing above may generate damage. + // Repaint explicitly so the newly rendered list is shown without needing user input. + repaint_webview(); +} + +void SpeedDialWebDialog::repaint_webview() +{ + wxWebView* wv = browser(); + if (!wv) + return; + // Portable invalidate; the platform blocks below reach the widget/layer that actually paints. + wv->Refresh(); +#ifdef __WXOSX__ + if (void* nb = wv->GetNativeBackend()) + WKWebView_force_display(nb); + wv->Update(); +#elif defined(__linux__) + // WebKitGTK's WebKitWebView owns its own GdkWindow, so invalidating the wxWebView wrapper + // (the GtkScrolledWindow) does not redraw it. + if (void* nb = wv->GetNativeBackend()) + gtk_widget_queue_draw((GtkWidget*) nb); +#else + wv->Update(); +#endif } // Rounded corners: the webview paints an opaque rectangle, so round the whole top-level window. @@ -331,8 +356,8 @@ void SpeedDialWebDialog::run_action(const std::string& id, const std::string& ti return; // Only plugin actions get the "Run plugin?" confirm. Built-in commands act immediately. - const bool ask = a->kind == AppActionKind::Plugin && reg.should_ask(id); - const std::string atitle = a->title(); + const bool ask = a->kind == AppActionKind::Plugin && reg.should_ask(id); + const std::string atitle = a->title(); const ConfigOptionMode required = a->required_mode; // Settings the current mode hides require a switch first. Ask while the dial is still up; a @@ -341,16 +366,15 @@ void SpeedDialWebDialog::run_action(const std::string& id, const std::string& ti const wxString setting = title.empty() ? from_u8(atitle) : from_u8(title); if (required == comDevelop) { RichMessageDialog dlg(wxGetApp().mainframe, - wxString::Format(_L("\"%s\" is a Developer setting. Enable Developer mode to edit it?"), - setting), + wxString::Format(_L("\"%s\" is a Developer setting. Enable Developer mode to edit it?"), setting), _L("Developer setting"), wxOK | wxCANCEL); if (dlg.ShowModal() != wxID_OK) return; wxGetApp().enable_developer_mode(); } else { RichMessageDialog dlg(wxGetApp().mainframe, - wxString::Format(_L("\"%s\" is a %s setting. Switch from %s mode to %s mode to edit it?"), - setting, mode_label(required), mode_label(wxGetApp().get_mode()), mode_label(required)), + wxString::Format(_L("\"%s\" is a %s setting. Switch from %s mode to %s mode to edit it?"), setting, + mode_label(required), mode_label(wxGetApp().get_mode()), mode_label(required)), _L("Switch settings mode"), wxOK | wxCANCEL); if (dlg.ShowModal() != wxID_OK) return; diff --git a/src/slic3r/GUI/SpeedDialDialog.hpp b/src/slic3r/GUI/SpeedDialDialog.hpp index 8ae745b446..d3b75f2256 100644 --- a/src/slic3r/GUI/SpeedDialDialog.hpp +++ b/src/slic3r/GUI/SpeedDialDialog.hpp @@ -28,6 +28,10 @@ private: void send_actions(); void search_tabs(); void apply_rounded_shape(); + // Forces the webview to repaint after it is mapped / re-rendered. The popup is transparent and + // chrome-less, so a missed frame leaves it blank until input; platform-specific because the + // widget that actually paints is not always the wxWebView wrapper. + void repaint_webview(); void on_dpi_changed(const wxRect& suggested_rect) override; bool m_page_ready{false}; diff --git a/src/slic3r/GUI/StagedBuild.hpp b/src/slic3r/GUI/StagedBuild.hpp new file mode 100644 index 0000000000..8f4c0454c3 --- /dev/null +++ b/src/slic3r/GUI/StagedBuild.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +namespace Slic3r { namespace GUI { + +// A panel whose constructor builds the skeleton and queues the rest as steps, run in order +// on the main thread, one at a time. +class StagedBuild +{ +public: + virtual ~StagedBuild() = default; + + bool built() const + { + return m_next_step == m_steps.size() && + std::all_of(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return child->built(); }); + } + + // Runs one step and returns true while more remain. Own steps first, then whatever a + // child queued after its steps were forwarded. + bool build_step() + { + if (m_next_step < m_steps.size()) { + // Copied out, since a step may queue more steps and reallocate m_steps. + auto step = std::move(m_steps[m_next_step++]); + step(); + } else if (StagedBuild* child = unfinished_child()) { + child->build_step(); + } + return !built(); + } + +protected: + void add_build_step(std::function step) { m_steps.push_back(std::move(step)); } + + // Queues one step per step the child has now, and keeps the child so that built() waits + // for any it queues later. + void add_build_steps_of(StagedBuild& child) + { + m_children.push_back(&child); + for (size_t i = child.m_next_step; i < child.m_steps.size(); ++i) + add_build_step([&child] { child.build_step(); }); + } + +private: + StagedBuild* unfinished_child() const + { + auto it = std::find_if(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return !child->built(); }); + return it == m_children.end() ? nullptr : *it; + } + + std::vector> m_steps; + std::vector m_children; + size_t m_next_step{ 0 }; +}; + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index fb0029edc3..218371f0b5 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -1328,18 +1328,21 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint wxBoxSizer *bSizer_left = new wxBoxSizer(wxVERTICAL); - auto m_monitoring_sizer = create_monitoring_page(); - bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0); + // The sizers are nested here so each step only appends to its own. + add_build_step([this, bSizer_left] { + auto m_monitoring_sizer = create_monitoring_page(); + bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0); - auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG); - m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING)); - m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING)); - m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0); + auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG); + m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING)); + m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING)); + m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0); - m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT); - m_project_task_panel->init_bitmaps(); - m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0); + m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT); + m_project_task_panel->init_bitmaps(); + m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0); + }); // auto m_panel_separotor2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); // m_panel_separotor2->SetBackgroundColour(STATUS_PANEL_BG); @@ -1359,8 +1362,10 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint m_machine_ctrl_panel->SetDoubleBuffered(true); auto m_machine_control = create_machine_control_page(m_machine_ctrl_panel); m_machine_ctrl_panel->SetSizer(m_machine_control); - m_machine_ctrl_panel->Layout(); - m_machine_control->Fit(m_machine_ctrl_panel); + add_build_step([this, m_machine_control] { + m_machine_ctrl_panel->Layout(); + m_machine_control->Fit(m_machine_ctrl_panel); + }); bSizer_status_below->Add(m_machine_ctrl_panel, 0, wxALL, 0); @@ -1375,8 +1380,11 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint m_panel_separotor_bottom->SetBackgroundColour(STATUS_PANEL_BG); bSizer_status->Add(m_panel_separotor_bottom, 0, wxEXPAND | wxALL, 0); - this->SetSizerAndFit(bSizer_status); - this->Layout(); + this->SetSizer(bSizer_status); + add_build_step([this, bSizer_status] { + bSizer_status->SetSizeHints(this); + this->Layout(); + }); } StatusBasePanel::~StatusBasePanel() @@ -1614,16 +1622,24 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) wxBoxSizer *bSizer_control = new wxBoxSizer(wxVERTICAL); - auto temp_axis_ctrl_sizer = create_temp_axis_group(parent); - auto m_filament_load_sizer = create_filament_group(parent); - + // The slot sizers keep each group's place in bSizer_control. + wxBoxSizer *temp_axis_slot = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *filament_slot = new wxBoxSizer(wxVERTICAL); /* ams control box or live nozzle-rack panel (rack printers switch between the two) */ wxSizer *ams_rack_sizer = new wxBoxSizer(wxHORIZONTAL); - ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT); - m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent); - m_panel_nozzle_rack->Show(false); - ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT); + add_build_step([this, parent, temp_axis_slot, filament_slot] { + temp_axis_slot->Add(create_temp_axis_group(parent), 0, wxEXPAND); + filament_slot->Add(create_filament_group(parent), 0, wxEXPAND); + }); + add_build_step([this, parent, ams_rack_sizer] { + ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT); + }); + add_build_step([this, parent, ams_rack_sizer] { + m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent); + m_panel_nozzle_rack->Show(false); + ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT); + }); m_ams_rack_switch = new SwitchBoard(parent, _L("Filament"), _L("Hotends"), wxSize(FromDIP(126), FromDIP(26))); m_ams_rack_switch->updateState("left"); @@ -1631,12 +1647,12 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) m_ams_rack_switch->Bind(wxCUSTOMEVT_SWITCH_POS, &StatusBasePanel::on_ams_rack_switch, this); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(8)); - bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); + bSizer_control->Add(temp_axis_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); bSizer_control->Add(m_ams_rack_switch, 0, wxALIGN_CENTRE|wxTOP, FromDIP(6)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6)); bSizer_control->Add(ams_rack_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6)); - bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); + bSizer_control->Add(filament_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(4)); bSizer_right->Add(bSizer_control, 1, wxEXPAND | wxALL, 0); @@ -2228,8 +2244,10 @@ void StatusBasePanel::expand_filament_loading(wxMouseEvent& e) m_filament_step->Show(tag_show); Layout(); Fit(); - wxGetApp().mainframe->m_monitor->get_status_panel()->Layout(); - wxGetApp().mainframe->m_monitor->Layout(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) { + monitor->get_status_panel()->Layout(); + monitor->Layout(); + } } void StatusBasePanel::show_ams_group(bool show) @@ -2240,7 +2258,8 @@ void StatusBasePanel::show_ams_group(bool show) m_ams_control->Fit(); Layout(); Fit(); - wxGetApp().mainframe->m_monitor->Layout(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->Layout(); } // On rack printers, don't clobber the rack view when the user has the switch on "Hotends". @@ -2253,7 +2272,8 @@ void StatusBasePanel::show_ams_group(bool show) m_ams_control->Fit(); Layout(); Fit(); - wxGetApp().mainframe->m_monitor->Layout(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) + monitor->Layout(); } } @@ -2277,8 +2297,10 @@ void StatusBasePanel::show_filament_load_group(bool show) Layout(); Fit(); - wxGetApp().mainframe->m_monitor->get_status_panel()->Layout(); - wxGetApp().mainframe->m_monitor->Layout(); + if (MonitorPanel* monitor = MonitorPanel::if_built()) { + monitor->get_status_panel()->Layout(); + monitor->Layout(); + } } } @@ -2403,6 +2425,12 @@ void StatusPanel::update_camera_state(MachineObject* obj) StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name) : StatusBasePanel(parent, id, pos, size, style) +{ + // Wires the controls the base class builds in steps, so it is the last step. + add_build_step([this] { wire_controls(); }); +} + +void StatusPanel::wire_controls() { init_scaled_buttons(); m_buttons.push_back(m_bpButton_z_10); @@ -2514,47 +2542,50 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co StatusPanel::~StatusPanel() { - // Disconnect Events - m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this); - m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this); - m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); - m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); - m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); - m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); - m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this); + // The controls only exist once the last build step has run. + if (built()) { + // Disconnect Events + m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this); + m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this); + m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); + m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); + m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); + m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); + m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this); - m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); - m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); - m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this); - m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this); - m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this); - m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this); + m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); + m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); + m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this); + m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this); + m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this); + m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this); - m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this); - m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this); + m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this); + m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this); - m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this); - /*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/ + m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this); + /*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/ - //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this); - m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this); - m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this); - m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this); - m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this); - m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this); - m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this); - m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this); - m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this); - m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this); - m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this); - m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this); - m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this); + m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this); + m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this); + m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this); + m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this); + m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this); + m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this); + m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this); + m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this); + m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this); + m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this); + m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this); + m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this); + m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this); + } // remove warning dialogs if (abort_dlg != nullptr) @@ -5216,7 +5247,9 @@ void StatusPanel::set_default() m_filament_step->Hide(); error_info_reset(); #ifndef __WXGTK__ - SetFocus(); + // Also reached while the panel is built off screen. + if (IsShownOnScreen()) + SetFocus(); #endif } diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 341bb9ab69..d79c3d3915 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -36,6 +36,7 @@ #include "HMS.hpp" #include "PartSkipDialog.hpp" #include "DeviceErrorDialog.hpp" +#include "StagedBuild.hpp" class StepIndicator; @@ -375,7 +376,7 @@ public: void paint(wxPaintEvent&); }; -class StatusBasePanel : public wxScrolledWindow +class StatusBasePanel : public wxScrolledWindow, public StagedBuild { protected: wxBitmap m_item_placeholder; @@ -443,7 +444,7 @@ protected: wxMediaCtrl3 * m_media_ctrl; - MediaPlayCtrl * m_media_play_ctrl; + MediaPlayCtrl * m_media_play_ctrl{nullptr}; Label * m_staticText_printing; wxStaticBitmap *m_bitmap_thumbnail; @@ -629,6 +630,7 @@ class StatusPanel : public StatusBasePanel { private: friend class MonitorPanel; + void wire_controls(); protected: std::shared_ptr m_slice_info_popup; @@ -679,7 +681,7 @@ protected: std::map m_print_connect_types; std::vector