Commit Graph
30203 Commits
Author SHA1 Message Date
5ede9711f5 Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize (#14948)
* For dialog without explicitly `SetMinSize`, we should use `SetSizerAndFit` instead, otherwise the dialog will not show correctly on GTK3. (OrcaSlicer/OrcaSlicer#14561)
- and if `SetSizer` is called before the full layout has been built, then an extra `SetSizeHints` should be called before layout/fit so the min size can be properly set automatically based on children's min sizes accordingly.

* Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize

Replace SetSizer() with SetSizerAndFit() in 11 dialog constructors that
neither call SetMinSize() nor SetSizeHints(), ensuring proper minimum
size propagation from child widgets on GTK3.

SetSizerAndFit internally calls sizer->SetSizeHints(window), which
sets the window's minimum size based on children — the same fix
applied to ProjectDropDialog in 8a7662083e.

Also drop sizer->Fit(this) calls where present, since they only
resize but don't set the min size hint needed by GTK3.

Co-Authored-By: Claude <noreply@anthropic.com>

* Update code style

* Update TroubleshootDialog.hpp

* Fix unsaved preset dialog layout

* Fix MsgDialog layout

* Fix other 3 instances in MsgDialog.cpp

* Fix a few more instances

* Fix printer option dialog too big on Windows

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-07-28 14:32:55 +08:00
Ian Bassi 6bcb809dd0 Calibrations improvements (#14759) 2026-07-27 20:11:44 -03:00
Ian Bassi 33dfb66aa5 Cyclic ordering improvement (#14784) 2026-07-27 19:58:54 -03:00
Maksym PyrozhokandIan Bassi ef7bfeda9c Cyclic ordering (#13578)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-27 19:52:29 -03:00
47ccca7f72 Full Orca translation via AI (tagged) (#14970)
Co-authored-by: Felix14_v2 <75726196+Felix14-v2@users.noreply.github.com>
Co-authored-by: π² <189209038+pi-squared-studio@users.noreply.github.com>
2026-07-27 19:31:33 -03:00
SoftFever 5b475e5e98 Route the in-place toolchange tower entry through the skip-point gap
On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).
2026-07-28 00:15:59 +08:00
SoftFever 56810c8c7f Port the skip-points gap wall to WipeTower2
prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.
2026-07-27 21:18:53 +08:00
SoftFever c3c37e474a Print the WipeTower2 shell with a non-support, non-soluble filament
Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.
2026-07-27 12:30:57 +08:00
Kiss Lorand 04e13200aa Support bugfixes (#14678) 2026-07-26 19:16:19 -03:00
SoftFever 1696d5ca39 Use the squared rib tower size in arrange estimates
estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.
2026-07-27 03:08:47 +08:00
SoftFever 466c36eaa3 Complete the rib wipe tower port in WipeTower2
The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.
2026-07-27 03:07:07 +08:00
SoftFever 5792fef805 Merge branch 'main' into feature/update_wipetower 2026-07-27 00:51:29 +08:00
SoftFever bc016af1c9 Fix post-slice self-invalidation on custom multi-extruder printers 2026-07-27 00:49:08 +08:00
SoftFever 7a378d2fc4 Sync WipeTower from BambuStudio(through ca1881761) 2026-07-27 00:48:44 +08:00
Alexandre Folle de Menezes ed86efcf56 Improve and complement pt-BR translation (#14963) 2026-07-26 12:59:24 -03:00
Kris Austin 306d4b73ef test: stop littering the working directory with debug files (#14785) 2026-07-26 12:53:56 -03:00
SoftFever f60e0e776e Add back 02.03.00 plugin to support debugging (#14966)
* Add back 02.03.00 plugin to support debugging
2026-07-26 23:49:53 +08:00
SoftFever d84a211b91 fix crashes when switching bbl printers 2026-07-26 22:15:53 +08:00
NoisyfoxandClaude 63044b7661 feat: Add layout debugging/inspecting tool (#14919)
* Add wxInspector dep

* Initial intergration of wxInspector

* docs: add wxInspector plugins design spec

Design spec for two wxInspector plugins (DPIAware + CustomWidgets) that expose
OrcaSlicer's custom control properties in the inspector property grid.

Covers: DPIAware scale-factor properties, Button, CheckBox, TextInput,
SwitchButton, ProgressBar, Label, and LabeledStaticBox.

* docs: add wxInspector plugins implementation plan

6-task plan covering: source changes to existing widget headers,
DPIAwarePlugin, CustomWidgetsPlugin, registration helper,
MainFrame/CMake wiring, and build verification.

* feat: add getters/setters for wxInspector plugin access

Add minimal public accessors to DPIAware (set_scale_factor,
set_prev_scale_factor, set_em_unit, force_rescale), Button
(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked),
TextInput (GetCornerRadius), and LabeledStaticBox
(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale).

* feat: add wxInspector plugin registration helper

Add RegisterOrcaInspectorPlugins() inline function that creates
and registers the DPIAwarePlugin and CustomWidgetsPlugin as
static instances (matching wxInspector's built-in pattern).

* feat: add DPIAware wxInspector plugin

Exposes DPI scaling properties (scale_factor, prev_scale_factor,
em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog
widgets. Uses dynamic_cast for detection and a template helper
to capture the correct static type for lambda accessors.

* feat: add OrcaCustomWidgets wxInspector plugin

Exposes Orca-specific properties on 7 widget types:
- Button: Style, Type, Selected
- CheckBox: Half Checked
- TextInput: Label, Text Value, Corner Radius
- SwitchButton: Value
- ProgressBar: Proportion, Show Number
- Label: Is Hyperlink, Font Point Size
- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale

Each widget type uses dynamic_cast for safe detection.

* feat: wire wxInspector plugins into MainFrame and build

Call RegisterOrcaInspectorPlugins() in MainFrame constructor after
SetupInspectorAccelerator(). Add all 5 plugin source files to
SLIC3R_GUI_SOURCES in CMakeLists.txt.

* fix: move plugin registration to GUI_App::on_init_inner

Register plugins once in app init rather than in MainFrame
constructor, which may be recreated during the application
lifetime.

* fix: include plugin headers in Registration.hpp for complete types

Static locals require complete type. Include DPIAwarePlugin.hpp and
CustomWidgetsPlugin.hpp instead of forward-declaring. Also remove
unused include from MainFrame.cpp (registration moved to GUI_App).

* fix: qualify DPIFrame/DPIDialog with Slic3r::GUI namespace

* Make DPIDialog inspectable. For other dialogs, we will add them if necessary later.

* docs: add spec for moving wxInspectable into DPIAware template

Move wxInspector::wxInspectable base class from DPIDialog and MainFrame
into the common DPIAware<P> template, making all DPIAware widgets
automatically visible in the inspector tree.

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: add implementation plan for moving wxInspectable into DPIAware

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: update spec/plan — move SetupInspectorAccelerator into DPIAware too

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware

DPIAware<P> now inherits wxInspector::wxInspectable and calls
SetupInspectorAccelerator in its constructor, making all DPIAware
widgets automatically appear in the inspector tree with the
Ctrl+Shift+I shortcut. DPIDialog now uses 'using' to inherit the
constructor. Remove redundant wxInspectable inheritance and
SetupInspectorAccelerator calls from DPIDialog and MainFrame.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use LB_HYPERLINK constant instead of magic number 0x0020

Co-Authored-By: Claude <noreply@anthropic.com>

* Clean up

* Fix Linux build

* Don't build wxInspector sample

* Use shallow clone

* Try fix flatpak build

* Attempt to fix build again

* Fix build failure caused by https://github.com/wxWidgets/wxWidgets/commit/436c16135ec7ddf580f44624bf74c592aae43b66

* wxWidgets build only download required submodules

* This should fix build on Windows on ARM

* Enable PIC

* Disable layout inspector by default for public release

* Use wxInspector 1.0.0 release

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-26 20:25:25 +08:00
Kris Austin 11fdb472d6 fix: hide the extruder switch on single-variant printers (#14930) 2026-07-25 18:46:55 -03:00
Kiss Lorand 68ce4da19f Fix overhang fan speed bugs (#14788) 2026-07-25 18:38:21 -03:00
Kiss Lorand fc5aec425e Fix stale plate membership during plate grid rearrangement (#14850) 2026-07-25 18:08:12 -03:00
SoftFever a62fb17e03 Remove cloud deletion of owned plugins from the plugin dialog (#14946)
Owned ("Mine") cloud plugins now offer the same local-only Delete as local
plugins: it removes the installed package and leaves the plugin in the cloud,
still reinstallable. Deleting a plugin from the cloud belongs on the plugin hub
and is no longer reachable from OrcaSlicer, so the whole cloud-delete chain is
removed down to the REST binding.

The deleted row is restored locally instead of via a blocking cloud refetch, so
it survives being offline, and it comes back without the deleted package's error
state.
2026-07-25 22:31:40 +08:00
SoftFever 9a72dda79a Fix plugin configuration not taking effect, and improve the plugin config UI (#14944)
# Description

Changing a slicing plugin's configuration had no effect on the sliced
result until you forced a re-slice some other way; it now applies
immediately. Print, printer and filament presets also keep their plugin
configuration separately, so configuring a plugin on one no longer wipes
out what you set on another.

A plugin's custom configuration page gets the same round of improvements
in both the Plugins dialog and the per-preset dialog: it follows the
app's light/dark theme, keeps its state while you edit instead of
resetting under the cursor, and can tell whether it is being edited
globally or for a preset, so "Restore defaults" can be labeled for what
it will actually do. The two bundled examples show this off — Twistify
now ships a custom configuration UI, and Inspector is themed, groups

# Screenshots/Recordings/Graphs



https://github.com/user-attachments/assets/02ca062a-5143-49a3-abe0-a2a040b3a928



## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-25 20:00:56 +08:00
Ian Bassi d6cb667b89 Localization refactor pt 2 (#14907) 2026-07-24 09:41:24 -03:00
Ian Chua 43725128d3 chore: make console logging optional (#14932)
# Description

Adding optional flag for enabling console logging introduced in #14439.
Console logging not working with LLDB-DAP on VSCode as per #14897
Code changes won't fix this because it is a LLDB-DAP issue documented in
#14909

Use flag `-DUSE_SLIC3R_CONSOLE_LOG=ON` or `-DUSE_SLIC3R_CONSOLE_LOG=OFF`
to enable/disable it.
Alternatively in you VSCode's settings.json, 
```
"cmake.configureSettings": {
   "USE_SLIC3R_CONSOLE_LOG": "OFF"
}
```

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-24 14:23:21 +08:00
SoftFever 51612b504a Silence CMake configure warnings (#14925)
# Description

Remove DEPENDS/empty-COMMAND args that are invalid in the
add_custom_command(TARGET) form (CMP0175), fix the FindDraco.cmake case
mismatch, and opt Boost lookup into upstream BoostConfig via CMP0167 for
the OpenVDB module and the CGAL find.
# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-24 14:13:35 +08:00
Ian Chua 1ce21c98ac chore: make console logging optional 2026-07-24 12:22:57 +08:00
SoftFever 5b32213d44 Silence CMake configure warnings
Remove DEPENDS/empty-COMMAND args that are invalid in the
add_custom_command(TARGET) form (CMP0175), fix the FindDraco.cmake case
mismatch, and opt Boost lookup into upstream BoostConfig via CMP0167 for
the OpenVDB module and the CGAL find.
2026-07-24 00:56:56 +08:00
Felix14_v2andπ² 5c4e7c8d0b More updates for Russian localization (#14876)
Co-authored-by: π² <189209038+pi-squared-studio@users.noreply.github.com>
2026-07-23 13:48:58 -03:00
SoftFever 43f38c0eb6 Attempt to fix crash on switching printer (#14870)
# Description

attempt to fix #14851

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 23:24:29 +08:00
Kris Austin 137e8e5e92 docs(tests): add a layered guide to the test tree (#14628) 2026-07-23 09:19:37 -03:00
Ian Chua f1f65342f0 fix: reserve plugin enable state during rescans (#14860)
# Description

Plugin discovery now distinguishes an unavailable or invalid
.install_state.json from a valid sidecar. During transient filesystem
replacement, the existing enabled state is preserved instead of being
reset, preventing plugins from unexpectedly losing their auto-load
behavior during rescans.

Thanks @WeLizard for pointing this out.

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 12:24:36 +08:00
Ian Chua 738af9f4eb Merge branch 'main' into fix/plugin-install-state 2026-07-23 12:24:19 +08:00
Ian Chua bbe39629ed fix: plugin bugs (#14855)
# Description

Fixes some bugs reported by users and the community

# Fix 1: Latest Version
If the user updates the plugin version on OrcaCloud without a changelog,
the latest version shown on OrcaSlicer won't be accurate.
## Issue
OrcaSlicer uses the changelog returned by OrcaCloud backend as the
source of truth, even though we query a latest version from the backend
as well. The changelog only consists of version entries that have
changelogs so if the latest one, e.g. 1.2.0 has no changelog, but 1.1.0
has, the latest version will be interpreted as 1.1.0.
## Fix
Don't use the changelog for version tracking, just use it for the
plugins dialog changelog tab.

# Fix 2: Host theme script / window.orca bridge leaks into cross-origin
child iframes
If a plugin's embedded app renders its own `<iframe>` (its real
catalog/dashboard UI, or a third-party auth/payment widget), that child
document also gets, uninvited: the host theme `<style>` block fighting
whatever CSS the child page already defines.
## Issue
OrcaSlicer injects host themed scripts and window.orca bridge into every
page, potentially breaking any cross origin child frames or `<iframes>`
in general.
## Fix
Only inject top level frames by checking `if (window.top !==
window.self) return;`.

# Fix 3: Transferring Slicing Pipeline Plugin config when switching
printers (#14832)
Select an entry in the Process tab's "Slicing Pipeline Plugin" picker,
switch the active printer preset, then click **Transfer** in the
"modified settings" dialog — OrcaSlicer crashes immediately and
repeatably. Changing other settings (e.g. a single scalar option) does
not reproduce it; only this picker does.
## Issue
`slicing_pipeline_plugin` is a vector option (`coStrings`) with an empty
default. `deep_diff` diffs vector options per-index, so selecting a
plugin produced a `"slicing_pipeline_plugin#0"` dirty key instead of a
plain one. Unlike genuine per-extruder options, this key wasn't caught
by the printer-switch filter that discards stale per-extruder changes,
so it got cached and replayed through `ConfigBase::apply_only`'s
`'#'`-indexed branch, which calls `ConfigOptionVector::set_at()` on the
freshly-reloaded (and still empty) destination vector. `set_at()`'s only
empty-vector guard is an `assert()`, which is compiled out of Release
builds, so it dereferences `values.front()` on an empty vector —
undefined behavior, matching the reported ACCESS_VIOLATION.
## Fix
Treat `slicing_pipeline_plugin` as a single atomic value in `deep_diff`
(`Preset.cpp`), same as `printable_area`/`thumbnails`/etc.,
since it isn't actually per-extruder data. It's now diffed and replayed
as a whole option (`ConfigOptionVector::set()`, a plain vector
assignment) instead of the index-based `set_at()` path — removing the
crash unconditionally, regardless of whether the two printers share the
same extruder configuration.

# Fix 4: Stale .whl cache
After a .whl was loaded once, if at runtime, the .whl is replaced with a
new one, the plugin system will use the stale .whl cache.
## Fix
Added an option in the context menu to Reload or Delete Cache and Reload
for locally installed plugins. The assumption here is that users
shouldn't be modify cloud plugins, and if they want to develop on a
subscribed cloud plugin, they should create a local copy of it.

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-23 12:24:06 +08:00
Alexandre Folle de Menezes 607648c61f Improve and complement ptBR translation (#14847) 2026-07-22 20:53:44 -03:00
Ian Bassi a7c0ab52f0 Treat nozzle diameter mismatch as a warning (#14890) 2026-07-22 19:09:57 -03:00
Kris Austin f23ad64303 fix: infill rotation template crashes and misbehaves on raft prints (#14894) 2026-07-22 17:51:55 -03:00
Kris Austin b8cfa32a41 fix: multi-material slicing crash with line width 0 (#14455) 2026-07-22 16:49:37 -03:00
Kiss Lorand 5edd4963eb Fix acceleration/jerk state after custom G-code (#14613) 2026-07-22 16:30:43 -03:00
Kris Austin 857adad293 fix: startup crash on macOS when a printer has a print host set (#14747) 2026-07-22 15:14:49 -03:00
Ian Bassi a3ef75586e Update home readme (#14905) 2026-07-22 14:42:32 -03:00
Bartok 3400a49696 docs(readme): remove empty unclosed h3 before community heading (#14834) 2026-07-22 14:32:50 -03:00
Bartok e08af1294d docs(readme): document macOS Homebrew cask install (#14899)
Signed-off-by: Bartok9 <danielrpike9@gmail.com>
2026-07-22 14:24:23 -03:00
Nathan b118c03830 locales: update Traditional Chinese translation terminology (#14889) 2026-07-22 14:16:45 -03:00
Neko.vecter 0ff966c9b7 i18n: v2.4.2-zh-cn (#14896) 2026-07-22 14:12:17 -03:00
packerlschupfer 705c82688e PartPlate::check_outside: guard m_plater null deref (fix CLI rotate SIGSEGV) (#14415) 2026-07-22 09:50:32 -03:00
Wegerichandyw4z c31a55c6e0 Remove numeric concatenation when offering shared profiles (#14875)
Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-07-22 09:48:28 -03:00
Ian Chua bc6ae243ca Merge branch 'main' into fix/plugin-bugs 2026-07-22 13:14:03 +08:00
SoftFever 7db0a59cc1 fix: H2C carousel - port BBS NozzleStatusRecorder for per-slot purge tracking (#14800)
## Problem

On H2C (carousel) printers, the wipe tower purge volume calculation in
`_make_wipe_tower()` tracks filament state **per-extruder** (2 slots).
Since H2C has up to 7 carousel nozzle slots on a single extruder, all
filaments sharing that extruder are collapsed into one tracking slot.
This causes:

Test:

[5cubes.3mf.zip](https://github.com/user-attachments/files/30092551/5cubes.3mf.zip)

- **Massive redundant AMS flushing** every filament change on the
carousel triggers a full purge against the "previous" filament, even
when the target nozzle slot already has the correct filament loaded
- **60.9g total weight** instead of ~17g (**3.5× material waste**)
- **3h09m print time** instead of ~1h57m (**60% longer**)

## Root Cause

The code uses `nozzle_cur_filament_ids[extruder_id]` (a 2-element array)
to track which filament was last used for each extruder. BambuStudio
uses `NozzleStatusRecorder`, which tracks per `group_id` (physical
carousel slot 0..6).

## Changes

| File | Change |
|---|---|
| `Print.cpp` | Replace `nozzle_cur_filament_ids` with
`NozzleStatusRecorder`. Use `get_nozzle_for_filament()` to resolve the
physical carousel slot per layer. Select `filament_prime_volume_nc` for
nozzle changes, `filament_prime_volume` for filament changes. |
| `PrintConfig.hpp` | Add `ConfigOptionFloats filament_prime_volume`
(per-filament EC prime volume, missing from upstream but present in BBS
and H2C profiles) |
| `PrintConfig.cpp` | Register `filament_prime_volume` with default
45mm³ (matching BBS) |
| `Preset.cpp` | Add `filament_prime_volume` to preset keys |

Also includes `tests/compare_analyzer/` - two standalone Python tools
for G-code slice comparison and temperature timeline analysis (stdlib
only, no dependencies).

## Test Results (5-color H2C Hybrid print, same 3mf project)

| Metric | Upstream (broken) | **Fixed** | BBS (reference) |
|---|---|---|---|
| **Total weight** | 60.90g | **16.20g**  | 17.47g |
| **Print time** | 3h09m | **1h57m**  | 1h51m |
| **Filament changes** | 105 | 105 | 140 |
| **Tool changes** | 35 | 35 | 35 |
| **Critical discrepancies vs BBS** | ⚠️ YES |  None | — |


## Analysis Tools (`tests/compare_analyzer/`)

Two standalone Python tools (stdlib only, no dependencies) for deep
G-code comparison:

- **`compare_slices.py`** - comprehensive .3mf slice comparison:
filament usage, nozzle mapping, tool change sequences, prime tower
analysis, temperature timeline, retract parameters, and automatic
critical discrepancy detection (weight/time anomalies)
- **`show_temp_plot.py`** - interactive HTML temperature timeline
plotter for visualising heater profiles during multi-nozzle prints
(supports single-file and side-by-side comparison)

Usage:
```bash
python3 tests/compare_analyzer/compare_slices.py file1.3mf file2.3mf --labels "Upstream" "Fixed"
python3 tests/compare_analyzer/show_temp_plot.py file1.3mf file2.3mf
```


## Screenshots

### OrcaSlicer Upstream (unfixed) - 60.90g, 3h09m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 22 58"
src="https://github.com/user-attachments/assets/3efb2bff-ff1e-43db-9669-feafa5921b51"
/>


### OrcaSlicer Fixed - 16.20g, 1h57m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 23 08"
src="https://github.com/user-attachments/assets/c1d36dc5-9b01-4691-80ef-7364540e1f4e"
/>

### BambuStudio Reference - 17.47g, 1h51m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 24 52"
src="https://github.com/user-attachments/assets/5c0b11e2-64f4-40e9-8c7c-3f42519786c3"
/>

### Temperature Timeline: Upstream vs Fixed
<img width="1511" height="829" alt="Screenshot 2026-07-16 at 15 23 35"
src="https://github.com/user-attachments/assets/926c2cb5-dfd4-4ce0-bb40-82e6165eb134"
/>


### Temperature Timeline: Fixed vs BBS

<img width="1512" height="825" alt="Screenshot 2026-07-16 at 15 23 51"
src="https://github.com/user-attachments/assets/85c72dda-e779-4aa6-8118-fb17e5f8482d"
/>


## Compatibility

Safe for non-carousel printers: when each extruder has a single nozzle,
`group_id == extruder_id`, so `NozzleStatusRecorder` behaves identically
to the original per-extruder tracking. The `filament_prime_volume`
default (45mm³) matches the existing global `prime_volume` default.

## Reference
BambuStudio `Print.cpp` `_make_wipe_tower()` L3341-3392 -
`NozzleStatusRecorder` pattern.
2026-07-22 12:59:17 +08:00