Add Local-Z wipe tower reserve planning and pass ordering

Add startup profiling for GUI initialization
Drop unusable Bonjour sockets and harden socket cleanup
Add /FS to MSVC parallel compile options
This commit is contained in:
Rad
2026-03-10 19:33:36 +01:00
parent 7c7d06a159
commit 153d35ded0
21 changed files with 961 additions and 132 deletions
+1 -1
View File
@@ -191,7 +191,7 @@ endif ()
if (MSVC) if (MSVC)
if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL) if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL)
add_compile_options(/MP) add_compile_options(/MP /FS)
endif () endif ()
# /bigobj (Increase Number of Sections in .Obj file) # /bigobj (Increase Number of Sections in .Obj file)
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
+275 -21
View File
@@ -5,6 +5,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "I18N.hpp" #include "I18N.hpp"
#include "GCode.hpp" #include "GCode.hpp"
#include "LocalZOrderOptimizer.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp" #include "EdgeGrid.hpp"
@@ -12,6 +13,7 @@
#include "GCode/PrintExtents.hpp" #include "GCode/PrintExtents.hpp"
#include "GCode/Thumbnails.hpp" #include "GCode/Thumbnails.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
#include "GCode/WipeTower2.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "Utils.hpp" #include "Utils.hpp"
@@ -428,7 +430,9 @@ static inline Point wipe_tower_point_to_object_point(GCode& gcodegen, const Vec2
std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const
{ {
if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool)
throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); throw Slic3r::InvalidArgument(Slic3r::format(
"Error: WipeTowerIntegration::append_tcr unexpected toolchange: layer_idx=%1% tool_change_idx=%2% requested=%3% expected=%4% initial=%5%",
m_layer_idx, m_tool_change_idx, new_extruder_id, tcr.new_tool, tcr.initial_tool));
std::string gcode; std::string gcode;
@@ -688,7 +692,9 @@ std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::T
std::string WipeTowerIntegration::append_tcr2(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const std::string WipeTowerIntegration::append_tcr2(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const
{ {
if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool)
throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); throw Slic3r::InvalidArgument(Slic3r::format(
"Error: WipeTowerIntegration::append_tcr unexpected toolchange: layer_idx=%1% tool_change_idx=%2% requested=%3% expected=%4% initial=%5%",
m_layer_idx, m_tool_change_idx, new_extruder_id, tcr.new_tool, tcr.initial_tool));
std::string gcode; std::string gcode;
@@ -922,25 +928,204 @@ std::string WipeTowerIntegration::prime(GCode& gcodegen)
return gcode; return gcode;
} }
std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned) std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned,
double local_z_nominal_layer_z)
{ {
std::string gcode; std::string gcode;
auto emit_local_z_unplanned_toolchange = [&]() -> std::string { auto emit_local_z_unplanned_toolchange = [&]() -> std::string {
if (extruder_id < 0 || !gcodegen.writer().need_toolchange(extruder_id)) if (extruder_id < 0 || !gcodegen.writer().need_toolchange(extruder_id))
return ""; return "";
if (m_layer_idx < 0 || size_t(m_layer_idx) >= m_local_z_reserve_boxes.size() ||
// Local-Z phase-b may introduce extra intra-layer toolchanges that were not part size_t(m_layer_idx) >= m_local_z_reserve_slot_idx.size()) {
// of the preplanned wipe tower sequence. Replaying a full wipe-tower template for
// each of those extra switches overprints the same tower layer at micro-step Zs
// and turns the tower into mush. Keep these extra switches local-Z-only by doing
// a direct toolchange here and leave the normal wipe tower plan untouched.
BOOST_LOG_TRIVIAL(debug) << "Local-Z unplanned toolchange using direct extruder switch" BOOST_LOG_TRIVIAL(debug) << "Local-Z unplanned toolchange using direct extruder switch"
<< " layer_idx=" << m_layer_idx << " layer_idx=" << m_layer_idx
<< " extruder_id=" << extruder_id << " extruder_id=" << extruder_id
<< " tool_change_idx=" << m_tool_change_idx; << " tool_change_idx=" << m_tool_change_idx;
return gcodegen.set_extruder(unsigned(extruder_id), return gcodegen.set_extruder(unsigned(extruder_id),
gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value); gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value);
}
size_t &slot_idx = m_local_z_reserve_slot_idx[size_t(m_layer_idx)];
const auto &layer_slots = m_local_z_reserve_boxes[size_t(m_layer_idx)];
if (slot_idx >= layer_slots.size() || layer_slots.empty()) {
BOOST_LOG_TRIVIAL(warning) << "Local-Z toolchange reserve exhausted"
<< " layer_idx=" << m_layer_idx
<< " extruder_id=" << extruder_id
<< " reserved_slots=" << layer_slots.size()
<< " consumed_slots=" << slot_idx;
return gcodegen.set_extruder(unsigned(extruder_id),
gcodegen.writer().get_position().z() - gcodegen.config().z_offset.value);
}
const WipeTower::box_coordinates &slot = layer_slots[slot_idx++];
const double current_z = gcodegen.writer().get_position().z();
const double tower_z = local_z_nominal_layer_z >= 0. ? local_z_nominal_layer_z : current_z;
const double toolchange_print_z = tower_z - gcodegen.config().z_offset.value;
if (m_layer_idx >= (int)m_tool_changes.size() || m_tool_changes[m_layer_idx].empty()) {
BOOST_LOG_TRIVIAL(debug) << "Local-Z reserve has no matching wipe-tower layer metadata, using direct extruder switch"
<< " layer_idx=" << m_layer_idx
<< " extruder_id=" << extruder_id;
return gcodegen.set_extruder(unsigned(extruder_id), toolchange_print_z);
}
const float layer_height = m_tool_changes[m_layer_idx].front().layer_height;
if (gcodegen.m_curr_print != nullptr && gcodegen.writer().extruder() != nullptr) {
const Print& print = *gcodegen.m_curr_print;
const PrintConfig& print_config = print.config();
const size_t current_tool = gcodegen.writer().extruder()->id();
std::vector<std::vector<float>> wipe_volumes = WipeTower2::extract_wipe_volumes(print_config);
WipeTower2 local_z_wipe_tower(print_config,
print.default_region_config(),
print.get_plate_index(),
print.get_plate_origin(),
wipe_volumes,
current_tool);
for (size_t extruder_idx = 0; extruder_idx < print_config.nozzle_diameter.size(); ++extruder_idx)
local_z_wipe_tower.set_extruder(extruder_idx, print_config);
local_z_wipe_tower.set_current_tool(current_tool);
local_z_wipe_tower.set_layer(float(toolchange_print_z), layer_height, 0, m_layer_idx == 0, false);
WipeTower::ToolChangeResult local_z_tcr =
local_z_wipe_tower.local_z_tool_change(size_t(extruder_id), slot, float(print_config.prime_volume));
BOOST_LOG_TRIVIAL(debug) << "Local-Z toolchange emitted via wipe tower mini-toolchange"
<< " layer_idx=" << m_layer_idx
<< " extruder_id=" << extruder_id
<< " reserve_slot=" << (slot_idx - 1);
return gcodegen.is_BBL_Printer() ? append_tcr(gcodegen, local_z_tcr, extruder_id, tower_z) :
append_tcr2(gcodegen, local_z_tcr, extruder_id, tower_z);
}
const float nozzle_diameter = float(gcodegen.config().nozzle_diameter.get_at(size_t(extruder_id)));
const float line_width = nozzle_diameter * 1.25f;
const float extra_flow = float(gcodegen.config().wipe_tower_extra_flow.value) / 100.f;
const float extra_spacing = float(gcodegen.config().wipe_tower_extra_spacing.value) / 100.f;
const float filament_area = float((M_PI / 4.f) * std::pow(gcodegen.config().filament_diameter.get_at(size_t(extruder_id)), 2));
const float flow_per_mm =
filament_area > 0.f ?
(layer_height * (line_width - layer_height * float(1.f - M_PI / 4.f)) / filament_area) * std::max(0.f, extra_flow) :
0.f;
if (line_width <= 0.f || flow_per_mm <= 0.f)
return gcodegen.set_extruder(unsigned(extruder_id), toolchange_print_z);
const float inset_x = std::min(line_width, std::max(0.f, (slot.ru.x() - slot.ld.x()) * 0.25f));
const float inset_y = std::min(line_width * 0.5f, std::max(0.f, (slot.ru.y() - slot.rd.y()) * 0.25f));
const float x_min = slot.ld.x() + inset_x;
const float x_max = slot.rd.x() - inset_x;
const float y_min = slot.ld.y() + inset_y;
const float y_max = slot.lu.y() - inset_y;
if (x_max - x_min <= float(EPSILON) || y_max - y_min <= float(EPSILON))
return gcodegen.set_extruder(unsigned(extruder_id), toolchange_print_z);
std::vector<Vec2f> local_path;
local_path.reserve(16);
local_path.emplace_back(x_min, y_min);
local_path.emplace_back(x_max, y_min);
bool moving_left = true;
float y = y_min;
const float line_spacing = std::max(line_width, line_width * std::max(1.f, extra_spacing));
while (y + line_spacing < y_max - float(EPSILON)) {
y = std::min(y + line_spacing, y_max);
local_path.emplace_back(moving_left ? x_max : x_min, y);
local_path.emplace_back(moving_left ? x_min : x_max, y);
moving_left = !moving_left;
}
const float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f {
return Eigen::Rotation2Df(alpha) * pt + m_wipe_tower_pos;
};
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
const Vec2f start_pos = transform_wt_pt(local_path.front());
const Vec2f start_machine = start_pos + plate_origin_2d;
gcodegen.m_next_wipe_x = start_pos.x();
gcodegen.m_next_wipe_y = start_pos.y();
gcode += gcodegen.writer().unlift();
if (gcodegen.writer().extruder() != nullptr) {
auto type = ZHopType(gcodegen.m_config.z_hop_types.get_at(gcodegen.m_writer.extruder()->id()));
if (type == ZHopType::zhtAuto)
type = ZHopType::zhtSpiral;
if (gcodegen.m_config.z_hop_when_prime.get_at(gcodegen.m_writer.extruder()->id()))
gcode += gcodegen.retract(false, false, gcodegen.to_lift_type(type));
}
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
gcode += gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, start_machine), erMixed,
"Travel to Local-Z wipe tower reserve");
gcode += gcodegen.unretract();
if (!is_approx(tower_z, current_z)) {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(tower_z, "Travel to Local-Z tower layer");
gcode += gcodegen.writer().unretract();
}
gcode += gcodegen.set_extruder(unsigned(extruder_id), toolchange_print_z);
gcode += gcodegen.writer().travel_to_z(tower_z, "Force restore Local-Z tower Z", true);
{
Vec3d restored_position{gcodegen.writer().get_position()};
restored_position.z() = tower_z;
gcodegen.writer().set_position(restored_position);
}
gcode += gcodegen.unretract();
gcode += gcodegen.writer().travel_to_xy(start_machine.cast<double>(), "Return to Local-Z wipe tower reserve");
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + float_to_string_decimal_point(layer_height, 3) + "\n";
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) + ExtrusionEntity::role_to_string(erWipeTower) + "\n";
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) + float_to_string_decimal_point(line_width, 3) + "\n";
double feedrate = std::max(1.0, double(gcodegen.config().wipe_tower_max_purge_speed.value)) * 60.0;
if (m_layer_idx == 0)
feedrate = std::min(feedrate, std::max(1.0, double(gcodegen.config().initial_layer_speed.value)) * 60.0);
gcode += gcodegen.writer().set_speed(feedrate, "Local-Z wipe tower reserve");
for (size_t point_idx = 1; point_idx < local_path.size(); ++point_idx) {
const Vec2f machine_pt = transform_wt_pt(local_path[point_idx]) + plate_origin_2d;
const double length = (local_path[point_idx] - local_path[point_idx - 1]).norm();
if (length <= EPSILON)
continue;
gcode += gcodegen.writer().extrude_to_xy(machine_pt.cast<double>(), flow_per_mm * float(length),
point_idx == 1 ? "Local-Z tower purge" : std::string());
}
const Vec2f end_machine = transform_wt_pt(local_path.back()) + plate_origin_2d;
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_machine));
gcodegen.m_wipe.reset_path();
for (size_t point_idx = local_path.size(); point_idx-- > 0;) {
const Vec2f machine_pt = transform_wt_pt(local_path[point_idx]) + plate_origin_2d;
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, machine_pt));
}
if (!is_approx(tower_z, current_z)) {
const Extruder *active_extruder = gcodegen.writer().extruder();
const bool can_wipe = active_extruder != nullptr &&
gcodegen.config().wipe.get_at(active_extruder->id()) &&
gcodegen.m_wipe.has_path() &&
scale_(gcodegen.config().wipe_distance.get_at(active_extruder->id())) > SCALED_EPSILON;
if (can_wipe) {
Wipe::RetractionValues wipe_retractions = gcodegen.m_wipe.calculateWipeRetractionLengths(gcodegen, false);
gcode += gcodegen.writer().retract(true, wipe_retractions.retractLengthBeforeWipe);
gcode += gcodegen.m_wipe.wipe(gcodegen, wipe_retractions.retractLengthDuringWipe, false, false);
}
gcode += gcodegen.writer().retract();
gcodegen.m_wipe.reset_path();
gcode += gcodegen.writer().travel_to_z(current_z, "Travel back to Local-Z pass");
gcode += gcodegen.writer().unretract();
}
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
BOOST_LOG_TRIVIAL(debug) << "Local-Z toolchange emitted on wipe tower reserve"
<< " layer_idx=" << m_layer_idx
<< " extruder_id=" << extruder_id
<< " reserve_slot=" << (slot_idx - 1);
return gcode;
}; };
if (local_z_unplanned) if (local_z_unplanned)
@@ -2633,6 +2818,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream& file, ThumbnailsGenerato
if (has_wipe_tower && !layers_to_print.empty()) { if (has_wipe_tower && !layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(),
*print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes,
print.wipe_tower_data().local_z_reserve_boxes,
*print.wipe_tower_data().final_purge.get())); *print.wipe_tower_data().final_purge.get()));
// BBS // BBS
file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height")); file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
@@ -5108,6 +5294,31 @@ LayerResult GCode::process_layer(const Print& print,
LocalZPassBucket* bucket { nullptr }; LocalZPassBucket* bucket { nullptr };
}; };
auto same_local_z_pass_group = [](const LocalZPassRef& lhs, const LocalZPassRef& rhs) {
assert(lhs.bucket != nullptr && rhs.bucket != nullptr);
assert(lhs.bucket->plan != nullptr && rhs.bucket->plan != nullptr);
return lhs.layer_to_print_idx == rhs.layer_to_print_idx &&
std::abs(lhs.bucket->plan->print_z - rhs.bucket->plan->print_z) <= EPSILON;
};
auto local_z_bucket_extruders = [](const LocalZPassBucket& bucket) {
std::vector<unsigned int> extruders;
extruders.reserve(bucket.by_extruder.size());
for (const auto& by_extruder_entry : bucket.by_extruder) {
if (!by_extruder_entry.second.empty())
extruders.push_back(by_extruder_entry.first);
}
return extruders;
};
auto shared_local_z_extruder = [](const std::vector<unsigned int>& lhs, const std::vector<unsigned int>& rhs) -> int {
for (unsigned int extruder_id : lhs) {
if (std::find(rhs.begin(), rhs.end(), extruder_id) != rhs.end())
return static_cast<int>(extruder_id);
}
return -1;
};
std::vector<LocalZPassRef> local_z_pass_refs; std::vector<LocalZPassRef> local_z_pass_refs;
if (local_z_perimeter_phase_b_enabled) { if (local_z_perimeter_phase_b_enabled) {
for (size_t layer_to_print_idx = 0; layer_to_print_idx < local_z_layer_contexts.size(); ++layer_to_print_idx) { for (size_t layer_to_print_idx = 0; layer_to_print_idx < local_z_layer_contexts.size(); ++layer_to_print_idx) {
@@ -5140,15 +5351,42 @@ LayerResult GCode::process_layer(const Print& print,
} }
} }
std::vector<unsigned int> layer_extruders = layer_tools.extruders;
for (const auto& by_extruder_entry : by_extruder) {
if (std::find(layer_extruders.begin(), layer_extruders.end(), by_extruder_entry.first) == layer_extruders.end())
layer_extruders.emplace_back(by_extruder_entry.first);
}
if (!local_z_pass_refs.empty()) { if (!local_z_pass_refs.empty()) {
const int local_z_phase_b_start_extruder = const int local_z_phase_b_start_extruder =
(has_wipe_tower && m_writer.extruder() != nullptr) ? int(m_writer.extruder()->id()) : -1; (has_wipe_tower && m_writer.extruder() != nullptr) ? int(m_writer.extruder()->id()) : -1;
int local_z_phase_b_active_extruder = (m_writer.extruder() != nullptr) ? int(m_writer.extruder()->id()) : -1;
bool local_z_phase_b_changed_extruder = false; bool local_z_phase_b_changed_extruder = false;
BOOST_LOG_TRIVIAL(info) << "Local-Z phase-b emitting" BOOST_LOG_TRIVIAL(info) << "Local-Z phase-b emitting"
<< " print_z=" << print_z << " print_z=" << print_z
<< " perimeter_passes=" << local_z_pass_refs.size(); << " perimeter_passes=" << local_z_pass_refs.size();
gcode += "; local-z phase-b perimeter passes begin\n"; gcode += "; local-z phase-b perimeter passes begin\n";
for (const LocalZPassRef& pass_ref : local_z_pass_refs) { size_t pass_ref_idx = 0;
while (pass_ref_idx < local_z_pass_refs.size()) {
size_t pass_group_end = pass_ref_idx + 1;
while (pass_group_end < local_z_pass_refs.size() &&
same_local_z_pass_group(local_z_pass_refs[pass_ref_idx], local_z_pass_refs[pass_group_end])) {
++pass_group_end;
}
std::vector<std::vector<unsigned int>> pass_group_extruders;
pass_group_extruders.reserve(pass_group_end - pass_ref_idx);
for (size_t group_idx = pass_ref_idx; group_idx < pass_group_end; ++group_idx)
pass_group_extruders.push_back(local_z_bucket_extruders(*local_z_pass_refs[group_idx].bucket));
// Buckets that land on the same Local-Z plane are independent, so prefer
// whichever one lets us keep the currently active extruder.
const std::vector<size_t> ordered_pass_group =
LocalZOrderOptimizer::order_pass_group(pass_group_extruders, local_z_phase_b_active_extruder);
for (size_t ordered_group_idx = 0; ordered_group_idx < ordered_pass_group.size(); ++ordered_group_idx) {
const size_t group_local_idx = ordered_pass_group[ordered_group_idx];
const LocalZPassRef& pass_ref = local_z_pass_refs[pass_ref_idx + group_local_idx];
assert(pass_ref.bucket != nullptr && pass_ref.bucket->plan != nullptr); assert(pass_ref.bucket != nullptr && pass_ref.bucket->plan != nullptr);
const SubLayerPlan& pass_plan = *pass_ref.bucket->plan; const SubLayerPlan& pass_plan = *pass_ref.bucket->plan;
const double pass_z = pass_plan.print_z + m_config.z_offset.value; const double pass_z = pass_plan.print_z + m_config.z_offset.value;
@@ -5171,16 +5409,31 @@ LayerResult GCode::process_layer(const Print& print,
gcode += m_writer.travel_to_z(pass_z, "Local-Z perimeter pass"); gcode += m_writer.travel_to_z(pass_z, "Local-Z perimeter pass");
} }
for (auto& by_extruder_entry : pass_ref.bucket->by_extruder) { const std::vector<unsigned int>& group_bucket_extruders = pass_group_extruders[group_local_idx];
const unsigned int local_extruder_id = by_extruder_entry.first; int preferred_last_extruder = -1;
std::vector<ObjectByExtruder>& objects_by_extruder = by_extruder_entry.second; if (ordered_group_idx + 1 < ordered_pass_group.size()) {
preferred_last_extruder =
shared_local_z_extruder(group_bucket_extruders,
pass_group_extruders[ordered_pass_group[ordered_group_idx + 1]]);
}
const std::vector<unsigned int> ordered_bucket_extruders =
LocalZOrderOptimizer::order_bucket_extruders(group_bucket_extruders,
local_z_phase_b_active_extruder,
preferred_last_extruder);
for (unsigned int local_extruder_id : ordered_bucket_extruders) {
auto by_extruder_it = pass_ref.bucket->by_extruder.find(local_extruder_id);
if (by_extruder_it == pass_ref.bucket->by_extruder.end())
continue;
std::vector<ObjectByExtruder>& objects_by_extruder = by_extruder_it->second;
if (objects_by_extruder.empty()) if (objects_by_extruder.empty())
continue; continue;
if (has_wipe_tower && m_writer.need_toolchange(local_extruder_id)) if (has_wipe_tower && m_writer.need_toolchange(local_extruder_id))
local_z_phase_b_changed_extruder = true; local_z_phase_b_changed_extruder = true;
if (has_wipe_tower && m_wipe_tower) { if (has_wipe_tower && m_wipe_tower) {
gcode += m_wipe_tower->tool_change(*this, int(local_extruder_id), false, true); gcode += m_wipe_tower->tool_change(*this, int(local_extruder_id), false, true, print_z + m_config.z_offset.value);
// Local-Z phase-b uses the wipe tower outside the normal per-layer // Local-Z phase-b uses the wipe tower outside the normal per-layer
// extruder loop, so mirror the usual toolchange bookkeeping here. // extruder loop, so mirror the usual toolchange bookkeeping here.
// This forces the next object path to refresh WIDTH/HEIGHT tags // This forces the next object path to refresh WIDTH/HEIGHT tags
@@ -5232,11 +5485,17 @@ LayerResult GCode::process_layer(const Print& print,
} }
m_config.reduce_crossing_wall.value = saved_reduce_crossing_wall; m_config.reduce_crossing_wall.value = saved_reduce_crossing_wall;
} }
local_z_phase_b_active_extruder = int(local_extruder_id);
} }
m_nominal_z = saved_nominal_z; m_nominal_z = saved_nominal_z;
m_last_layer_z = saved_last_layer_z; m_last_layer_z = saved_last_layer_z;
} }
pass_ref_idx = pass_group_end;
}
// Keep wipe tower planning synchronized with the normal per-layer extruder loop: // Keep wipe tower planning synchronized with the normal per-layer extruder loop:
// Local-Z may do extra toolchanges, but wipe tower was planned against the layer loop order. // Local-Z may do extra toolchanges, but wipe tower was planned against the layer loop order.
// Restore the pre-pass tool so wipe tower tool_change() consumes the expected sequence. // Restore the pre-pass tool so wipe tower tool_change() consumes the expected sequence.
@@ -5248,7 +5507,8 @@ LayerResult GCode::process_layer(const Print& print,
<< " restore_extruder=" << local_z_phase_b_start_extruder; << " restore_extruder=" << local_z_phase_b_start_extruder;
gcode += "; local-z phase-b restore pre-pass extruder for wipe tower\n"; gcode += "; local-z phase-b restore pre-pass extruder for wipe tower\n";
if (m_wipe_tower) { if (m_wipe_tower) {
gcode += m_wipe_tower->tool_change(*this, local_z_phase_b_start_extruder, false, true); gcode += m_wipe_tower->tool_change(*this, local_z_phase_b_start_extruder, false, true,
print_z + m_config.z_offset.value);
m_last_processor_extrusion_role = erWipeTower; m_last_processor_extrusion_role = erWipeTower;
} else { } else {
gcode += this->set_extruder(static_cast<unsigned int>(local_z_phase_b_start_extruder), print_z); gcode += this->set_extruder(static_cast<unsigned int>(local_z_phase_b_start_extruder), print_z);
@@ -5266,12 +5526,6 @@ LayerResult GCode::process_layer(const Print& print,
} }
gcode += "; local-z phase-b perimeter passes end\n"; gcode += "; local-z phase-b perimeter passes end\n";
} }
std::vector<unsigned int> layer_extruders = layer_tools.extruders;
for (const auto& by_extruder_entry : by_extruder) {
if (std::find(layer_extruders.begin(), layer_extruders.end(), by_extruder_entry.first) == layer_extruders.end())
layer_extruders.emplace_back(by_extruder_entry.first);
}
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
for (unsigned int extruder_id : layer_extruders) { for (unsigned int extruder_id : layer_extruders) {
if (print.config().skirt_type == stCombined && !print.skirt().empty()) if (print.config().skirt_type == stCombined && !print.skirt().empty())
+13 -2
View File
@@ -79,6 +79,7 @@ public:
const Vec3d plate_origin, const Vec3d plate_origin,
const std::vector<WipeTower::ToolChangeResult> &priming, const std::vector<WipeTower::ToolChangeResult> &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes, const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const std::vector<std::vector<WipeTower::box_coordinates>> &local_z_reserve_boxes,
const WipeTower::ToolChangeResult &final_purge) : const WipeTower::ToolChangeResult &final_purge) :
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.prime_tower_width.value)), m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.prime_tower_width.value)),
@@ -87,9 +88,11 @@ public:
m_extruder_offsets(print_config.extruder_offset.values), m_extruder_offsets(print_config.extruder_offset.values),
m_priming(priming), m_priming(priming),
m_tool_changes(tool_changes), m_tool_changes(tool_changes),
m_local_z_reserve_boxes(local_z_reserve_boxes),
m_final_purge(final_purge), m_final_purge(final_purge),
m_layer_idx(-1), m_layer_idx(-1),
m_tool_change_idx(0), m_tool_change_idx(0),
m_local_z_reserve_slot_idx(local_z_reserve_boxes.size(), 0),
m_plate_origin(plate_origin), m_plate_origin(plate_origin),
m_single_extruder_multi_material(print_config.single_extruder_multi_material), m_single_extruder_multi_material(print_config.single_extruder_multi_material),
m_enable_timelapse_print(print_config.timelapse_type.value == TimelapseType::tlSmooth), m_enable_timelapse_print(print_config.timelapse_type.value == TimelapseType::tlSmooth),
@@ -97,10 +100,16 @@ public:
{} {}
std::string prime(GCode &gcodegen); std::string prime(GCode &gcodegen);
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } void next_layer() {
++ m_layer_idx;
m_tool_change_idx = 0;
if (m_layer_idx >= 0 && size_t(m_layer_idx) < m_local_z_reserve_slot_idx.size())
m_local_z_reserve_slot_idx[size_t(m_layer_idx)] = 0;
}
// If local_z_unplanned is true, emit a wipe/toolchange without consuming the preplanned // If local_z_unplanned is true, emit a wipe/toolchange without consuming the preplanned
// per-layer wipe-tower sequence (used by Local-Z phase-b extra toolchanges). // per-layer wipe-tower sequence (used by Local-Z phase-b extra toolchanges).
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned = false); std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, bool local_z_unplanned = false,
double local_z_nominal_layer_z = -1.);
bool is_empty_wipe_tower_gcode(GCode &gcodegen, int extruder_id, bool finish_layer); bool is_empty_wipe_tower_gcode(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen); std::string finalize(GCode &gcodegen);
std::vector<float> used_filament_length() const; std::vector<float> used_filament_length() const;
@@ -127,10 +136,12 @@ private:
// Reference to cached values at the Printer class. // Reference to cached values at the Printer class.
const std::vector<WipeTower::ToolChangeResult> &m_priming; const std::vector<WipeTower::ToolChangeResult> &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes; const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
const std::vector<std::vector<WipeTower::box_coordinates>> &m_local_z_reserve_boxes;
const WipeTower::ToolChangeResult &m_final_purge; const WipeTower::ToolChangeResult &m_final_purge;
// Current layer index. // Current layer index.
int m_layer_idx; int m_layer_idx;
int m_tool_change_idx; int m_tool_change_idx;
std::vector<size_t> m_local_z_reserve_slot_idx;
double m_last_wipe_tower_print_z = 0.f; double m_last_wipe_tower_print_z = 0.f;
// BBS // BBS
+1
View File
@@ -263,6 +263,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
{ {
m_is_BBL_printer = object.print()->is_BBL_printer(); m_is_BBL_printer = object.print()->is_BBL_printer();
m_print_full_config = &object.print()->full_print_config(); m_print_full_config = &object.print()->full_print_config();
m_print_config_ptr = &object.print()->config();
m_print_object_ptr = &object; m_print_object_ptr = &object;
// Mixed filament support. // Mixed filament support.
m_mixed_mgr = &object.print()->mixed_filament_manager(); m_mixed_mgr = &object.print()->mixed_filament_manager();
+170 -4
View File
@@ -1278,6 +1278,7 @@ WipeTower2::WipeTower2(const PrintConfig& config,
, m_extra_flow(float(config.wipe_tower_extra_flow / 100.)) , m_extra_flow(float(config.wipe_tower_extra_flow / 100.))
, m_extra_spacing_wipe(float(config.wipe_tower_extra_spacing / 100. * config.wipe_tower_extra_flow / 100.)) , m_extra_spacing_wipe(float(config.wipe_tower_extra_spacing / 100. * config.wipe_tower_extra_flow / 100.))
, m_extra_spacing_ramming(float(config.wipe_tower_extra_spacing / 100.)) , m_extra_spacing_ramming(float(config.wipe_tower_extra_spacing / 100.))
, m_local_z_wipe_tower_purge_lines(float(config.local_z_wipe_tower_purge_lines))
, m_y_shift(0.f) , m_y_shift(0.f)
, m_z_pos(0.f) , m_z_pos(0.f)
, m_bridging(float(config.wipe_tower_bridging)) , m_bridging(float(config.wipe_tower_bridging))
@@ -1612,6 +1613,63 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
return construct_tcr(writer, false, old_tool, false); return construct_tcr(writer, false, old_tool, false);
} }
WipeTower::ToolChangeResult WipeTower2::local_z_tool_change(size_t new_tool,
const WipeTower::box_coordinates& cleaning_box,
float wipe_volume)
{
const size_t old_tool = m_current_tool;
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting, m_printer_model);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.append(";--------------------\n"
"; CP TOOLCHANGE START\n");
writer.comment_with_value(" toolchange #", m_num_tool_changes + 1);
writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " +
m_filpar[new_tool].material + "\n")
.c_str())
.append(";--------------------\n");
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_Start) + "\n");
writer.speed_override_backup();
writer.speed_override(100);
writer.set_initial_position(cleaning_box.ld, m_wipe_tower_width, m_wipe_tower_depth, 0.f);
if (m_set_extruder_trimpot)
writer.set_extruder_trimpot(750);
const int old_tool_temp = is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature;
const int new_tool_temp = is_first_layer() ? m_filpar[new_tool].first_layer_temperature : m_filpar[new_tool].temperature;
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, old_tool_temp, new_tool_temp);
toolchange_Change(writer, new_tool, m_filpar[new_tool].material);
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(), writer.y() - m_perimeter_width);
toolchange_Wipe(writer, cleaning_box, wipe_volume);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
++m_num_tool_changes;
if (m_set_extruder_trimpot)
writer.set_extruder_trimpot(550);
writer.speed_override_restore();
writer.feedrate(m_travel_speed * 60.f)
.flush_planner_queue()
.reset_extruder()
.append("; CP TOOLCHANGE END\n"
";------------------\n"
"\n\n");
if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
WipeTower::ToolChangeResult result = construct_tcr(writer, false, old_tool, false);
result.purge_volume = wipe_volume;
return result;
}
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
void WipeTower2::toolchange_Unload(WipeTowerWriter2& writer, void WipeTower2::toolchange_Unload(WipeTowerWriter2& writer,
const WipeTower::box_coordinates& cleaning_box, const WipeTower::box_coordinates& cleaning_box,
@@ -1991,9 +2049,11 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
// If spare layers are excluded -> if 1 or less toolchange has been done, it must be still the first layer, too. So slow down. // If spare layers are excluded -> if 1 or less toolchange has been done, it must be still the first layer, too. So slow down.
bool first_layer = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers); bool first_layer = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers);
float feedrate = first_layer ? m_first_layer_speed * 60.f : std::min(m_wipe_tower_max_purge_speed * 60.f, m_infill_speed * 60.f); float feedrate = first_layer ? m_first_layer_speed * 60.f : std::min(m_wipe_tower_max_purge_speed * 60.f, m_infill_speed * 60.f);
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth(); float reserve_depth = m_layer_info->local_z_reserve_depth();
WipeTower::box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth - (current_depth - m_perimeter_width)), float current_depth = std::max(0.f, m_layer_info->depth - m_layer_info->toolchanges_depth() - reserve_depth);
m_wipe_tower_width - 2 * m_perimeter_width, current_depth - m_perimeter_width); float fill_depth = std::max(0.f, current_depth - m_perimeter_width);
WipeTower::box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth - fill_depth),
m_wipe_tower_width - 2 * m_perimeter_width, fill_depth);
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
@@ -2242,6 +2302,54 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i
WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume)); WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume));
} }
void WipeTower2::plan_local_z_reserve(float z_par, float layer_height_par, size_t reserve_slot_count, float wipe_volume)
{
if (reserve_slot_count == 0)
return;
assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON);
if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par)
m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
const float mini_wipe_depth = m_local_z_wipe_tower_purge_lines * m_perimeter_width * m_extra_spacing_wipe;
const float wipe_width = std::max(0.f, m_wipe_tower_width - 3.f * m_perimeter_width);
const float wiping_depth = wipe_width > WT_EPSILON ?
get_wipe_depth(std::max(0.f, wipe_volume), layer_height_par, m_perimeter_width, m_extra_flow,
m_extra_spacing_wipe, wipe_width) :
0.f;
float max_ramming_depth = 0.f;
if (wipe_width > WT_EPSILON) {
for (const FilamentParameters& filament : m_filpar) {
const bool do_ramming = (m_semm && m_enable_filament_ramming) || filament.multitool_ramming;
if (!do_ramming || filament.ramming_speed.empty())
continue;
const float line_width = m_perimeter_width * filament.ramming_line_width_multiplicator;
const float line_step =
(m_perimeter_width * filament.ramming_line_width_multiplicator * filament.ramming_step_multiplicator) *
m_extra_spacing_ramming;
if (line_width <= WT_EPSILON || line_step <= WT_EPSILON)
continue;
const float ramming_volume = 0.25f * std::accumulate(filament.ramming_speed.begin(), filament.ramming_speed.end(), 0.f);
const float length_to_extrude = volume_to_length(ramming_volume, line_width, layer_height_par);
const float ramming_depth =
(float(int(length_to_extrude / wipe_width) + 1) * line_step);
max_ramming_depth = std::max(max_ramming_depth, ramming_depth);
}
}
const float full_toolchange_depth = max_ramming_depth + wiping_depth;
const float slot_depth =
std::max(2.5f * m_perimeter_width, std::max(mini_wipe_depth + m_perimeter_width, full_toolchange_depth + m_perimeter_width));
WipeTowerInfo &layer = m_plan.back();
layer.local_z_reserve_slot_depth = std::max(layer.local_z_reserve_slot_depth, slot_depth);
layer.local_z_reserve_slot_count += reserve_slot_count;
}
void WipeTower2::plan_tower() void WipeTower2::plan_tower()
{ {
// Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards // Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
@@ -2252,7 +2360,7 @@ void WipeTower2::plan_tower()
m_current_height = 0.f; m_current_height = 0.f;
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) {
float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth()); float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].planned_depth());
m_plan[layer_index].depth = this_layer_depth; m_plan[layer_index].depth = this_layer_depth;
if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width) if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
@@ -2431,6 +2539,64 @@ std::vector<std::pair<float, float>> WipeTower2::get_z_and_depth_pairs() const
return out; return out;
} }
static Vec2f rotate_local_z_reserve_point(const Vec2f& pt, float tower_width, float tower_depth, float y_shift, float internal_angle_deg)
{
Vec2f shifted = pt;
shifted.x() -= tower_width / 2.f;
shifted.y() += y_shift - tower_depth / 2.f;
const double angle = internal_angle_deg * double(M_PI / 180.);
const double c = std::cos(angle);
const double s = std::sin(angle);
return Vec2f(float(shifted.x() * c - shifted.y() * s) + tower_width / 2.f,
float(shifted.x() * s + shifted.y() * c) + tower_depth / 2.f);
}
std::vector<std::vector<WipeTower::box_coordinates>> WipeTower2::get_local_z_reserve_boxes() const
{
std::vector<std::vector<WipeTower::box_coordinates>> out;
out.reserve(m_plan.size());
for (size_t layer_idx = 0; layer_idx < m_plan.size(); ++layer_idx) {
const WipeTowerInfo& layer = m_plan[layer_idx];
std::vector<WipeTower::box_coordinates> layer_boxes;
layer_boxes.reserve(layer.local_z_reserve_slot_count);
if (layer.local_z_reserve_slot_count > 0 && layer.local_z_reserve_slot_depth > WT_EPSILON) {
const float y_shift = layer.depth < m_wipe_tower_depth - m_perimeter_width ?
(m_wipe_tower_depth - layer.depth - m_perimeter_width) / 2.f : 0.f;
const float internal_angle = (layer_idx % 2 == 0) ? 180.f : 0.f;
for (size_t slot_idx = 0; slot_idx < layer.local_z_reserve_slot_count; ++slot_idx) {
const float slot_start = layer.toolchanges_depth() + float(slot_idx) * layer.local_z_reserve_slot_depth;
const float width = std::max(0.f, m_wipe_tower_width - 2.f * m_perimeter_width);
const float height = std::max(0.f, layer.local_z_reserve_slot_depth - m_perimeter_width);
if (width <= WT_EPSILON || height <= WT_EPSILON)
continue;
const Vec2f ld_unrotated(m_perimeter_width, slot_start + m_perimeter_width);
const Vec2f rd_unrotated = ld_unrotated + Vec2f(width, 0.f);
const Vec2f ru_unrotated = ld_unrotated + Vec2f(width, height);
const Vec2f lu_unrotated = ld_unrotated + Vec2f(0.f, height);
const Vec2f ld = rotate_local_z_reserve_point(ld_unrotated, m_wipe_tower_width, m_wipe_tower_depth, y_shift, internal_angle);
const Vec2f rd = rotate_local_z_reserve_point(rd_unrotated, m_wipe_tower_width, m_wipe_tower_depth, y_shift, internal_angle);
const Vec2f ru = rotate_local_z_reserve_point(ru_unrotated, m_wipe_tower_width, m_wipe_tower_depth, y_shift, internal_angle);
const Vec2f lu = rotate_local_z_reserve_point(lu_unrotated, m_wipe_tower_width, m_wipe_tower_depth, y_shift, internal_angle);
const float min_x = std::min({ld.x(), rd.x(), ru.x(), lu.x()});
const float max_x = std::max({ld.x(), rd.x(), ru.x(), lu.x()});
const float min_y = std::min({ld.y(), rd.y(), ru.y(), lu.y()});
const float max_y = std::max({ld.y(), rd.y(), ru.y(), lu.y()});
layer_boxes.emplace_back(Vec2f(min_x, min_y), max_x - min_x, max_y - min_y);
}
}
out.emplace_back(std::move(layer_boxes));
}
return out;
}
Polygon WipeTower2::generate_rib_polygon(const WipeTower::box_coordinates& wt_box) Polygon WipeTower2::generate_rib_polygon(const WipeTower::box_coordinates& wt_box)
{ {
auto get_current_layer_rib_len = [](float cur_height, float max_height, float max_len) -> float { auto get_current_layer_rib_len = [](float cur_height, float max_height, float max_len) -> float {
+9
View File
@@ -50,12 +50,14 @@ public:
// Appends into internal structure m_plan containing info about the future wipe tower // Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z. // to be used before building begins. The entries must be added ordered in z.
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume = 0.f); void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume = 0.f);
void plan_local_z_reserve(float z_par, float layer_height_par, size_t reserve_slot_count, float wipe_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result" // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result); void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
float get_depth() const { return m_wipe_tower_depth; } float get_depth() const { return m_wipe_tower_depth; }
std::vector<std::pair<float, float>> get_z_and_depth_pairs() const; std::vector<std::pair<float, float>> get_z_and_depth_pairs() const;
std::vector<std::vector<WipeTower::box_coordinates>> get_local_z_reserve_boxes() const;
float get_brim_width() const { return m_wipe_tower_brim_width_real; } float get_brim_width() const { return m_wipe_tower_brim_width_real; }
float get_wipe_tower_height() const { return m_wipe_tower_height; } float get_wipe_tower_height() const { return m_wipe_tower_height; }
@@ -118,6 +120,8 @@ public:
// Returns gcode for a toolchange and a final print head position. // Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first. // On the first layer, extrude a brim around the future wipe tower first.
WipeTower::ToolChangeResult tool_change(size_t new_tool); WipeTower::ToolChangeResult tool_change(size_t new_tool);
WipeTower::ToolChangeResult local_z_tool_change(size_t new_tool, const WipeTower::box_coordinates& cleaning_box, float wipe_volume);
void set_current_tool(size_t tool) { m_current_tool = tool; }
// Fill the unfilled space with a sparse infill. // Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false. // Call this method only if layer_finished() is false.
@@ -254,6 +258,7 @@ private:
float m_extra_flow = 1.f; float m_extra_flow = 1.f;
float m_extra_spacing_wipe = 1.f; float m_extra_spacing_wipe = 1.f;
float m_extra_spacing_ramming = 1.f; float m_extra_spacing_ramming = 1.f;
float m_local_z_wipe_tower_purge_lines = 3.f;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; } bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
@@ -289,6 +294,10 @@ private:
float height; // layer height float height; // layer height
float depth; // depth of the layer based on all layers above float depth; // depth of the layer based on all layers above
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; } float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
float local_z_reserve_slot_depth { 0.f };
size_t local_z_reserve_slot_count { 0 };
float local_z_reserve_depth() const { return local_z_reserve_slot_depth * float(local_z_reserve_slot_count); }
float planned_depth() const { return toolchanges_depth() + local_z_reserve_depth(); }
std::vector<ToolChange> tool_changes; std::vector<ToolChange> tool_changes;
+72
View File
@@ -0,0 +1,72 @@
#ifndef slic3r_LocalZOrderOptimizer_hpp_
#define slic3r_LocalZOrderOptimizer_hpp_
#include <algorithm>
#include <numeric>
#include <vector>
namespace Slic3r {
namespace LocalZOrderOptimizer {
inline bool bucket_contains_extruder(const std::vector<unsigned int> &extruders, int extruder_id)
{
return extruder_id >= 0 &&
std::find(extruders.begin(), extruders.end(), static_cast<unsigned int>(extruder_id)) != extruders.end();
}
inline std::vector<unsigned int> order_bucket_extruders(std::vector<unsigned int> extruders,
int current_extruder,
int preferred_last_extruder = -1)
{
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
if (extruders.empty())
return extruders;
if (current_extruder >= 0) {
auto current_it = std::find(extruders.begin(), extruders.end(), static_cast<unsigned int>(current_extruder));
if (current_it != extruders.end())
std::rotate(extruders.begin(), current_it, extruders.end());
}
if (preferred_last_extruder >= 0 && extruders.size() > 1 && static_cast<int>(extruders.front()) != preferred_last_extruder) {
auto preferred_it = std::find(extruders.begin() + 1, extruders.end(), static_cast<unsigned int>(preferred_last_extruder));
if (preferred_it != extruders.end())
std::rotate(preferred_it, preferred_it + 1, extruders.end());
}
return extruders;
}
inline std::vector<size_t> order_pass_group(const std::vector<std::vector<unsigned int>> &group_extruders, int current_extruder)
{
std::vector<size_t> remaining(group_extruders.size());
std::iota(remaining.begin(), remaining.end(), size_t(0));
std::vector<size_t> ordered;
ordered.reserve(group_extruders.size());
int active_extruder = current_extruder;
while (!remaining.empty()) {
auto next_it = std::find_if(remaining.begin(), remaining.end(), [&](size_t idx) {
return bucket_contains_extruder(group_extruders[idx], active_extruder);
});
if (next_it == remaining.end())
next_it = remaining.begin();
const size_t next_idx = *next_it;
ordered.push_back(next_idx);
const std::vector<unsigned int> ordered_bucket = order_bucket_extruders(group_extruders[next_idx], active_extruder);
if (!ordered_bucket.empty())
active_extruder = static_cast<int>(ordered_bucket.back());
remaining.erase(next_it);
}
return ordered;
}
} // namespace LocalZOrderOptimizer
} // namespace Slic3r
#endif
+1 -1
View File
@@ -883,7 +883,7 @@ static std::vector<std::string> s_Preset_print_options {
"tree_support_brim_width", "gcode_comments", "gcode_label_objects", "tree_support_brim_width", "gcode_comments", "gcode_label_objects",
"initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width", "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width",
"make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes", "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes",
"wipe_tower_cone_angle", "wipe_tower_extra_spacing","wipe_tower_max_purge_speed", "wipe_tower_cone_angle", "wipe_tower_extra_spacing","wipe_tower_max_purge_speed", "local_z_wipe_tower_purge_lines",
"wipe_tower_wall_type", "wipe_tower_extra_rib_length", "wipe_tower_rib_width", "wipe_tower_fillet_wall", "wipe_tower_wall_type", "wipe_tower_extra_rib_length", "wipe_tower_rib_width", "wipe_tower_fillet_wall",
"wipe_tower_filament", "wiping_volumes_extruders","wipe_tower_bridging", "wipe_tower_extra_flow","single_extruder_multi_material_priming", "wipe_tower_filament", "wiping_volumes_extruders","wipe_tower_bridging", "wipe_tower_extra_flow","single_extruder_multi_material_priming",
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
+103
View File
@@ -9,6 +9,9 @@
#include "common_func/common_func.hpp" #include "common_func/common_func.hpp"
#include <algorithm> #include <algorithm>
#include <chrono>
#include <cctype>
#include <cstdlib>
#include <set> #include <set>
#include <fstream> #include <fstream>
#include <unordered_map> #include <unordered_map>
@@ -32,6 +35,30 @@
namespace Slic3r { namespace Slic3r {
namespace {
bool startup_profile_enabled()
{
static const bool enabled = [] {
const char* value = std::getenv("ORCA_STARTUP_PROFILE");
if (value == nullptr)
return false;
std::string normalized(value);
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return normalized == "1" || normalized == "true" || normalized == "yes" || normalized == "on";
}();
return enabled;
}
void startup_profile_log(const std::string& message)
{
if (startup_profile_enabled())
BOOST_LOG_TRIVIAL(warning) << "[StartupProfile] " << message;
}
} // namespace
static std::vector<std::string> s_project_options { static std::vector<std::string> s_project_options {
"flush_volumes_vector", "flush_volumes_vector",
"flush_volumes_matrix", "flush_volumes_matrix",
@@ -250,6 +277,10 @@ void PresetBundle::copy_files(const std::string& from)
PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule,
const PresetPreferences& preferred_selection/* = PresetPreferences()*/) const PresetPreferences& preferred_selection/* = PresetPreferences()*/)
{ {
const bool startup_profile = startup_profile_enabled();
const auto total_start = std::chrono::steady_clock::now();
auto phase_start = total_start;
// First load the vendor specific system presets. // First load the vendor specific system presets.
PresetsConfigSubstitutions substitutions; PresetsConfigSubstitutions substitutions;
std::string errors_cummulative; std::string errors_cummulative;
@@ -258,6 +289,13 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, substitution_rule %1%, preferred printer_model_id %2%")%substitution_rule%preferred_selection.printer_model_id; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, substitution_rule %1%, preferred printer_model_id %2%")%substitution_rule%preferred_selection.printer_model_id;
//BBS: change system config to json //BBS: change system config to json
std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule); std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule);
if (startup_profile) {
const auto now = std::chrono::steady_clock::now();
startup_profile_log("PresetBundle::load_presets step=load_system_presets_from_json step_ms=" +
std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - phase_start).count()) +
" total_ms=" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - total_start).count()));
phase_start = now;
}
// BBS load preset from user's folder, load system default if // BBS load preset from user's folder, load system default if
// BBS: change directories by design // BBS: change directories by design
@@ -267,16 +305,34 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
} else { } else {
load_user_presets(dir_user_presets, substitution_rule); load_user_presets(dir_user_presets, substitution_rule);
} }
if (startup_profile) {
const auto now = std::chrono::steady_clock::now();
startup_profile_log("PresetBundle::load_presets step=load_user_presets step_ms=" +
std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - phase_start).count()) +
" total_ms=" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - total_start).count()));
phase_start = now;
}
this->update_multi_material_filament_presets(); this->update_multi_material_filament_presets();
this->update_compatible(PresetSelectCompatibleType::Never); this->update_compatible(PresetSelectCompatibleType::Never);
this->load_selections(config, preferred_selection); this->load_selections(config, preferred_selection);
if (startup_profile) {
const auto now = std::chrono::steady_clock::now();
startup_profile_log("PresetBundle::load_presets step=post_load_selection step_ms=" +
std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - phase_start).count()) +
" total_ms=" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(now - total_start).count()));
}
set_calibrate_printer(""); set_calibrate_printer("");
//BBS: add config related logs //BBS: add config related logs
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" finished, returned substitutions %1%")%substitutions.size(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" finished, returned substitutions %1%")%substitutions.size();
if (startup_profile) {
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - total_start).count();
startup_profile_log("PresetBundle::load_presets end substitutions=" + std::to_string(substitutions.size()) +
" total_ms=" + std::to_string(total_ms));
}
return substitutions; return substitutions;
} }
@@ -1204,6 +1260,9 @@ void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string,
//BBS: add json related logic, load system presets from json //BBS: add json related logic, load system presets from json
std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_presets_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule) std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_presets_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule)
{ {
const bool startup_profile = startup_profile_enabled();
const auto total_start = std::chrono::steady_clock::now();
//BBS: add config related logs //BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%")%compatibility_rule; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%")%compatibility_rule;
if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)
@@ -1245,6 +1304,7 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
for (auto &vendor_name : vendor_names) for (auto &vendor_name : vendor_names)
{ {
const auto vendor_start = std::chrono::steady_clock::now();
if (validation_mode && !vendor_to_validate.empty() && vendor_name != vendor_to_validate && vendor_name != ORCA_FILAMENT_LIBRARY) if (validation_mode && !vendor_to_validate.empty() && vendor_name != vendor_to_validate && vendor_name != ORCA_FILAMENT_LIBRARY)
continue; continue;
@@ -1279,6 +1339,12 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
errors_cummulative += "\n"; errors_cummulative += "\n";
} }
} }
if (startup_profile) {
const auto vendor_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - vendor_start).count();
startup_profile_log("PresetBundle::load_system_presets_from_json vendor=" + vendor_name +
" vendor_ms=" + std::to_string(vendor_ms));
}
} }
if (first) { if (first) {
@@ -1289,6 +1355,11 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
this->update_system_maps(); this->update_system_maps();
//BBS: add config related logs //BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%")%errors_cummulative; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%")%errors_cummulative;
if (startup_profile) {
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - total_start).count();
startup_profile_log("PresetBundle::load_system_presets_from_json end vendor_count=" + std::to_string(vendor_names.size()) +
" total_ms=" + std::to_string(total_ms));
}
return std::make_pair(std::move(substitutions), errors_cummulative); return std::make_pair(std::move(substitutions), errors_cummulative);
} }
@@ -2729,6 +2800,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_from_json( std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_from_json(
const std::string &path, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle) const std::string &path, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle)
{ {
const bool startup_profile = startup_profile_enabled();
const auto total_start = std::chrono::steady_clock::now();
// Enable substitutions for user config bundle, throw an exception when loading a system profile. // Enable substitutions for user config bundle, throw an exception when loading a system profile.
ConfigSubstitutionContext substitution_context { compatibility_rule }; ConfigSubstitutionContext substitution_context { compatibility_rule };
PresetsConfigSubstitutions substitutions; PresetsConfigSubstitutions substitutions;
@@ -2830,6 +2904,14 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//goto __error_process; //goto __error_process;
} }
if (startup_profile) {
startup_profile_log("PresetBundle::load_vendor_configs_from_json vendor=" + vendor_name +
" machine_models=" + std::to_string(machine_model_subfiles.size()) +
" process=" + std::to_string(process_subfiles.size()) +
" filaments=" + std::to_string(filament_subfiles.size()) +
" machines=" + std::to_string(machine_subfiles.size()));
}
if (flags.has(LoadConfigBundleAttribute::LoadFilamentOnly)) { if (flags.has(LoadConfigBundleAttribute::LoadFilamentOnly)) {
machine_model_subfiles.clear(); machine_model_subfiles.clear();
machine_subfiles.clear(); machine_subfiles.clear();
@@ -3171,6 +3253,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
presets = &this->prints; presets = &this->prints;
configs.clear(); configs.clear();
filament_id_maps.clear(); filament_id_maps.clear();
auto process_start = std::chrono::steady_clock::now();
for (auto& subfile : process_subfiles) for (auto& subfile : process_subfiles)
{ {
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded); std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded);
@@ -3182,12 +3265,18 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
} }
} }
if (startup_profile) {
const auto process_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - process_start).count();
startup_profile_log("PresetBundle::load_vendor_configs_from_json vendor=" + vendor_name +
" section=process section_ms=" + std::to_string(process_ms));
}
//3.2) paste the filaments //3.2) paste the filaments
presets = &this->filaments; presets = &this->filaments;
configs.clear(); configs.clear();
filament_id_maps.clear(); filament_id_maps.clear();
const auto is_orca_lib = vendor_name == ORCA_FILAMENT_LIBRARY; const auto is_orca_lib = vendor_name == ORCA_FILAMENT_LIBRARY;
auto filament_start = std::chrono::steady_clock::now();
for (auto& subfile : filament_subfiles) for (auto& subfile : filament_subfiles)
{ {
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets,
@@ -3200,6 +3289,11 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
} }
} }
if (startup_profile) {
const auto filament_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - filament_start).count();
startup_profile_log("PresetBundle::load_vendor_configs_from_json vendor=" + vendor_name +
" section=filament section_ms=" + std::to_string(filament_ms));
}
if (is_orca_lib) { if (is_orca_lib) {
m_config_maps = configs; m_config_maps = configs;
m_filament_id_maps = filament_id_maps; m_filament_id_maps = filament_id_maps;
@@ -3209,6 +3303,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
presets = &this->printers; presets = &this->printers;
configs.clear(); configs.clear();
filament_id_maps.clear(); filament_id_maps.clear();
auto machine_start = std::chrono::steady_clock::now();
for (auto& subfile : machine_subfiles) for (auto& subfile : machine_subfiles)
{ {
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded); std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded);
@@ -3220,6 +3315,14 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
} }
} }
if (startup_profile) {
const auto machine_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - machine_start).count();
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - total_start).count();
startup_profile_log("PresetBundle::load_vendor_configs_from_json vendor=" + vendor_name +
" section=machine section_ms=" + std::to_string(machine_ms) +
" presets_loaded=" + std::to_string(presets_loaded) +
" total_ms=" + std::to_string(total_ms));
}
//BBS: add config related logs //BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", finished, presets_loaded %1%")%presets_loaded; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", finished, presets_loaded %1%")%presets_loaded;
+56
View File
@@ -52,6 +52,50 @@ template class PrintState<PrintObjectStep, posCount>;
PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {} PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {}
PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {} PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {}
static size_t estimate_local_z_wipe_tower_reserve_slots(const PrintObject& print_object, coordf_t print_z)
{
const Layer* object_layer = print_object.get_layer_at_printz(print_z, EPSILON);
if (object_layer == nullptr)
return 0;
const auto& intervals = print_object.local_z_intervals();
const auto& plans = print_object.local_z_sublayer_plan();
if (intervals.empty() || plans.empty())
return 0;
const size_t layer_id = size_t(object_layer->id());
const auto interval_it = std::find_if(intervals.begin(), intervals.end(), [layer_id](const LocalZInterval& interval) {
return interval.layer_id == layer_id;
});
if (interval_it == intervals.end() || !interval_it->has_mixed_paint || interval_it->sublayer_count <= 1 ||
interval_it->first_sublayer_idx >= plans.size()) {
return 0;
}
const size_t first_idx = interval_it->first_sublayer_idx;
const size_t end_idx = std::min(plans.size(), first_idx + interval_it->sublayer_count);
size_t reserve_slots = 0;
int previous_extruder = -1;
for (size_t plan_idx = first_idx; plan_idx < end_idx; ++plan_idx) {
const SubLayerPlan& plan = plans[plan_idx];
if (!plan.split_interval)
continue;
for (size_t extruder_id = 0; extruder_id < plan.painted_masks_by_extruder.size(); ++extruder_id) {
if (plan.painted_masks_by_extruder[extruder_id].empty())
continue;
if (previous_extruder != int(extruder_id)) {
++reserve_slots;
previous_extruder = int(extruder_id);
}
}
}
if (reserve_slots > 0)
++reserve_slots;
return reserve_slots;
}
//BBS //BBS
// ORCA: Now this is a parameter // ORCA: Now this is a parameter
//float Print::min_skirt_length = 0; //float Print::min_skirt_length = 0;
@@ -2879,6 +2923,17 @@ void Print::_make_wipe_tower()
current_extruder_id = extruder_id; current_extruder_id = extruder_id;
} }
} }
if (m_config.dithering_local_z_mode) {
size_t local_z_reserve_slots = 0;
for (const PrintObject* print_object : m_objects)
local_z_reserve_slots += estimate_local_z_wipe_tower_reserve_slots(*print_object, layer_tools.print_z);
if (local_z_reserve_slots > 0) {
wipe_tower.plan_local_z_reserve((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height,
local_z_reserve_slots, (float) m_config.prime_volume);
}
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0) if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break; break;
@@ -2890,6 +2945,7 @@ void Print::_make_wipe_tower()
wipe_tower.generate(m_wipe_tower_data.tool_changes); wipe_tower.generate(m_wipe_tower_data.tool_changes);
m_wipe_tower_data.depth = wipe_tower.get_depth(); m_wipe_tower_data.depth = wipe_tower.get_depth();
m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs(); m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs();
m_wipe_tower_data.local_z_reserve_boxes = wipe_tower.get_local_z_reserve_boxes();
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width(); m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height(); m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
+2
View File
@@ -778,6 +778,7 @@ struct WipeTowerData
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth; float depth;
std::vector<std::pair<float, float>> z_and_depth_pairs; std::vector<std::pair<float, float>> z_and_depth_pairs;
std::vector<std::vector<WipeTower::box_coordinates>> local_z_reserve_boxes;
float brim_width; float brim_width;
float height; float height;
@@ -788,6 +789,7 @@ struct WipeTowerData
used_filament.clear(); used_filament.clear();
number_of_toolchanges = -1; number_of_toolchanges = -1;
depth = 0.f; depth = 0.f;
local_z_reserve_boxes.clear();
brim_width = 0.f; brim_width = 0.f;
} }
+10
View File
@@ -6026,6 +6026,16 @@ void PrintConfigDef::init_fff_params()
def->max = 300.; def->max = 300.;
def->set_default_value(new ConfigOptionPercent(100.)); def->set_default_value(new ConfigOptionPercent(100.));
def = this->add("local_z_wipe_tower_purge_lines", coFloat);
def->label = L("Local-Z mini wipe lines");
def->tooltip = L("Number of purge lines reserved for each runtime Local-Z wipe tower toolchange. "
"Higher values improve cleanup but increase tower depth. "
"Only used when Local-Z dithering and the prime tower are enabled.");
def->sidetext = L("lines");
def->mode = comAdvanced;
def->min = 1.0;
def->set_default_value(new ConfigOptionFloat(3.0));
def = this->add("idle_temperature", coInts); def = this->add("idle_temperature", coInts);
def->label = L("Idle temperature"); def->label = L("Idle temperature");
def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups. " def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups. "
+1
View File
@@ -1403,6 +1403,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, prime_tower_brim_chamfer_max_width)) ((ConfigOptionFloat, prime_tower_brim_chamfer_max_width))
((ConfigOptionFloat, wipe_tower_bridging)) ((ConfigOptionFloat, wipe_tower_bridging))
((ConfigOptionPercent, wipe_tower_extra_flow)) ((ConfigOptionPercent, wipe_tower_extra_flow))
((ConfigOptionFloat, local_z_wipe_tower_purge_lines))
((ConfigOptionFloats, flush_volumes_matrix)) ((ConfigOptionFloats, flush_volumes_matrix))
((ConfigOptionFloats, flush_volumes_vector)) ((ConfigOptionFloats, flush_volumes_vector))
+5
View File
@@ -774,6 +774,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"wipe_tower_no_sparse_layers"}) "wipe_tower_no_sparse_layers"})
toggle_line(el, have_prime_tower && !is_BBL_Printer); toggle_line(el, have_prime_tower && !is_BBL_Printer);
const bool local_z_dithering_enabled =
config->has("dithering_local_z_mode") && config->option("dithering_local_z_mode") != nullptr &&
config->opt_bool("dithering_local_z_mode");
toggle_line("local_z_wipe_tower_purge_lines", have_prime_tower && !is_BBL_Printer && local_z_dithering_enabled);
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type"); WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone); toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
toggle_line("wipe_tower_extra_rib_length", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwRib); toggle_line("wipe_tower_extra_rib_length", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwRib);
+108 -1
View File
@@ -27,6 +27,8 @@
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
#include <algorithm> #include <algorithm>
#include <chrono>
#include <cctype>
#include <iterator> #include <iterator>
#include <exception> #include <exception>
#include <cstdlib> #include <cstdlib>
@@ -179,6 +181,71 @@ namespace GUI {
class MainFrame; class MainFrame;
namespace {
bool startup_profile_enabled()
{
static const bool enabled = [] {
const char* value = std::getenv("ORCA_STARTUP_PROFILE");
if (value == nullptr)
return false;
std::string normalized(value);
std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return normalized == "1" || normalized == "true" || normalized == "yes" || normalized == "on";
}();
return enabled;
}
class StartupProfiler
{
public:
explicit StartupProfiler(const char* phase)
: m_phase(phase)
, m_enabled(startup_profile_enabled())
, m_phase_start(std::chrono::steady_clock::now())
, m_step_start(m_phase_start)
{
if (m_enabled)
BOOST_LOG_TRIVIAL(warning) << "[StartupProfile] phase=" << m_phase << " begin";
}
bool enabled() const { return m_enabled; }
void mark(const char* step)
{
if (!m_enabled)
return;
const auto now = std::chrono::steady_clock::now();
const auto step_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_step_start).count();
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_phase_start).count();
BOOST_LOG_TRIVIAL(warning) << "[StartupProfile] phase=" << m_phase << " step=" << step << " step_ms=" << step_ms << " total_ms=" << total_ms;
m_step_start = now;
}
void note(const std::string& message) const
{
if (m_enabled)
BOOST_LOG_TRIVIAL(warning) << "[StartupProfile] phase=" << m_phase << " " << message;
}
~StartupProfiler()
{
if (!m_enabled)
return;
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_phase_start).count();
BOOST_LOG_TRIVIAL(warning) << "[StartupProfile] phase=" << m_phase << " end total_ms=" << total_ms;
}
private:
const char* m_phase;
bool m_enabled;
std::chrono::steady_clock::time_point m_phase_start;
std::chrono::steady_clock::time_point m_step_start;
};
} // namespace
void start_ping_test() void start_ping_test()
{ {
return; return;
@@ -1069,21 +1136,29 @@ GUI_App::GUI_App()
, m_wcp_download_manager(&WCPDownloadManager::getInstance()) , m_wcp_download_manager(&WCPDownloadManager::getInstance())
, m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>()) , m_other_instance_message_handler(std::make_unique<OtherInstanceMessageHandler>())
{ {
StartupProfiler profiler("GUI_App::GUI_App");
//app config initializes early becasuse it is used in instance checking in Snapmaker_Orca.cpp //app config initializes early becasuse it is used in instance checking in Snapmaker_Orca.cpp
this->init_app_config(); this->init_app_config();
profiler.mark("init_app_config");
this->init_download_path(); this->init_download_path();
profiler.mark("init_download_path");
#if wxUSE_WEBVIEW_EDGE #if wxUSE_WEBVIEW_EDGE
this->init_webview_runtime(); this->init_webview_runtime();
profiler.mark("init_webview_runtime");
#endif #endif
reset_to_active(); reset_to_active();
profiler.mark("reset_to_active");
// test // test
m_page_http_server.setPort(PAGE_HTTP_PORT); m_page_http_server.setPort(PAGE_HTTP_PORT);
m_page_http_server.set_request_handler(HttpServer::web_server_handle_request); m_page_http_server.set_request_handler(HttpServer::web_server_handle_request);
m_page_http_server.start(); m_page_http_server.start();
profiler.mark("m_page_http_server.start");
m_fltviews.set_app(this); m_fltviews.set_app(this);
profiler.mark("m_fltviews.set_app");
} }
void GUI_App::shutdown(bool isRecreate) void GUI_App::shutdown(bool isRecreate)
@@ -2067,11 +2142,14 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
} }
void GUI_App::copy_web_resources() { void GUI_App::copy_web_resources() {
StartupProfiler profiler("GUI_App::copy_web_resources");
auto data_web_path = boost::filesystem::path(data_dir()) / "web"; auto data_web_path = boost::filesystem::path(data_dir()) / "web";
if (!boost::filesystem::exists(data_web_path / "flutter_web")) { if (!boost::filesystem::exists(data_web_path / "flutter_web")) {
auto source_path = boost::filesystem::path(resources_dir()) / "web" / "flutter_web"; auto source_path = boost::filesystem::path(resources_dir()) / "web" / "flutter_web";
auto target_path = data_web_path / "flutter_web"; auto target_path = data_web_path / "flutter_web";
copy_directory_recursively(source_path, target_path); copy_directory_recursively(source_path, target_path);
profiler.mark("copy flutter_web (missing target)");
} else { } else {
auto source_version_file = boost::filesystem::path(resources_dir()) / "web" / "flutter_web" / "version.json"; auto source_version_file = boost::filesystem::path(resources_dir()) / "web" / "flutter_web" / "version.json";
auto target_version_file = data_web_path / "flutter_web" / "version.json"; auto target_version_file = data_web_path / "flutter_web" / "version.json";
@@ -2087,10 +2165,13 @@ void GUI_App::copy_web_resources() {
auto source_path = boost::filesystem::path(resources_dir()) / "web" / "flutter_web"; auto source_path = boost::filesystem::path(resources_dir()) / "web" / "flutter_web";
auto target_path = data_web_path / "flutter_web"; auto target_path = data_web_path / "flutter_web";
copy_directory_recursively(source_path, target_path); copy_directory_recursively(source_path, target_path);
profiler.mark("copy flutter_web (version upgrade)");
} else {
profiler.note("flutter_web already up to date");
} }
} }
catch (std::exception& e) { catch (std::exception& e) {
profiler.note(std::string("version check failed: ") + e.what());
} }
} }
} }
@@ -2276,6 +2357,8 @@ class wxBoostLog : public wxLog
bool GUI_App::on_init_inner() bool GUI_App::on_init_inner()
{ {
StartupProfiler profiler("GUI_App::on_init_inner");
wxLog::SetActiveTarget(new wxBoostLog()); wxLog::SetActiveTarget(new wxBoostLog());
#if BBL_RELEASE_TO_PUBLIC #if BBL_RELEASE_TO_PUBLIC
wxLog::SetLogLevel(wxLOG_Message); wxLog::SetLogLevel(wxLOG_Message);
@@ -2288,6 +2371,7 @@ bool GUI_App::on_init_inner()
#ifdef NDEBUG #ifdef NDEBUG
wxImage::SetDefaultLoadFlags(0); // ignore waring in release build wxImage::SetDefaultLoadFlags(0); // ignore waring in release build
#endif #endif
profiler.mark("wx init/log/image handlers");
#if defined(_WIN32) && ! defined(_WIN64) #if defined(_WIN32) && ! defined(_WIN64)
// BBS: remove 32bit build prompt // BBS: remove 32bit build prompt
@@ -2414,6 +2498,7 @@ bool GUI_App::on_init_inner()
init_label_colours(); init_label_colours();
init_fonts(); init_fonts();
wxGetApp().Update_dark_mode_flag(); wxGetApp().Update_dark_mode_flag();
profiler.mark("language/theme/fonts");
#ifdef _MSW_DARK_MODE #ifdef _MSW_DARK_MODE
@@ -2451,6 +2536,7 @@ bool GUI_App::on_init_inner()
remove_old_networking_plugins(); remove_old_networking_plugins();
} }
} }
profiler.mark("version/network plugin cleanup");
if(app_config->get("version") != SLIC3R_VERSION) { if(app_config->get("version") != SLIC3R_VERSION) {
app_config->set("version", SLIC3R_VERSION); app_config->set("version", SLIC3R_VERSION);
@@ -2485,11 +2571,14 @@ bool GUI_App::on_init_inner()
// just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory
// supplied as argument to --datadir; in that case we should still run the wizard // supplied as argument to --datadir; in that case we should still run the wizard
preset_bundle->setup_directories(); preset_bundle->setup_directories();
profiler.mark("preset_bundle->setup_directories");
copy_web_resources(); copy_web_resources();
profiler.mark("copy_web_resources");
if (m_init_app_config_from_older) if (m_init_app_config_from_older)
copy_older_config(); copy_older_config();
profiler.mark("copy_older_config_if_needed");
if (is_editor()) { if (is_editor()) {
#ifdef __WXMSW__ #ifdef __WXMSW__
@@ -2610,6 +2699,7 @@ bool GUI_App::on_init_inner()
preset_bundle->set_default_suppressed(true); preset_bundle->set_default_suppressed(true);
preset_bundle->backup_user_folder(); preset_bundle->backup_user_folder();
profiler.mark("preset_bundle->backup_user_folder");
Bind(EVT_SHOW_IP_DIALOG, &GUI_App::show_ip_address_enter_dialog_handler, this); Bind(EVT_SHOW_IP_DIALOG, &GUI_App::show_ip_address_enter_dialog_handler, this);
@@ -2634,7 +2724,9 @@ bool GUI_App::on_init_inner()
} }
} */ } */
copy_network_if_available(); copy_network_if_available();
profiler.mark("copy_network_if_available");
on_init_network(); on_init_network();
profiler.mark("on_init_network");
if (m_agent && m_agent->is_user_login()) { if (m_agent && m_agent->is_user_login()) {
enable_user_preset_folder(true); enable_user_preset_folder(true);
@@ -2654,6 +2746,7 @@ bool GUI_App::on_init_inner()
show_error(nullptr, ex.what()); show_error(nullptr, ex.what());
} }
//} //}
profiler.mark("preset_bundle->load_presets");
#ifdef WIN32 #ifdef WIN32
#if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3) #if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
@@ -2673,6 +2766,7 @@ bool GUI_App::on_init_inner()
auto downloadUlr = m_updateDialog->getUrl(); auto downloadUlr = m_updateDialog->getUrl();
wxLaunchDefaultBrowser(downloadUlr); wxLaunchDefaultBrowser(downloadUlr);
}); });
profiler.mark("mainframe construction");
// hide settings tabs after first Layout // hide settings tabs after first Layout
if (is_editor()) { if (is_editor()) {
@@ -2698,6 +2792,7 @@ bool GUI_App::on_init_inner()
} }
else else
load_current_presets(); load_current_presets();
profiler.mark("load_current_presets");
if (plater_ != nullptr) { if (plater_ != nullptr) {
plater_->reset_project_dirty_initial_presets(); plater_->reset_project_dirty_initial_presets();
@@ -2710,6 +2805,7 @@ bool GUI_App::on_init_inner()
#endif #endif
mainframe->Show(true); mainframe->Show(true);
BOOST_LOG_TRIVIAL(info) << "main frame firstly shown"; BOOST_LOG_TRIVIAL(info) << "main frame firstly shown";
profiler.mark("mainframe->Show");
obj_list()->set_min_height(); obj_list()->set_min_height();
@@ -2786,6 +2882,8 @@ bool GUI_App::on_init_inner()
"configuration file.\nPlease note, application settings will be lost, but printer profiles will not be affected.")); "configuration file.\nPlease note, application settings will be lost, but printer profiles will not be affected."));
} }
profiler.mark("on_init_inner return");
return true; return true;
} }
@@ -2931,16 +3029,21 @@ void GUI_App::copy_network_if_available()
bool GUI_App::on_init_network(bool try_backup) bool GUI_App::on_init_network(bool try_backup)
{ {
StartupProfiler profiler("GUI_App::on_init_network");
bool create_network_agent = false; bool create_network_agent = false;
auto should_load_networking_plugin = app_config->get_bool("installed_networking"); auto should_load_networking_plugin = app_config->get_bool("installed_networking");
if(!should_load_networking_plugin) { if(!should_load_networking_plugin) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Don't load plugin as installed_networking is false"; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Don't load plugin as installed_networking is false";
profiler.note("installed_networking=false, plugin load skipped");
} else { } else {
int load_agent_dll = Slic3r::NetworkAgent::initialize_network_module(); int load_agent_dll = Slic3r::NetworkAgent::initialize_network_module();
profiler.mark("NetworkAgent::initialize_network_module");
__retry: __retry:
if (!load_agent_dll) { if (!load_agent_dll) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, load dll ok"; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, load dll ok";
if (check_networking_version()) { if (check_networking_version()) {
profiler.mark("check_networking_version");
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, compatibility version"; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, compatibility version";
auto bambu_source = Slic3r::NetworkAgent::get_bambu_source_entry(); auto bambu_source = Slic3r::NetworkAgent::get_bambu_source_entry();
if (!bambu_source) { if (!bambu_source) {
@@ -2957,6 +3060,7 @@ __retry:
int result = Slic3r::NetworkAgent::unload_network_module(); int result = Slic3r::NetworkAgent::unload_network_module();
BOOST_LOG_TRIVIAL(info) << "on_init_network, version mismatch, unload_network_module, result = " << result; BOOST_LOG_TRIVIAL(info) << "on_init_network, version mismatch, unload_network_module, result = " << result;
load_agent_dll = Slic3r::NetworkAgent::initialize_network_module(true); load_agent_dll = Slic3r::NetworkAgent::initialize_network_module(true);
profiler.mark("initialize_network_module(backup)");
try_backup = false; try_backup = false;
goto __retry; goto __retry;
} }
@@ -3023,6 +3127,7 @@ __retry:
std::string country_code = app_config->get_country_code(); std::string country_code = app_config->get_country_code();
m_agent->set_country_code(country_code); m_agent->set_country_code(country_code);
m_agent->start(); m_agent->start();
profiler.mark("m_agent->start");
} }
} }
else { else {
@@ -3036,6 +3141,8 @@ __retry:
m_user_manager = new Slic3r::UserManager(); m_user_manager = new Slic3r::UserManager();
} }
profiler.note(std::string("create_network_agent=") + (create_network_agent ? "true" : "false"));
return true; return true;
} }
+1 -1
View File
@@ -6181,7 +6181,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"brim_width", "wall_loops", "wall_filament", "sparse_infill_density", "sparse_infill_filament", "top_shell_layers", "brim_width", "wall_loops", "wall_filament", "sparse_infill_density", "sparse_infill_filament", "top_shell_layers",
"enable_support", "support_filament", "support_interface_filament", "enable_support", "support_filament", "support_interface_filament",
"support_top_z_distance", "support_bottom_z_distance", "raft_layers", "support_top_z_distance", "support_bottom_z_distance", "raft_layers",
"wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "wipe_tower_max_purge_speed", "wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "local_z_wipe_tower_purge_lines", "wipe_tower_max_purge_speed",
"wipe_tower_wall_type", "wipe_tower_extra_rib_length","wipe_tower_rib_width","wipe_tower_fillet_wall", "wipe_tower_wall_type", "wipe_tower_extra_rib_length","wipe_tower_rib_width","wipe_tower_fillet_wall",
"wipe_tower_filament", "wipe_tower_filament",
"best_object_pos" "best_object_pos"
+1
View File
@@ -2445,6 +2445,7 @@ void TabPrint::build()
optgroup->append_single_option_line("wipe_tower_bridging", "multimaterial_settings_prime_tower#maximal-bridging-distance"); optgroup->append_single_option_line("wipe_tower_bridging", "multimaterial_settings_prime_tower#maximal-bridging-distance");
optgroup->append_single_option_line("wipe_tower_extra_spacing", "multimaterial_settings_prime_tower#wipe-tower-purge-lines-spacing"); optgroup->append_single_option_line("wipe_tower_extra_spacing", "multimaterial_settings_prime_tower#wipe-tower-purge-lines-spacing");
optgroup->append_single_option_line("wipe_tower_extra_flow", "multimaterial_settings_prime_tower#extra-flow-for-purge"); optgroup->append_single_option_line("wipe_tower_extra_flow", "multimaterial_settings_prime_tower#extra-flow-for-purge");
optgroup->append_single_option_line("local_z_wipe_tower_purge_lines", "multimaterial_settings_prime_tower");
optgroup->append_single_option_line("wipe_tower_max_purge_speed", "multimaterial_settings_prime_tower#maximum-wipe-tower-print-speed"); optgroup->append_single_option_line("wipe_tower_max_purge_speed", "multimaterial_settings_prime_tower#maximum-wipe-tower-print-speed");
optgroup->append_single_option_line("wipe_tower_wall_type", "multimaterial_settings_prime_tower#wall-type"); optgroup->append_single_option_line("wipe_tower_wall_type", "multimaterial_settings_prime_tower#wall-type");
optgroup->append_single_option_line("wipe_tower_cone_angle", "multimaterial_settings_prime_tower#stabilization-cone-apex-angle"); optgroup->append_single_option_line("wipe_tower_cone_angle", "multimaterial_settings_prime_tower#stabilization-cone-apex-angle");
+24 -24
View File
@@ -23,18 +23,20 @@ namespace {
void quarantine_socket(udp::socket& socket, bool& socket_usable, const char* action, const std::exception& e) void quarantine_socket(udp::socket& socket, bool& socket_usable, const char* action, const std::exception& e)
{ {
#ifdef __APPLE__
socket_usable = false; socket_usable = false;
boost::system::error_code ec; boost::system::error_code ec;
socket.cancel(ec); socket.cancel(ec);
socket.close(ec); socket.close(ec);
BOOST_LOG_TRIVIAL(error) << action << ": " << e.what(); BOOST_LOG_TRIVIAL(error) << action << ": " << e.what();
#else }
(void) socket;
(void) socket_usable; template <typename SocketT>
(void) action; void retain_usable_socket(std::vector<SocketT*>& sockets, SocketT* socket)
BOOST_LOG_TRIVIAL(error) << e.what(); {
#endif if (socket->is_usable())
sockets.emplace_back(socket);
else
delete socket;
} }
} }
@@ -699,21 +701,19 @@ UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multica
UdpSocket::~UdpSocket() UdpSocket::~UdpSocket()
{ {
try { boost::system::error_code ec;
// 取消所有异步操作 // 取消所有异步操作
socket.cancel(); socket.cancel(ec);
// 关闭socket // 关闭socket
if (socket.is_open()) { if (socket.is_open())
socket.close(); socket.close(ec);
}
if (ec)
BOOST_LOG_TRIVIAL(error) << "Error closing socket in destructor: " << ec.message();
else
BOOST_LOG_TRIVIAL(debug) << "UdpSocket destroyed, socket closed"; BOOST_LOG_TRIVIAL(debug) << "UdpSocket destroyed, socket closed";
} }
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Error closing socket in destructor: " << e.what();
}
}
void UdpSocket::cancel() void UdpSocket::cancel()
{ {
@@ -942,12 +942,12 @@ void Bonjour::priv::lookup_perform()
// create ipv4 socket for each interface // create ipv4 socket for each interface
// each will send to querry to for both ipv4 and ipv6 // each will send to querry to for both ipv4 and ipv6
for (const auto& intrfc : interfaces) for (const auto& intrfc : interfaces)
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, intrfc, io_service)); retain_usable_socket(sockets, new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, intrfc, io_service));
} else { } else {
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message(); BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message();
} }
if (sockets.empty()) if (sockets.empty())
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, io_service)); retain_usable_socket(sockets, new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP4, io_service));
// ipv6 interfaces // ipv6 interfaces
interfaces.clear(); interfaces.clear();
//udp::resolver::query query(host, PORT, boost::asio::ip::resolver_query_base::numeric_service); //udp::resolver::query query(host, PORT, boost::asio::ip::resolver_query_base::numeric_service);
@@ -962,9 +962,9 @@ void Bonjour::priv::lookup_perform()
// create ipv6 socket for each interface // create ipv6 socket for each interface
// each will send to querry to for both ipv4 and ipv6 // each will send to querry to for both ipv4 and ipv6
for (const auto& intrfc : interfaces) for (const auto& intrfc : interfaces)
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, intrfc, io_service)); retain_usable_socket(sockets, new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, intrfc, io_service));
if (interfaces.empty()) if (interfaces.empty())
sockets.emplace_back(new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, io_service)); retain_usable_socket(sockets, new LookupSocket(txt_keys, service, service_dn, protocol, replyfn, BonjourRequest::MCAST_IP6, io_service));
} else { } else {
BOOST_LOG_TRIVIAL(info)<< "Failed to resolve ipv6 interfaces: " << ec.message(); BOOST_LOG_TRIVIAL(info)<< "Failed to resolve ipv6 interfaces: " << ec.message();
} }
@@ -1047,12 +1047,12 @@ void Bonjour::priv::resolve_perform()
// create ipv4 socket for each interface // create ipv4 socket for each interface
// each will send to querry to for both ipv4 and ipv6 // each will send to querry to for both ipv4 and ipv6
for (const auto& intrfc : interfaces) for (const auto& intrfc : interfaces)
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service)); retain_usable_socket(sockets, new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, intrfc, io_service));
} else { } else {
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message(); BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv4 interfaces: " << ec.message();
} }
if (sockets.empty()) if (sockets.empty())
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, io_service)); retain_usable_socket(sockets, new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP4, io_service));
// ipv6 interfaces // ipv6 interfaces
interfaces.clear(); interfaces.clear();
@@ -1066,9 +1066,9 @@ void Bonjour::priv::resolve_perform()
// create ipv6 socket for each interface // create ipv6 socket for each interface
// each will send to querry to for both ipv4 and ipv6 // each will send to querry to for both ipv4 and ipv6
for (const auto& intrfc : interfaces) for (const auto& intrfc : interfaces)
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service)); retain_usable_socket(sockets, new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, intrfc, io_service));
if (interfaces.empty()) if (interfaces.empty())
sockets.emplace_back(new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, io_service)); retain_usable_socket(sockets, new ResolveSocket(hostname, reply_callback, BonjourRequest::MCAST_IP6, io_service));
} else { } else {
BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv6 interfaces: " << ec.message(); BOOST_LOG_TRIVIAL(info) << "Failed to resolve ipv6 interfaces: " << ec.message();
} }
+1
View File
@@ -162,6 +162,7 @@ public:
void send(); void send();
void async_receive(); void async_receive();
void cancel(); void cancel();
bool is_usable() const { return m_socket_usable; }
protected: protected:
void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes); void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes);
virtual SharedSession create_session() const = 0; virtual SharedSession create_session() const = 0;
+1
View File
@@ -11,6 +11,7 @@ add_executable(${_TEST_NAME}_tests
test_elephant_foot_compensation.cpp test_elephant_foot_compensation.cpp
test_geometry.cpp test_geometry.cpp
test_mixed_filament.cpp test_mixed_filament.cpp
test_local_z_order_optimizer.cpp
test_placeholder_parser.cpp test_placeholder_parser.cpp
test_polygon.cpp test_polygon.cpp
test_mutable_polygon.cpp test_mutable_polygon.cpp
@@ -0,0 +1,29 @@
#include <catch2/catch.hpp>
#include "libslic3r/LocalZOrderOptimizer.hpp"
using namespace Slic3r;
TEST_CASE("Local-Z bucket ordering starts with the active extruder when possible", "[LocalZ][GCode]")
{
const std::vector<unsigned int> ordered =
LocalZOrderOptimizer::order_bucket_extruders({1, 2}, 2, 1);
REQUIRE(ordered == std::vector<unsigned int>{2, 1});
}
TEST_CASE("Local-Z pass groups keep a matching active extruder bucket first", "[LocalZ][GCode]")
{
const std::vector<size_t> ordered =
LocalZOrderOptimizer::order_pass_group({{1}, {2}}, 2);
REQUIRE(ordered == std::vector<size_t>{1, 0});
}
TEST_CASE("Local-Z pass groups keep original order when no bucket matches the active extruder", "[LocalZ][GCode]")
{
const std::vector<size_t> ordered =
LocalZOrderOptimizer::order_pass_group({{1}, {2}, {3}}, 0);
REQUIRE(ordered == std::vector<size_t>{0, 1, 2});
}