Merge branch 'main' into enh-port-edit-gcode-dlg

This commit is contained in:
Ocraftyone 2024-01-21 14:12:39 -05:00 committed by GitHub
commit 11fd73a90d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2622 additions and 745 deletions

View file

@ -157,6 +157,8 @@ set(lisbslic3r_sources
GCode/PrintExtents.hpp
GCode/RetractWhenCrossingPerimeters.cpp
GCode/RetractWhenCrossingPerimeters.hpp
GCode/SmallAreaInfillFlowCompensator.cpp
GCode/SmallAreaInfillFlowCompensator.hpp
GCode/SpiralVase.cpp
GCode/SpiralVase.hpp
GCode/SeamPlacer.cpp

View file

@ -1849,6 +1849,15 @@ static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(c
//BBS: add sort logic for seq-print
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order)
{
auto find_object_index = [](const Model& model, const ModelObject* obj) {
for (int index = 0; index < model.objects.size(); index++)
{
if (model.objects[index] == obj)
return index;
}
return -1;
};
// Build up map from ModelInstance* to PrintInstance*
std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance;
model_instance_to_print_instance.reserve(print.num_object_instances());
@ -1856,10 +1865,16 @@ std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Pri
for (const PrintInstance &print_instance : print_object->instances())
{
if (init_order)
const_cast<ModelInstance*>(print_instance.model_instance)->arrange_order = print_instance.model_instance->id().id;
const_cast<ModelInstance*>(print_instance.model_instance)->arrange_order = find_object_index(print.model(), print_object->model_object());
model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance);
}
std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first->arrange_order < r.first->arrange_order; });
if (init_order) {
// Re-assign the arrange_order so each instance has a unique order number
for (int k = 0; k < model_instance_to_print_instance.size(); k++) {
const_cast<ModelInstance*>(model_instance_to_print_instance[k].first)->arrange_order = k + 1;
}
}
std::vector<const PrintInstance*> instances;
instances.reserve(model_instance_to_print_instance.size());
@ -1980,6 +1995,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
} else
m_enable_extrusion_role_markers = false;
if (!print.config().small_area_infill_flow_compensation_model.empty())
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
// if thumbnail type of BTT_TFT, insert above header
// if not, it is inserted under the header in its normal spot
const GCodeThumbnailsFormat m_gcode_thumbnail_format = print.full_print_config().opt_enum<GCodeThumbnailsFormat>("thumbnails_format");
@ -2215,8 +2233,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders());
// Order object instances using a nearest neighbor search.
print_object_instances_ordering = chain_print_object_instances(print);
print_object_instances_ordering =
// By default, order object instances using a nearest neighbor search.
print.config().print_order == PrintOrder::Default ? chain_print_object_instances(print)
// Otherwise same order as the object list
: sort_object_instances_by_model_order(print);
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
@ -2540,9 +2561,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
this->placeholder_parser().set("current_object_idx", int(finished_objects));
//BBS: remove printing_by_object_gcode
//std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id);
std::string printing_by_object_gcode;
std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, printing_by_object_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, printing_by_object_gcode, initial_extruder_id, false);
@ -4020,7 +4039,7 @@ LayerResult GCode::process_layer(
std::vector<InstanceToPrint> instances_to_print;
bool has_prime_tower = print.config().enable_prime_tower
&& print.extruders().size() > 1
&& (print.config().print_sequence == PrintSequence::ByLayer
&& ((print.config().print_sequence == PrintSequence::ByLayer && print.config().print_order == PrintOrder::Default)
|| (print.config().print_sequence == PrintSequence::ByObject && print.objects().size() == 1));
if (has_prime_tower) {
int plate_idx = print.get_plate_index();
@ -5268,15 +5287,25 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
for (const Line& line : path.polyline.lines()) {
const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length;
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
description += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode += m_writer.extrude_to_xy(
this->point_to_gcode(line.b),
e_per_mm * line_length,
dE,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
}
} else {
// BBS: start to generate gcode from arc fitting data which includes line and arc
const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result;
for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) {
std::string tempDescription = description;
switch (fitting_result[fitting_index].path_type) {
case EMovePathType::Linear_move: {
size_t start_index = fitting_result[fitting_index].start_point_index;
@ -5285,10 +5314,19 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
const double line_length = line.length() * SCALING_FACTOR;
path_length += line_length;
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode += m_writer.extrude_to_xy(
this->point_to_gcode(line.b),
e_per_mm * line_length,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
dE,
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
}
break;
}
@ -5298,12 +5336,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
path_length += arc_length;
auto dE = e_per_mm * arc_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(arc_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, arc_length);
}
}
gcode += m_writer.extrude_arc_to_xy(
this->point_to_gcode(arc.end_point),
center_offset,
e_per_mm * arc_length,
dE,
arc.direction == ArcDirection::Arc_Dir_CCW,
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
break;
}
default:
@ -5325,6 +5372,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role());
for (size_t i = 1; i < new_points.size(); i++) {
std::string tempDescription = description;
const ProcessedPoint &processed_point = new_points[i];
const ProcessedPoint &pre_processed_point = new_points[i-1];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
@ -5363,8 +5411,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += m_writer.set_speed(new_speed, "", comment);
last_set_speed = new_speed;
}
auto dE = e_per_mm * line_length;
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
auto oldE = dE;
dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
}
}
gcode +=
m_writer.extrude_to_xy(p, e_per_mm * line_length, GCodeWriter::full_gcode_comment ? description : "");
m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
prev = p;

