diff --git a/src/libslic3r/GCode/WipeTowerEstimate.cpp b/src/libslic3r/GCode/WipeTowerEstimate.cpp index 6fee774e9c..f40f2899fd 100644 --- a/src/libslic3r/GCode/WipeTowerEstimate.cpp +++ b/src/libslic3r/GCode/WipeTowerEstimate.cpp @@ -96,12 +96,9 @@ 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; - // No tool change, nothing to purge; smooth timelapse still primes once. - size_t purge_count = 0; - if (filaments_cnt > 1) - purge_count = dual_nozzle ? filaments_cnt : filaments_cnt - 1; - else if (smooth_timelapse) - purge_count = 1; + // 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; // Type2 purges one volume per tool change. Type1 plans per filament below; here the volume // only decides whether a tower exists. diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index bf511ec657..e73d43b782 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -2341,10 +2341,12 @@ WipeTowerFootprint PartPlate::estimate_wipe_tower_footprint(const DynamicPrintCo if (std::find(plate_extruders.begin(), plate_extruders.end(), id) == plate_extruders.end()) plate_extruders.push_back(id); // The wipe tower filament joins the tool ordering even when unused (Print::extruders), so - // validation counts it. + // validation counts it - but only where there is a tower to join, which is the + // has_wipe_tower() half of that guard. const ConfigOption *wipe_tower_filament_opt = config.option("wipe_tower_filament"); + const ConfigOption *enable_prime_tower_opt = config.option("enable_prime_tower"); const int wipe_tower_filament = wipe_tower_filament_opt != nullptr ? wipe_tower_filament_opt->getInt() : 0; - if (plate_extruders.size() > 1 && wipe_tower_filament > 0 && + if (enable_prime_tower_opt != nullptr && enable_prime_tower_opt->getBool() && plate_extruders.size() > 1 && wipe_tower_filament > 0 && std::find(plate_extruders.begin(), plate_extruders.end(), wipe_tower_filament) == plate_extruders.end()) plate_extruders.push_back(wipe_tower_filament); if (plate_extruders.empty()) diff --git a/tests/libslic3r/test_wipe_tower_estimate.cpp b/tests/libslic3r/test_wipe_tower_estimate.cpp index ae0a92f40a..fee5c1f152 100644 --- a/tests/libslic3r/test_wipe_tower_estimate.cpp +++ b/tests/libslic3r/test_wipe_tower_estimate.cpp @@ -171,22 +171,28 @@ TEST_CASE("A single filament only gets a tower when one is printed anyway", "[Wi config.set_key_value("raft_layers", new ConfigOptionInt(0)); config.set_deserialize_strict("timelapse_type", "1"); - // Smooth timelapse primes the single filament once: 10 mm, lifted to the floor. + // A tower printed with no tool change is exactly the planner's idle depth: there is + // nothing to purge, and WipeTower2 sizes it at the stability floor. CHECK_THAT(estimate(config, 1, 0.2, 100.).depth, WithinAbs(20., 1e-9)); - CHECK_THAT(estimate(config, 1, 0.2, 5.).depth, WithinAbs(10., 1e-9)); + CHECK_THAT(estimate(config, 1, 0.2, 5.).depth, WithinAbs(WipeTower::get_limit_depth_by_height(5.f), 1e-9)); } -TEST_CASE("A tool change reserves the stability floor even with nothing to purge", "[WipeTowerEstimate]") { - // The purge volumes are configurable down to zero, but the tool changes are still printed on - // the tower and the generator still floors it, so the estimate has to floor it too. - const double height = GENERATE(5., 100.); - const float floor = WipeTower::get_limit_depth_by_height(float(height)); - DynamicPrintConfig config = make_config(GENERATE("rectangle", "rib")); +TEST_CASE("A tool change reserves a tower even with nothing to purge", "[WipeTowerEstimate]") { + // The purge volumes are configurable down to zero, but the tool changes are still printed + // on the tower and both planners still floor it - so the estimate has to floor it too. + // Type1 plans per filament and already reserves one; Type2 has only the volume to go on. + const double height = GENERATE(5., 100.); + const float floor = WipeTower::get_limit_depth_by_height(float(height)); + const char *wall = GENERATE("rectangle", "rib"); + DynamicPrintConfig config = make_config(wall); config.set_key_value("prime_volume", new ConfigOptionFloat(0.)); + config.set_key_value("filament_prime_volume", new ConfigOptionFloats({0.})); - CHECK(estimate(config, 3, 0.2, height).depth >= floor); + CHECK(estimate(config, 3, 0.2, height, WipeTowerType::Type2).depth >= floor); + CHECK(estimate(config, 3, 0.2, height, WipeTowerType::Type1).depth >= floor); // Still nothing for a lone filament with no other reason. - CHECK_THAT(estimate(config, 1, 0.2, height).depth, WithinAbs(0., 1e-9)); + CHECK_THAT(estimate(config, 1, 0.2, height, WipeTowerType::Type2).depth, WithinAbs(0., 1e-9)); + CHECK_THAT(estimate(config, 1, 0.2, height, WipeTowerType::Type1).depth, WithinAbs(0., 1e-9)); } TEST_CASE("Both wall types agree on whether there is a tower at all", "[WipeTowerEstimate]") {