Skip to content
Closed
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
6 changes: 6 additions & 0 deletions resources/profiles/BBL/filament/fdm_filament_common.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@
"no_slow_down_for_cooling_on_outwalls": [
"0"
],
"cooling_slowdown_logic": [
"uniform_cooling"
],
"cooling_perimeter_transition_distance": [
"10"
],
"slow_down_layer_time": [
"8"
],
Expand Down
3 changes: 3 additions & 0 deletions resources/profiles/BBL/process/fdm_process_common.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@
"travel_acceleration": [
"10000"
],
"travel_short_distance_acceleration": [
"250"
],
"travel_speed": [
"400"
],
Expand Down
8 changes: 7 additions & 1 deletion resources/profiles/BBL/process/fdm_process_dual_common.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@
"10000",
"10000"
],
"travel_short_distance_acceleration": [
"250",
"250",
"250",
"250"
],
"top_surface_acceleration": [
"2000",
"2000",
Expand Down Expand Up @@ -242,4 +248,4 @@
"80%"
],
"z_direction_outwall_speed_continuous": "1"
}
}
32 changes: 27 additions & 5 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2717,11 +2717,16 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
for (auto value : m_config.travel_acceleration.values) {
travel_accelerations.emplace_back((unsigned int) floor(value + 0.5));
}
std::vector<unsigned int> short_travel_accelerations;
for (auto value : m_config.travel_short_distance_acceleration.values) {
short_travel_accelerations.emplace_back((unsigned int) floor(value + 0.5));
}
std::vector<unsigned int> first_layer_travel_accelerations;
for (auto value : m_config.initial_layer_travel_acceleration.values) {
first_layer_travel_accelerations.emplace_back((unsigned int) floor(value + 0.5));
}
m_writer.set_travel_acceleration(travel_accelerations);
m_writer.set_travel_short_acceleration(short_travel_accelerations);
m_writer.set_first_layer_travel_acceleration(first_layer_travel_accelerations);
// OrcaSlicer: calib
if (print.calib_params().mode == CalibMode::Calib_PA_Line) {
Expand Down Expand Up @@ -6352,6 +6357,23 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string

// use G1 because we rely on paths being straight (G0 may make round paths)
if (travel.size() >= 2) {
// Determine if we should use short travel acceleration
// This helps reduce VFA (Vertical Fine Artifacts) by using lower acceleration
// for short travels near external perimeters
bool use_short_travel_accel = false;
if (!this->on_first_layer()) {
// Check if short travel acceleration is enabled (value > 0)
unsigned int extruder_id = m_writer.filament()->id();
auto& short_accel = m_writer.get_travel_short_acceleration();
if (extruder_id < short_accel.size() && short_accel[extruder_id] > 0) {
// Use short travel acceleration for external perimeters with short travel distance
double travel_length = unscaled(travel.length());
double min_travel = FILAMENT_CONFIG(retraction_minimum_travel);
use_short_travel_accel = (role == erExternalPerimeter || role == erOverhangPerimeter) &&
travel_length < min_travel;
}
}

// OrcaSlicer
if (this->on_first_layer()) {
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0 && !this->is_BBL_Printer())
Expand All @@ -6362,13 +6384,13 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
if (m_spiral_vase) {
// No lazy z lift for spiral vase mode
for (size_t i = 1; i < travel.size(); ++i)
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment, use_short_travel_accel);
} else {
if (travel.size() == 2) {
// No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
const auto &dest2d = this->point_to_gcode(travel.points.back());
Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z);
gcode += m_writer.travel_to_xyz(dest3d, comment);
gcode += m_writer.travel_to_xyz(dest3d, comment, use_short_travel_accel);
} else {
// Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
// ratio at the last point
Expand All @@ -6377,15 +6399,15 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
// Lift to normal z at beginning
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
gcode += m_writer.travel_to_xyz(dest3d, comment);
gcode += m_writer.travel_to_xyz(dest3d, comment, use_short_travel_accel);
} else if (z != DBL_MAX && i == travel.size() - 1) {
// Apply z_ratio for the very last point
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
Vec3d dest3d(dest2d(0), dest2d(1), z);
gcode += m_writer.travel_to_xyz(dest3d, comment);
gcode += m_writer.travel_to_xyz(dest3d, comment, use_short_travel_accel);
} else {
// For all points in between, no z change
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment );
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment, use_short_travel_accel);
}
}
}
Expand Down
128 changes: 124 additions & 4 deletions src/libslic3r/GCode/CoolingBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,88 @@ static inline float extruder_range_slow_down_proportional(std::vector<PerExtrude
return total_after_slowdown;
}

// Slow down an extruder range for ConsistentSurface logic.
// This function first tries to slow down only non-visible features (infill, internal perimeters),
// and only slows down external perimeters if more time is needed.
// Returns the remaining time stretch that couldn't be achieved.
static inline float extruder_range_slow_down_consistent_surface(
std::vector<PerExtruderAdjustments *>::iterator it_begin,
std::vector<PerExtruderAdjustments *>::iterator it_end,
float time_stretch,
AdjustableFeatureType additional_slowdown_features)
{
if (time_stretch <= 0.f)
return 0.f;

// Slow down. Try to equalize the feedrates for the allowed feature types.
std::vector<PerExtruderAdjustments *> by_min_print_speed(it_begin, it_end);

// Find the highest adjustable feedrate among the extruders for allowed features.
float feedrate = 0.f;
for (PerExtruderAdjustments *adj : by_min_print_speed) {
adj->idx_line_begin = 0;
adj->idx_line_end = 0;
for (size_t i = 0; i < adj->n_lines_adjustable; ++i) {
const CoolingLine &line = adj->lines[i];
if (line.adjustable(additional_slowdown_features) && line.feedrate > feedrate)
feedrate = line.feedrate;
}
}

if (feedrate == 0.f)
return time_stretch; // No adjustable features found

// Sort by slow_down_min_speed, maximum speed first.
std::sort(by_min_print_speed.begin(), by_min_print_speed.end(),
[](const PerExtruderAdjustments *p1, const PerExtruderAdjustments *p2) {
return p1->slow_down_min_speed > p2->slow_down_min_speed;
});

// Slow down, fast moves first.
for (auto adj = by_min_print_speed.begin(); adj != by_min_print_speed.end();) {
float feedrate_limit = (*adj)->slow_down_min_speed;
float time_stretch_max = 0.f;

for (auto it = adj; it != by_min_print_speed.end(); ++it)
time_stretch_max += (*it)->time_stretch_when_slowing_down_to_feedrate(feedrate_limit, additional_slowdown_features);

if (time_stretch_max >= time_stretch) {
// We can achieve the required time stretch by slowing down to some feedrate above feedrate_limit
// Binary search for the right feedrate
float feedrate_high = feedrate;
float feedrate_low = feedrate_limit;
for (int iter = 0; iter < 20; ++iter) {
float feedrate_mid = (feedrate_high + feedrate_low) / 2.f;
float stretch = 0.f;
for (auto it = adj; it != by_min_print_speed.end(); ++it)
stretch += (*it)->time_stretch_when_slowing_down_to_feedrate(feedrate_mid, additional_slowdown_features);
if (stretch < time_stretch)
feedrate_high = feedrate_mid;
else
feedrate_low = feedrate_mid;
if (std::abs(stretch - time_stretch) < 0.01f)
break;
}
for (auto it = adj; it != by_min_print_speed.end(); ++it)
(*it)->slow_down_to_feedrate(feedrate_low, additional_slowdown_features);
return 0.f; // Time stretch achieved
} else {
// Slow down to minimum for these features
time_stretch -= time_stretch_max;
for (auto it = adj; it != by_min_print_speed.end(); ++it)
(*it)->slow_down_to_feedrate(feedrate_limit, additional_slowdown_features);
}

// Skip extruders with nearly the same slow_down_min_speed
auto next = adj;
for (++next; next != by_min_print_speed.end() && (*next)->slow_down_min_speed > (*adj)->slow_down_min_speed - EPSILON; ++next)
;
adj = next;
}

return time_stretch; // Return remaining time stretch that couldn't be achieved
}

