From e14e186bb7c134383928f8f5ce8793e43703077d Mon Sep 17 00:00:00 2001 From: Derrick Date: Mon, 9 Feb 2026 22:25:41 +0800 Subject: [PATCH] Fix wipe tower loaded outside of plate boundaries (#12191) * Add checks to snap wipe tower back to origin when it was loaded outside the plate boundary * Fix Wipe Tower preview outside plate boundary and warning notification - snaps wipe tower preview to nearest edge if it was loaded outside of plate boundary. - Added warning notification to notify position change --- src/slic3r/GUI/GLCanvas3D.cpp | 25 +++++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 11 ++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 64b511ebe6..fd9d1da218 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -38,6 +38,7 @@ #include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/Utils/MacDarkMode.hpp" +#include #include #if ENABLE_RETINA_GL @@ -2831,6 +2832,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float y = dynamic_cast(proj_cfg.option("wipe_tower_y"))->get_at(plate_id); float w = dynamic_cast(m_config->option("prime_tower_width"))->value; float a = dynamic_cast(proj_cfg.option("wipe_tower_rotation_angle"))->value; + // BBS float v = dynamic_cast(m_config->option("prime_volume"))->value; Vec3d plate_origin = ppl.get_plate(plate_id)->get_origin(); @@ -2857,9 +2859,29 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } coordf_t plate_bbox_x_min_local_coord = plate_bbox_2d.min(0) - plate_origin(0); + coordf_t plate_bbox_y_min_local_coord = plate_bbox_2d.min(1) - plate_origin(1); coordf_t plate_bbox_x_max_local_coord = plate_bbox_2d.max(0) - plate_origin(0); coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1); + const float tower_w = (float) wipe_tower_size(0); + const float tower_h = (float) wipe_tower_size(1); + const float min_x = (float) plate_bbox_x_min_local_coord + margin; + const float max_x = (float) plate_bbox_x_max_local_coord - margin; + const float min_y = (float) plate_bbox_y_min_local_coord + margin; + const float max_y = (float) plate_bbox_y_max_local_coord - margin; + + // snap wipe tower back to nearest edge if it was initially loaded outside the plate boundary + float new_x = (x < min_x) ? min_x : ((x + tower_w > max_x) ? (max_x - tower_w) : x); + float new_y = (y < min_y) ? min_y : ((y + tower_h > max_y) ? (max_y - tower_h) : y); + + if (new_x != x || new_y != y) { + // do notification + _set_warning_notification(EWarning::PreviewPrimeTowerOutside, true); + x = new_x; + y = new_y; + } + + if (!current_print->is_step_done(psWipeTower) || !current_print->wipe_tower_data().wipe_tower_mesh_data) { // update for wipe tower position { @@ -9625,6 +9647,9 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) case EWarning::PrimeTowerOutside: text = _u8L("The prime tower extends beyond the plate boundary."); break; + case EWarning::PreviewPrimeTowerOutside: + text = _u8L("Prime tower position exceeded build plate boundaries and was repositioned to the nearest valid edge."); + break; case EWarning::NozzleFilamentIncompatible: { text = _u8L(get_nozzle_filament_incompatible_text()); break; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 17a388c731..6434ab4389 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -381,13 +381,14 @@ class GLCanvas3D ToolHeightOutside, TPUPrintableError, FilamentPrintableError, - LeftExtruderPrintableError, // before slice - RightExtruderPrintableError, // before slice - MultiExtruderPrintableError, // after slice - MultiExtruderHeightOutside, // after slice + LeftExtruderPrintableError, // before slice + RightExtruderPrintableError, // before slice + MultiExtruderPrintableError, // after slice + MultiExtruderHeightOutside, // after slice FilamentUnPrintableOnFirstLayer, MixUsePLAAndPETG, - PrimeTowerOutside, + PrimeTowerOutside, // after slice + PreviewPrimeTowerOutside, // before slice NozzleFilamentIncompatible, MixtureFilamentIncompatible, FlushingVolumeZero