OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::AdaptivePAProcessor Class Reference

Class for processing G-code layers with adaptive pressure advance. More...

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/GCode/AdaptivePAProcessor.hpp>

Collaboration diagram for Slic3r::AdaptivePAProcessor:

Public Member Functions

 AdaptivePAProcessor (GCode &gcodegen)
 Constructor for AdaptivePAProcessor.
std::string process_layer (std::string &&gcode)
 Processes a layer of G-code and applies adaptive pressure advance.
void resetPreviousPA (double PA)
 Manually sets adaptive PA internal value.

Static Public Member Functions

static std::string validate_adaptive_pa_model (const std::string &model_str)
 Validates an adaptive pressure advance model string.

Private Member Functions

AdaptivePAInterpolator * getInterpolator (unsigned int config_index)
 Get the PA interpolator attached to the specified filament config index.

Private Attributes

GCode & m_gcodegen
 Reference to the GCode object.
std::unordered_map< unsigned int, std::unique_ptr< AdaptivePAInterpolator > > m_AdaptivePAInterpolators
 Map between Interpolator objects and filament config indices (null when adaptive PA is off).
const PrintConfig & m_config
 Reference to the print configuration.
double m_last_predicted_pa
 Last predicted pressure advance value.
double m_max_next_feedrate
 Maximum feed rate (speed) for the upcomming island. If no speed is found, the previous island speed is used.
double m_next_feedrate
 First feed rate (speed) for the upcomming island.
double m_current_feedrate
 Current, latest feedrate.
int m_last_config_index
 Filament config index of the last PA_CHANGE tag.
bool m_enabled {false}
 Whether any filament config index has both PA and adaptive PA on, the only ones that emit PA_CHANGE tags.
std::regex m_pa_change_pattern
 Regular expression to detect PA_CHANGE pattern.
std::regex m_g1_f_pattern
 Regular expression to detect G1 F pattern.
std::smatch m_match
 Match results for regular expressions.

Detailed Description

Class for processing G-code layers with adaptive pressure advance.

Constructor & Destructor Documentation

◆ AdaptivePAProcessor()

Slic3r::AdaptivePAProcessor::AdaptivePAProcessor ( GCode & gcodegen)

Constructor for AdaptivePAProcessor.

This constructor initializes the AdaptivePAProcessor with a reference to a GCode object. It also initializes the configuration reference, pressure advance interpolation object, and regular expression patterns used for processing the G-code.

Parameters
gcodegenA reference to the GCode object that generates the G-code.
40 : m_gcodegen(gcodegen),
41 m_config(gcodegen.config()),
44 m_next_feedrate(0.0),
47 m_pa_change_pattern(R"(; PA_CHANGE:T(\d+) MM3MM:([0-9]*\.[0-9]+) ACCEL:(\d+) BR:(\d+) RC:(\d+) OV:(\d+))"),
48 m_g1_f_pattern(R"(G1 F([0-9]+))")
49{
50 const size_t indices = std::max(m_config.adaptive_pressure_advance.size(), m_config.enable_pressure_advance.size());
51 for (size_t i = 0; i < indices && !m_enabled; ++i)
52 m_enabled = m_config.adaptive_pressure_advance.get_at(i) && m_config.enable_pressure_advance.get_at(i);
53}
std::regex m_pa_change_pattern
Regular expression to detect PA_CHANGE pattern.
Definition AdaptivePAProcessor.hpp:83
double m_next_feedrate
First feed rate (speed) for the upcomming island.
Definition AdaptivePAProcessor.hpp:78
bool m_enabled
Whether any filament config index has both PA and adaptive PA on, the only ones that emit PA_CHANGE t...
Definition AdaptivePAProcessor.hpp:81
double m_max_next_feedrate
Maximum feed rate (speed) for the upcomming island. If no speed is found, the previous island speed i...
Definition AdaptivePAProcessor.hpp:77
GCode & m_gcodegen
Reference to the GCode object.
Definition AdaptivePAProcessor.hpp:73
double m_last_predicted_pa
Last predicted pressure advance value.
Definition AdaptivePAProcessor.hpp:76
int m_last_config_index
Filament config index of the last PA_CHANGE tag.
Definition AdaptivePAProcessor.hpp:80
const PrintConfig & m_config
Reference to the print configuration.
Definition AdaptivePAProcessor.hpp:75
std::regex m_g1_f_pattern
Regular expression to detect G1 F pattern.
Definition AdaptivePAProcessor.hpp:84
double m_current_feedrate
Current, latest feedrate.
Definition AdaptivePAProcessor.hpp:79

References m_config, m_current_feedrate, m_enabled, m_g1_f_pattern, m_gcodegen, m_last_config_index, m_last_predicted_pa, m_max_next_feedrate, m_next_feedrate, and m_pa_change_pattern.

Member Function Documentation

◆ getInterpolator()

AdaptivePAInterpolator * Slic3r::AdaptivePAProcessor::getInterpolator ( unsigned int config_index)
private

Get the PA interpolator attached to the specified filament config index.

Parameters
config_indexThe filament config index (one per filament and extruder variant) for which the PA interpolation model is to be returned.
Returns
The Adaptive PA Interpolator object corresponding to that index, or nullptr when adaptive PA is off for it.
58 {
59 auto [it, inserted] = m_AdaptivePAInterpolators.try_emplace(config_index);
60 // Only enable model for the index if both PA and adaptive PA options are enabled
61 if (inserted && m_config.adaptive_pressure_advance.get_at(config_index) && m_config.enable_pressure_advance.get_at(config_index)) {
62 it->second = std::make_unique<AdaptivePAInterpolator>();
63 it->second->parseAndSetData(m_config.adaptive_pressure_advance_model.get_at(config_index));
64 }
65 return it->second.get();
66}
std::unordered_map< unsigned int, std::unique_ptr< AdaptivePAInterpolator > > m_AdaptivePAInterpolators
Map between Interpolator objects and filament config indices (null when adaptive PA is off).
Definition AdaptivePAProcessor.hpp:74

References m_AdaptivePAInterpolators, and m_config.

Referenced by process_layer().

Here is the caller graph for this function:

◆ process_layer()

std::string Slic3r::AdaptivePAProcessor::process_layer ( std::string && gcode)

Processes a layer of G-code and applies adaptive pressure advance.

This method processes the G-code for a single layer, identifying the appropriate pressure advance settings and applying them based on the current state and configurations.

Parameters
gcodeA string containing the G-code for the layer.
Returns
A string containing the processed G-code with adaptive pressure advance applied.
77 {
78 // Without PA_CHANGE tags the loop below would only terminate the layer's last line.
79 if (!m_enabled && gcode.find("; PA_CHANGE") == std::string::npos) {
80 if (!gcode.empty() && gcode.back() != '\n')
81 gcode += '\n';
82 return std::move(gcode);
83 }
84 std::istringstream stream(gcode);
85 std::string line;
86 std::ostringstream output;
87 double mm3mm_value = 0.0;
88 unsigned int accel_value = 0;
89 std::string pa_change_line;
90 bool wipe_command = false;
91
92 // Iterate through each line of the layer G-code
93 while (std::getline(stream, line)) {
94
95 // If a wipe start command is found, ignore all speed changes till the wipe end part is found
96 if (line.find("WIPE_START") != std::string::npos) {
97 wipe_command = true;
98 }
99
100 // Update current feed rate (this is preceding an extrude or wipe command only). Ignore any speed changes that are emitted during a wipe move.
101 // Travel feedrate is output as part of a G1 X Y (Z) F command
102 if ( (line.find("G1 F") == 0) && (!wipe_command) ) { // prune lines quickly before running pattern matching
103 std::size_t pos = line.find('F');
104 if (pos != std::string::npos){
105 m_current_feedrate = std::stod(line.substr(pos + 1)) / 60.0; // Convert from mm/min to mm/s
106 }
107 }
108
109 // Wipe end found, continue searching for current feed rate.
110 if (line.find("WIPE_END") != std::string::npos) {
111 wipe_command = false;
112 }
113
114 // Reset next feedrate to zero enable searching for the first encountered
115 // feedrate change command after the PA change tag.
116 m_next_feedrate = 0;
117
118 // Check for PA_CHANGE pattern in the line
119 // We will only find this pattern for extruders where adaptive PA is enabled.
120 // If there is mixed extruders in the layer (i.e. with adaptive PA on and off
121 // this will only update the extruders where the adaptive PA is enabled
122 // as these are the only ones where the PA pattern is output
123 // For a mixed extruder layer with both adaptive PA enabled and disabled when the new tool is selected
124 // the PA for that material is set. As no tag below will be found for this extruder, the original PA is retained.
125 if (line.find("; PA_CHANGE") == 0) { // prune lines quickly before running regex check as regex is more expensive to run
126 if (std::regex_search(line, m_match, m_pa_change_pattern)) {
127 // The tag carries the filament config index, which selects the PA settings of the filament's extruder variant
128 int config_index = std::stoi(m_match[1].str());
129 mm3mm_value = std::stod(m_match[2].str());
130 accel_value = std::stod(m_match[3].str());
131 int isBridge = std::stoi(m_match[4].str());
132 int roleChange = std::stoi(m_match[5].str());
133 int isOverhang = std::stoi(m_match[6].str());
134
135 // Check if the filament config index has changed
136 bool config_index_changed = (config_index != m_last_config_index);
137 m_last_config_index = config_index;
138
139 // Save the PA_CHANGE line to output later after finding feedrate
140 pa_change_line = line;
141
142 // Look ahead for feedrate before any line containing both G and E commands
143 std::streampos current_pos = stream.tellg();
144 std::string next_line;
145 double temp_feed_rate = 0;
146 bool extrude_move_found = false;
147 int line_counter = 0;
148
149 // Carry on searching on the layer gcode lines to find the print speed
150 // If a G1 Fxxxx pattern is found, the new speed is identified
151 // Carry on searching for feedrates to find the maximum print speed
152 // until a feature change pattern or a wipe command is detected
153 while (std::getline(stream, next_line)) {
154 line_counter++;
155 // Found an extrude move, set extrude move found flag and move to the next line
156 if ((!extrude_move_found) && next_line.find("G1 ") == 0 &&
157 next_line.find('X') != std::string::npos &&
158 next_line.find('Y') != std::string::npos &&
159 next_line.find('E') != std::string::npos) {
160 // Pattern matched, break the loop
161 extrude_move_found = true;
162 continue;
163 }
164
165 // Found a travel move after we've found at least one extrude move
166 // We now need to stop searching for speeds as we're done printing this island
167 if (next_line.find("G1 ") == 0 &&
168 next_line.find('X') != std::string::npos && // X is present
169 next_line.find('Y') != std::string::npos && // Y is present
170 next_line.find('E') == std::string::npos && // no "E" present
171 extrude_move_found) { // An extrude move has happened already
172 // First travel move after extrude move found. Stop searching
173 break;
174 }
175
176 // Found a WIPE command
177 // If we have a wipe command, usually the wipe speed is different (larger) than the max print speed
178 // for that feature. So stop searching if a wipe command is found because we do not want to overwrite the
179 // speed used for PA calculation by the Wipe speed.
180 if (next_line.find("WIPE") != std::string::npos) {
181 break; // Stop searching if wipe command is found
182 }
183
184 // Found another PA_CHANGE pattern
185 // If RC = 1, it means we have a role change, so stop trying to find the max speed for the feature.
186 // This is possibly redundant as a new feature would always have a travel move preceding it
187 // but check anyway. However check last so to not invoke it without reason...
188 if (next_line.find("; PA_CHANGE") == 0) { // prune lines quickly before running pattern matching
189 std::size_t rc_pos = next_line.rfind("RC:");
190 if (rc_pos != std::string::npos) {
191 int rc_value = std::stoi(next_line.substr(rc_pos + 3));
192 if (rc_value == 1) {
193 break; // Role change found, stop searching
194 }
195 }
196 }
197
198 // Found a Feedrate change command
199 // If the new feedrate is greater than any feedrate encountered so far after the PA change command, use that to calculate the PA value
200 // Also if this is the first feedrate we encounter, store it as the next feedrate.
201 if (next_line.find("G1 F") == 0) { // prune lines quickly before running pattern matching
202 std::size_t pos = next_line.find('F');
203 if (pos != std::string::npos) {
204 double feedrate = std::stod(next_line.substr(pos + 1)) / 60.0; // Convert from mm/min to mm/s
205 if(line_counter==1){ // this is the first command after the PA change pattern, and hence before any extrusion has happened. Reset
206 // the current speed to this one
207 m_current_feedrate = feedrate;
208 }
209 if (temp_feed_rate < feedrate) {
210 temp_feed_rate = feedrate;
211 }
212 if(m_next_feedrate < EPSILON){ // This the first feedrate found after the PA Change command
213 m_next_feedrate = feedrate;
214 }
215 }
216 continue;
217 }
218 }
219
220 // If we found a new maximum feedrate after the PA change command, use it
221 if (temp_feed_rate > 0) {
222 m_max_next_feedrate = temp_feed_rate;
223 } else // If we didnt find a new feedrate at all after the PA change command, use the current feedrate.
225
226 // Restore stream position
227 stream.clear();
228 stream.seekg(current_pos);
229
230 // Calculate the predicted PA using the upcomming feature maximum feedrate
231 // Get the interpolator for the active tool
232 AdaptivePAInterpolator* interpolator = getInterpolator(m_last_config_index);
233
234 double predicted_pa = 0;
235 double adaptive_PA_speed = 0;
236
237 if(!interpolator){ // Tool not found in the interpolator map
238 // Tool not found in the PA interpolator to tool map
239 predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
240 if(m_config.gcode_comments) output << "; APA: Tool doesnt have APA enabled\n";
241 } else if (!interpolator->isInitialised() || (!m_config.adaptive_pressure_advance.get_at(m_last_config_index)) )
242 // Check if the model is not initialised by the constructor for the active extruder
243 // Also check that adaptive PA is enabled for that extruder. This should not be needed
244 // as the PA change flag should not be set upstream (in the GCode.cpp file) if adaptive PA is disabled
245 // however check for robustness sake.
246 {
247 // Model failed or adaptive pressure advance not enabled - use default value from m_config
248 predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
249 if(m_config.gcode_comments) output << "; APA: Interpolator setup failed, using default pressure advance\n";
250 } else { // Model setup succeeded
251 // Proceed to identify the print speed to use to calculate the adaptive PA value
252 if(isOverhang > 0){ // If we are in an overhang area, use the minimum between current print speed
253 // and any speed immediately after
254 // In most cases the current speed is the minimum one;
255 // however if slowdown for layer cooling is enabled, the overhang
256 // may be slowed down more than the current speed.
257 adaptive_PA_speed = (m_current_feedrate == 0 || m_next_feedrate == 0) ?
260 }else{ // If this is not an overhang area, use the maximum speed from the current and
261 // upcomming speeds for the island.
262 adaptive_PA_speed = std::max(m_max_next_feedrate,m_current_feedrate);
263 }
264
265 // Calculate the adaptive PA value
266 predicted_pa = (*interpolator)(mm3mm_value * adaptive_PA_speed, accel_value);
267
268 // This is a bridge, use the dedicated PA setting.
269 if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
270 predicted_pa = m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index);
271
272 if (predicted_pa < 0) { // If extrapolation fails, fall back to the default PA for the extruder.
273 predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
274 if(m_config.gcode_comments) output << "; APA: Interpolation failed, using fallback pressure advance value\n";
275 }
276 }
277 if(m_config.gcode_comments) {
278 // Output debug GCode comments
279 output << pa_change_line << '\n'; // Output PA change command tag
280 if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
281 output << "; APA Model Override (bridge)\n";
282 output << "; APA Current Speed: " << std::to_string(m_current_feedrate) << "\n";
283 output << "; APA Next Speed: " << std::to_string(m_next_feedrate) << "\n";
284 output << "; APA Max Next Speed: " << std::to_string(m_max_next_feedrate) << "\n";
285 output << "; APA Speed Used: " << std::to_string(adaptive_PA_speed) << "\n";
286 output << "; APA Flow rate: " << std::to_string(mm3mm_value * m_max_next_feedrate) << "\n";
287 output << "; APA Prev PA: " << std::to_string(m_last_predicted_pa) << " New PA: " << std::to_string(predicted_pa) << "\n";
288 }
289 if (config_index_changed || std::fabs(predicted_pa - m_last_predicted_pa) > EPSILON) {
290 output << m_gcodegen.writer().set_pressure_advance(predicted_pa); // Use m_writer to set pressure advance
291 m_last_predicted_pa = predicted_pa; // Update the last predicted PA value
292 }
293 }
294 }else {
295 // Output the current line as this isn't a PA change tag
296 output << line << '\n';
297 }
298 }
299
300 return output.str();
301}
#define EPSILON
Definition ColorUtils.cpp:64
bool isInitialised() const
Returns the initialization status.
Definition AdaptivePAInterpolator.hpp:44
std::smatch m_match
Match results for regular expressions.
Definition AdaptivePAProcessor.hpp:85
AdaptivePAInterpolator * getInterpolator(unsigned int config_index)
Get the PA interpolator attached to the specified filament config index.
Definition AdaptivePAProcessor.cpp:58
@ gcode
Definition Time.hpp:14
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16

