Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Experimental small area flow compensation #3334

Merged
merged 7 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libslic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,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
Expand Down
53 changes: 47 additions & 6 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,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");
Expand Down Expand Up @@ -5190,15 +5193,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;
Expand All @@ -5207,10 +5220,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;
}
Expand All @@ -5220,12 +5242,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:
Expand All @@ -5247,6 +5278,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);
Expand Down Expand Up @@ -5285,8 +5317,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;

Expand Down
4 changes: 4 additions & 0 deletions src/libslic3r/GCode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "GCode/ExtrusionProcessor.hpp"

#include "GCode/PressureEqualizer.hpp"
#include "GCode/SmallAreaInfillFlowCompensator.hpp"

#include <memory>
#include <map>
Expand Down Expand Up @@ -531,6 +532,8 @@ class GCode {

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.
Expand Down Expand Up @@ -593,6 +596,7 @@ class GCode {
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);
Expand Down
88 changes: 88 additions & 0 deletions src/libslic3r/GCode/SmallAreaInfillFlowCompensator.cpp
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions src/libslic3r/GCode/SmallAreaInfillFlowCompensator.hpp
Original file line number Diff line number Diff line change
@@ -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_ */
1 change: 1 addition & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,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;
Expand Down
3 changes: 3 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@ PRINT_CONFIG_CLASS_DEFINE(

((ConfigOptionEnum<WallSequence>, wall_sequence))
((ConfigOptionBool, is_infill_first))
((ConfigOptionBool, small_area_infill_flow_compensation))
)

PRINT_CONFIG_CLASS_DEFINE(
Expand Down Expand Up @@ -1067,6 +1068,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, enable_filament_ramming))
((ConfigOptionBool, support_multi_bed_types))

// Small Area Infill Flow Compensation
mjonuschat marked this conversation as resolved.
Show resolved Hide resolved
((ConfigOptionStrings, small_area_infill_flow_compensation_model))
)

// This object is mapped to Perl as Slic3r::Config::Print.
Expand Down
4 changes: 4 additions & 0 deletions src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
apply(config, &new_conf);
}
toggle_line("timelapse_type", is_BBL_Printer);


bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation");
toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation);
}

void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
Expand Down
10 changes: 9 additions & 1 deletion src/slic3r/GUI/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1970,6 +1970,14 @@ void TabPrint::build()
optgroup->append_single_option_line("only_one_wall_first_layer");
optgroup->append_single_option_line("reduce_crossing_wall");
optgroup->append_single_option_line("max_travel_detour_distance");

optgroup = page->new_optgroup(L("Small Area Infill Flow Compensation (experimental)"), L"param_advanced");
optgroup->append_single_option_line("small_area_infill_flow_compensation");
Option option = optgroup->get_option("small_area_infill_flow_compensation_model");
option.opt.full_width = true;
option.opt.is_code = true;
option.opt.height = 15;
optgroup->append_single_option_line(option);

optgroup = page->new_optgroup(L("Bridging"), L"param_advanced");
optgroup->append_single_option_line("bridge_flow");
Expand Down Expand Up @@ -2200,7 +2208,7 @@ void TabPrint::build()
optgroup->append_single_option_line("gcode_comments");
optgroup->append_single_option_line("gcode_label_objects");
optgroup->append_single_option_line("exclude_object");
Option option = optgroup->get_option("filename_format");
option = optgroup->get_option("filename_format");
// option.opt.full_width = true;
option.opt.is_code = true;
option.opt.multiline = true;
Expand Down
Loading