- Add all_tool_states map to IXexModesCtrl Row, storing all tool
assignments including those not currently visible due to grid size
- active_tools_string() now serializes from all_tool_states so
off-screen assignments survive the round trip through a smaller grid
- Button clicks keep all_tool_states in sync with visible btn_states
- Display anchors to the gantry row containing the Primary assignment
so reducing gantry count keeps the meaningful row visible rather
than always defaulting to row 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add per-plate iXex mode icon to the plate toolbar (normal, hover, dark, dark-hover SVG variants)
- Left-click cycles through available modes; right-click shows a popup menu with all modes as radio items
- Mode changes are recorded in the undo/redo snapshot system
- Fix double context menu: suppress EVT_GLCANVAS_PLATE_RIGHT_CLICK and EVT_GLCANVAS_RIGHT_CLICK when the iXex icon popup was already shown
- Remove ixex_parallel_mode combo from Print Settings > Other > Special mode (superseded by per-plate icon)
- Remove dead code: refresh_ixex_mode_combo(), m_ixex_mode_combo member, related Tab reload hook
- iXex mode persisted in 3MF project files via existing plate metadata serialization
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix crash-inconsistent draft shield
* Skip empty skirt G-code and fix skirt_done handling
Track whether a skirt was actually generated for the current print Z and avoid appending empty skirt G-code. Only pop m_skirt_done when a skirt was emitted to prevent corrupting skirt state across multiple instances per layer. Also use a temporary string (moved into the main gcode) to detect emptiness before updating state. Preserves existing first-layer clearing behavior.
* Union ex brims
Revert "Union ex brims"
This reverts commit bbc9a39faf318dc2df093eb2bdcebf19a4162fe9.
Update Brim.cpp
* dont repeat paths
* Update Brim.cpp
* multimaterial brim independiente
* Normal brim if is by object
* fix print order
* cleaning 1
* cleaning 2
* Normal brim if multimaterial on first layer
* fix artifact
* combine_brim optional
refactoring
* refactoring gcode.cpp
* refactoring brim.cpp
Update Brim.cpp
* Remove multimaterial first-layer check for brims
Stop detecting extruders used on the first layer and remove the is_multimaterial_first_layer guard. Simplify can_combine_brims to only consider combine_brims and whether printing is ByObject, allowing brims to be combined across extruders unless printing by object or combine_brims is disabled. Cleans up unused code and simplifies brim-generation conditions.
* Remove material specification from unified brim comment
* Update PrintConfig.cpp
Config key rename (breaking for saved profiles — call out in PR):
ixex_carriage_width_x/y → ixex_nozzle_clearance_x/y
Labels updated to "Nozzle Clearance X/Y" with consistent tooltips
describing the measurement as nozzle-to-collision-side-edge distance.
Strip math fix:
Previously halved the clearance value (× 0.5) under the assumption
the nozzle was centered in the carriage. The measurement is now the
literal nozzle-to-edge distance, so the × 0.5 factor is removed.
The collision strip width now equals the configured value directly.
Carriage box visualization (GCodeViewer):
- Add per-carriage box_offset_x/y so the nozzle marker sits at the
physically correct edge of the footprint box rather than centered.
- X: zone-based by default (nozzle at inner edge facing bed center).
Copy secondaries inherit the primary's X orientation (same movement
direction). Mirror secondaries use the collision-side edge.
- Y: always row-based regardless of copy/mirror mode. Gantry is always
behind the nozzle (high-Y); front-row primaries with a back-row
secondary override to place nozzle at the low-Y edge.
- Fix stale mesh bug: GLModel::init_from() is a no-op when already
initialized. Call reset() before init_from() so mesh rebuilds
correctly when nozzle clearance values change in config.
- Remove m_ixex_toolhead_box_dims (was the now-unnecessary cache key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
params.excluded_regions flows into libnest2d PlacementConfig.m_excluded_regions
which is declared but never consumed by the NFP placer — the field is dead code.
m_unselected is the working path: it becomes fixeditems that are preloaded as
physical fixed obstacles in the NFP computation, same as the wipe tower.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-arrange now injects the carriage collision zones as hard excluded
regions (is_virt_object) when an iXex parallel mode is active, using the
same BoundingBoxf3 data already stored for rendering and violation checks.
Objects will no longer be placed in the mirror-edge danger strips.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds std::optional<BoundingBoxf> m_ixex_primary_zone_box to PartPlate,
populated by calc_ixex_zones() alongside the existing secondary/collision
zone geometry. The new ixex_primary_zone() getter calls ensure_ixex_zones()
so callers always get fresh data. ArrangeJob::process() replaces the
full-bed bedpts with the primary zone corners when the getter returns a
value, so auto-arrange no longer drops objects into the bed center.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The thin rectangular outlines around copy/mirror zones caused visible
aliasing and appeared to flash during interaction. Removed the border
GLModel, its build code in calc_ixex_zones(), the render block, and
the unused border field from IXexTheme.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same class of bug as the TabPrinter fix: clear_pages() destroys all
page widgets but didn't null m_ixex_mode_combo, causing a SIGSEGV in
refresh_ixex_mode_combo() when load_current_preset() ran after a
page rebuild (e.g. triggered by Plater::reset()).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Arrays cannot be captured by value in C++ lambdas; replaced with a
static local declared inside the event handler.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces first-class parallel printing (copy/mirror modes) for printers
with multiple independent X-axis carriages. Branded iXex (independent X
extruder), targeting Klipper firmware with a firmware-agnostic design.
- PrintConfig: new printer options declaring iXex capability and geometry
(is_ixex, ixex_gantry_count, ixex_tools_per_gantry, carriage dims,
tool layout, and per-mode name/role/gcode arrays)
- Preset: iXex keys registered in printer and process preset option lists
- Tab: IXexModesCtrl visual grid editor in Printer preset tab; mode
dropdown in Process → Others tab; clear_pages() nulls iXex pointers to
prevent dangling-pointer crash on preset save
- PartPlate: 2D zone visualization (active/dimmed/dividers) and
placement-violation detection (has_ixex_placement_violations) that
blocks slicing when objects fall outside the primary zone
- Plater: violation detection wired into update_background_process so
the Slice button is disabled with an error notification on violation
- GCode: mode-activation G-code injected before machine_start_gcode
- GCodeViewer: multi-carriage toolhead markers in sequential preview,
filament legend annotated with active carriage count and mode name
* Fix return from assembly linux
* Tie deferred callback to canvas lifetime and add shutdown guards
Use m_canvas->CallAfter instead of wxGetApp().CallAfter to ensure the callback is only executed if the canvas is still alive.
Add early returns if wxGetApp().is_closing() is true before accessing plater or the 3D canvas, reducing the risk of use-after-free during application shutdown.
Addresses feedback regarding potential UI teardown race conditions.
* copilot suggestion
* Use local reference to wxGetApp() in Return button callback
Replace multiple calls to wxGetApp() with a single local reference
to improve readability and avoid redundant function calls.
* Don't assume 0.4mm nozzle; format as "unknown" if not defined.
* Skip nozzle diameter and hardness checks if nozzle info unknown.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
# Description
The Qidi Max 4 and Q2 don't report what they are via /server/info like
the other Qidi printers apparently do. This means that filament matching
is a fool's erand because you'll never actually match anything. this
adds a fallback where we just use the configured machine type. I'm not
sure why we wouldn't just do that all the time, but I wanted to change
as little as possible.
Here's the entirety of the `/server/info` output from a Qidi Max 4:
```
{
"result": {
"klippy_connected": true,
"klippy_state": "ready",
"components": [
"secrets",
"template",
"klippy_connection",
"jsonrpc",
"internal_transport",
"application",
"websockets",
"database",
"dbus_manager",
"file_manager",
"authorization",
"klippy_apis",
"shell_command",
"machine",
"data_store",
"proc_stats",
"job_state",
"job_queue",
"history",
"http_client",
"announcements",
"webcam",
"extensions",
"octoprint_compat",
"timelapse",
"frp_manager",
"mqtt"
],
"failed_components": [],
"registered_directories": [
"config",
"logs",
"gcodes",
"timelapse",
"timelapse_frames",
"config_examples",
"docs"
],
"warnings": [],
"websocket_count": 3,
"moonraker_version": "?",
"missing_klippy_requirements": [],
"api_version": [
1,
4,
0
],
"api_version_string": "1.4.0"
}
}
```
There's no machine name or hostname. This is also what the response
looks like from a Q2, including the exact same API version numbers, so
we cannot match on that either.
In GLCanvas3D::on_mouse, avoid calling SetFocus when the currently focused window is a text input control (wxTextCtrl, wxComboBox or wxSpinCtrl). This prevents deselection bugs when the mouse leaves the window while a text control is focused. Also commented out explicit wx text-control includes near the top of the file.
Only TextCtrl needed
cleaning
Removed unused wxWidgets header includes.
Update GLCanvas3D.cpp
Co-authored-by: yw4z <yw4z@outlook.com>
Co-authored-by: yw4z <ywsyildiz@gmail.com>
The Qidi Max 4 doesn't report that it's a Qidi Max 4 via /server/info
like the other Qidi printers apparently do. This means that filament
matching is a fool's erand because you'll never actually match anything.
this adds a fallback where we just use the configured machine type. I'm
not sure why we wouldn't just do that all the time, but I wanted to
change as little as possible.
Fix: generic locale fallback on all platforms when language is unavailable
Move the locale fallback chain out of the #ifdef __linux__ block so it
applies on macOS and Windows too. Add a base-language fallback step that
strips the region code (e.g. en_IL -> en) before trying the full
fallback chain (current locale, system, best, en_US, en_UK).
Previously, if wxLocale::IsAvailable() failed on non-Linux, the app
would show an error and exit. Now it gracefully falls back to a working
locale.
Co-authored-by: SoftFever <softfeverever@gmail.com>