mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
Fix review findings in the shared wipe tower estimate
A raft is not a reason to reserve a tower. Print::apply runs normalize_fdm_2, which clears enable_prime_tower for a plate that purges one filament unless smooth timelapse or wrapping detection is on, so a single-filament plate with a raft prints no tower at all and the estimate was reserving bed area for one. Drop the input; need_wipe_tower is now exactly the two exceptions normalize_fdm_2 honours, named there so the next reason added has to be checked against it. The GUI preview and the validation containment check each re-derived "is a tower printed here" from the filament count instead of reading the estimate, so both missed the towers printed with no tool change to purge for. They now take the answer from the footprint, which is the drift this shared estimate exists to remove. A tower that is not printed estimates to zero, so its hull is degenerate and every check on it passes trivially - the containment check needs no gate of its own. WipeTowerData::width was written only by the pre-generation estimate and left at zero for the whole post-generation life of the Print, while its neighbour depth held the real value. Set it from the generator in both branches. The plate's height scan transformed every model part's full mesh per instance on each scene reload, discarding all but the z extent. The cached convex hull has the same z extent. A plate loaded from a sliced .gcode.3mf holds no objects and its filaments live in slice_filaments_info; the config-taking get_extruders overload returned an empty list for it, which sized the tower for a placeholder two filaments. It now answers the way the wx overload does, without reaching the plater. Also drop estimate_wipe_tower_size, which has no callers.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_t filaments_cnt, double layer_height, double max_object_height, bool any_raft)
|
||||
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_t filaments_cnt, double layer_height, double max_object_height)
|
||||
{
|
||||
WipeTowerFootprint footprint;
|
||||
footprint.height = max_object_height;
|
||||
@@ -56,8 +56,9 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_
|
||||
const bool dual_nozzle = nozzle_opt != nullptr && nozzle_opt->values.size() == 2;
|
||||
const bool rib_wall = opt_enum("wipe_tower_wall_type", int(WipeTowerWallType::wtwRectangle)) == int(WipeTowerWallType::wtwRib);
|
||||
const bool smooth_timelapse = opt_enum("timelapse_type", int(TimelapseType::tlTraditional)) == int(TimelapseType::tlSmooth);
|
||||
// Reasons a tower is printed with no tool change to purge for.
|
||||
const bool need_wipe_tower = smooth_timelapse || opt_bool("enable_wrapping_detection") || any_raft;
|
||||
// Reasons a tower is printed with no tool change to purge for: the ones that stop
|
||||
// normalize_fdm_2 clearing enable_prime_tower. Its mixed-filament case is not modelled.
|
||||
const bool need_wipe_tower = smooth_timelapse || opt_bool("enable_wrapping_detection");
|
||||
|
||||
// No tool change, nothing to purge; smooth timelapse still primes once.
|
||||
size_t purge_count = 0;
|
||||
@@ -80,7 +81,9 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_
|
||||
|
||||
// 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.
|
||||
if (volume < EPSILON && !need_wipe_tower)
|
||||
// A tool change is a reason on its own: the generator floors the tower whatever the
|
||||
// purge volumes resolve to.
|
||||
if (volume < EPSILON && filaments_cnt < 2 && !need_wipe_tower)
|
||||
return footprint;
|
||||
|
||||
const double min_depth = WipeTower::get_limit_depth_by_height(float(max_object_height));
|
||||
|
||||
@@ -21,9 +21,9 @@ struct WipeTowerFootprint
|
||||
// changes, so a count derived from the model must include them
|
||||
// (Print::extruders(true)) or a real tower is sized as if it were never built.
|
||||
// layer_height: thinnest layer the tower will be planned at.
|
||||
// any_raft: any object on the plate prints a raft, which puts the tower on every layer
|
||||
// below it. Caller-resolved: raft_layers is a PrintObjectConfig key, absent
|
||||
// from Print's config and overridable per object.
|
||||
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_t filaments_cnt, double layer_height, double max_object_height, bool any_raft);
|
||||
//
|
||||
// A raft is deliberately not a reason: normalize_fdm_2 clears enable_prime_tower for a plate
|
||||
// purging one filament unless smooth timelapse or wrapping detection is on.
|
||||
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, size_t filaments_cnt, double layer_height, double max_object_height);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
+14
-15
@@ -1081,22 +1081,21 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
|
||||
if (print_config.enable_wrapping_detection.value && !intersection({wrapping_poly}, convex_hulls_temp).empty()) {
|
||||
return {L("Prime Tower") + L(" is too close to clumping detection area, and collisions will be caused.\n")};
|
||||
}
|
||||
// Skip the containment check for towers that will never be printed (single-filament
|
||||
// prints without smooth timelapse keep the config's tower position but emit nothing).
|
||||
// No gate on "is there a tower": one that is not printed estimates to zero, so the hull
|
||||
// is degenerate and every check passes. Re-deriving it here missed the wrapping-detection
|
||||
// tower on a single-filament plate.
|
||||
// Pre-generation only the body square is tested — the auto-brim estimate can overshoot
|
||||
// the generated brim by several mm and must not hard-fail a print that physically fits.
|
||||
// Post-generation the mesh bottom already includes the real brim, so the exact
|
||||
// footprint is tested.
|
||||
if (filaments_count > 1 || print.enable_timelapse_print()) {
|
||||
// The shared printable polygon is plate-local, while the tower polygons above are
|
||||
// already shifted by the plate origin.
|
||||
Polygons printable_polys = print.get_extruder_shared_printable_polygon();
|
||||
const Point plate_shift(scale_(plate_origin.x()), scale_(plate_origin.y()));
|
||||
for (Polygon &p : printable_polys)
|
||||
p.translate(plate_shift);
|
||||
if (!diff(convex_hulls_temp, printable_polys).empty())
|
||||
return {L("Prime Tower") + L(" is partially outside the printable area, and it cannot be printed.\n")};
|
||||
}
|
||||
// The shared printable polygon is plate-local, while the tower polygons above are
|
||||
// already shifted by the plate origin.
|
||||
Polygons printable_polys = print.get_extruder_shared_printable_polygon();
|
||||
const Point plate_shift(scale_(plate_origin.x()), scale_(plate_origin.y()));
|
||||
for (Polygon &p : printable_polys)
|
||||
p.translate(plate_shift);
|
||||
if (!diff(convex_hulls_temp, printable_polys).empty())
|
||||
return {L("Prime Tower") + L(" is partially outside the printable area, and it cannot be printed.\n")};
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -4005,16 +4004,14 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
|
||||
|
||||
double max_height = 0.;
|
||||
double layer_height = std::numeric_limits<double>::max();
|
||||
bool any_raft = false;
|
||||
for (const PrintObject *object : m_objects) {
|
||||
max_height = std::max(max_height, unscale_(double(object->size().z())));
|
||||
layer_height = std::min(layer_height, object->config().layer_height.value);
|
||||
any_raft = any_raft || object->config().raft_layers.value > 0;
|
||||
}
|
||||
if (max_height < EPSILON)
|
||||
return m_wipe_tower_data;
|
||||
|
||||
const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(m_config, filaments_cnt, layer_height, max_height, any_raft);
|
||||
const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(m_config, filaments_cnt, layer_height, max_height);
|
||||
WipeTowerData &data = const_cast<Print *>(this)->m_wipe_tower_data;
|
||||
data.depth = float(footprint.depth);
|
||||
data.width = float(footprint.width);
|
||||
@@ -4244,6 +4241,7 @@ void Print::_make_wipe_tower()
|
||||
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
|
||||
wipe_tower.generate_new(m_wipe_tower_data.tool_changes);
|
||||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||||
m_wipe_tower_data.width = wipe_tower.width();
|
||||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||||
m_wipe_tower_data.bbx = wipe_tower.get_bbx();
|
||||
m_wipe_tower_data.rib_offset = wipe_tower.get_rib_offset();
|
||||
@@ -4357,6 +4355,7 @@ void Print::_make_wipe_tower()
|
||||
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
|
||||
wipe_tower.generate(m_wipe_tower_data.tool_changes);
|
||||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||||
m_wipe_tower_data.width = wipe_tower.width();
|
||||
m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs();
|
||||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||||
m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
|
||||
|
||||
@@ -782,8 +782,8 @@ struct WipeTowerData
|
||||
|
||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float depth;
|
||||
// Effective width (a rib wall squares the tower). Pre-generation estimate only; once the
|
||||
// tower exists its mesh is exact.
|
||||
// Effective width (a rib wall squares the tower): the estimate until generation, then the
|
||||
// generated width, so it never disagrees with depth.
|
||||
float width;
|
||||
std::vector<std::pair<float, float>> z_and_depth_pairs;
|
||||
float brim_width;
|
||||
|
||||
Reference in New Issue
Block a user