View file

@ -23,6 +23,7 @@
#include "GCode/ExtrusionProcessor.hpp"
#include "GCode/PressureEqualizer.hpp"
#include "GCode/SmallAreaInfillFlowCompensator.hpp"
#include <memory>
#include <map>
@ -536,6 +537,8 @@ private:
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
std::unique_ptr<SmallAreaInfillFlowCompensator> m_small_area_infill_flow_compensator;
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
@ -598,6 +601,7 @@ private:
friend class WipeTowerIntegration;
friend class PressureEqualizer;
friend class Print;
friend class SmallAreaInfillFlowCompensator;
};
std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print& print, bool init_order = false);

View file

@ -0,0 +1,88 @@
// Modify the flow of extrusion lines inversely proportional to the length of
// the extrusion line. When infill lines get shorter the flow rate will auto-
// matically be reduced to mitigate the effect of small infill areas being
// over-extruded.
// Based on original work by Alexander Þór licensed under the GPLv3:
// https://github.com/Alexander-T-Moss/Small-Area-Flow-Comp
#include <math.h>
#include <cstring>
#include <cfloat>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "SmallAreaInfillFlowCompensator.hpp"
namespace Slic3r {
bool nearly_equal(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
SmallAreaInfillFlowCompensator::SmallAreaInfillFlowCompensator(const Slic3r::GCodeConfig& config)
{
for (auto& line : config.small_area_infill_flow_compensation_model.values) {
std::istringstream iss(line);
std::string value_str;
double eLength = 0.0;
if (std::getline(iss, value_str, ',')) {
try {
eLength = std::stod(value_str);
if (std::getline(iss, value_str, ',')) {
eLengths.push_back(eLength);
flowComps.push_back(std::stod(value_str));
}
} catch (...) {
std::stringstream ss;
ss << "Error parsing data point in small area infill compensation model:" << line << std::endl;
throw Slic3r::InvalidArgument(ss.str());
}
}
}
for (int i = 0; i < eLengths.size(); i++) {
if (i == 0) {
if (!nearly_equal(eLengths[i], 0.0)) {
throw Slic3r::InvalidArgument("First extrusion length for small area infill compensation model must be 0");
}
} else {
if (nearly_equal(eLengths[i], 0.0)) {
throw Slic3r::InvalidArgument("Only the first extrusion length for small area infill compensation model can be 0");
}
if (eLengths[i] <= eLengths[i - 1]) {
throw Slic3r::InvalidArgument("Extrusion lengths for subsequent points must be increasing");
}
}
}
if (!flowComps.empty() && !nearly_equal(flowComps.back(), 1.0)) {
throw Slic3r::InvalidArgument("Final compensation factor for small area infill flow compensation model must be 1.0");
}
flowModel.set_points(eLengths, flowComps);
}
double SmallAreaInfillFlowCompensator::flow_comp_model(const double line_length)
{
if (line_length == 0 || line_length > max_modified_length()) {
return 1.0;
}
return flowModel(line_length);
}
double SmallAreaInfillFlowCompensator::modify_flow(const double line_length, const double dE, const ExtrusionRole role)
{
if (role == ExtrusionRole::erSolidInfill || role == ExtrusionRole::erTopSolidInfill || role == ExtrusionRole::erBottomSurface) {
return dE * flow_comp_model(line_length);
}
return dE;
}
} // namespace Slic3r

View file

@ -0,0 +1,35 @@
#ifndef slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_
#define slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "../ExtrusionEntity.hpp"
#include "spline/spline.h"
namespace Slic3r {
class SmallAreaInfillFlowCompensator
{
public:
SmallAreaInfillFlowCompensator() = delete;
explicit SmallAreaInfillFlowCompensator(const Slic3r::GCodeConfig& config);
~SmallAreaInfillFlowCompensator() = default;
double modify_flow(const double line_length, const double dE, const ExtrusionRole role);
private:
// Model points
std::vector<double> eLengths;
std::vector<double> flowComps;
// TODO: Cubic Spline
tk::spline flowModel;
double flow_comp_model(const double line_length);
double max_modified_length() { return eLengths.back(); }
};
} // namespace Slic3r
#endif /* slic3r_GCode_SmallAreaInfillFlowCompensator_hpp_ */

View file

@ -836,7 +836,7 @@ end:
// BBS: backup all in one dir
std::string Model::get_auxiliary_file_temp_path()
{
return get_backup_path("/Auxiliaries");
return get_backup_path("Auxiliaries");
}
// BBS: backup dir

View file

@ -786,7 +786,7 @@ static std::vector<std::string> s_Preset_print_options {
"independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
@ -817,6 +817,7 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder", "wiping_volumes_extruders","wipe_tower_bridging", "single_extruder_multi_material_priming",
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
"small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model",
};
static std::vector<std::string> s_Preset_filament_options {

View file

@ -314,6 +314,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
//|| opt_key == "resolution"
//BBS: when enable arc fitting, we must re-generate perimeter
|| opt_key == "enable_arc_fitting"
|| opt_key == "print_order"
|| opt_key == "wall_sequence") {
osteps.emplace_back(posPerimeters);
osteps.emplace_back(posEstimateCurledExtrusions);
@ -1043,6 +1044,7 @@ boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.
StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
{
std::vector<unsigned int> extruders = this->extruders();
unsigned int nozzles = m_config.nozzle_diameter.size();
if (m_objects.empty())
return {std::string()};
@ -1050,7 +1052,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (extruders.empty())
return { L("No extrusions under current settings.") };
if (extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) {
if (nozzles < 2 && extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) {
auto ret = check_multi_filament_valid(*this);
if (!ret.string.empty())
{

View file

@ -195,6 +195,12 @@ static t_config_enum_values s_keys_map_PrintSequence {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintSequence)
static t_config_enum_values s_keys_map_PrintOrder{
{ "default", int(PrintOrder::Default) },
{ "as_obj_list", int(PrintOrder::AsObjectList)},
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintOrder)
static t_config_enum_values s_keys_map_SlicingMode {
{ "regular", int(SlicingMode::Regular) },
{ "even_odd", int(SlicingMode::EvenOdd) },
@ -1171,6 +1177,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<PrintSequence>(PrintSequence::ByLayer));
def = this->add("print_order", coEnum);
def->label = L("Layer order");
def->tooltip = L("Print order within a single layer");
def->enum_keys_map = &ConfigOptionEnum<PrintOrder>::get_enum_values();
def->enum_values.push_back("default");
def->enum_values.push_back("as_obj_list");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("As object list"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PrintOrder>(PrintOrder::Default));
def = this->add("slow_down_for_layer_cooling", coBools);
def->label = L("Slow printing down for better layer cooling");
def->tooltip = L("Enable this option to slow printing speed down to make the final layer time not shorter than "
@ -2668,6 +2685,26 @@ def = this->add("filament_loading_speed", coFloats);
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("small_area_infill_flow_compensation", coBool);
def->label = L("Enable Flow Compensation");
def->tooltip = L("Enable flow compensation for small infill areas");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("small_area_infill_flow_compensation_model", coStrings);
def->label = L("Flow Compensation Model");
def->tooltip = L(
"Flow Compensation Model, used to adjust the flow for small infill "
"areas. The model is expressed as a comma separated pair of values for "
"extrusion length and flow correction factors, one per line, in the "
"following format: \"1.234,5.678\"");
def->mode = comAdvanced;
def->gui_flags = "serialized";
def->multiline = true;
def->full_width = true;
def->height = 15;
def->set_default_value(new ConfigOptionStrings{"0,0", "\n0.2,0.4444", "\n0.4,0.6145", "\n0.6,0.7059", "\n0.8,0.7619", "\n1.5,0.8571", "\n2,0.8889", "\n3,0.9231", "\n5,0.9520", "\n10,1"});
{
struct AxisDefault {
std::string name;

View file

@ -106,6 +106,13 @@ enum class PrintSequence {
Count,
};
enum class PrintOrder
{
Default,
AsObjectList,
Count,
};
enum class SlicingMode
{
// Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons.
@ -920,6 +927,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<WallSequence>, wall_sequence))
((ConfigOptionBool, is_infill_first))
((ConfigOptionBool, small_area_infill_flow_compensation))
)
PRINT_CONFIG_CLASS_DEFINE(
@ -1067,6 +1075,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, enable_filament_ramming))
((ConfigOptionBool, support_multi_bed_types))
// Small Area Infill Flow Compensation
((ConfigOptionStrings, small_area_infill_flow_compensation_model))
)
// This object is mapped to Perl as Slic3r::Config::Print.
@ -1098,6 +1108,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInts, overhang_fan_speed))
((ConfigOptionEnumsGeneric, overhang_fan_threshold))
((ConfigOptionEnum<PrintSequence>,print_sequence))
((ConfigOptionEnum<PrintOrder>, print_order))
((ConfigOptionInts, first_layer_print_sequence))
((ConfigOptionBools, slow_down_for_layer_cooling))
((ConfigOptionInts, close_fan_the_first_x_layers))

View file

@ -928,6 +928,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wipe_on_loops"
|| opt_key == "wipe_speed") {
steps.emplace_back(posPerimeters);
} else if (
opt_key == "small_area_infill_flow_compensation"
|| opt_key == "small_area_infill_flow_compensation_model") {
steps.emplace_back(posSlice);
} else if (opt_key == "gap_infill_speed"
|| opt_key == "filter_out_gap_fill" ) {
// Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero.