mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-21 15:51:10 -06:00
Merge remote-tracking branch 'origin/master' into gui_translate_to_cpp
This commit is contained in:
commit
8e0cd35c23
30 changed files with 2595 additions and 1460 deletions
|
@ -90,8 +90,8 @@ ShinyNode* _ShinyManager_dummyNodeTable[] = { NULL };
|
|||
|
||||
/* primary hash function */
|
||||
SHINY_INLINE uint32_t hash_value(void* a_pParent, void* a_pZone) {
|
||||
// uint32_t a = (uint32_t) a_pParent + (uint32_t) a_pZone;
|
||||
uint32_t a = *reinterpret_cast<uint32_t*>(&a_pParent) + *reinterpret_cast<uint32_t*>(&a_pZone);
|
||||
uint32_t a = (uint32_t) a_pParent + (uint32_t) a_pZone;
|
||||
// uint32_t a = *reinterpret_cast<uint32_t*>(&a_pParent) + *reinterpret_cast<uint32_t*>(&a_pZone);
|
||||
|
||||
a = (a+0x7ed55d16) + (a<<12);
|
||||
a = (a^0xc761c23c) ^ (a>>19);
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
|
||||
#define setenv(k, v, o) _putenv_s(k, v)
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::string escape_string_cstyle(const std::string &str)
|
||||
|
@ -309,7 +305,6 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
|
|||
|
||||
void ConfigBase::setenv_()
|
||||
{
|
||||
#ifdef setenv
|
||||
t_config_option_keys opt_keys = this->keys();
|
||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
||||
// prepend the SLIC3R_ prefix
|
||||
|
@ -322,9 +317,8 @@ void ConfigBase::setenv_()
|
|||
for (size_t i = 0; i < envname.size(); ++i)
|
||||
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
|
||||
|
||||
setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
|
||||
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConfigBase::load(const std::string &file)
|
||||
|
|
|
@ -1161,6 +1161,8 @@ public:
|
|||
const ConfigDef* def() const override { return nullptr; };
|
||||
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
|
||||
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
|
||||
template<class T> const T* opt(const t_config_option_key &opt_key) const
|
||||
{ return dynamic_cast<const T*>(this->option(opt_key)); }
|
||||
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
|
||||
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
|
||||
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
#if 0
|
||||
// Enable debugging and asserts, even in the release build.
|
||||
#define DEBUG
|
||||
|
@ -267,22 +269,6 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen)
|
|||
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id())
|
||||
|
||||
inline void write(FILE *file, const std::string &what)
|
||||
{
|
||||
fwrite(what.data(), 1, what.size(), file);
|
||||
}
|
||||
|
||||
// Write a string into a file. Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
inline void writeln(FILE *file, const std::string &what)
|
||||
{
|
||||
if (! what.empty()) {
|
||||
write(file, what);
|
||||
if (what.back() != '\n')
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Collect pairs of object_layer + support_layer sorted by print_z.
|
||||
// object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON.
|
||||
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject &object)
|
||||
|
@ -364,6 +350,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
|
||||
void GCode::do_export(Print *print, const char *path)
|
||||
{
|
||||
PROFILE_CLEAR();
|
||||
|
||||
// Remove the old g-code if it exists.
|
||||
boost::nowide::remove(path);
|
||||
|
||||
|
@ -395,14 +383,25 @@ void GCode::do_export(Print *print, const char *path)
|
|||
msg += " !!!!! End of an error report for the custom G-code template ...\n";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
|
||||
// Write the profiler measurements to file
|
||||
PROFILE_UPDATE();
|
||||
PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str());
|
||||
}
|
||||
|
||||
void GCode::_do_export(Print &print, FILE *file)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
// resets time estimator
|
||||
m_time_estimator.reset();
|
||||
m_time_estimator.set_dialect(print.config.gcode_flavor);
|
||||
|
||||
// How many times will be change_layer() called?
|
||||
// change_layer() in turn increments the progress bar status.
|
||||
m_layer_count = 0;
|
||||
|
@ -486,7 +485,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
|
||||
|
||||
// Write information on the generator.
|
||||
fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
_write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
// Write notes (content of the Print Settings tab -> Notes)
|
||||
{
|
||||
std::list<std::string> lines;
|
||||
|
@ -495,10 +494,10 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Remove the trailing '\r' from the '\r\n' sequence.
|
||||
if (! line.empty() && line.back() == '\r')
|
||||
line.pop_back();
|
||||
fprintf(file, "; %s\n", line.c_str());
|
||||
_write_format(file, "; %s\n", line.c_str());
|
||||
}
|
||||
if (! lines.empty())
|
||||
fprintf(file, "\n");
|
||||
_write(file, "\n");
|
||||
}
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
|
@ -506,16 +505,16 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
if (print.has_support_material())
|
||||
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
if (print.config.first_layer_extrusion_width.value > 0)
|
||||
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
fprintf(file, "\n");
|
||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
_write_format(file, "\n");
|
||||
}
|
||||
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
|
@ -558,7 +557,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
|
||||
// Disable fan.
|
||||
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
|
||||
write(file, m_writer.set_fan(0, true));
|
||||
_write(file, m_writer.set_fan(0, true));
|
||||
|
||||
// Let the start-up script prime the 1st printing tool.
|
||||
m_placeholder_parser.set("initial_tool", initial_extruder_id);
|
||||
|
@ -575,24 +574,24 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Set extruder(s) temperature before and after start G-code.
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
||||
// Write the custom start G-code
|
||||
writeln(file, start_gcode);
|
||||
_writeln(file, start_gcode);
|
||||
// Process filament-specific gcode in extruder order.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
if (has_wipe_tower) {
|
||||
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
||||
} else {
|
||||
// Only initialize the initial extruder.
|
||||
writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
||||
}
|
||||
} else {
|
||||
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
|
||||
writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||
}
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
||||
|
||||
// Set other general things.
|
||||
write(file, this->preamble());
|
||||
|
||||
_write(file, this->preamble());
|
||||
|
||||
// Initialize a motion planner for object-to-object travel moves.
|
||||
if (print.config.avoid_crossing_perimeters.value) {
|
||||
// Collect outer contours of all objects over all layers.
|
||||
|
@ -640,7 +639,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
}
|
||||
|
||||
// Set initial extruder only after custom start G-code.
|
||||
write(file, this->set_extruder(initial_extruder_id));
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config.complete_objects.value) {
|
||||
|
@ -670,8 +669,8 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
||||
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
write(file, this->retract());
|
||||
write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
_write(file, this->retract());
|
||||
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
m_enable_cooling_markers = true;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
m_avoid_crossing_perimeters.disable_once = true;
|
||||
|
@ -683,7 +682,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
||||
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
writeln(file, between_objects_gcode);
|
||||
_writeln(file, between_objects_gcode);
|
||||
}
|
||||
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
|
||||
m_cooling_buffer->reset();
|
||||
|
@ -696,7 +695,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data());
|
||||
}
|
||||
if (m_pressure_equalizer)
|
||||
write(file, m_pressure_equalizer->process("", true));
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
++ finished_objects;
|
||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||
// Reset it when starting another object from 1st layer.
|
||||
|
@ -716,8 +715,8 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Prusa Multi-Material wipe tower.
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
write(file, m_wipe_tower->prime(*this));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
|
@ -727,16 +726,17 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
write(file, this->retract());
|
||||
fprintf(file, "M300 S800 P500\n");
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
fprintf(file, "M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
fprintf(file, "M1 S10\n");
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
|
@ -747,26 +747,29 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
|
||||
}
|
||||
if (m_pressure_equalizer)
|
||||
write(file, m_pressure_equalizer->process("", true));
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
write(file, m_wipe_tower->finalize(*this));
|
||||
_write(file, m_wipe_tower->finalize(*this));
|
||||
}
|
||||
|
||||
// Write end commands to file.
|
||||
write(file, this->retract());
|
||||
write(file, m_writer.set_fan(false));
|
||||
_write(file, this->retract());
|
||||
_write(file, m_writer.set_fan(false));
|
||||
// Process filament-specific gcode in extruder order.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// Process the end_filament_gcode for the active filament only.
|
||||
writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
||||
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
||||
} else {
|
||||
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
|
||||
writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||
_writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||
}
|
||||
writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
|
||||
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
write(file, m_writer.postamble());
|
||||
_writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
|
||||
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
_write(file, m_writer.postamble());
|
||||
|
||||
// calculates estimated printing time
|
||||
m_time_estimator.calculate_time();
|
||||
|
||||
// Get filament stats.
|
||||
print.filament_stats.clear();
|
||||
|
@ -774,35 +777,37 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
print.total_extruded_volume = 0.;
|
||||
print.total_weight = 0.;
|
||||
print.total_cost = 0.;
|
||||
print.estimated_print_time = m_time_estimator.get_time_hms();
|
||||
for (const Extruder &extruder : m_writer.extruders()) {
|
||||
double used_filament = extruder.used_filament();
|
||||
double extruded_volume = extruder.extruded_volume();
|
||||
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
||||
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
||||
print.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament));
|
||||
fprintf(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
|
||||
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
|
||||
if (filament_weight > 0.) {
|
||||
print.total_weight = print.total_weight + filament_weight;
|
||||
fprintf(file, "; filament used = %.1lf\n", filament_weight);
|
||||
_write_format(file, "; filament used = %.1lf\n", filament_weight);
|
||||
if (filament_cost > 0.) {
|
||||
print.total_cost = print.total_cost + filament_cost;
|
||||
fprintf(file, "; filament cost = %.1lf\n", filament_cost);
|
||||
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
|
||||
}
|
||||
}
|
||||
print.total_used_filament = print.total_used_filament + used_filament;
|
||||
print.total_used_filament = print.total_used_filament + used_filament;
|
||||
print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
|
||||
}
|
||||
fprintf(file, "; total filament cost = %.1lf\n", print.total_cost);
|
||||
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
|
||||
_write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str());
|
||||
|
||||
// Append full config.
|
||||
fprintf(file, "\n");
|
||||
_write(file, "\n");
|
||||
{
|
||||
StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) {
|
||||
StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
if (key != "compatible_printers")
|
||||
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
_write_format(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -893,7 +898,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
|
|||
// the custom start G-code emited these.
|
||||
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
|
||||
if (! temp_set_by_gcode)
|
||||
write(file, set_temp_gcode);
|
||||
_write(file, set_temp_gcode);
|
||||
}
|
||||
|
||||
// Write 1st layer extruder temperatures into the G-code.
|
||||
|
@ -916,7 +921,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
// Set temperature of the first printing extruder only.
|
||||
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
|
||||
if (temp > 0)
|
||||
write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
_write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
} else {
|
||||
// Set temperatures of all the printing extruders.
|
||||
for (unsigned int tool_id : print.extruders()) {
|
||||
|
@ -924,7 +929,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
if (print.config.ooze_prevention.value)
|
||||
temp += print.config.standby_temperature_delta.value;
|
||||
if (temp > 0)
|
||||
write(file, m_writer.set_temperature(temp, wait, tool_id));
|
||||
_write(file, m_writer.set_temperature(temp, wait, tool_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1358,7 +1363,7 @@ void GCode::process_layer(
|
|||
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
|
||||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
|
||||
write(file, gcode);
|
||||
_write(file, gcode);
|
||||
}
|
||||
|
||||
void GCode::apply_print_config(const PrintConfig &print_config)
|
||||
|
@ -1993,6 +1998,53 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
|||
return gcode;
|
||||
}
|
||||
|
||||
void GCode::_write(FILE* file, const char *what, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
// writes string to file
|
||||
fwrite(what, 1, size, file);
|
||||
// updates time estimator and gcode lines vector
|
||||
m_time_estimator.add_gcode_block(what);
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::_writeln(FILE* file, const std::string &what)
|
||||
{
|
||||
if (! what.empty())
|
||||
_write(file, (what.back() == '\n') ? what : (what + '\n'));
|
||||
}
|
||||
|
||||
void GCode::_write_format(FILE* file, const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int buflen;
|
||||
{
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
buflen =
|
||||
#ifdef _MSC_VER
|
||||
::_vscprintf(format, args2)
|
||||
#else
|
||||
::vsnprintf(nullptr, 0, format, args2)
|
||||
#endif
|
||||
+ 1;
|
||||
va_end(args2);
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
bool buffer_dynamic = buflen > 1024;
|
||||
char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
|
||||
int res = ::vsnprintf(bufptr, buflen, format, args);
|
||||
if (res > 0)
|
||||
_write(file, bufptr, res);
|
||||
if (buffer_dynamic)
|
||||
free(bufptr);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
std::string gcode;
|
||||
|
@ -2182,8 +2234,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::retract(bool toolchange)
|
||||
std::string GCode::retract(bool toolchange)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
@ -273,6 +274,21 @@ protected:
|
|||
// Index of a last object copy extruded.
|
||||
std::pair<const PrintObject*, Point> m_last_obj_copy;
|
||||
|
||||
// Time estimator
|
||||
GCodeTimeEstimator m_time_estimator;
|
||||
|
||||
// Write a string into a file.
|
||||
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str(), what.size()); }
|
||||
void _write(FILE* file, const char *what, size_t size);
|
||||
|
||||
// Write a string into a file.
|
||||
// Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
void _writeln(FILE* file, const std::string& what);
|
||||
|
||||
// Formats and write into a file the given data.
|
||||
void _write_format(FILE* file, const char* format, ...);
|
||||
|
||||
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
||||
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
|
|
|
@ -4,17 +4,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
std::string
|
||||
_format_z(float z)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(3)
|
||||
<< z;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
SpiralVase::process_layer(const std::string &gcode)
|
||||
std::string SpiralVase::process_layer(const std::string &gcode)
|
||||
{
|
||||
/* This post-processor relies on several assumptions:
|
||||
- all layers are processed through it, including those that are not supposed
|
||||
|
@ -27,7 +17,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
// If we're not going to modify G-code, just feed it to the reader
|
||||
// in order to update positions.
|
||||
if (!this->enable) {
|
||||
this->_reader.parse(gcode, {});
|
||||
this->_reader.parse_buffer(gcode);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
|
@ -38,16 +28,17 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
bool set_z = false;
|
||||
|
||||
{
|
||||
//FIXME Performance warning: This copies the GCodeConfig of the reader.
|
||||
GCodeReader r = this->_reader; // clone
|
||||
r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||
(GCodeReader &, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd == "G1") {
|
||||
if (line.extruding()) {
|
||||
total_layer_length += line.dist_XY();
|
||||
} else if (line.has('Z')) {
|
||||
layer_height += line.dist_Z();
|
||||
r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||
(GCodeReader &reader, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
if (line.extruding(reader)) {
|
||||
total_layer_length += line.dist_XY(reader);
|
||||
} else if (line.has(Z)) {
|
||||
layer_height += line.dist_Z(reader);
|
||||
if (!set_z) {
|
||||
z = line.new_Z();
|
||||
z = line.new_Z(reader);
|
||||
set_z = true;
|
||||
}
|
||||
}
|
||||
|
@ -59,23 +50,23 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
z -= layer_height;
|
||||
|
||||
std::string new_gcode;
|
||||
this->_reader.parse(gcode, [&new_gcode, &z, &layer_height, &total_layer_length]
|
||||
(GCodeReader &, GCodeReader::GCodeLine line) {
|
||||
if (line.cmd == "G1") {
|
||||
if (line.has('Z')) {
|
||||
this->_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length]
|
||||
(GCodeReader &reader, GCodeReader::GCodeLine line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
if (line.has_z()) {
|
||||
// If this is the initial Z move of the layer, replace it with a
|
||||
// (redundant) move to the last Z of previous layer.
|
||||
line.set('Z', _format_z(z));
|
||||
new_gcode += line.raw + '\n';
|
||||
line.set(reader, Z, z);
|
||||
new_gcode += line.raw() + '\n';
|
||||
return;
|
||||
} else {
|
||||
float dist_XY = line.dist_XY();
|
||||
float dist_XY = line.dist_XY(reader);
|
||||
if (dist_XY > 0) {
|
||||
// horizontal move
|
||||
if (line.extruding()) {
|
||||
if (line.extruding(reader)) {
|
||||
z += dist_XY * layer_height / total_layer_length;
|
||||
line.set('Z', _format_z(z));
|
||||
new_gcode += line.raw + '\n';
|
||||
line.set(reader, Z, z);
|
||||
new_gcode += line.raw() + '\n';
|
||||
}
|
||||
return;
|
||||
|
||||
|
@ -87,7 +78,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
}
|
||||
}
|
||||
}
|
||||
new_gcode += line.raw + '\n';
|
||||
new_gcode += line.raw() + '\n';
|
||||
});
|
||||
|
||||
return new_gcode;
|
||||
|
|
|
@ -13,7 +13,7 @@ class SpiralVase {
|
|||
SpiralVase(const PrintConfig &config)
|
||||
: enable(false), _config(&config)
|
||||
{
|
||||
this->_reader.Z = this->_config->z_offset;
|
||||
this->_reader.z() = this->_config->z_offset;
|
||||
this->_reader.apply_config(*this->_config);
|
||||
};
|
||||
std::string process_layer(const std::string &gcode);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void GCodeReader::apply_config(const GCodeConfig &config)
|
||||
|
@ -18,66 +20,89 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
|
|||
m_extrusion_axis = m_config.get_extrusion_axis()[0];
|
||||
}
|
||||
|
||||
void GCodeReader::parse(const std::string &gcode, callback_t callback)
|
||||
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
std::istringstream ss(gcode);
|
||||
std::string line;
|
||||
while (std::getline(ss, line))
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
void GCodeReader::parse_line(std::string line, callback_t callback)
|
||||
{
|
||||
GCodeLine gline(this);
|
||||
gline.raw = line;
|
||||
if (this->verbose)
|
||||
std::cout << line << std::endl;
|
||||
|
||||
// strip comment
|
||||
{
|
||||
size_t pos = line.find(';');
|
||||
if (pos != std::string::npos) {
|
||||
gline.comment = line.substr(pos+1);
|
||||
line.erase(pos);
|
||||
}
|
||||
}
|
||||
PROFILE_FUNC();
|
||||
|
||||
// command and args
|
||||
const char *c = ptr;
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
boost::split(args, line, boost::is_any_of(" "));
|
||||
|
||||
// first one is cmd
|
||||
gline.cmd = args.front();
|
||||
args.erase(args.begin());
|
||||
|
||||
for (std::string &arg : args) {
|
||||
if (arg.size() < 2) continue;
|
||||
gline.args.insert(std::make_pair(arg[0], arg.substr(1)));
|
||||
PROFILE_BLOCK(command_and_args);
|
||||
// Skip the whitespaces.
|
||||
command.first = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = command.second = skip_word(command.first);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
Axis axis = NUM_AXES;
|
||||
switch (*c) {
|
||||
case 'X': axis = X; break;
|
||||
case 'Y': axis = Y; break;
|
||||
case 'Z': axis = Z; break;
|
||||
case 'F': axis = F; break;
|
||||
default:
|
||||
if (*c == m_extrusion_axis)
|
||||
axis = E;
|
||||
break;
|
||||
}
|
||||
if (axis != NUM_AXES) {
|
||||
// Try to parse the numeric value.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
gline.m_axis[int(axis)] = float(v);
|
||||
gline.m_mask |= 1 << int(axis);
|
||||
c = pend;
|
||||
} else
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
} else
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
}
|
||||
|
||||
// convert extrusion axis
|
||||
if (m_extrusion_axis != 'E') {
|
||||
const auto it = gline.args.find(m_extrusion_axis);
|
||||
if (it != gline.args.end()) {
|
||||
std::swap(gline.args['E'], it->second);
|
||||
gline.args.erase(it);
|
||||
}
|
||||
if (gline.has(E) && m_config.use_relative_e_distances)
|
||||
m_position[E] = 0;
|
||||
|
||||
// Skip the rest of the line.
|
||||
for (; ! is_end_of_line(*c); ++ c);
|
||||
|
||||
// Copy the raw string including the comment, without the trailing newlines.
|
||||
if (c > ptr) {
|
||||
PROFILE_BLOCK(copy_raw_string);
|
||||
gline.m_raw.assign(ptr, c);
|
||||
}
|
||||
|
||||
if (gline.has('E') && m_config.use_relative_e_distances)
|
||||
this->E = 0;
|
||||
|
||||
if (callback) callback(*this, gline);
|
||||
|
||||
// update coordinates
|
||||
if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") {
|
||||
this->X = gline.new_X();
|
||||
this->Y = gline.new_Y();
|
||||
this->Z = gline.new_Z();
|
||||
this->E = gline.new_E();
|
||||
this->F = gline.new_F();
|
||||
|
||||
// Skip the trailing newlines.
|
||||
if (*c == '\r')
|
||||
++ c;
|
||||
if (*c == '\n')
|
||||
++ c;
|
||||
|
||||
if (m_verbose)
|
||||
std::cout << gline.m_raw << std::endl;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
if (*command.first == 'G') {
|
||||
int cmd_len = int(command.second - command.first);
|
||||
if ((cmd_len == 2 && (command.first[1] == '0' || command.first[1] == '1')) ||
|
||||
(cmd_len == 3 && command.first[1] == '9' && command.first[2] == '2')) {
|
||||
for (size_t i = 0; i < NUM_AXES; ++ i)
|
||||
if (gline.has(Axis(i)))
|
||||
this->m_position[i] = gline.value(Axis(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,22 +114,64 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
|||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
void GCodeReader::GCodeLine::set(char arg, std::string value)
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
const std::string space(" ");
|
||||
if (this->has(arg)) {
|
||||
size_t pos = this->raw.find(space + arg)+2;
|
||||
size_t end = this->raw.find(' ', pos+1);
|
||||
this->raw = this->raw.replace(pos, end-pos, value);
|
||||
} else {
|
||||
size_t pos = this->raw.find(' ');
|
||||
if (pos == std::string::npos) {
|
||||
this->raw += space + arg + value;
|
||||
} else {
|
||||
this->raw = this->raw.replace(pos, 0, space + arg + value);
|
||||
const char *c = m_raw.c_str();
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = skip_word(c);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
if (*c == axis) {
|
||||
// Try to parse the numeric value.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
value = float(v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
this->args[arg] = value;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GCodeReader::GCodeLine::set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(decimal_digits) << new_value;
|
||||
|
||||
char match[3] = " X";
|
||||
if (int(axis) < 3)
|
||||
match[1] += int(axis);
|
||||
else if (axis == F)
|
||||
match[1] = 'F';
|
||||
else {
|
||||
assert(axis == E);
|
||||
match[1] = reader.extrusion_axis();
|
||||
}
|
||||
|
||||
if (this->has(axis)) {
|
||||
size_t pos = m_raw.find(match)+2;
|
||||
size_t end = m_raw.find(' ', pos+1);
|
||||
m_raw = m_raw.replace(pos, end-pos, ss.str());
|
||||
} else {
|
||||
size_t pos = m_raw.find(' ');
|
||||
if (pos == std::string::npos)
|
||||
m_raw += std::string(match) + ss.str();
|
||||
else
|
||||
m_raw = m_raw.replace(pos, 0, std::string(match) + ss.str());
|
||||
}
|
||||
m_axis[axis] = new_value;
|
||||
m_mask |= 1 << int(axis);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,54 +11,127 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class GCodeReader {
|
||||
public:
|
||||
public:
|
||||
class GCodeLine {
|
||||
public:
|
||||
GCodeReader* reader;
|
||||
std::string raw;
|
||||
std::string cmd;
|
||||
std::string comment;
|
||||
std::map<char,std::string> args;
|
||||
|
||||
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
||||
|
||||
bool has(char arg) const { return this->args.count(arg) > 0; };
|
||||
float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); };
|
||||
float new_X() const { return this->has('X') ? float(atof(this->args.at('X').c_str())) : this->reader->X; };
|
||||
float new_Y() const { return this->has('Y') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; };
|
||||
float new_Z() const { return this->has('Z') ? float(atof(this->args.at('Z').c_str())) : this->reader->Z; };
|
||||
float new_E() const { return this->has('E') ? float(atof(this->args.at('E').c_str())) : this->reader->E; };
|
||||
float new_F() const { return this->has('F') ? float(atof(this->args.at('F').c_str())) : this->reader->F; };
|
||||
float dist_X() const { return this->new_X() - this->reader->X; };
|
||||
float dist_Y() const { return this->new_Y() - this->reader->Y; };
|
||||
float dist_Z() const { return this->new_Z() - this->reader->Z; };
|
||||
float dist_E() const { return this->new_E() - this->reader->E; };
|
||||
float dist_XY() const {
|
||||
float x = this->dist_X();
|
||||
float y = this->dist_Y();
|
||||
GCodeLine() { reset(); }
|
||||
void reset() { m_mask = 0; memset(m_axis, 0, sizeof(m_axis)); m_raw.clear(); }
|
||||
|
||||
const std::string& raw() const { return m_raw; }
|
||||
const std::string cmd() const {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
|
||||
return std::string(cmd, GCodeReader::skip_word(cmd));
|
||||
}
|
||||
const std::string comment() const
|
||||
{ size_t pos = m_raw.find(';'); return (pos == std::string::npos) ? "" : m_raw.substr(pos + 1); }
|
||||
|
||||
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
|
||||
float value(Axis axis) const { return m_axis[axis]; }
|
||||
bool has_value(char axis, float &value) const;
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
|
||||
float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); }
|
||||
float dist_X(const GCodeReader &reader) const { return this->has(X) ? (this->x() - reader.x()) : 0; }
|
||||
float dist_Y(const GCodeReader &reader) const { return this->has(Y) ? (this->y() - reader.y()) : 0; }
|
||||
float dist_Z(const GCodeReader &reader) const { return this->has(Z) ? (this->z() - reader.z()) : 0; }
|
||||
float dist_E(const GCodeReader &reader) const { return this->has(E) ? (this->e() - reader.e()) : 0; }
|
||||
float dist_XY(const GCodeReader &reader) const {
|
||||
float x = this->has(X) ? (this->x() - reader.x()) : 0;
|
||||
float y = this->has(Y) ? (this->y() - reader.y()) : 0;
|
||||
return sqrt(x*x + y*y);
|
||||
};
|
||||
bool extruding() const { return this->cmd == "G1" && this->dist_E() > 0; };
|
||||
bool retracting() const { return this->cmd == "G1" && this->dist_E() < 0; };
|
||||
bool travel() const { return this->cmd == "G1" && ! this->has('E'); };
|
||||
void set(char arg, std::string value);
|
||||
}
|
||||
bool cmd_is(const char *cmd_test) const {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
|
||||
int len = strlen(cmd_test);
|
||||
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
|
||||
}
|
||||
bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; }
|
||||
bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; }
|
||||
bool travel() const { return this->cmd_is("G1") && ! this->has(E); }
|
||||
void set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits = 3);
|
||||
|
||||
bool has_x() const { return this->has(X); }
|
||||
bool has_y() const { return this->has(Y); }
|
||||
bool has_z() const { return this->has(Z); }
|
||||
bool has_e() const { return this->has(E); }
|
||||
bool has_f() const { return this->has(F); }
|
||||
float x() const { return m_axis[X]; }
|
||||
float y() const { return m_axis[Y]; }
|
||||
float z() const { return m_axis[Z]; }
|
||||
float e() const { return m_axis[E]; }
|
||||
float f() const { return m_axis[F]; }
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
float m_axis[NUM_AXES];
|
||||
uint32_t m_mask;
|
||||
friend class GCodeReader;
|
||||
};
|
||||
|
||||
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
|
||||
|
||||
float X, Y, Z, E, F;
|
||||
bool verbose;
|
||||
callback_t callback;
|
||||
|
||||
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), m_extrusion_axis('E') {};
|
||||
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); }
|
||||
void apply_config(const GCodeConfig &config);
|
||||
void apply_config(const DynamicPrintConfig &config);
|
||||
void parse(const std::string &gcode, callback_t callback);
|
||||
void parse_line(std::string line, callback_t callback);
|
||||
|
||||
template<typename Callback>
|
||||
void parse_buffer(const std::string &buffer, Callback callback)
|
||||
{
|
||||
const char *ptr = buffer.c_str();
|
||||
GCodeLine gline;
|
||||
while (*ptr != 0) {
|
||||
gline.reset();
|
||||
ptr = this->parse_line(ptr, gline, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_buffer(const std::string &buffer)
|
||||
{ this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); }
|
||||
|
||||
template<typename Callback>
|
||||
const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback)
|
||||
{
|
||||
std::pair<const char*, const char*> cmd;
|
||||
const char *end = parse_line_internal(ptr, gline, cmd);
|
||||
callback(*this, gline);
|
||||
update_coordinates(gline, cmd);
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void parse_line(const std::string &line, Callback callback)
|
||||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
float x() const { return m_position[X]; }
|
||||
float& y() { return m_position[Y]; }
|
||||
float y() const { return m_position[Y]; }
|
||||
float& z() { return m_position[Z]; }
|
||||
float z() const { return m_position[Z]; }
|
||||
float& e() { return m_position[E]; }
|
||||
float e() const { return m_position[E]; }
|
||||
float& f() { return m_position[F]; }
|
||||
float f() const { return m_position[F]; }
|
||||
|
||||
char extrusion_axis() const { return m_extrusion_axis; }
|
||||
|
||||
private:
|
||||
const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
|
||||
static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
|
||||
static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; }
|
||||
static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); }
|
||||
static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); }
|
||||
static const char* skip_whitespaces(const char *c) { for (; is_whitespace(*c); ++ c); return c; }
|
||||
static const char* skip_word(const char *c) { for (; ! is_end_of_word(*c); ++ c); return c; }
|
||||
|
||||
GCodeConfig m_config;
|
||||
char m_extrusion_axis;
|
||||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,22 +2,323 @@
|
|||
#define slic3r_GCodeTimeEstimator_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "GCodeReader.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GCodeTimeEstimator : public GCodeReader {
|
||||
//
|
||||
// Some of the algorithms used by class GCodeTimeEstimator were inpired by
|
||||
// Cura Engine's class TimeEstimateCalculator
|
||||
// https://github.com/Ultimaker/CuraEngine/blob/master/src/timeEstimate.h
|
||||
//
|
||||
class GCodeTimeEstimator
|
||||
{
|
||||
public:
|
||||
float time = 0; // in seconds
|
||||
|
||||
void parse(const std::string &gcode);
|
||||
void parse_file(const std::string &file);
|
||||
|
||||
protected:
|
||||
float acceleration = 9000;
|
||||
void _parser(GCodeReader&, const GCodeReader::GCodeLine &line);
|
||||
static float _accelerated_move(double length, double v, double acceleration);
|
||||
};
|
||||
enum EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
Inches
|
||||
};
|
||||
|
||||
enum EAxis : unsigned char
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
E,
|
||||
Num_Axis
|
||||
};
|
||||
|
||||
enum EPositioningType : unsigned char
|
||||
{
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
private:
|
||||
struct Axis
|
||||
{
|
||||
float position; // mm
|
||||
float max_feedrate; // mm/s
|
||||
float max_acceleration; // mm/s^2
|
||||
float max_jerk; // mm/s
|
||||
};
|
||||
|
||||
struct Feedrates
|
||||
{
|
||||
float feedrate; // mm/s
|
||||
float axis_feedrate[Num_Axis]; // mm/s
|
||||
float abs_axis_feedrate[Num_Axis]; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
GCodeFlavor dialect;
|
||||
EUnits units;
|
||||
EPositioningType positioning_xyz_type;
|
||||
EPositioningType positioning_e_type;
|
||||
Axis axis[Num_Axis];
|
||||
float feedrate; // mm/s
|
||||
float acceleration; // mm/s^2
|
||||
float retract_acceleration; // mm/s^2
|
||||
float additional_time; // s
|
||||
float minimum_feedrate; // mm/s
|
||||
float minimum_travel_feedrate; // mm/s
|
||||
float extrude_factor_override_percentage;
|
||||
};
|
||||
|
||||
public:
|
||||
struct Block
|
||||
{
|
||||
struct FeedrateProfile
|
||||
{
|
||||
float entry; // mm/s
|
||||
float cruise; // mm/s
|
||||
float exit; // mm/s
|
||||
};
|
||||
|
||||
struct Trapezoid
|
||||
{
|
||||
float distance; // mm
|
||||
float accelerate_until; // mm
|
||||
float decelerate_after; // mm
|
||||
FeedrateProfile feedrate;
|
||||
|
||||
float acceleration_time(float acceleration) const;
|
||||
float cruise_time() const;
|
||||
float deceleration_time(float acceleration) const;
|
||||
float cruise_distance() const;
|
||||
|
||||
// This function gives the time needed to accelerate from an initial speed to reach a final distance.
|
||||
static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration);
|
||||
|
||||
// This function gives the final speed while accelerating at the given constant acceleration from the given initial speed along the given distance.
|
||||
static float speed_from_distance(float initial_feedrate, float distance, float acceleration);
|
||||
};
|
||||
|
||||
struct Flags
|
||||
{
|
||||
bool recalculate;
|
||||
bool nominal_length;
|
||||
};
|
||||
|
||||
Flags flags;
|
||||
|
||||
float delta_pos[Num_Axis]; // mm
|
||||
float acceleration; // mm/s^2
|
||||
float max_entry_speed; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
|
||||
FeedrateProfile feedrate;
|
||||
Trapezoid trapezoid;
|
||||
|
||||
// Returns the length of the move covered by this block, in mm
|
||||
float move_length() const;
|
||||
|
||||
// Returns true if this block is a retract/unretract move only
|
||||
float is_extruder_only_move() const;
|
||||
|
||||
// Returns true if this block is a move with no extrusion
|
||||
float is_travel_move() const;
|
||||
|
||||
// Returns the time spent accelerating toward cruise speed, in seconds
|
||||
float acceleration_time() const;
|
||||
|
||||
// Returns the time spent at cruise speed, in seconds
|
||||
float cruise_time() const;
|
||||
|
||||
// Returns the time spent decelerating from cruise speed, in seconds
|
||||
float deceleration_time() const;
|
||||
|
||||
// Returns the distance covered at cruise speed, in mm
|
||||
float cruise_distance() const;
|
||||
|
||||
// Calculates this block's trapezoid
|
||||
void calculate_trapezoid();
|
||||
|
||||
// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
|
||||
// acceleration within the allotted distance.
|
||||
static float max_allowable_speed(float acceleration, float target_velocity, float distance);
|
||||
|
||||
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration:
|
||||
static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration);
|
||||
|
||||
// This function gives you the point at which you must start braking (at the rate of -acceleration) if
|
||||
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
|
||||
// a total travel of distance. This can be used to compute the intersection point between acceleration and
|
||||
// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
|
||||
static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance);
|
||||
};
|
||||
|
||||
typedef std::vector<Block> BlocksList;
|
||||
|
||||
private:
|
||||
GCodeReader _parser;
|
||||
State _state;
|
||||
Feedrates _curr;
|
||||
Feedrates _prev;
|
||||
BlocksList _blocks;
|
||||
float _time; // s
|
||||
|
||||
public:
|
||||
GCodeTimeEstimator();
|
||||
|
||||
// Calculates the time estimate from the given gcode in string format
|
||||
void calculate_time_from_text(const std::string& gcode);
|
||||
|
||||
// Calculates the time estimate from the gcode contained in the file with the given filename
|
||||
void calculate_time_from_file(const std::string& file);
|
||||
|
||||
// Calculates the time estimate from the gcode contained in given list of gcode lines
|
||||
void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
|
||||
|
||||
// Adds the given gcode line
|
||||
void add_gcode_line(const std::string& gcode_line);
|
||||
|
||||
void add_gcode_block(const char *ptr);
|
||||
void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
|
||||
|
||||
// Calculates the time estimate from the gcode lines added using add_gcode_line()
|
||||
void calculate_time();
|
||||
|
||||
// Set current position on the given axis with the given value
|
||||
void set_axis_position(EAxis axis, float position);
|
||||
|
||||
void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec);
|
||||
void set_axis_max_acceleration(EAxis axis, float acceleration);
|
||||
void set_axis_max_jerk(EAxis axis, float jerk);
|
||||
|
||||
// Returns current position on the given axis
|
||||
float get_axis_position(EAxis axis) const;
|
||||
|
||||
float get_axis_max_feedrate(EAxis axis) const;
|
||||
float get_axis_max_acceleration(EAxis axis) const;
|
||||
float get_axis_max_jerk(EAxis axis) const;
|
||||
|
||||
void set_feedrate(float feedrate_mm_sec);
|
||||
float get_feedrate() const;
|
||||
|
||||
void set_acceleration(float acceleration_mm_sec2);
|
||||
float get_acceleration() const;
|
||||
|
||||
void set_retract_acceleration(float acceleration_mm_sec2);
|
||||
float get_retract_acceleration() const;
|
||||
|
||||
void set_minimum_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_feedrate() const;
|
||||
|
||||
void set_minimum_travel_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_travel_feedrate() const;
|
||||
|
||||
void set_extrude_factor_override_percentage(float percentage);
|
||||
float get_extrude_factor_override_percentage() const;
|
||||
|
||||
void set_dialect(GCodeFlavor dialect);
|
||||
GCodeFlavor get_dialect() const;
|
||||
|
||||
void set_units(EUnits units);
|
||||
EUnits get_units() const;
|
||||
|
||||
void set_positioning_xyz_type(EPositioningType type);
|
||||
EPositioningType get_positioning_xyz_type() const;
|
||||
|
||||
void set_positioning_e_type(EPositioningType type);
|
||||
EPositioningType get_positioning_e_type() const;
|
||||
|
||||
void add_additional_time(float timeSec);
|
||||
void set_additional_time(float timeSec);
|
||||
float get_additional_time() const;
|
||||
|
||||
void set_default();
|
||||
|
||||
// Call this method before to start adding lines using add_gcode_line() when reusing an instance of GCodeTimeEstimator
|
||||
void reset();
|
||||
|
||||
// Returns the estimated time, in seconds
|
||||
float get_time() const;
|
||||
|
||||
// Returns the estimated time, in format HHh MMm SSs
|
||||
std::string get_time_hms() const;
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _reset_blocks();
|
||||
|
||||
// Calculates the time estimate
|
||||
void _calculate_time();
|
||||
|
||||
// Processes the given gcode line
|
||||
void _process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Move
|
||||
void _processG1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Dwell
|
||||
void _processG4(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Inches
|
||||
void _processG20(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Millimeters
|
||||
void _processG21(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Move to Origin (Home)
|
||||
void _processG28(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Absolute Positioning
|
||||
void _processG90(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Relative Positioning
|
||||
void _processG91(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Position
|
||||
void _processG92(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Sleep or Conditional stop
|
||||
void _processM1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to absolute mode
|
||||
void _processM82(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to relative mode
|
||||
void _processM83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Extruder Temperature and Wait
|
||||
void _processM109(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set max printing acceleration
|
||||
void _processM201(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set maximum feedrate
|
||||
void _processM203(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set default acceleration
|
||||
void _processM204(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Advanced settings
|
||||
void _processM205(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extrude factor override percentage
|
||||
void _processM221(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set allowable instantaneous speed change
|
||||
void _processM566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void _simulate_st_synchronize();
|
||||
|
||||
void _forward_pass();
|
||||
void _reverse_pass();
|
||||
|
||||
void _planner_forward_pass_kernel(Block& prev, Block& curr);
|
||||
void _planner_reverse_pass_kernel(Block& curr, Block& next);
|
||||
|
||||
void _recalculate_trapezoids();
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ std::string GCodeWriter::preamble()
|
|||
gcode << "G21 ; set units to millimeters\n";
|
||||
gcode << "G90 ; use absolute coordinates\n";
|
||||
}
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) {
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfMarlin) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) {
|
||||
if (this->config.use_relative_e_distances) {
|
||||
gcode << "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
|
|
|
@ -567,8 +567,8 @@ std::string Print::validate() const
|
|||
if (std::abs(dmr - 0.4) > EPSILON)
|
||||
return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
|
||||
#endif
|
||||
if (this->config.gcode_flavor != gcfRepRap)
|
||||
return "The Wipe Tower is currently only supported for the RepRap (Marlin / Sprinter) G-code flavor.";
|
||||
if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin)
|
||||
return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.";
|
||||
if (! this->config.use_relative_e_distances)
|
||||
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
||||
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
|
||||
|
|
|
@ -233,8 +233,9 @@ public:
|
|||
PrintRegionPtrs regions;
|
||||
PlaceholderParser placeholder_parser;
|
||||
// TODO: status_cb
|
||||
std::string estimated_print_time;
|
||||
double total_used_filament, total_extruded_volume, total_cost, total_weight;
|
||||
std::map<size_t,float> filament_stats;
|
||||
std::map<size_t, float> filament_stats;
|
||||
PrintState<PrintStep, psCount> state;
|
||||
|
||||
// ordered collections of extrusion paths to build skirt loops and brim
|
||||
|
|
|
@ -653,21 +653,23 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->enum_values.push_back("repetier");
|
||||
def->enum_values.push_back("teacup");
|
||||
def->enum_values.push_back("makerware");
|
||||
def->enum_values.push_back("marlin");
|
||||
def->enum_values.push_back("sailfish");
|
||||
def->enum_values.push_back("mach3");
|
||||
def->enum_values.push_back("machinekit");
|
||||
def->enum_values.push_back("smoothie");
|
||||
def->enum_values.push_back("no-extrusion");
|
||||
def->enum_labels.push_back("RepRap (Marlin/Sprinter)");
|
||||
def->enum_labels.push_back("RepRap/Sprinter");
|
||||
def->enum_labels.push_back("Repetier");
|
||||
def->enum_labels.push_back("Teacup");
|
||||
def->enum_labels.push_back("MakerWare (MakerBot)");
|
||||
def->enum_labels.push_back("Marlin");
|
||||
def->enum_labels.push_back("Sailfish (MakerBot)");
|
||||
def->enum_labels.push_back("Mach3/LinuxCNC");
|
||||
def->enum_labels.push_back("Machinekit");
|
||||
def->enum_labels.push_back("Smoothie");
|
||||
def->enum_labels.push_back("No extrusion");
|
||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
|
||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfMarlin);
|
||||
|
||||
def = this->add("infill_acceleration", coFloat);
|
||||
def->label = "Infill";
|
||||
|
@ -1920,6 +1922,7 @@ std::string FullPrintConfig::validate()
|
|||
if (this->use_firmware_retraction.value &&
|
||||
this->gcode_flavor.value != gcfSmoothie &&
|
||||
this->gcode_flavor.value != gcfRepRap &&
|
||||
this->gcode_flavor.value != gcfMarlin &&
|
||||
this->gcode_flavor.value != gcfMachinekit &&
|
||||
this->gcode_flavor.value != gcfRepetier)
|
||||
return "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware";
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
namespace Slic3r {
|
||||
|
||||
enum GCodeFlavor {
|
||||
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion, gcfSmoothie, gcfRepetier,
|
||||
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
|
||||
gcfSmoothie, gcfNoExtrusion,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
|
@ -50,6 +51,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
|
|||
keys_map["repetier"] = gcfRepetier;
|
||||
keys_map["teacup"] = gcfTeacup;
|
||||
keys_map["makerware"] = gcfMakerWare;
|
||||
keys_map["marlin"] = gcfMarlin;
|
||||
keys_map["sailfish"] = gcfSailfish;
|
||||
keys_map["smoothie"] = gcfSmoothie;
|
||||
keys_map["mach3"] = gcfMach3;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.38.5"
|
||||
#define SLIC3R_VERSION "1.39.0"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef long coord_t;
|
||||
|
@ -102,7 +102,7 @@ inline std::string debug_out_path(const char *name, ...)
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
enum Axis { X=0, Y, Z };
|
||||
enum Axis { X=0, Y, Z, E, F, NUM_AXES };
|
||||
|
||||
template <class T>
|
||||
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
|
||||
|
|
|
@ -29,7 +29,7 @@ void AppConfig::set_defaults()
|
|||
{
|
||||
// Reset the empty fields to defaults.
|
||||
if (get("autocenter").empty())
|
||||
set("autocenter", "1");
|
||||
set("autocenter", "0");
|
||||
// Disable background processing by default as it is not stable.
|
||||
if (get("background_processing").empty())
|
||||
set("background_processing", "0");
|
||||
|
|
|
@ -228,4 +228,42 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
|||
return out;
|
||||
}
|
||||
|
||||
std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &preset_bundle)
|
||||
{
|
||||
const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config;
|
||||
const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config;
|
||||
|
||||
float layer_height = float(print_config.opt_float("layer_height"));
|
||||
int num_perimeters = print_config.opt_int("perimeters");
|
||||
bool thin_walls = print_config.opt_bool("thin_walls");
|
||||
float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0));
|
||||
|
||||
Flow external_perimeter_flow = Flow::new_from_config_width(
|
||||
frExternalPerimeter,
|
||||
*print_config.opt<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_width"),
|
||||
nozzle_diameter, layer_height, false);
|
||||
Flow perimeter_flow = Flow::new_from_config_width(
|
||||
frPerimeter,
|
||||
*print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"),
|
||||
nozzle_diameter, layer_height, false);
|
||||
|
||||
std::string out;
|
||||
if (num_perimeters > 0) {
|
||||
int num_lines = std::min(num_perimeters * 2, 10);
|
||||
char buf[256];
|
||||
sprintf(buf, "Recommended object thin wall thickness for layer height %.2f and ", layer_height);
|
||||
out += buf;
|
||||
// Start with the width of two closely spaced
|
||||
double width = external_perimeter_flow.width + external_perimeter_flow.spacing();
|
||||
for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) {
|
||||
if (i > 2)
|
||||
out += ", ";
|
||||
sprintf(buf, "%d lines: %.2lf mm", i, width);
|
||||
out += buf;
|
||||
width += perimeter_flow.spacing() * (thin_walls ? 1.f : 2.f);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -13,11 +13,16 @@ class PresetHints
|
|||
public:
|
||||
// Produce a textual description of the cooling logic of a currently active filament.
|
||||
static std::string cooling_description(const Preset &preset);
|
||||
|
||||
// Produce a textual description of the maximum flow achived for the current configuration
|
||||
// (the current printer, filament and print settigns).
|
||||
// This description will be useful for getting a gut feeling for the maximum volumetric
|
||||
// print speed achievable with the extruder.
|
||||
static std::string maximum_volumetric_flow_description(const PresetBundle &preset_bundle);
|
||||
|
||||
// Produce a textual description of a recommended thin wall thickness
|
||||
// from the provided number of perimeters and the external / internal perimeter width.
|
||||
static std::string recommended_thin_wall_thickness(const PresetBundle &preset_bundle);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue