diff --git a/src/libslic3r/GCode/WipeTowerEstimate.cpp b/src/libslic3r/GCode/WipeTowerEstimate.cpp index d8cfe74527..ac1508d9c0 100644 --- a/src/libslic3r/GCode/WipeTowerEstimate.cpp +++ b/src/libslic3r/GCode/WipeTowerEstimate.cpp @@ -107,6 +107,12 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT // normalize_fdm_2 clearing enable_prime_tower. Its mixed-filament case is not modelled. const bool need_wipe_tower = smooth_timelapse || wrapping; + // Fewer than two filaments cannot make a tool change, so only wrapping detection or smooth + // timelapse print a tower then. The flush volume is no proof of one: it is read from the + // matrix of every configured slot, nonzero even when a single one of them is used. + if (filaments_cnt < 2 && !need_wipe_tower) + return footprint; + // A tower printed for one of the reasons above has no tool change to purge for; both // planners give it the idle depth below and nothing more. const size_t purge_count = filaments_cnt > 1 ? (dual_nozzle ? filaments_cnt : filaments_cnt - 1) : 0; @@ -151,14 +157,6 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT purges[longest_ramming].filament_change_length = float(float_at("filament_change_length", filament_ids[longest_ramming], 0.) * double(nozzles.size() - 1)); } - // Both wall types decide this together: over-reserving only wastes bed area, but reporting - // no tower for one that is built collapses the validation hull to a point. - // A tool change is a reason on its own (see the base commit); Type1 already reserves - // per filament, Type2 has only the volume, which can resolve to zero. - const bool has_purge = type1 ? !purges.empty() : volume > EPSILON; - if (!has_purge && filaments_cnt < 2 && !need_wipe_tower) - return footprint; - const double min_depth = WipeTower::get_limit_depth_by_height(float(max_object_height)); const float perimeter_width = float(nozzle_diameter) * 1.25f; // Width_To_Nozzle_Ratio // With nothing to purge, plan_tower_new sizes the tower for wrapping detection or the @@ -171,7 +169,10 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT if (!purges.empty()) side = WipeTower::estimate_rib_tower_bbox_side(purges, float(width), float(layer_height), float(nozzle_diameter), float(extra_spacing), float(rib_width), float(extra_rib_length), float(max_object_height)); else { - const double square = has_purge ? std::sqrt(volume / layer_height * extra_spacing) : idle_depth; + // Type2 squares the tower from its purge volume; Type1 with no purge list (a lone + // filament kept for timelapse or wrapping) sizes for the idle depth. + const bool has_purge = !type1 && volume > EPSILON; + const double square = has_purge ? std::sqrt(volume / layer_height * extra_spacing) : idle_depth; side = WipeTower::rib_footprint_side(float(square), float(square), float(rib_width), float(extra_rib_length), float(max_object_height)); } footprint.width = footprint.depth = side; diff --git a/tests/libslic3r/test_wipe_tower_estimate.cpp b/tests/libslic3r/test_wipe_tower_estimate.cpp index 20644200f3..22b9b2d6c5 100644 --- a/tests/libslic3r/test_wipe_tower_estimate.cpp +++ b/tests/libslic3r/test_wipe_tower_estimate.cpp @@ -335,6 +335,10 @@ TEST_CASE("The shipped defaults size the tower from the flush matrix", "[WipeTow const double flush_volume = WipeTower2::estimate_semm_flush_volume(config, 2); const double expected = std::max(double(WipeTower::get_limit_depth_by_height(5.f)), flush_volume / (0.2 * 50.)); CHECK_THAT(estimate(config, 2, 0.2, 5.).depth, WithinAbs(expected, 1e-6)); + + // The flush volume is nonzero for one slot, but a lone filament makes no tool change. + REQUIRE(WipeTower2::estimate_semm_flush_volume(config, 1) > 0.); + CHECK_THAT(estimate(config, 1, 0.2, 5.).depth, WithinAbs(0., 1e-9)); } TEST_CASE("A config missing a tower key falls back to that key's default", "[WipeTowerEstimate]") {