FIX: modify the behavior of wipe tower

The prime tower is not allowed to extend beyond the plate boundary when moving it.
jira: STUDIO-11817 & STUDIO-12313

Change-Id: Icbb85dad26531b0ac01f088fc086b4ff499ac423
This commit is contained in:
zhimin.zeng 2025-05-23 11:18:24 +08:00 committed by lane.wei
parent 8a22fab6d7
commit 8f4d5f44d7
4 changed files with 25 additions and 52 deletions

View File

@ -75,7 +75,6 @@ static constexpr double INSET_OVERLAP_TOLERANCE = 0.4;
static constexpr double EXTERNAL_INFILL_MARGIN = 3;
static constexpr double BRIDGE_INFILL_MARGIN = 1;
static constexpr double WIPE_TOWER_MARGIN = 0.2;
static constexpr double MIN_WIPE_TOWER_SIZE = 5;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)

View File

@ -3198,34 +3198,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1);
if (!current_print->is_step_done(psWipeTower) || !current_print->wipe_tower_data().wipe_tower_mesh_data) {
Vec2d min_wipe_tower_size(MIN_WIPE_TOWER_SIZE, MIN_WIPE_TOWER_SIZE);
// update for wipe tower position
{
bool need_update = false;
if (x + margin + min_wipe_tower_size(0) > plate_bbox_x_max_local_coord) {
x = plate_bbox_x_max_local_coord - min_wipe_tower_size(0) - margin;
need_update = true;
} else if (x < margin + plate_bbox_x_min_local_coord) {
x = margin + plate_bbox_x_min_local_coord;
need_update = true;
}
if (need_update) {
ConfigOptionFloat wt_x_opt(x);
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_id, 0);
need_update = false;
}
if (y + margin + min_wipe_tower_size(1) > plate_bbox_y_max_local_coord) {
y = plate_bbox_y_max_local_coord - min_wipe_tower_size(1) - margin;
need_update = true;
} else if (y < margin) {
y = margin;
need_update = true;
}
if (need_update) {
ConfigOptionFloat wt_y_opt(y);
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_y"))->set_at(&wt_y_opt, plate_id, 0);
}
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
(float) wipe_tower_size(0), (float) wipe_tower_size(1), (float) wipe_tower_size(2),
a,
@ -3241,15 +3215,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_id)->get_build_volume(true);
BoundingBox plate_bbox2d = BoundingBox(scaled(Vec2f(plate_bbox.min[0], plate_bbox.min[1])), scaled(Vec2f(plate_bbox.max[0], plate_bbox.max[1])));
Vec2f offset = WipeTower::move_box_inside_box(tower_bottom_bbox, plate_bbox2d, scaled(margin));
if (!is_approx(offset[0], 0.f)) {
ConfigOptionFloat wt_x_opt(x + offset[0]);
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_id, 0);
}
if (!is_approx(offset[1], 0.f)) {
ConfigOptionFloat wt_y_opt(y + offset[1]);
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_y"))->set_at(&wt_y_opt, plate_id, 0);
}
int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview(1000 + plate_id, x + plate_origin(0) + offset[0], y + plate_origin(1) + offset[1],
int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
current_print->wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
current_print->wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh,
true,a,/*!print->is_step_done(psWipeTower)*/ true, m_initialized);
@ -3284,10 +3250,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
//BBS:exclude the assmble view
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
_set_warning_notification_if_needed(EWarning::GCodeConflict);
_set_warning_notification(EWarning::FilamentUnPrintableOnFirstLayer, false);
_set_warning_notification_if_needed(EWarning::MultiExtruderPrintableError);
_set_warning_notification_if_needed(EWarning::MultiExtruderHeightOutside);
_update_slice_error_status();
// checks for geometry outside the print volume to render it accordingly
if (!m_volumes.empty()) {
ModelInstanceEPrintVolumeState state;
@ -3297,8 +3260,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
// const bool objectLimited = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Limited);
bool show_read_wipe_tower = wxGetApp().plater()->get_partplate_list().get_selected_plate()->fff_print()->is_step_done(psWipeTower);
bool wipe_tower_outside = m_volumes.check_wipe_tower_outside_state(m_bed.build_volume());
_set_warning_notification(EWarning::PrimeTowerOutside, !wipe_tower_outside);
bool show_wipe_tower_outside_error = show_read_wipe_tower ? !wipe_tower_outside : false;
_set_warning_notification(EWarning::PrimeTowerOutside, show_wipe_tower_outside_error);
auto clash_flag = construct_error_string(object_results, get_object_clashed_text());
auto unprintable_flag= construct_extruder_unprintable_error(object_results, get_left_extruder_unprintable_text(), get_right_extruder_unprintable_text());
@ -3322,7 +3287,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
bool mix_pla_and_petg = cur_plate->check_mixture_of_pla_and_petg(wxGetApp().preset_bundle->full_config());
_set_warning_notification(EWarning::MixUsePLAAndPETG, !mix_pla_and_petg);
bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid && filament_printable;
bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid && filament_printable && !show_wipe_tower_outside_error;
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, model_fits));
ppl.get_curr_plate()->update_slice_ready_status(model_fits);
}
@ -3450,13 +3415,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co
if (wxGetApp().is_editor()) {
//BBS: always load shell at preview, do this in load_shells
//m_gcode_viewer.update_shells_color_by_extruder(m_config);
_set_warning_notification_if_needed(EWarning::ToolHeightOutside);
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
_set_warning_notification_if_needed(EWarning::GCodeConflict);
_set_warning_notification_if_needed(EWarning::MultiExtruderPrintableError);
_set_warning_notification_if_needed(EWarning::MultiExtruderHeightOutside);
_set_warning_notification_if_needed(EWarning::FilamentUnPrintableOnFirstLayer);
_update_slice_error_status();
}
m_gcode_viewer.refresh(gcode_result, str_tool_colors);
@ -6529,6 +6488,16 @@ void GLCanvas3D::render_thumbnail_framebuffer(const std::shared_ptr<OpenGLManage
//BBS: GUI refractor
void GLCanvas3D::_update_slice_error_status()
{
_set_warning_notification_if_needed(EWarning::ToolHeightOutside);
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
_set_warning_notification_if_needed(EWarning::GCodeConflict);
_set_warning_notification_if_needed(EWarning::MultiExtruderPrintableError);
_set_warning_notification_if_needed(EWarning::MultiExtruderHeightOutside);
_set_warning_notification_if_needed(EWarning::FilamentUnPrintableOnFirstLayer);
}
void GLCanvas3D::_switch_toolbars_icon_filename()
{
BackgroundTexture::Metadata background_data;
@ -10868,7 +10837,8 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
text = _u8L("PLA and PETG filaments detected in the mixture. Adjust parameters according to the Wiki to ensure print quality.");
break;
case EWarning::PrimeTowerOutside:
text = _u8L("The prime tower extends beyond the plate boundary, which may cause part of the prime tower to lie outside the printable area after slicing.");
text = _u8L("The prime tower extends beyond the plate boundary.");
error = ErrorType::SLICING_ERROR;
break;
case EWarning::AsemblyInvalid:
{

View File

@ -1181,6 +1181,8 @@ public:
private:
bool _is_shown_on_screen() const;
void _update_slice_error_status();
void _switch_toolbars_icon_filename();
bool _init_toolbars();
bool _init_main_toolbar();

View File

@ -1214,13 +1214,15 @@ void Selection::translate(const Vec3d &displacement, TransformationType transfor
Vec3d tower_origin = m_cache.volumes_data[i].get_volume_position();
Vec3d actual_displacement = displacement;
bool show_read_wipe_tower = wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->fff_print()->is_step_done(psWipeTower);
const double margin = WIPE_TOWER_MARGIN;
float brim_width = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_float("prime_tower_brim_width");
const double margin = show_read_wipe_tower ? WIPE_TOWER_MARGIN : brim_width + 0.5; // 0.5 is the line width of wipe tower
actual_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() *
m_cache.volumes_data[i].get_instance_mirror_matrix())
.inverse() *
displacement;
BoundingBoxf3 tower_bbox = show_read_wipe_tower ? v.bounding_box() : BoundingBoxf3(Vec3d(0, 0, 0), Vec3d(MIN_WIPE_TOWER_SIZE, MIN_WIPE_TOWER_SIZE, MIN_WIPE_TOWER_SIZE));
BoundingBoxf3 tower_bbox = v.bounding_box();
tower_bbox.translate(actual_displacement + tower_origin);
BoundingBox tower_bbox2d = BoundingBox(scaled(Vec2f(tower_bbox.min[0], tower_bbox.min[1])), scaled(Vec2f(tower_bbox.max[0], tower_bbox.max[1])));
Vec2f offset = WipeTower::move_box_inside_box(tower_bbox2d, plate_bbox2d,scaled(margin));