mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 23:01:17 +00:00
Wipe tower sparse layers combination (#15841)
This commit is contained in:
@@ -49,6 +49,48 @@ std::vector<float> compute_compacted_wipe_tower_z(const std::vector<std::vector<
|
||||
return tower_z;
|
||||
}
|
||||
|
||||
bool wipe_tower_sparse_layers_combined(const PrintConfig &config)
|
||||
{
|
||||
return config.wipe_tower_sparse_layers_combination.value && ! wipe_tower_sparse_layers_skipped(config) &&
|
||||
config.timelapse_type.value != TimelapseType::tlSmooth && ! config.enable_wrapping_detection.value;
|
||||
}
|
||||
|
||||
bool wipe_tower_layer_is_combined_away(const std::vector<WipeTower::ToolChangeResult> &layer_tool_changes)
|
||||
{
|
||||
return ! layer_tool_changes.empty() && layer_tool_changes.front().combined_away;
|
||||
}
|
||||
|
||||
std::vector<char> combine_sparse_wipe_tower_layers(std::vector<float> &layer_height,
|
||||
const std::vector<char> &layer_is_sparse,
|
||||
const std::vector<float> &max_layer_height,
|
||||
size_t first_layer_idx)
|
||||
{
|
||||
assert(layer_is_sparse.size() == layer_height.size() && max_layer_height.size() == layer_height.size());
|
||||
std::vector<char> combined_away(layer_height.size(), 0);
|
||||
float pending_height = 0.f; // what the layers folded away so far add up to
|
||||
for (size_t i = 0; i < layer_height.size(); ++i) {
|
||||
// A toolchange has to purge at its own z, so it neither folds away nor takes over the run
|
||||
// below it - and a run always flushes on its own last layer, so nothing is ever pending here.
|
||||
if (! layer_is_sparse[i] || i <= first_layer_idx) {
|
||||
pending_height = 0.f;
|
||||
continue;
|
||||
}
|
||||
const float merged = pending_height + layer_height[i];
|
||||
// Hand the run on only if the next layer can swallow the whole thing; a layer already past
|
||||
// the cap is left alone rather than shrunk.
|
||||
const bool next_takes_it = i + 1 < layer_height.size() && layer_is_sparse[i + 1] &&
|
||||
merged + layer_height[i + 1] <= max_layer_height[i + 1] + float(EPSILON);
|
||||
if (next_takes_it) {
|
||||
combined_away[i] = 1;
|
||||
pending_height = merged;
|
||||
} else {
|
||||
layer_height[i] = merged;
|
||||
pending_height = 0.f;
|
||||
}
|
||||
}
|
||||
return combined_away;
|
||||
}
|
||||
|
||||
inline float align_round(float value, float base)
|
||||
{
|
||||
return std::round(value / base) * base;
|
||||
@@ -1904,6 +1946,7 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
|
||||
//m_bridging(float(config.wipe_tower_bridging)),
|
||||
m_bridging(10.f),
|
||||
m_sparse_layers_skipped(wipe_tower_sparse_layers_skipped(config)),
|
||||
m_sparse_layers_combined(wipe_tower_sparse_layers_combined(config)),
|
||||
m_gcode_flavor(config.gcode_flavor),
|
||||
m_travel_speed(config.travel_speed.get_at(get_extruder_index(config, (unsigned int)initial_tool))),
|
||||
m_current_tool(initial_tool),
|
||||
@@ -2028,6 +2071,16 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
||||
float nozzle_diameter = float(config.nozzle_diameter.get_at(idx));
|
||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||
|
||||
// Orca: max_layer_height is per nozzle, so read it through the filament->nozzle map rather than
|
||||
// by filament id. Zero means three quarters of the nozzle diameter, as in Slicing.cpp.
|
||||
{
|
||||
const std::vector<int> &filament_map = config.filament_map.values; // 1 based nozzle indices
|
||||
const size_t nozzle_idx = idx < filament_map.size() && filament_map[idx] > 0 ? size_t(filament_map[idx] - 1) : 0;
|
||||
const float max_layer_height = float(config.max_layer_height.get_at(nozzle_idx));
|
||||
m_filpar[idx].max_layer_height = max_layer_height > 0.f ? max_layer_height
|
||||
: 0.75f * float(config.nozzle_diameter.get_at(nozzle_idx));
|
||||
}
|
||||
|
||||
float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx));
|
||||
if (max_vol_speed!= 0.f)
|
||||
m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
|
||||
@@ -3001,7 +3054,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (! m_sparse_layers_skipped || toolchanges_on_layer)
|
||||
if (layer_is_printed(toolchanges_on_layer))
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
@@ -3898,7 +3951,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer_new(bool extrude_perimeter,
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (!m_sparse_layers_skipped || toolchanges_on_layer)
|
||||
if (layer_is_printed(toolchanges_on_layer))
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
@@ -4008,7 +4061,7 @@ WipeTower::ToolChangeResult WipeTower::finish_block(const WipeTowerBlock &block,
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (!m_sparse_layers_skipped || toolchanges_on_layer)
|
||||
if (layer_is_printed(toolchanges_on_layer))
|
||||
if (filament_id < m_used_filament_length.size())
|
||||
m_used_filament_length[filament_id] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
@@ -4125,7 +4178,7 @@ WipeTower::ToolChangeResult WipeTower::finish_block_solid(const WipeTowerBlock &
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (!m_sparse_layers_skipped || toolchanges_on_layer)
|
||||
if (layer_is_printed(toolchanges_on_layer))
|
||||
if (filament_id < m_used_filament_length.size())
|
||||
m_used_filament_length[filament_id] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
@@ -4668,6 +4721,15 @@ void WipeTower::calc_block_infill_gap()
|
||||
m_extra_spacing = 1.f;
|
||||
}
|
||||
|
||||
// A folded layer is generated like any other but thrown away by the emitter, so its extrusions must
|
||||
// not be charged to the filament used.
|
||||
bool WipeTower::layer_is_printed(bool toolchanges_on_layer) const
|
||||
{
|
||||
if (m_layer_info != m_plan.end() && m_layer_info->combined_away)
|
||||
return false;
|
||||
return ! m_sparse_layers_skipped || toolchanges_on_layer;
|
||||
}
|
||||
|
||||
void WipeTower::plan_tower_new()
|
||||
{
|
||||
if (m_wipe_tower_brim_width < 0) m_wipe_tower_brim_width = get_auto_brim_by_height(m_wipe_tower_height);
|
||||
@@ -4820,6 +4882,9 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
|
||||
if (m_plan.empty())
|
||||
return;
|
||||
//m_extra_spacing = 1.f;
|
||||
// Before planning: the layer heights this rewrites feed the extrusion flow of every later pass.
|
||||
if (m_sparse_layers_combined)
|
||||
combine_sparse_wipe_tower_plan(m_plan, m_filpar, m_first_layer_idx, m_current_tool);
|
||||
m_wipe_tower_height = m_plan.back().z;//real wipe_tower_height
|
||||
plan_tower_new();
|
||||
m_layer_info = m_plan.begin();
|
||||
@@ -5014,6 +5079,9 @@ void WipeTower::generate_new(std::vector<std::vector<WipeTower::ToolChangeResult
|
||||
if (only_generate_wall && !timelapse_wall.gcode.empty()) {
|
||||
layer_result.insert(layer_result.begin(), std::move(timelapse_wall));
|
||||
}
|
||||
if (layer.combined_away)
|
||||
for (WipeTower::ToolChangeResult &tcr : layer_result)
|
||||
tcr.combined_away = true;
|
||||
result.emplace_back(std::move(layer_result));
|
||||
}
|
||||
assert(m_outer_wall.size() == m_plan.size());
|
||||
@@ -5179,7 +5247,7 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall(bool is_new_mode)
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (!m_sparse_layers_skipped || toolchanges_on_layer)
|
||||
if (layer_is_printed(toolchanges_on_layer))
|
||||
if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
return construct_tcr(writer, false, old_tool, true, false, 0.f, false);
|
||||
|
||||
@@ -152,6 +152,10 @@ public:
|
||||
bool is_contact = false;
|
||||
NozzleChangeResult nozzle_change_result;
|
||||
|
||||
// Orca: folded into a later, thicker layer, so the emitter drops it. Set by the tower, so
|
||||
// the two cannot disagree about which layers print.
|
||||
bool combined_away = false;
|
||||
|
||||
// Sum the total length of the extrusion.
|
||||
float total_extrusion_length_in_plane() {
|
||||
float e_length = 0.f;
|
||||
@@ -391,6 +395,8 @@ public:
|
||||
float filament_tower_interface_pre_extrusion_dist = 0;
|
||||
float filament_tower_interface_pre_extrusion_length = 0;
|
||||
float filament_petg_pre_extrusion_offset_dist = 0;
|
||||
// Tallest layer this filament's nozzle can lay down; caps the sparse layer combination.
|
||||
float max_layer_height = 0.f;
|
||||
};
|
||||
|
||||
|
||||
@@ -522,6 +528,7 @@ private:
|
||||
//float m_extra_loading_move = 0.f;
|
||||
float m_bridging = 0.f;
|
||||
bool m_sparse_layers_skipped = false;
|
||||
bool m_sparse_layers_combined = false;
|
||||
// BBS: remove useless config
|
||||
//bool m_set_extruder_trimpot = false;
|
||||
bool m_adhesion = true;
|
||||
@@ -595,6 +602,8 @@ private:
|
||||
}
|
||||
// Calculates depth for all layers and propagates them downwards
|
||||
void plan_tower();
|
||||
// Whether the layer reaches the G-code, and so whether its extrusions count as filament used.
|
||||
bool layer_is_printed(bool toolchanges_on_layer) const;
|
||||
|
||||
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
|
||||
void make_wipe_tower_square();
|
||||
@@ -634,6 +643,8 @@ private:
|
||||
float depth; // depth of the layer based on all layers above
|
||||
float extra_spacing;
|
||||
bool extruder_fill{true};
|
||||
// Folded into a later, thicker layer, so this one prints nothing at all.
|
||||
bool combined_away{false};
|
||||
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
|
||||
|
||||
std::vector<ToolChange> tool_changes;
|
||||
@@ -700,6 +711,62 @@ std::vector<float> compute_compacted_wipe_tower_z(const std::vector<std::vector<
|
||||
float base_z = 0.f);
|
||||
|
||||
|
||||
// Combination rule for wipe_tower_sparse_layers_combination. Nothing is compacted - the tower keeps
|
||||
// following the object - but a run of consecutive toolchange-free layers prints as one thicker layer,
|
||||
// the way infill combination merges sparse infill. Shared so that neither tower generator nor the
|
||||
// G-code emitter can combine on its own.
|
||||
|
||||
// Whether sparse layers are really combined. Skipping them outright is the stronger answer to the
|
||||
// same problem and wins over this; smooth timelapse and wrapping detection need a tower on every
|
||||
// layer, so they rule it out too.
|
||||
bool wipe_tower_sparse_layers_combined(const PrintConfig &config);
|
||||
|
||||
// A planned layer folded into a later, thicker one prints nothing at all.
|
||||
bool wipe_tower_layer_is_combined_away(const std::vector<WipeTower::ToolChangeResult> &layer_tool_changes);
|
||||
|
||||
// Folds runs of sparse layers into one. layer_height is raised in place on the layer that prints a
|
||||
// run - always its last, so the merged extrusion lands on top of what it covers - and the returned
|
||||
// mask marks the layers that now print nothing. A run stops growing once one more layer would pass
|
||||
// max_layer_height of the nozzle that prints it. first_layer_idx and below never combine: the
|
||||
// tower's first layer carries the brim.
|
||||
std::vector<char> combine_sparse_wipe_tower_layers(std::vector<float> &layer_height,
|
||||
const std::vector<char> &layer_is_sparse,
|
||||
const std::vector<float> &max_layer_height,
|
||||
size_t first_layer_idx);
|
||||
|
||||
// Applies the rule above to a planned tower. Either generator's plan fits: both carry height,
|
||||
// tool_changes and combined_away per layer, and index their filament parameters by tool.
|
||||
template<class PlanLayers, class FilamentParams>
|
||||
void combine_sparse_wipe_tower_plan(PlanLayers &plan, const FilamentParams &filpar, size_t first_layer_idx, size_t initial_tool)
|
||||
{
|
||||
const size_t n = plan.size();
|
||||
std::vector<float> heights(n);
|
||||
std::vector<char> sparse(n);
|
||||
std::vector<float> caps(n);
|
||||
|
||||
// A layer with no toolchange prints with the filament the layer below left loaded.
|
||||
size_t tool = initial_tool;
|
||||
for (const auto &layer : plan)
|
||||
if (! layer.tool_changes.empty()) {
|
||||
tool = layer.tool_changes.front().old_tool;
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
heights[i] = plan[i].height;
|
||||
sparse[i] = plan[i].tool_changes.empty() ? 1 : 0;
|
||||
caps[i] = tool < filpar.size() ? filpar[tool].max_layer_height : 0.f;
|
||||
if (! plan[i].tool_changes.empty())
|
||||
tool = plan[i].tool_changes.back().new_tool;
|
||||
}
|
||||
|
||||
const std::vector<char> combined_away = combine_sparse_wipe_tower_layers(heights, sparse, caps, first_layer_idx);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
plan[i].height = heights[i];
|
||||
plan[i].combined_away = combined_away[i] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // WipeTowerPrusaMM_hpp_
|
||||
|
||||
@@ -1033,6 +1033,7 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
|
||||
m_z_pos(0.f),
|
||||
m_bridging(float(config.wipe_tower_bridging)),
|
||||
m_sparse_layers_skipped(wipe_tower_sparse_layers_skipped(config)),
|
||||
m_sparse_layers_combined(wipe_tower_sparse_layers_combined(config)),
|
||||
m_gcode_flavor(config.gcode_flavor),
|
||||
m_travel_speed(config.travel_speed.get_at(get_extruder_index(config, (unsigned int)initial_tool))),
|
||||
m_infill_speed(default_region_config.sparse_infill_speed.get_at(get_extruder_index(config, (unsigned int)initial_tool))),
|
||||
@@ -1150,6 +1151,16 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
|
||||
float nozzle_diameter = float(config.nozzle_diameter.get_at(idx));
|
||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||
|
||||
// Orca: max_layer_height is per nozzle, so read it through the filament->nozzle map rather than
|
||||
// by filament id. Zero means three quarters of the nozzle diameter, as in Slicing.cpp.
|
||||
{
|
||||
const std::vector<int> &filament_map = config.filament_map.values; // 1 based nozzle indices
|
||||
const size_t nozzle_idx = idx < filament_map.size() && filament_map[idx] > 0 ? size_t(filament_map[idx] - 1) : 0;
|
||||
const float max_layer_height = float(config.max_layer_height.get_at(nozzle_idx));
|
||||
m_filpar[idx].max_layer_height = max_layer_height > 0.f ? max_layer_height
|
||||
: 0.75f * float(config.nozzle_diameter.get_at(nozzle_idx));
|
||||
}
|
||||
|
||||
float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx));
|
||||
if (max_vol_speed!= 0.f)
|
||||
m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
|
||||
@@ -2103,7 +2114,9 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (! m_sparse_layers_skipped || toolchanges_on_layer || first_layer) {
|
||||
// A folded layer prints nothing, so it consumes nothing and adds no height of its own.
|
||||
const bool combined_away = m_layer_info != m_plan.end() && m_layer_info->combined_away;
|
||||
if ((! m_sparse_layers_skipped || toolchanges_on_layer || first_layer) && ! combined_away) {
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
m_current_height += m_layer_info->height;
|
||||
@@ -2435,6 +2448,10 @@ void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>>
|
||||
if (m_plan.empty())
|
||||
return;
|
||||
|
||||
// Before planning: the layer heights this rewrites feed the extrusion flow of every later pass.
|
||||
if (m_sparse_layers_combined)
|
||||
combine_sparse_wipe_tower_plan(m_plan, m_filpar, m_first_layer_idx, m_current_tool);
|
||||
|
||||
plan_tower();
|
||||
#if 1
|
||||
for (int i=0;i<5;++i) {
|
||||
@@ -2533,6 +2550,10 @@ void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>>
|
||||
layer_result[idx] = merge_tcr(layer_result[idx], finish_layer_tcr);
|
||||
}
|
||||
|
||||
if (layer.combined_away)
|
||||
for (WipeTower::ToolChangeResult &tcr : layer_result)
|
||||
tcr.combined_away = true;
|
||||
|
||||
result.emplace_back(std::move(layer_result));
|
||||
|
||||
if (m_used_filament_length_until_layer.empty() || m_used_filament_length_until_layer.back().first != layer.z)
|
||||
|
||||
@@ -200,6 +200,8 @@ public:
|
||||
float tower_interface_pre_extrusion_length = 0.f;
|
||||
float tower_ironing_area = 4.f;
|
||||
float tower_interface_purge_length = 0.f;
|
||||
// Tallest layer this filament's nozzle can lay down; caps the sparse layer combination.
|
||||
float max_layer_height = 0.f;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -268,6 +270,7 @@ private:
|
||||
float m_extra_loading_move = 0.f;
|
||||
float m_bridging = 0.f;
|
||||
bool m_sparse_layers_skipped = false;
|
||||
bool m_sparse_layers_combined = false;
|
||||
bool m_set_extruder_trimpot = false;
|
||||
bool m_adhesion = true;
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
@@ -368,6 +371,8 @@ private:
|
||||
float z; // z position of the layer
|
||||
float height; // layer height
|
||||
float depth; // depth of the layer based on all layers above
|
||||
// Folded into a later, thicker layer, so this one prints nothing at all.
|
||||
bool combined_away{false};
|
||||
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
|
||||
|
||||
std::vector<ToolChange> tool_changes;
|
||||
|
||||
Reference in New Issue
Block a user