diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 6584566f40..c339da566a 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -93,6 +93,9 @@ 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 = 1.; +// Margin for system placement of the wipe tower (defaults, re-placement, CLI). Positions +// within WIPE_TOWER_MARGIN stay valid: a user drag down to that limit is respected. +static constexpr double WIPE_TOWER_AUTO_MARGIN = 15.; //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) diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index beb244a39c..bf511ec657 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -2404,13 +2404,26 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic const BoundingBox cb = get_extents(WipeTower2::cone_base_polygon(w, depth, wt_size.z(), cone_angle_opt->getFloat())); wp_brim_width += float(std::max({0., unscaled(cb.max.x()) - w, unscaled(cb.max.y()) - depth, -unscaled(cb.min.x()), -unscaled(cb.min.y())})); } + // A position valid by WIPE_TOWER_MARGIN is the user's choice and stays untouched; an + // invalid one is re-placed with the comfort margin (falling back to the validity bounds + // on cramped plates). std::clamp is UB if lo > hi, so keep every hi >= lo. const float margin = WIPE_TOWER_MARGIN + wp_brim_width; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: wp_brim_width %1%") % wp_brim_width; - - // A tower too deep for the plate leaves no valid position: clamping with hi < lo is UB and - // in release silently returns the negative hi. - x = std::clamp(x, margin, std::max(margin, (float)plate_width - w - margin)); - y = std::clamp(y, margin, std::max(margin, (float)plate_depth - depth - margin)); + const float x_hi = std::max(margin, (float) plate_width - w - margin); + const float y_hi = std::max(margin, (float) plate_depth - depth - margin); + const float margin_c = (float) WIPE_TOWER_AUTO_MARGIN + wp_brim_width; + float x_lo_c = margin_c, x_hi_c = (float) plate_width - w - margin_c; + if (x_lo_c > x_hi_c) { x_lo_c = margin; x_hi_c = x_hi; } + float y_lo_c = margin_c, y_hi_c = (float) plate_depth - depth - margin_c; + if (y_lo_c > y_hi_c) { y_lo_c = margin; y_hi_c = y_hi; } + // Drag clamps reach this limit through the volume's bounding box (post-slice: the real + // mesh, a couple of mm inside this reserved estimate), so a drop can land slightly out + // of bounds — snap it onto the bound; only far-out positions get the comfort re-place. + const float tol = 5.f; + if (x < margin - tol || x > x_hi + tol) x = std::clamp(x, x_lo_c, x_hi_c); + else x = std::clamp(x, margin, x_hi); + if (y < margin - tol || y > y_hi + tol) y = std::clamp(y, y_lo_c, y_hi_c); + else y = std::clamp(y, margin, y_hi); wt_pos(0) = x; wt_pos(1) = y; wt_pos(2) = 0.f; @@ -4504,7 +4517,7 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini // Brim-aware margin: the brim extends outward from the tower position. const float brim_width = float(footprint.brim_width); - const float margin = WIPE_TOWER_MARGIN + brim_width; + const float margin = WIPE_TOWER_AUTO_MARGIN + brim_width; // clamp wipe tower position within plate boundaries {