// Slow down an extruder range to slow_down_layer_time.
// Return the total time for the complete layer.
static inline void extruder_range_slow_down_non_proportional(std::vector<PerExtruderAdjustments *>::iterator it_begin,
Expand Down Expand Up @@ -184,13 +266,33 @@ float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments
// Only insert entries, which are adjustable (have cooling enabled and non-zero stretchable time).
// Collect total print time of non-adjustable extruders.
float elapsed_time_total0 = 0.f;

// Check if any extruder uses ConsistentSurface logic
bool any_consistent_surface = false;

for (PerExtruderAdjustments &adj : per_extruder_adjustments) {
// Curren total time for this extruder.
// Current total time for this extruder.
adj.time_total = adj.elapsed_time_total();
// Maximum time for this extruder, when all extrusion moves are slowed down to min_extrusion_speed.
adj.time_maximum = adj.maximum_time_after_slowdown(true);
if (adj.cooling_slow_down_enabled && adj.lines.size() > 0) {
by_slowdown_time.emplace_back(&adj);

// For ConsistentSurface logic, prepare the non-adjustable segments
if (adj.cooling_slowdown_logic == cslConsistentSurface) {
any_consistent_surface = true;
// Initialize adjustable fields for all lines
for (CoolingLine &line : adj.lines) {
if (line.type & CoolingLine::TYPE_ADJUSTABLE) {
line.adjustable_length = line.length;
line.adjustable_time = line.time;
line.adjustable_time_max = line.time_max;
}
}
// Create non-adjustable segments at the end of perimeter loops
adj.create_non_adjustable_segments(adj.cooling_perimeter_transition_distance);
}

if (!m_cooling_logic_proportional)
// sorts the lines, also sets adj.time_non_adjustable
adj.sort_lines_by_decreasing_feedrate();
Expand All @@ -214,10 +316,28 @@ float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments
float max_time = elapsed_time_total0;
for (auto it = cur_begin; it != by_slowdown_time.end(); ++it) max_time += (*it)->time_maximum;
if (max_time > slow_down_layer_time) {
if (m_cooling_logic_proportional)
float time_stretch = slow_down_layer_time - total;

// Check if this extruder uses ConsistentSurface logic
if (adj.cooling_slowdown_logic == cslConsistentSurface) {
// ConsistentSurface: Two-phase slowdown
// Phase 1: Try slowing down only non-external perimeter features (infill, internal perimeters)
float remaining = extruder_range_slow_down_consistent_surface(
cur_begin, by_slowdown_time.end(), time_stretch, AdjustableFeatureType::None);

// Phase 2: If still not enough time, allow external perimeter and first internal slowdown
if (remaining > 0.f) {
extruder_range_slow_down_consistent_surface(
cur_begin, by_slowdown_time.end(), remaining,
AdjustableFeatureType::ExternalPerimeters | AdjustableFeatureType::FirstInternalPerimeters);
}
} else if (m_cooling_logic_proportional) {
// Uniform cooling with proportional slowdown
extruder_range_slow_down_proportional(cur_begin, by_slowdown_time.end(), elapsed_time_total0, total, slow_down_layer_time);
else
extruder_range_slow_down_non_proportional(cur_begin, by_slowdown_time.end(), slow_down_layer_time - total);
} else {
// Uniform cooling with non-proportional slowdown
extruder_range_slow_down_non_proportional(cur_begin, by_slowdown_time.end(), time_stretch);
}
} else {
// Slow down to maximum possible.
for (auto it = cur_begin; it != by_slowdown_time.end(); ++it) (*it)->slowdown_to_minimum_feedrate(true);
Expand Down
7 changes: 7 additions & 0 deletions src/libslic3r/GCode/GCodeEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ std::vector<PerExtruderAdjustments> GCodeEditor::parse_layer_gcode( const
adj.cooling_slow_down_enabled = m_config.slow_down_for_layer_cooling.get_at(extruder_id);
adj.slow_down_layer_time = float(m_config.slow_down_layer_time.get_at(extruder_id));
adj.slow_down_min_speed = float(m_config.slow_down_min_speed.get_at(extruder_id));

// Read ConsistentSurface cooling settings
if (m_config.cooling_slowdown_logic.values.size() > extruder_id)
adj.cooling_slowdown_logic = static_cast<CoolingSlowdownLogicType>(m_config.cooling_slowdown_logic.values[extruder_id]);
if (m_config.cooling_perimeter_transition_distance.values.size() > extruder_id)
adj.cooling_perimeter_transition_distance = float(m_config.cooling_perimeter_transition_distance.values[extruder_id]);

map_extruder_to_per_extruder_adjustment[extruder_id] = i;
}

Expand Down
Loading
Loading