mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 01:41:03 +00:00
Merge upstream main: multi-variant printers, X-Ray view, preview and startup work
The two conflicts are both places where upstream landed on top of IDEX/IQEX
code. In GCode.cpp the relocated file header block meets the IMEX placeholder
block, and the placeholders are set first: file_start_gcode is processed through
the placeholder parser now, which throws on a name it does not know, so a script
naming {imex_mode} would abort the export if the header ran first. In
test_multifilament.cpp both sides appended a case at the end of the file.
This commit is contained in:
+218
-198
@@ -972,6 +972,27 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// A folded tower layer is thicker than the object layer it sits on, so the height process_layer
|
||||
// emitted is not the tower's. Both writers declare one, but each hardcodes a tag dialect - Type 1
|
||||
// forces s_IsBBLPrinter and writes "; LAYER_HEIGHT:", Type 2 writes ";HEIGHT:" - and the processor
|
||||
// reads only its printer's, so a Type 1 tower on a non-BBL printer loses it and the merged layer
|
||||
// is drawn and costed as a thin one. Declare it here, where the printer is known, unless the tower
|
||||
// already wrote the right tag. _extrude puts the object's height back on the next object path,
|
||||
// since process_layer forces the role to erWipeTower on any layer with a tower.
|
||||
std::string WipeTowerIntegration::tower_height_tag(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr,
|
||||
const std::string &tcr_gcode) const
|
||||
{
|
||||
const std::string tag = ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height);
|
||||
if (! m_sparse_layers_combined || std::abs(gcodegen.m_last_height - tcr.layer_height) <= EPSILON ||
|
||||
tcr_gcode.find(tag) != std::string::npos)
|
||||
return {};
|
||||
// Keep m_last_height what the G-code last declared, so a second visit does not repeat it.
|
||||
gcodegen.m_last_height = tcr.layer_height;
|
||||
char buf[64];
|
||||
sprintf(buf, "%s%g\n", tag.c_str(), tcr.layer_height);
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const
|
||||
{
|
||||
if (new_filament_id != -1 && new_filament_id != tcr.new_tool)
|
||||
@@ -1469,6 +1490,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
config.set_key_value("filament_start_gcode", new ConfigOptionString(start_filament_gcode_str));
|
||||
std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_filament_id, &config);
|
||||
unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
|
||||
gcode += tower_height_tag(gcodegen, tcr, tcr_gcode);
|
||||
gcode += tcr_gcode;
|
||||
// Count the toolchange only when the emitted block really changed the tool —
|
||||
// tower visits without a filament change must not advance the ordinal.
|
||||
@@ -1772,6 +1794,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
std::string tcr_gcode,
|
||||
tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
|
||||
unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
|
||||
gcode += tower_height_tag(gcodegen, tcr, tcr_gcode);
|
||||
gcode += tcr_gcode;
|
||||
check_add_eol(toolchange_gcode_str);
|
||||
|
||||
@@ -1919,7 +1942,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
|
||||
// resulting in a wipe tower with sparse layers.
|
||||
double wipe_tower_z = -1;
|
||||
bool ignore_sparse = false;
|
||||
// Folded into a later, thicker layer that prints at its own z: nothing to emit.
|
||||
bool ignore_sparse = wipe_tower_layer_is_combined_away(m_tool_changes[m_layer_idx]);
|
||||
if (m_sparse_layers_skipped) {
|
||||
wipe_tower_z = m_last_wipe_tower_print_z;
|
||||
ignore_sparse = wipe_tower_layer_is_sparse(m_tool_changes[m_layer_idx]) && m_layer_idx != 0;
|
||||
@@ -1937,7 +1961,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
|
||||
// resulting in a wipe tower with sparse layers.
|
||||
double wipe_tower_z = -1;
|
||||
bool ignore_sparse = false;
|
||||
// Folded into a later, thicker layer that prints at its own z: nothing to emit.
|
||||
bool ignore_sparse = wipe_tower_layer_is_combined_away(m_tool_changes[m_layer_idx]);
|
||||
if (m_sparse_layers_skipped) {
|
||||
ignore_sparse = wipe_tower_layer_is_sparse(m_tool_changes[m_layer_idx]);
|
||||
wipe_tower_z = m_compacted_tower_z[m_layer_idx];
|
||||
@@ -1967,7 +1992,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
if (m_layer_idx >= (int) m_tool_changes.size())
|
||||
return true;
|
||||
|
||||
bool ignore_sparse = false;
|
||||
bool ignore_sparse = wipe_tower_layer_is_combined_away(m_tool_changes[m_layer_idx]);
|
||||
if (m_sparse_layers_skipped)
|
||||
ignore_sparse = wipe_tower_layer_is_sparse(m_tool_changes[m_layer_idx]);
|
||||
|
||||
@@ -2452,7 +2477,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2504,7 +2530,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
}
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + err_msg;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2528,7 +2555,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + ex.what();
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2641,7 +2669,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
if (ret) {
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2660,7 +2689,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -3075,162 +3105,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
if (m_config.small_area_infill_flow_compensation.value && !m_config.small_area_infill_flow_compensation_model.empty())
|
||||
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
|
||||
|
||||
// Process file_start_gcode - written at the very top of the file, before any header
|
||||
{
|
||||
std::string top_gcode_template = print.config().file_start_gcode.value;
|
||||
if (!top_gcode_template.empty()) {
|
||||
DynamicConfig top_config;
|
||||
// file_start_gcode runs before the parser copy that normally restores these, so set them here.
|
||||
PlaceholderParser::update_timestamp(top_config);
|
||||
PlaceholderParser::update_user_name(top_config);
|
||||
top_config.set_key_value("print_time_total_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Total_Sec_Placeholder)));
|
||||
top_config.set_key_value("print_time_day", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Day_Placeholder)));
|
||||
top_config.set_key_value("print_time_hour", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Hour_Placeholder)));
|
||||
top_config.set_key_value("print_time_minute", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Minute_Placeholder)));
|
||||
top_config.set_key_value("print_time_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Sec_Placeholder)));
|
||||
top_config.set_key_value("used_filament_length", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder)));
|
||||
std::string top_gcode = print.placeholder_parser().process(top_gcode_template, 0, &top_config);
|
||||
if (!top_gcode.empty())
|
||||
file.writeln(top_gcode);
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: Don't output Header block if BTT thumbnail is identified in the list
|
||||
// Get the thumbnails value as a string
|
||||
std::string thumbnails_value = print.config().option<ConfigOptionString>("thumbnails")->value;
|
||||
// search string for the BTT_TFT label
|
||||
bool has_BTT_thumbnail = (thumbnails_value.find("BTT_TFT") != std::string::npos);
|
||||
|
||||
if(!has_BTT_thumbnail){
|
||||
file.write_format("; HEADER_BLOCK_START\n");
|
||||
// Write information on the generator.
|
||||
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
|
||||
if (is_bbl_printers)
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
//BBS: total layer number
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
|
||||
//Orca: extra check for bbl printer
|
||||
if (is_bbl_printers) {
|
||||
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
|
||||
// list all label_object_id with sorted order here
|
||||
m_enable_exclude_object = true;
|
||||
m_label_objects_ids.clear();
|
||||
m_label_objects_ids.reserve(print.num_object_instances());
|
||||
for (const PrintObject *print_object : print.objects())
|
||||
for (const PrintInstance &print_instance : print_object->instances())
|
||||
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
|
||||
|
||||
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
|
||||
|
||||
std::string objects_id_list = "; model label id: ";
|
||||
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
|
||||
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
|
||||
file.writeln(objects_id_list);
|
||||
} else {
|
||||
m_enable_exclude_object = false;
|
||||
m_label_objects_ids.clear();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string filament_density_list = "; filament_density: ";
|
||||
(filament_density_list+=m_config.filament_density.serialize()) +='\n';
|
||||
file.writeln(filament_density_list);
|
||||
|
||||
std::string filament_diameter_list = "; filament_diameter: ";
|
||||
(filament_diameter_list += m_config.filament_diameter.serialize()) += '\n';
|
||||
file.writeln(filament_diameter_list);
|
||||
|
||||
coordf_t max_height_z = -1;
|
||||
for (const auto& object : print.objects())
|
||||
max_height_z = std::max(object->layers().back()->print_z, max_height_z);
|
||||
|
||||
std::ostringstream max_height_z_tip;
|
||||
max_height_z_tip<<"; max_z_height: " << std::fixed << std::setprecision(2) << max_height_z << '\n';
|
||||
file.writeln(max_height_z_tip.str());
|
||||
}
|
||||
|
||||
{
|
||||
auto used_filaments = print.get_slice_used_filaments(false);
|
||||
std::ostringstream out;
|
||||
out << "; filament: ";
|
||||
for (size_t idx = 0; idx < used_filaments.size(); ++idx) {
|
||||
if (idx != 0)
|
||||
out << ',';
|
||||
out << used_filaments[idx] + 1;
|
||||
}
|
||||
file.writeln(out.str());
|
||||
}
|
||||
|
||||
file.write_format("; HEADER_BLOCK_END\n\n");
|
||||
}
|
||||
|
||||
// BBS: write global config at the beginning of gcode file because printer
|
||||
// need these config information
|
||||
// Append full config, delimited by two 'phony' configuration keys
|
||||
// CONFIG_BLOCK_START and CONFIG_BLOCK_END. The delimiters are structured
|
||||
// as configuration key / value pairs to be parsable by older versions of
|
||||
// PrusaSlicer G-code viewer.
|
||||
{
|
||||
if (is_bbl_printers && !skip_config_block) {
|
||||
file.write("; CONFIG_BLOCK_START\n");
|
||||
std::string full_config;
|
||||
append_full_config(print, full_config);
|
||||
if (!full_config.empty())
|
||||
file.write(full_config);
|
||||
|
||||
// SoftFever: write compatiple image
|
||||
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
|
||||
file.write_format("; first_layer_bed_temperature = %d\n",
|
||||
first_layer_bed_temperature);
|
||||
file.write_format(
|
||||
"; first_layer_temperature = %d\n",
|
||||
print.config().nozzle_temperature_initial_layer.get_at(0));
|
||||
file.write("; CONFIG_BLOCK_END\n\n");
|
||||
} else if (thumbnail_cb != nullptr) {
|
||||
// generate the thumbnails
|
||||
auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
|
||||
|
||||
if (errors != enum_bitmask<ThumbnailError>()) {
|
||||
std::string error_str = format("Invalid thumbnails value:");
|
||||
error_str += GCodeThumbnails::get_error_string(errors);
|
||||
throw Slic3r::ExportError(error_str);
|
||||
}
|
||||
|
||||
if (!thumbnails.empty())
|
||||
GCodeThumbnails::export_thumbnails_to_file(
|
||||
thumbnail_cb, print.get_plate_index(), thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects().front();
|
||||
const double layer_height = first_object->config().layer_height.value;
|
||||
const double initial_layer_print_height = print.config().initial_layer_print_height.value;
|
||||
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
|
||||
const PrintRegion ®ion = print.get_print_region(region_id);
|
||||
file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
|
||||
file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
|
||||
file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
|
||||
file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||
if (print.has_support_material())
|
||||
file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||
if (print.config().initial_layer_line_width.value > 0)
|
||||
file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, initial_layer_print_height, true).width());
|
||||
file.write_format("\n");
|
||||
}
|
||||
|
||||
file.write_format("; EXECUTABLE_BLOCK_START\n");
|
||||
|
||||
// SoftFever
|
||||
if( m_enable_exclude_object)
|
||||
file.write(set_object_info(&print));
|
||||
|
||||
// adds tags for time estimators
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
|
||||
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
m_placeholder_parser_integration.parser = print.placeholder_parser();
|
||||
m_placeholder_parser_integration.parser.update_timestamp();
|
||||
@@ -3365,16 +3239,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// Orca: Initialise AdaptivePA processor filter
|
||||
m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this, tool_ordering.all_extruders());
|
||||
|
||||
// Emit machine envelope limits for the Marlin firmware.
|
||||
this->print_machine_envelope(file, print);
|
||||
|
||||
// Disable fan.
|
||||
if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) {
|
||||
file.write(m_writer.set_fan(0));
|
||||
//BBS: disable additional fan
|
||||
file.write(m_writer.set_additional_fan(0));
|
||||
}
|
||||
|
||||
// Update output variables after the extruders were initialized.
|
||||
m_placeholder_parser_integration.init(m_writer);
|
||||
|
||||
@@ -3868,6 +3732,156 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
if (!imex_active_mode_gcode.empty())
|
||||
imex_processed_gcode = this->placeholder_parser_process(
|
||||
"imex_mode_gcode", imex_active_mode_gcode, initial_extruder_id);
|
||||
// Expand the file header only after the start-up placeholders and writer state are ready.
|
||||
// Use the regular custom G-code path so invalid placeholders report the template name.
|
||||
if (!print.config().file_start_gcode.value.empty())
|
||||
file.writeln(this->placeholder_parser_process("file_start_gcode", print.config().file_start_gcode.value, initial_extruder_id));
|
||||
|
||||
// Orca: Don't output Header block if BTT thumbnail is identified in the list
|
||||
// Get the thumbnails value as a string
|
||||
std::string thumbnails_value = print.config().option<ConfigOptionString>("thumbnails")->value;
|
||||
// search string for the BTT_TFT label
|
||||
bool has_BTT_thumbnail = (thumbnails_value.find("BTT_TFT") != std::string::npos);
|
||||
|
||||
if(!has_BTT_thumbnail){
|
||||
file.write_format("; HEADER_BLOCK_START\n");
|
||||
// Write information on the generator.
|
||||
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
|
||||
if (is_bbl_printers)
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
//BBS: total layer number
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
|
||||
//Orca: extra check for bbl printer
|
||||
if (is_bbl_printers) {
|
||||
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
|
||||
// list all label_object_id with sorted order here
|
||||
m_enable_exclude_object = true;
|
||||
m_label_objects_ids.clear();
|
||||
m_label_objects_ids.reserve(print.num_object_instances());
|
||||
for (const PrintObject *print_object : print.objects())
|
||||
for (const PrintInstance &print_instance : print_object->instances())
|
||||
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
|
||||
|
||||
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
|
||||
|
||||
std::string objects_id_list = "; model label id: ";
|
||||
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
|
||||
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
|
||||
file.writeln(objects_id_list);
|
||||
} else {
|
||||
m_enable_exclude_object = false;
|
||||
m_label_objects_ids.clear();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string filament_density_list = "; filament_density: ";
|
||||
(filament_density_list+=m_config.filament_density.serialize()) +='\n';
|
||||
file.writeln(filament_density_list);
|
||||
|
||||
std::string filament_diameter_list = "; filament_diameter: ";
|
||||
(filament_diameter_list += m_config.filament_diameter.serialize()) += '\n';
|
||||
file.writeln(filament_diameter_list);
|
||||
|
||||
coordf_t max_height_z = -1;
|
||||
for (const auto& object : print.objects())
|
||||
max_height_z = std::max(object->layers().back()->print_z, max_height_z);
|
||||
|
||||
std::ostringstream max_height_z_tip;
|
||||
max_height_z_tip<<"; max_z_height: " << std::fixed << std::setprecision(2) << max_height_z << '\n';
|
||||
file.writeln(max_height_z_tip.str());
|
||||
}
|
||||
|
||||
{
|
||||
auto used_filaments = print.get_slice_used_filaments(false);
|
||||
std::ostringstream out;
|
||||
out << "; filament: ";
|
||||
for (size_t idx = 0; idx < used_filaments.size(); ++idx) {
|
||||
if (idx != 0)
|
||||
out << ',';
|
||||
out << used_filaments[idx] + 1;
|
||||
}
|
||||
file.writeln(out.str());
|
||||
}
|
||||
|
||||
file.write_format("; HEADER_BLOCK_END\n\n");
|
||||
}
|
||||
|
||||
// BBS: write global config at the beginning of gcode file because printer
|
||||
// need these config information
|
||||
// Append full config, delimited by two 'phony' configuration keys
|
||||
// CONFIG_BLOCK_START and CONFIG_BLOCK_END. The delimiters are structured
|
||||
// as configuration key / value pairs to be parsable by older versions of
|
||||
// PrusaSlicer G-code viewer.
|
||||
{
|
||||
if (is_bbl_printers && !skip_config_block) {
|
||||
file.write("; CONFIG_BLOCK_START\n");
|
||||
std::string full_config;
|
||||
append_full_config(print, full_config);
|
||||
if (!full_config.empty())
|
||||
file.write(full_config);
|
||||
|
||||
// SoftFever: write compatiple image
|
||||
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
|
||||
file.write_format("; first_layer_bed_temperature = %d\n",
|
||||
first_layer_bed_temperature);
|
||||
file.write_format(
|
||||
"; first_layer_temperature = %d\n",
|
||||
print.config().nozzle_temperature_initial_layer.get_at(0));
|
||||
file.write("; CONFIG_BLOCK_END\n\n");
|
||||
} else if (thumbnail_cb != nullptr) {
|
||||
// generate the thumbnails
|
||||
auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
|
||||
|
||||
if (errors != enum_bitmask<ThumbnailError>()) {
|
||||
std::string error_str = format("Invalid thumbnails value:");
|
||||
error_str += GCodeThumbnails::get_error_string(errors);
|
||||
throw Slic3r::ExportError(error_str);
|
||||
}
|
||||
|
||||
if (!thumbnails.empty())
|
||||
GCodeThumbnails::export_thumbnails_to_file(
|
||||
thumbnail_cb, print.get_plate_index(), thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects().front();
|
||||
const double layer_height = first_object->config().layer_height.value;
|
||||
const double initial_layer_print_height = print.config().initial_layer_print_height.value;
|
||||
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
|
||||
const PrintRegion ®ion = print.get_print_region(region_id);
|
||||
file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
|
||||
file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
|
||||
file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
|
||||
file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||
if (print.has_support_material())
|
||||
file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||
if (print.config().initial_layer_line_width.value > 0)
|
||||
file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, initial_layer_print_height, true).width());
|
||||
file.write_format("\n");
|
||||
}
|
||||
|
||||
file.write_format("; EXECUTABLE_BLOCK_START\n");
|
||||
|
||||
// SoftFever
|
||||
if( m_enable_exclude_object)
|
||||
file.write(set_object_info(&print));
|
||||
|
||||
// adds tags for time estimators
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
|
||||
|
||||
// Emit machine envelope limits for the Marlin firmware.
|
||||
this->print_machine_envelope(file, print);
|
||||
|
||||
// Disable fan.
|
||||
if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) {
|
||||
file.write(m_writer.set_fan(0));
|
||||
//BBS: disable additional fan
|
||||
file.write(m_writer.set_additional_fan(0));
|
||||
}
|
||||
|
||||
std::string machine_start_gcode = this->placeholder_parser_process("machine_start_gcode", print.config().machine_start_gcode.value, initial_extruder_id);
|
||||
if (print.config().gcode_flavor != gcfKlipper) {
|
||||
@@ -4375,6 +4389,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
// Const input (tool-change fallback for non-wipe-tower prints)
|
||||
print.tool_ordering()));
|
||||
print.m_print_statistics.initial_tool = initial_extruder_id;
|
||||
print.m_print_statistics.initial_no_support_tool = initial_non_support_extruder_id;
|
||||
if (!is_bbl_printers) {
|
||||
file.write_format("; total filament used [g] = %.2lf\n",
|
||||
print.m_print_statistics.total_weight);
|
||||
@@ -8212,6 +8227,20 @@ double GCode::calc_max_volumetric_speed(const double layer_height, const double
|
||||
return res;
|
||||
}
|
||||
|
||||
// ORCA: Overlap at or below which the overhang fan switches on; negative when it does not depend on overlap
|
||||
// (Overhang_threshold_none cools every external perimeter).
|
||||
static float overhang_fan_overlap_threshold(int overhang_fan_threshold)
|
||||
{
|
||||
switch (overhang_fan_threshold) {
|
||||
case (int) Overhang_threshold_1_4: return 0.9f;
|
||||
case (int) Overhang_threshold_2_4: return 0.75f;
|
||||
case (int) Overhang_threshold_3_4: return 0.5f;
|
||||
case (int) Overhang_threshold_4_4: return 0.25f;
|
||||
case (int) Overhang_threshold_bridge: return 0.05f;
|
||||
default: return -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
std::string gcode;
|
||||
@@ -8342,7 +8371,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
_mm3_per_mm *= filament_flow_ratio;
|
||||
|
||||
if (path.role() == erTopSolidInfill) {
|
||||
_mm3_per_mm *= m_config.top_solid_infill_flow_ratio;
|
||||
_mm3_per_mm *= NOZZLE_CONFIG(top_solid_infill_flow_ratio);
|
||||
} else if (path.role() == erBottomSurface) {
|
||||
_mm3_per_mm *= m_config.bottom_solid_infill_flow_ratio;
|
||||
} else if (path.role() == erInternalBridgeInfill) {
|
||||
@@ -8554,6 +8583,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
|
||||
ConfigOptionPercents overhang_overlap_levels({90, 75, 50, 25, 13, 0});
|
||||
|
||||
// ORCA: Lets the path be split where the overhang fan switches, not only where the speed changes.
|
||||
// Bridges and overhang perimeters are cooled regardless of overlap.
|
||||
float fan_overlap_threshold = -1.f;
|
||||
if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers && path.role() != erBridgeInfill &&
|
||||
path.role() != erOverhangPerimeter)
|
||||
fan_overlap_threshold = overhang_fan_overlap_threshold(FILAMENT_CONFIG(overhang_fan_threshold));
|
||||
|
||||
if (NOZZLE_CONFIG(slowdown_for_curled_perimeters)){
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{FloatOrPercent{100, true},
|
||||
@@ -8574,7 +8610,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters),
|
||||
fan_overlap_threshold);
|
||||
}else{
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{FloatOrPercent{100, true},
|
||||
@@ -8593,7 +8630,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
FloatOrPercent{NOZZLE_CONFIG(bridge_speed) * 100 / ref_speed, true}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters),
|
||||
fan_overlap_threshold);
|
||||
}
|
||||
variable_speed = std::any_of(new_points.begin(), new_points.end(),
|
||||
[speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
|
||||
@@ -8745,28 +8783,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (role == erBridgeInfill || role == erOverhangPerimeter) { // ORCA: Split out bridge infill to internal and external to apply separate fan settings
|
||||
return true;
|
||||
}
|
||||
switch (overhang_fan_threshold) {
|
||||
case (int)Overhang_threshold_1_4:
|
||||
return overlap <= 0.9f;
|
||||
break;
|
||||
case (int)Overhang_threshold_2_4:
|
||||
return overlap <= 0.75f;
|
||||
break;
|
||||
case (int)Overhang_threshold_3_4:
|
||||
return overlap <= 0.5f;
|
||||
break;
|
||||
case (int)Overhang_threshold_4_4:
|
||||
return overlap <= 0.25f;
|
||||
break;
|
||||
case (int)Overhang_threshold_bridge:
|
||||
return overlap <= 0.05f;
|
||||
break;
|
||||
case (int)Overhang_threshold_none:
|
||||
if (overhang_fan_threshold == Overhang_threshold_none)
|
||||
return is_external_perimeter(role);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
const float overlap_threshold = overhang_fan_overlap_threshold(overhang_fan_threshold);
|
||||
return overlap_threshold >= 0.f && overlap <= overlap_threshold;
|
||||
};
|
||||
|
||||
std::string comment;
|
||||
|
||||
Reference in New Issue
Block a user