update libslic3r

This commit is contained in:
QIDI TECH
2025-05-05 19:52:57 +08:00
parent eae8e18c3a
commit 126534997a
180 changed files with 24833 additions and 5679 deletions

View File

@@ -64,6 +64,18 @@ void Print::clear()
m_objects.clear();
m_print_regions.clear();
m_model.clear_objects();
m_statistics_by_extruder_count.clear();
}
bool Print::has_tpu_filament() const
{
for (unsigned int filament_id : m_wipe_tower_data.tool_ordering.all_extruders()) {
std::string filament_name = m_config.filament_type.get_at(filament_id);
if (filament_name == "TPU") {
return true;
}
}
return false;
}
// Called by Print::apply().
@@ -90,10 +102,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"pressure_advance",
"enable_overhang_bridge_fan"
"overhang_fan_speed",
"pre_start_fan_time",
"overhang_fan_threshold",
"overhang_threshold_participating_cooling",
"slow_down_for_layer_cooling",
"default_acceleration",
"deretraction_speed",
"close_fan_the_first_x_layers",
"machine_end_gcode",
@@ -188,6 +200,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"retraction_distances_when_cut",
"filament_long_retractions_when_cut",
"filament_retraction_distances_when_cut",
"grab_length",
"bed_temperature_formula",
//w13
"additional_cooling_fan_speed_unseal",
//w14
@@ -196,6 +210,10 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
,"resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed"
//w17
,"seal"
//y58
,"box_temperature"
//y60
,"is_support_3mf"
};
static std::unordered_set<std::string> steps_ignore;
@@ -227,6 +245,20 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "resolution"
|| opt_key == "precise_z_height"
|| opt_key == "filament_shrink"
|| opt_key == "enable_circle_compensation"
|| opt_key == "circle_compensation_manual_offset"
|| opt_key == "circle_compensation_speed"
|| opt_key == "counter_coef_1"
|| opt_key == "counter_coef_2"
|| opt_key == "counter_coef_3"
|| opt_key == "hole_coef_1"
|| opt_key == "hole_coef_2"
|| opt_key == "hole_coef_3"
|| opt_key == "counter_limit_min"
|| opt_key == "counter_limit_max"
|| opt_key == "hole_limit_min"
|| opt_key == "hole_limit_max"
|| opt_key == "diameter_limit"
// Spiral Vase forces different kind of slicing than the normal model:
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
@@ -238,9 +270,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "nozzle_temperature_initial_layer"
|| opt_key == "filament_minimal_purge_on_wipe_tower"
|| opt_key == "filament_max_volumetric_speed"
|| opt_key == "filament_ramming_volumetric_speed"
|| opt_key == "gcode_flavor"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "nozzle_temperature"
|| opt_key == "filament_pre_cooling_temperature"
|| opt_key == "filament_ramming_travel_time"
// QDS
|| opt_key == "supertack_plate_temp"
|| opt_key == "cool_plate_temp"
@@ -248,21 +283,37 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "hot_plate_temp"
|| opt_key == "textured_plate_temp"
|| opt_key == "enable_prime_tower"
|| opt_key == "prime_tower_enable_framework"
|| opt_key == "prime_tower_width"
|| opt_key == "prime_tower_max_speed"
|| opt_key == "prime_tower_lift_speed"
|| opt_key == "prime_tower_lift_height"
|| opt_key == "prime_tower_brim_width"
|| opt_key == "prime_tower_skip_points"
|| opt_key == "prime_tower_rib_wall"
|| opt_key == "prime_tower_extra_rib_length"
|| opt_key == "prime_tower_rib_width"
|| opt_key == "prime_tower_fillet_wall"
|| opt_key == "first_layer_print_sequence"
|| opt_key == "other_layers_print_sequence"
|| opt_key == "other_layers_print_sequence_nums"
|| opt_key == "extruder_ams_count"
|| opt_key == "filament_map_mode"
|| opt_key == "filament_map"
|| opt_key == "filament_adhesiveness_category"
//|| opt_key == "wipe_tower_bridging"
|| opt_key == "wipe_tower_no_sparse_layers"
|| opt_key == "flush_volumes_matrix"
|| opt_key == "prime_volume"
|| opt_key == "filament_prime_volume"
|| opt_key == "flush_into_infill"
|| opt_key == "flush_into_support"
|| opt_key == "initial_layer_infill_speed"
|| opt_key == "travel_speed"
|| opt_key == "travel_speed_z"
|| opt_key == "initial_layer_speed"
|| opt_key == "default_acceleration"
|| opt_key == "travel_acceleration"
|| opt_key == "initial_layer_travel_acceleration"
//w34
|| opt_key == "support_multi_bed_types") {
//|| opt_key == "z_offset") {
@@ -270,10 +321,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
steps.emplace_back(psSkirtBrim);
} else if (opt_key == "filament_soluble"
|| opt_key == "filament_is_support"
|| opt_key == "impact_strength_z"
|| opt_key == "filament_scarf_seam_type"
|| opt_key == "filament_scarf_height"
|| opt_key == "filament_scarf_gap"
|| opt_key == "filament_scarf_length"
|| opt_key == "filament_change_length"
|| opt_key == "independent_support_layer_height") {
steps.emplace_back(psWipeTower);
// Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
@@ -288,7 +341,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
//|| opt_key == "resolution"
//QDS: when enable arc fitting, we must re-generate perimeter
|| opt_key == "enable_arc_fitting"
|| opt_key == "wall_sequence") {
|| opt_key == "wall_sequence"
|| opt_key == "z_direction_outwall_speed_continuous") {
osteps.emplace_back(posPerimeters);
osteps.emplace_back(posInfill);
osteps.emplace_back(posSupportMaterial);
@@ -536,6 +590,10 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
return -1;
};
std::vector<struct print_instance_info> print_instance_with_bounding_box;
bool all_objects_are_short = print.is_all_objects_are_short();
float obj_distance = all_objects_are_short ? scale_(0.5*MAX_OUTER_NOZZLE_RADIUS-0.1) : scale_(0.5*print.config().extruder_clearance_max_radius.value-0.1);
{
// sequential_print_horizontal_clearance_valid
Polygons convex_hulls_other;
@@ -543,11 +601,8 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
polygons->clear();
std::vector<size_t> intersecting_idxs;
bool all_objects_are_short = print.is_all_objects_are_short();
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float obj_distance = all_objects_are_short ? scale_(0.5*MAX_OUTER_NOZZLE_RADIUS-0.1) : scale_(0.5*print.config().extruder_clearance_max_radius.value-0.1);
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->instances().empty());
@@ -561,9 +616,10 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
Geometry::Transformation new_trans(model_instance0->get_transformation());
new_trans.set_offset({0.0, 0.0, model_instance0->get_offset().z()});
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
{ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())));
print_object->model_object()->convex_hull_2d(new_trans.get_matrix()));
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
@@ -749,6 +805,12 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
//print_instance_with_bounding_box.pop_back();
/*bool has_interlaced_objects = false;
for (int k = 0; k < print_instance_count; k++)
{
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
BoundingBox& bbox = print_instance_with_bounding_box[k].bounding_box;
bbox.offset( scale_(print_config.extruder_clearance_dist_to_rod.value*0.5) - obj_distance);
}
for (int k = 0; k < print_instance_count; k++)
{
auto inst = print_instance_with_bounding_box[k].print_instance;
auto bbox = print_instance_with_bounding_box[k].bounding_box;
@@ -778,8 +840,7 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
for (int k = 0; k < print_instance_count; k++)
{
auto inst = print_instance_with_bounding_box[k].print_instance;
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(scale_(print_config.extruder_clearance_dist_to_rod.value - print_config.extruder_clearance_max_radius.value));
auto bbox = print_instance_with_bounding_box[k].bounding_box;
auto iy1 = bbox.min.y();
auto iy2 = bbox.max.y();
(const_cast<ModelInstance*>(inst->model_instance))->arrange_order = k+1;
@@ -910,15 +971,25 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
float depth = print.wipe_tower_data(filaments_count).depth;
//float brim_width = print.wipe_tower_data(filaments_count).brim_width;
if (config.prime_tower_rib_wall.value)
width = depth;
Polygons convex_hulls_temp;
if (print.has_wipe_tower()) {
Polygon wipe_tower_convex_hull;
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
wipe_tower_convex_hull.rotate(a);
convex_hulls_temp.push_back(wipe_tower_convex_hull);
if (!print.is_step_done(psWipeTower)) {
Polygon wipe_tower_convex_hull;
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
wipe_tower_convex_hull.rotate(a);
convex_hulls_temp.push_back(wipe_tower_convex_hull);
} else {
//here, wipe_tower_polygon is not always convex.
Polygon wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom;
wipe_tower_polygon.translate(Point(scale_(x), scale_(y)));
convex_hulls_temp.push_back(wipe_tower_polygon);
}
}
if (!intersection(convex_hulls_other, convex_hulls_temp).empty()) {
if (warning) {
@@ -935,22 +1006,29 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
return {};
}
bool Print::check_multi_filaments_compatibility(const std::vector<std::string>& filament_types)
FilamentCompatibilityType Print::check_multi_filaments_compatibility(const std::vector<std::string>& filament_types)
{
bool has_high_temperature_filament = false;
bool has_low_temperature_filament = false;
bool has_mid_temperature_filament = false;
for (const auto& type : filament_types) {
if (get_filament_temp_type(type) ==FilamentTempType::HighTemp)
has_high_temperature_filament = true;
else if (get_filament_temp_type(type) == FilamentTempType::LowTemp)
has_low_temperature_filament = true;
else if (get_filament_temp_type(type) == FilamentTempType::HighLowCompatible)
has_mid_temperature_filament = true;
}
if (has_high_temperature_filament && has_low_temperature_filament)
return false;
return true;
return FilamentCompatibilityType::HighLowMixed;
else if (has_high_temperature_filament && has_mid_temperature_filament)
return FilamentCompatibilityType::HighMidMixed;
else if (has_low_temperature_filament && has_mid_temperature_filament)
return FilamentCompatibilityType::LowMidMixed;
else
return FilamentCompatibilityType::Compatible;
}
bool Print::is_filaments_compatible(const std::vector<int>& filament_types)
@@ -1002,10 +1080,36 @@ StringObjectException Print::check_multi_filament_valid(const Print& print)
for (const auto& extruder_idx : extruders)
filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
if (!check_multi_filaments_compatibility(filament_types))
return { L("Can not print multiple filaments which have large difference of temperature together. Otherwise, the extruder and nozzle may be blocked or damaged during printing") };
auto compatibility = check_multi_filaments_compatibility(filament_types);
bool enable_mix_printing = !print.need_check_multi_filaments_compatibility();
return {std::string()};
StringObjectException ret;
std::string hypertext = "filament_mix_print";
if(compatibility == FilamentCompatibilityType::HighLowMixed){
if(enable_mix_printing){
ret.string =L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage.");
ret.is_warning = true;
ret.hypetext = hypertext;
}
else{
ret.string =L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage. If you still want to print, you can enable the option in Preferences.");
}
}
else if (compatibility == FilamentCompatibilityType::HighMidMixed) {
ret.is_warning = true;
ret.hypetext = hypertext;
ret.string =L("Printing high-temp and mid-temp filaments together may cause nozzle clogging or printer damage.");
}
else if (compatibility == FilamentCompatibilityType::LowMidMixed) {
ret.is_warning = true;
ret.hypetext = hypertext;
ret.string = L("Printing mid-temp and low-temp filaments together may cause nozzle clogging or printer damage.");
}
return ret;
}
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
@@ -1025,6 +1129,10 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (!ret.string.empty())
{
ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP;
if (ret.is_warning && warning != nullptr) {
*warning = ret;
return {};
}
return ret;
}
}
@@ -1688,14 +1796,16 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
const AutoContourHolesCompensationParams &auto_contour_holes_compensation_params = AutoContourHolesCompensationParams(m_config);
if (!use_cache) {
//1.9.5
if (slice_time) {
start_time = (long long)Slic3r::Utils::get_current_milliseconds_time_utc();
}
for (PrintObject* obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->set_auto_circle_compenstaion_params(auto_contour_holes_compensation_params);
obj->make_perimeters();
}
else {
@@ -1810,21 +1920,42 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
}
if (this->set_started(psWipeTower)) {
{
std::vector<std::set<int>> geometric_unprintables(m_config.nozzle_diameter.size());
for (PrintObject* obj : m_objects) {
std::vector<std::set<int>> obj_geometric_unprintables = obj->detect_extruder_geometric_unprintables();
for (size_t idx = 0; idx < obj_geometric_unprintables.size(); ++idx) {
if (idx < geometric_unprintables.size()) {
geometric_unprintables[idx].insert(obj_geometric_unprintables[idx].begin(), obj_geometric_unprintables[idx].end());
}
}
}
this->set_geometric_unprintable_filaments(geometric_unprintables);
}
m_wipe_tower_data.clear();
m_tool_ordering.clear();
if (this->has_wipe_tower()) {
this->_make_wipe_tower();
} else if (this->config().print_sequence != PrintSequence::ByObject) {
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering(*this, -1, false);
}
else if (this->config().print_sequence != PrintSequence::ByObject) {
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering(*this, -1, false);
m_tool_ordering.sort_and_build_data(*this, -1, false);
if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
}
this->set_done(psWipeTower);
}
if (this->has_wipe_tower()) {
m_fake_wipe_tower.set_pos({ m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index) });
}
if (this->set_started(psSkirtBrim)) {
this->set_status(70, L("Generating skirt & brim"));
//1.9.5
if (slice_time) {
start_time = (long long)Slic3r::Utils::get_current_milliseconds_time_utc();
}
@@ -1852,10 +1983,45 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
if (this->config().print_sequence == PrintSequence::ByObject) {
// Order object instances for sequential print.
print_object_instances_ordering = sort_object_instances_by_model_order(*this);
std::vector<unsigned int> first_layer_used_filaments;
std::vector<std::vector<unsigned int>> all_filaments;
for (print_object_instance_sequential_active = print_object_instances_ordering.begin(); print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
for (size_t idx = 0; idx < tool_ordering.layer_tools().size(); ++idx) {
auto& layer_filament = tool_ordering.layer_tools()[idx].extruders;
all_filaments.emplace_back(layer_filament);
if (idx == 0)
first_layer_used_filaments.insert(first_layer_used_filaments.end(), layer_filament.begin(), layer_filament.end());
}
}
sort_remove_duplicates(first_layer_used_filaments);
auto used_filaments = collect_sorted_used_filaments(all_filaments);
this->set_slice_used_filaments(first_layer_used_filaments,used_filaments);
auto physical_unprintables = this->get_physical_unprintable_filaments(used_filaments);
auto geometric_unprintables = this->get_geometric_unprintable_filaments();
std::vector<int>filament_maps = this->get_filament_maps();
auto map_mode = get_filament_map_mode();
// get recommended filament map
if (map_mode < FilamentMapMode::fmmManual) {
filament_maps = ToolOrdering::get_recommended_filament_maps(all_filaments, this, map_mode, physical_unprintables, geometric_unprintables);
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) { return value + 1; });
update_filament_maps_to_config(filament_maps);
}
// check map valid both in auto and mannual mode
std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) {return value - 1; });
if (!ToolOrdering::check_tpu_group(used_filaments, filament_maps, &m_config)) {
int master_extruder_id = m_config.master_extruder_id.value - 1; // to 0 based
std::string nozzle_name = master_extruder_id == 0 ? L("left") : L("right");
std::string exception_str = L("TPU is incompatible with AMS and must be printed seperately in the ") + nozzle_name + L(" nozzle.\nPlease adjust the filament group accordingly.");
throw Slic3r::RuntimeError(exception_str);
}
// print_object_instances_ordering = sort_object_instances_by_max_z(print);
print_object_instance_sequential_active = print_object_instances_ordering.begin();
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
tool_ordering.sort_and_build_data(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1)) {
append(printExtruders, tool_ordering.tools_for_layer(layers_to_print.front().first).extruders);
}
@@ -1864,6 +2030,12 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
else {
tool_ordering = this->tool_ordering();
tool_ordering.assign_custom_gcodes(*this);
std::vector<unsigned int> first_layer_used_filaments;
if (!tool_ordering.layer_tools().empty())
first_layer_used_filaments = tool_ordering.layer_tools().front().extruders;
this->set_slice_used_filaments(first_layer_used_filaments, tool_ordering.all_extruders());
has_wipe_tower = this->has_wipe_tower() && tool_ordering.has_wipe_tower();
//QDS: have no single_extruder_multi_material_priming
#if 0
@@ -1943,8 +2115,7 @@ void Print::process(std::unordered_map<std::string, long long>* slice_time, bool
break;
}
}
// TODO adaptive layer height won't work with conflict checker because m_fake_wipe_tower's path is generated using fixed layer height
if(!m_no_check && !has_adaptive_layer_height)
if(!m_no_check /*&& !has_adaptive_layer_height*/)
{
using Clock = std::chrono::high_resolution_clock;
auto startTime = Clock::now();
@@ -1992,8 +2163,10 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
const Vec3d origin = this->get_plate_origin();
gcode.set_gcode_offset(origin(0), origin(1));
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
gcode.export_layer_filaments(result);
//QDS
result->conflict_result = m_conflict_result;
if (result != nullptr)
result->conflict_result = m_conflict_result;
return path.c_str();
}
@@ -2204,9 +2377,9 @@ std::vector<Point> Print::first_layer_wipe_tower_corners(bool check_wipe_tower_e
if (check_wipe_tower_existance && (!has_wipe_tower() || m_wipe_tower_data.tool_changes.empty()))
return corners;
{
double width = m_config.prime_tower_width + 2*m_wipe_tower_data.brim_width;
double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width;
Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width);
double width = m_wipe_tower_data.bbx.max.x() - m_wipe_tower_data.bbx.min.x();
double depth = m_wipe_tower_data.bbx.max.y() -m_wipe_tower_data.bbx.min.y();
Vec2d pt0 = m_wipe_tower_data.bbx.min + m_wipe_tower_data.rib_offset.cast<double>();
for (Vec2d pt : {
pt0,
Vec2d(pt0.x()+width, pt0.y() ),
@@ -2315,6 +2488,130 @@ void Print::finalize_first_layer_convex_hull()
m_first_layer_convex_hull = Geometry::convex_hull(m_first_layer_convex_hull.points);
}
void Print::update_filament_maps_to_config(std::vector<int> f_maps)
{
if (m_config.filament_map.values != f_maps)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament maps changed after pre-slicing.");
m_ori_full_print_config.option<ConfigOptionInts>("filament_map", true)->values = f_maps;
m_config.filament_map.values = f_maps;
m_full_print_config = m_ori_full_print_config;
m_full_print_config.update_values_to_printer_extruders_for_multiple_filaments(m_full_print_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant");
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";
t_config_option_keys print_diff;
DynamicPrintConfig filament_overrides;
for (auto& opt_key: extruder_retract_keys)
{
const ConfigOption *opt_new_filament = m_full_print_config.option(filament_prefix + opt_key);
const ConfigOption *opt_new_machine = m_full_print_config.option(opt_key);
const ConfigOption *opt_old_machine = m_config.option(opt_key);
if (opt_new_filament)
compute_filament_override_value(opt_key, opt_old_machine, opt_new_machine, opt_new_filament, m_full_print_config, print_diff, filament_overrides, f_maps);
}
t_config_option_keys keys(filament_options_with_variant.begin(), filament_options_with_variant.end());
m_config.apply_only(m_full_print_config, keys, true);
if (!print_diff.empty()) {
m_placeholder_parser.apply_config(filament_overrides);
m_config.apply(filament_overrides);
}
}
m_has_auto_filament_map_result = true;
}
std::vector<int> Print::get_filament_maps() const
{
return m_config.filament_map.values;
}
FilamentMapMode Print::get_filament_map_mode() const
{
return m_config.filament_map_mode;
}
std::vector<std::set<int>> Print::get_physical_unprintable_filaments(const std::vector<unsigned int>& used_filaments) const
{
int extruder_num = m_config.nozzle_diameter.size();
std::vector<std::set<int>>physical_unprintables(extruder_num);
if (extruder_num < 2)
return physical_unprintables;
auto get_unprintable_extruder_id = [&](unsigned int filament_idx)->int {
if (m_config.unprintable_filament_types.empty())
return -1;
for (int eid = 0; eid < m_config.unprintable_filament_types.values.size(); ++eid) {
std::vector<std::string> extruder_unprintables = split_string(m_config.unprintable_filament_types.values[eid], ',');
auto iter = std::find(extruder_unprintables.begin(), extruder_unprintables.end(), m_config.filament_type.values[filament_idx]);
if (iter != extruder_unprintables.end())
return eid;
}
return -1;
};
std::set<int> tpu_filaments;
for (auto f : used_filaments) {
if (m_config.filament_type.get_at(f) == "TPU")
tpu_filaments.insert(f);
}
if (tpu_filaments.size() > 1) {
throw Slic3r::RuntimeError(_u8L("Only supports up to one TPU filament."));
}
for (auto f : used_filaments) {
int extruder_id = get_unprintable_extruder_id(f);
if (extruder_id == -1)
continue;
physical_unprintables[extruder_id].insert(f);
}
return physical_unprintables;
}
std::vector<double> Print::get_extruder_printable_height() const
{
return m_config.extruder_printable_height.values;
}
std::vector<Polygons> Print::get_extruder_printable_polygons() const
{
std::vector<Polygons> extruder_printable_polys;
std::vector<std::vector<Vec2d>> extruder_printable_areas = m_config.extruder_printable_area.values;
for (const auto &e_printable_area : extruder_printable_areas) {
Polygons ploys = {Polygon::new_scale(e_printable_area)};
extruder_printable_polys.emplace_back(ploys);
}
return std::move(extruder_printable_polys);
}
std::vector<Polygons> Print::get_extruder_unprintable_polygons() const
{
std::vector<Vec2d> printable_area = m_config.printable_area.values;
Polygon printable_poly = Polygon::new_scale(printable_area);
std::vector<std::vector<Vec2d>> extruder_printable_areas = m_config.extruder_printable_area.values;
std::vector<Polygons> extruder_unprintable_polys;
for (const auto &e_printable_area : extruder_printable_areas) {
Polygons ploys = diff(printable_poly, Polygon::new_scale(e_printable_area));
extruder_unprintable_polys.emplace_back(ploys);
}
return std::move(extruder_unprintable_polys);
}
size_t Print::get_extruder_id(unsigned int filament_id) const
{
std::vector<int> filament_map = get_filament_maps();
if (filament_id < filament_map.size()) {
return filament_map[filament_id] - 1;
}
return 0;
}
// Wipe tower support.
bool Print::has_wipe_tower() const
{
@@ -2330,19 +2627,42 @@ bool Print::has_wipe_tower() const
const WipeTowerData& Print::wipe_tower_data(size_t filaments_cnt) const
{
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
if (! is_step_done(psWipeTower) && filaments_cnt !=0) {
// QDS
double width = m_config.prime_tower_width;
double layer_height = 0.2; // hard code layer height
double wipe_volume = m_config.prime_volume;
if (filaments_cnt == 1 && enable_timelapse_print()) {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume / (layer_height * width);
} else {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width);
}
const_cast<Print*>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
double max_height = 0;
for (size_t obj_idx = 0; obj_idx < m_objects.size(); obj_idx++) {
double object_z = (double) m_objects[obj_idx]->size().z();
max_height = std::max(unscale_(object_z), max_height);
}
if (max_height < EPSILON) return m_wipe_tower_data;
if (! is_step_done(psWipeTower) && filaments_cnt !=0) {
std::vector<double> filament_wipe_volume = m_config.filament_prime_volume.values;
double wipe_volume = get_max_element(filament_wipe_volume);
if (m_config.prime_tower_rib_wall.value) {
double layer_height = 0.08f; // hard code layer height
layer_height = m_objects.front()->config().layer_height.value;
int filament_depth_count = m_config.nozzle_diameter.values.size() == 2 ? filaments_cnt : filaments_cnt - 1;
if (filaments_cnt == 1 && enable_timelapse_print())
filament_depth_count = 1;
double depth = std::sqrt(wipe_volume * filament_depth_count / layer_height);
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
depth = std::max((double) min_wipe_tower_depth, depth);
const_cast<Print *>(this)->m_wipe_tower_data.depth = depth;
const_cast<Print *>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
}
else {
// QDS
double width = m_config.prime_tower_width;
double layer_height = 0.2; // hard code layer height
if (filaments_cnt == 1 && enable_timelapse_print()) {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume / (layer_height * width);
} else {
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width);
}
const_cast<Print *>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
}
if (m_config.prime_tower_brim_width < 0) const_cast<Print *>(this)->m_wipe_tower_data.brim_width = WipeTower::get_auto_brim_by_height(max_height);
}
return m_wipe_tower_data;
}
@@ -2355,19 +2675,14 @@ void Print::_make_wipe_tower()
{
m_wipe_tower_data.clear();
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
// QDS
const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON);
// Extract purging volumes for each extruder pair:
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i<number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin()+i*number_of_extruders, flush_matrix.begin()+(i+1)*number_of_extruders));
const unsigned int number_of_extruders = (unsigned int)(m_config.filament_colour.values.size());
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
// QDS: priming logic is removed, so don't consider it in tool ordering
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, false);
m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int)-1, false);
if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower.
@@ -2412,57 +2727,80 @@ void Print::_make_wipe_tower()
// Initialize the wipe tower.
// QDS: in QDT machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_config.prime_volume, m_wipe_tower_data.tool_ordering.first_extruder(),
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(),
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z);
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
wipe_tower.set_has_tpu_filament(this->has_tpu_filament());
wipe_tower.set_filament_map(this->get_filament_maps());
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder(i, m_config);
wipe_tower.set_need_reverse_travel(m_wipe_tower_data.tool_ordering.all_extruders());
// QDS: remove priming logic
//m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
// wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
std::set<int> used_filament_ids;
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
{
// QDS: priming logic is removed, so get the initial extruder by first_extruder()
unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.first_extruder();
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
bool is_mutli_extruder = m_config.nozzle_diameter.values.size() > 1;
size_t nozzle_nums = m_config.nozzle_diameter.values.size();
using FlushMatrix = std::vector<std::vector<float>>;
std::vector<FlushMatrix> multi_extruder_flush;
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, nozzle_id, nozzle_nums)));
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
multi_extruder_flush.emplace_back(wipe_volumes);
}
std::vector<int>filament_maps = get_filament_maps();
std::vector<unsigned int> nozzle_cur_filament_ids(nozzle_nums, -1);
unsigned int current_filament_id = m_wipe_tower_data.tool_ordering.first_extruder();
size_t cur_nozzle_id = filament_maps[current_filament_id] - 1;
nozzle_cur_filament_ids[cur_nozzle_id] = current_filament_id;
for (auto& layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id);
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, current_filament_id);
for (const auto extruder_id : layer_tools.extruders) {
// QDS: priming logic is removed, so no need to do toolchange for first extruder
if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */extruder_id != current_extruder_id) {
float volume_to_purge = wipe_volumes[current_extruder_id][extruder_id];
volume_to_purge *= m_config.flush_multiplier;
used_filament_ids.insert(layer_tools.extruders.begin(), layer_tools.extruders.end());
// Not all of that can be used for infill purging:
//volume_to_purge -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
for (const auto filament_id : layer_tools.extruders) {
if (filament_id == current_filament_id)
continue;
// try to assign some infills/objects for the wiping:
volume_to_purge = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_purge);
int nozzle_id = filament_maps[filament_id] - 1;
unsigned int pre_filament_id = nozzle_cur_filament_ids[nozzle_id];
// add back the minimal amount toforce on the wipe tower:
//volume_to_purge += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height,
current_extruder_id, extruder_id, m_config.prime_volume, volume_to_purge);
current_extruder_id = extruder_id;
float volume_to_purge = 0;
if (pre_filament_id != (unsigned int)(-1) && pre_filament_id != filament_id) {
volume_to_purge = multi_extruder_flush[nozzle_id][pre_filament_id][filament_id];
volume_to_purge *= m_config.flush_multiplier.get_at(nozzle_id);
volume_to_purge = pre_filament_id == -1 ? 0 :
layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_filament_id, filament_id, volume_to_purge);
}
//During the filament change, the extruder will extrude an extra length of grab_length for the corresponding detection, so the purge can reduce this length.
float grab_purge_volume = m_config.grab_length.get_at(nozzle_id) * 2.4; //(diameter/2)^2*PI=2.4
volume_to_purge = std::max(0.f, volume_to_purge - grab_purge_volume);
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, filament_id,
m_config.filament_prime_volume.values[filament_id], volume_to_purge);
current_filament_id = filament_id;
nozzle_cur_filament_ids[nozzle_id] = filament_id;
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
// if enable timelapse, slice all layer
if (enable_timelapse_print()) {
if (layer_tools.wipe_tower_partitions == 0)
wipe_tower.set_last_layer_extruder_fill(false);
if (layer_tools.wipe_tower_partitions == 0) wipe_tower.set_last_layer_extruder_fill(false);
continue;
}
@@ -2471,11 +2809,21 @@ void Print::_make_wipe_tower()
}
}
wipe_tower.set_used_filament_ids(std::vector<int>(used_filament_ids.begin(), used_filament_ids.end()));
std::vector<int> categories;
for (size_t i = 0; i < m_config.filament_adhesiveness_category.values.size(); ++i) {
categories.push_back(m_config.filament_adhesiveness_category.get_at(i));
}
wipe_tower.set_filament_categories(categories);
// Generate the wipe tower layers.
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
wipe_tower.generate(m_wipe_tower_data.tool_changes);
wipe_tower.generate_new(m_wipe_tower_data.tool_changes);
m_wipe_tower_data.depth = wipe_tower.get_depth();
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
m_wipe_tower_data.bbx = wipe_tower.get_bbx();
m_wipe_tower_data.rib_offset = wipe_tower.get_rib_offset();
// Unload the current filament over the purge tower.
coordf_t layer_height = m_objects.front()->config().layer_height.value;
@@ -2498,9 +2846,14 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
m_wipe_tower_data.construct_mesh(wipe_tower.width(), wipe_tower.get_depth(), wipe_tower.get_height(), wipe_tower.get_brim_width(), config().prime_tower_rib_wall.value,
wipe_tower.get_rib_width(), wipe_tower.get_rib_length(), config().prime_tower_fillet_wall.value);
const Vec3d origin = this->get_plate_origin();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(), m_wipe_tower_data.depth,
m_fake_wipe_tower.rib_offset = wipe_tower.get_rib_offset();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position() + m_fake_wipe_tower.rib_offset, wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(),
m_wipe_tower_data.depth,
m_wipe_tower_data.brim_width, {scale_(origin.x()), scale_(origin.y())});
m_fake_wipe_tower.outer_wall = wipe_tower.get_outer_wall();
}
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters
@@ -3160,7 +3513,7 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg
if (!ret) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":error parsing thin_fills found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
char error_buf[1024];
::sprintf(error_buf, "Error while parsing thin_fills at layer %d, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
::sprintf(error_buf, "Error while parsing thin_fills at layer %zu, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
throw Slic3r::FileIOError(error_buf);
}
}
@@ -3215,7 +3568,7 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg
if (!ret) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing perimeters found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
char error_buf[1024];
::sprintf(error_buf, "Error while parsing perimeters at layer %d, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
::sprintf(error_buf, "Error while parsing perimeters at layer %zu, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
throw Slic3r::FileIOError(error_buf);
}
}
@@ -3230,7 +3583,7 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg
if (!ret) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
char error_buf[1024];
::sprintf(error_buf, "Error while parsing fills at layer %d, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
::sprintf(error_buf, "Error while parsing fills at layer %zu, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
throw Slic3r::FileIOError(error_buf);
}
}
@@ -3310,7 +3663,7 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support
if (!ret) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at support_layer %1%, print_z %2%")%support_layer.id() %support_layer.print_z;
char error_buf[1024];
::sprintf(error_buf, "Error while parsing fills at support_layer %d, print_z %f", support_layer.id(), support_layer.print_z);
::sprintf(error_buf, "Error while parsing fills at support_layer %zu, print_z %f", support_layer.id(), support_layer.print_z);
throw Slic3r::FileIOError(error_buf);
}
}
@@ -3847,4 +4200,59 @@ Point PrintInstance::shift_without_plate_offset() const
return shift - Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
}
ExtrusionLayers FakeWipeTower::getTrueExtrusionLayersFromWipeTower() const
{
ExtrusionLayers wtels;
wtels.type = ExtrusionLayersType::WIPE_TOWER;
std::vector<float> layer_heights;
layer_heights.reserve(outer_wall.size());
auto pre = outer_wall.begin();
for (auto it = outer_wall.begin(); it != outer_wall.end(); ++it) {
if (it == outer_wall.begin())
layer_heights.push_back(it->first);
else {
layer_heights.push_back(it->first - pre->first);
++pre;
}
}
Point trans = {scale_(pos.x()), scale_(pos.y())};
for (auto it = outer_wall.begin(); it != outer_wall.end(); ++it) {
int index = std::distance(outer_wall.begin(), it);
ExtrusionLayer el;
ExtrusionPaths paths;
paths.reserve(it->second.size());
for (auto &polyline : it->second) {
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_heights[index]);
path.polyline = polyline;
for (auto &p : path.polyline.points) p += trans;
paths.push_back(path);
}
el.paths = std::move(paths);
el.bottom_z = it->first - layer_heights[index];
el.layer = nullptr;
wtels.push_back(el);
}
return wtels;
}
void WipeTowerData::construct_mesh(float width, float depth, float height, float brim_width, bool is_rib_wipe_tower, float rib_width, float rib_length,bool fillet_wall)
{
wipe_tower_mesh_data = WipeTowerMeshData{};
float first_layer_height=0.08; //brim height
if (!is_rib_wipe_tower) {
wipe_tower_mesh_data->real_wipe_tower_mesh = make_cube(width, depth, height);
wipe_tower_mesh_data->real_brim_mesh = make_cube(width + 2 * brim_width, depth + 2 * brim_width, first_layer_height);
wipe_tower_mesh_data->real_brim_mesh.translate({-brim_width, -brim_width, 0});
wipe_tower_mesh_data->bottom = {scaled(Vec2f{-brim_width, -brim_width}), scaled(Vec2f{width + brim_width, 0}), scaled(Vec2f{width + brim_width, depth + brim_width}),
scaled(Vec2f{0, depth})};
} else {
wipe_tower_mesh_data->real_wipe_tower_mesh = WipeTower::its_make_rib_tower(width, depth, height, rib_length, rib_width, fillet_wall);
wipe_tower_mesh_data->bottom = WipeTower::rib_section(width, depth, rib_length, rib_width, fillet_wall);
wipe_tower_mesh_data->bottom = offset(wipe_tower_mesh_data->bottom, scaled(brim_width)).front();
wipe_tower_mesh_data->real_brim_mesh = WipeTower::its_make_rib_brim(wipe_tower_mesh_data->bottom, first_layer_height);
wipe_tower_mesh_data->real_wipe_tower_mesh.translate(Vec3f(rib_offset[0], rib_offset[1],0));
wipe_tower_mesh_data->real_brim_mesh.translate(Vec3f(rib_offset[0], rib_offset[1], 0));
wipe_tower_mesh_data->bottom.translate(scaled(Vec2f(rib_offset[0], rib_offset[1])));
}
}
} // namespace Slic3r