References EPSILON, getInterpolator(), AdaptivePAInterpolator::isInitialised(), m_config, m_current_feedrate, m_enabled, m_gcodegen, m_last_config_index, m_last_predicted_pa, m_match, m_max_next_feedrate, m_next_feedrate, and m_pa_change_pattern.

Here is the call graph for this function:

◆ resetPreviousPA()

void Slic3r::AdaptivePAProcessor::resetPreviousPA ( double PA)
inline

Manually sets adaptive PA internal value.

This method manually sets the adaptive PA internally held value. Call this when changing tools or in any other case where the internally assumed last PA value may be incorrect

56{ m_last_predicted_pa = PA; };

References m_last_predicted_pa.

◆ validate_adaptive_pa_model()

std::string Slic3r::AdaptivePAProcessor::validate_adaptive_pa_model ( const std::string & model_str)
static

Validates an adaptive pressure advance model string.

Checks that:

  • Each non-empty line has exactly 3 comma-separated values (PA, flow, accel)
  • PA value is less than 2
  • Flow value is greater than PA value
  • Accel value is greater than flow value
Parameters
model_strThe model string to validate (typically from config)
Returns
Empty string if valid, or an error message describing the first issue found
304{
305 if (model_str.empty())
306 return {}; // Empty model is valid
307
308 std::istringstream model_stream(model_str);
309 std::string line;
310 int line_number = 0;
311
312 while (std::getline(model_stream, line)) {
313 ++line_number;
314
315 // Trim whitespace
316 const auto first = line.find_first_not_of(" \t\r\n");
317 if (first == std::string::npos)
318 continue; // Skip empty lines
319
320 const auto last = line.find_last_not_of(" \t\r\n");
321 line = line.substr(first, last - first + 1);
322
323 // Only numbers, commas and dots are allowed (no letters or other characters)
324 for (char c : line) {
325 if (!std::isdigit(static_cast<unsigned char>(c)) && c != ',' && c != '.') {
326 return Slic3r::format(_u8L("Line %1%: only numbers, commas and dots are allowed"), line_number);
327 }
328 }
329
330 // Count commas to validate format (should be exactly 2 for 3 values)
331 int comma_count = 0;
332 for (char c : line) {
333 if (c == ',') comma_count++;
334 }
335
336 if (comma_count != 2) {
337 return Slic3r::format(_u8L("Line %1%: must contain exactly 3 comma-separated values (PA, flow, acceleration)"), line_number);
338 }
339
340 // Parse and validate the values
341 try {
342 std::istringstream line_stream(line);
343 std::string value;
344
345 // Parse PA
346 if (!std::getline(line_stream, value, ','))
347 return Slic3r::format(_u8L("Line %1%: missing PA value"), line_number);
348 double pa = std::stod(value);
349
350 // Parse flow
351 if (!std::getline(line_stream, value, ','))
352 return Slic3r::format(_u8L("Line %1%: missing flow value"), line_number);
353 double flow = std::stod(value);
354
355 // Parse acceleration
356 if (!std::getline(line_stream, value, ','))
357 return Slic3r::format(_u8L("Line %1%: missing acceleration value"), line_number);
358 double accel = std::stod(value);
359
360 // Validate constraints
361 if (pa >= 2.0) {
362 return Slic3r::format(_u8L("Line %1%: PA value must be less than 2"), line_number);
363 }
364 if (flow <= pa) {
365 return Slic3r::format(_u8L("Line %1%: flow value must be greater than PA value"), line_number);
366 }
367 if (accel <= flow) {
368 return Slic3r::format(_u8L("Line %1%: acceleration value must be greater than flow value"), line_number);
369 }
370 } catch (const std::exception&) {
371 return Slic3r::format(_u8L("Line %1%: invalid numeric value"), line_number);
372 }
373 }
374
375 return {}; // All validations passed
376}
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, and Slic3r::format().

Referenced by Slic3r::GUI::ConfigManipulation::check_adaptive_pressure_advance_model(), and Slic3r::Print::validate().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_AdaptivePAInterpolators

std::unordered_map<unsigned int, std::unique_ptr<AdaptivePAInterpolator> > Slic3r::AdaptivePAProcessor::m_AdaptivePAInterpolators
private

Map between Interpolator objects and filament config indices (null when adaptive PA is off).

Referenced by getInterpolator().

◆ m_config

const PrintConfig& Slic3r::AdaptivePAProcessor::m_config
private

Reference to the print configuration.

Referenced by AdaptivePAProcessor(), getInterpolator(), and process_layer().

◆ m_current_feedrate

double Slic3r::AdaptivePAProcessor::m_current_feedrate
private

Current, latest feedrate.

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_enabled

bool Slic3r::AdaptivePAProcessor::m_enabled {false}
private

Whether any filament config index has both PA and adaptive PA on, the only ones that emit PA_CHANGE tags.

81{false};

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_g1_f_pattern

std::regex Slic3r::AdaptivePAProcessor::m_g1_f_pattern
private

Regular expression to detect G1 F pattern.

Referenced by AdaptivePAProcessor().

◆ m_gcodegen

GCode& Slic3r::AdaptivePAProcessor::m_gcodegen
private

Reference to the GCode object.

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_last_config_index

int Slic3r::AdaptivePAProcessor::m_last_config_index
private

Filament config index of the last PA_CHANGE tag.

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_last_predicted_pa

double Slic3r::AdaptivePAProcessor::m_last_predicted_pa
private

Last predicted pressure advance value.

Referenced by AdaptivePAProcessor(), process_layer(), and resetPreviousPA().

◆ m_match

std::smatch Slic3r::AdaptivePAProcessor::m_match
private

Match results for regular expressions.

Referenced by process_layer().

◆ m_max_next_feedrate

double Slic3r::AdaptivePAProcessor::m_max_next_feedrate
private

Maximum feed rate (speed) for the upcomming island. If no speed is found, the previous island speed is used.

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_next_feedrate

double Slic3r::AdaptivePAProcessor::m_next_feedrate
private

First feed rate (speed) for the upcomming island.

Referenced by AdaptivePAProcessor(), and process_layer().

◆ m_pa_change_pattern

std::regex Slic3r::AdaptivePAProcessor::m_pa_change_pattern
private

Regular expression to detect PA_CHANGE pattern.

Referenced by AdaptivePAProcessor(), and process_layer().


The documentation for this class was generated from the following files: