Merge branch 'main' into feat/plugin-lifecycle-evts

This commit is contained in:
Ian Chua
2026-09-22 14:21:36 +08:00
1052 changed files with 140050 additions and 5263 deletions
+24
View File
@@ -283,9 +283,17 @@ void AppConfig::set_defaults()
set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap));
}
if (get(SETTING_OPENGL_SCENE_CACHE).empty())
set_bool(SETTING_OPENGL_SCENE_CACHE, true);
if (get(SETTING_OPENGL_SKIP_IDENTICAL_FRAMES).empty())
set_bool(SETTING_OPENGL_SKIP_IDENTICAL_FRAMES, true);
// The getter already defaults, parses and clamps; write back what it resolves to.
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(get_plugin_pages_visible_count()));
set(SETTING_SPEED_DIAL_RECENT_COUNT, std::to_string(get_speed_dial_recent_count()));
if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty())
set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false);
@@ -1684,6 +1692,22 @@ int AppConfig::get_plugin_pages_visible_count() const
return std::clamp(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MIN, PLUGIN_PAGES_VISIBLE_COUNT_MAX);
}
int AppConfig::get_speed_dial_recent_count() const
{
std::string value = get(SETTING_SPEED_DIAL_RECENT_COUNT);
if (value.empty())
return SPEED_DIAL_RECENT_COUNT_DEFAULT;
int recent_count = SPEED_DIAL_RECENT_COUNT_DEFAULT;
try {
recent_count = std::stoi(value);
}
catch (...) {
return SPEED_DIAL_RECENT_COUNT_DEFAULT;
}
return std::clamp(recent_count, SPEED_DIAL_RECENT_COUNT_MIN, SPEED_DIAL_RECENT_COUNT_MAX);
}
std::vector<std::string> AppConfig::get_skipped_network_versions() const
{
std::vector<std::string> result;
+10
View File
@@ -33,6 +33,8 @@ using namespace nlohmann;
#define SETTING_OPENGL_AA_SAMPLES "opengl_antialiasing_samples"
#define SETTING_OPENGL_FXAA_ENABLED "opengl_fxaa_enabled"
#define SETTING_OPENGL_FPS_CAP "opengl_fps_cap"
#define SETTING_OPENGL_SCENE_CACHE "opengl_scene_cache"
#define SETTING_OPENGL_SKIP_IDENTICAL_FRAMES "opengl_skip_identical_frames"
#define SETTING_OPENGL_SHOW_FPS_OVERLAY "opengl_show_fps_overlay"
#define SETTING_OPENGL_REALISTIC_MODE "opengl_realistic_mode"
#define SETTING_OPENGL_REALISTIC_PHONG "opengl_realistic_phong"
@@ -46,6 +48,11 @@ using namespace nlohmann;
#define PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT 5
#define PLUGIN_PAGES_VISIBLE_COUNT_MAX 10
#define SETTING_SPEED_DIAL_RECENT_COUNT "speed_dial_recent_count"
#define SPEED_DIAL_RECENT_COUNT_MIN 0
#define SPEED_DIAL_RECENT_COUNT_DEFAULT 5
#define SPEED_DIAL_RECENT_COUNT_MAX 10
#if defined(_WIN32) || defined(_WIN64)
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"
#else
@@ -394,6 +401,9 @@ public:
// dropdown on the last tab.
int get_plugin_pages_visible_count() const;
// Number of recently launched actions shown at the top of the Speed Dial; 0 hides them.
int get_speed_dial_recent_count() const;
std::vector<std::string> get_skipped_network_versions() const;
void add_skipped_network_version(const std::string& version);
bool is_network_version_skipped(const std::string& version) const;
+3 -1
View File
@@ -776,7 +776,9 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
{
// Get stored option value.
const ConfigOption *raw_opt = this->option(opt_key);
assert(raw_opt != nullptr);
// Mirror the single-arg overload — assert() is a no-op under NDEBUG.
if (raw_opt == nullptr)
throw ConfigurationError("ConfigBase::get_abs_value(): \"" + opt_key + "\" is not defined");
if (raw_opt->type() != coFloatOrPercent)
throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
// Compute absolute value.
+11 -3
View File
@@ -2379,9 +2379,9 @@ namespace DoExport {
static const unsigned int MAX_TAGS_COUNT = 5;
std::vector<std::pair<std::string, std::string>> ret;
auto check = [&ret](const std::string& source, const std::string& gcode) {
auto check = [&ret, is_bbl_printer = print.is_BBL_printer()](const std::string& source, const std::string& gcode) {
std::vector<std::string> tags;
if (GCodeProcessor::contains_reserved_tags(gcode, MAX_TAGS_COUNT, tags)) {
if (GCodeProcessor::contains_reserved_tags(gcode, MAX_TAGS_COUNT, tags, is_bbl_printer)) {
if (!tags.empty()) {
size_t i = 0;
while (ret.size() < MAX_TAGS_COUNT && i < tags.size()) {
@@ -3073,6 +3073,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// file_start_gcode runs before the parser copy that normally restores these, so set them here.
PlaceholderParser::update_timestamp(top_config);
PlaceholderParser::update_user_name(top_config);
top_config.set_key_value("print_time_total_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Total_Sec_Placeholder)));
top_config.set_key_value("print_time_day", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Day_Placeholder)));
top_config.set_key_value("print_time_hour", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Hour_Placeholder)));
top_config.set_key_value("print_time_minute", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Minute_Placeholder)));
top_config.set_key_value("print_time_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Sec_Placeholder)));
top_config.set_key_value("used_filament_length", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder)));
std::string top_gcode = print.placeholder_parser().process(top_gcode_template, 0, &top_config);
@@ -3702,6 +3706,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->placeholder_parser().set("hold_chamber_temp_for_flat_print", new ConfigOptionBool(hold_chamber_temp_for_flat_print));
}
this->placeholder_parser().set("print_time_total_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Total_Sec_Placeholder)));
this->placeholder_parser().set("print_time_day", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Day_Placeholder)));
this->placeholder_parser().set("print_time_hour", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Hour_Placeholder)));
this->placeholder_parser().set("print_time_minute", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Minute_Placeholder)));
this->placeholder_parser().set("print_time_sec", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Print_Time_Sec_Placeholder)));
this->placeholder_parser().set("used_filament_length", new ConfigOptionString(GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Used_Filament_Length_Placeholder)));
@@ -5669,7 +5677,7 @@ LayerResult GCode::process_layer(
m_layer = &layer;
m_object_layer_over_raft = false;
if (!m_config.time_lapse_gcode.value.empty() && !is_BBL_Printer()) {
if (!need_insert_timelapse_gcode_for_traditional && !m_config.time_lapse_gcode.value.empty() && !is_BBL_Printer()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
+1
View File
@@ -710,6 +710,7 @@ private:
// Always check gcode placeholders when building in debug mode.
#if !defined(NDEBUG)
#undef ORCA_CHECK_GCODE_PLACEHOLDERS
#define ORCA_CHECK_GCODE_PLACEHOLDERS 1
#endif
+72 -10
View File
@@ -75,6 +75,10 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
" WIPE_TOWER_START",
" WIPE_TOWER_END",
" PA_CHANGE:",
"@PRINT_TIME_TOTAL_SEC@",
"@PRINT_TIME_DAY@",
"@PRINT_TIME_HOUR@",
"@PRINT_TIME_MINUTE@",
"@PRINT_TIME_SEC@",
"@USED_FILAMENT_LENGTH@"
};
@@ -98,6 +102,10 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
" WIPE_TOWER_START",
" WIPE_TOWER_END",
" PA_CHANGE:",
"@PRINT_TIME_TOTAL_SEC@",
"@PRINT_TIME_DAY@",
"@PRINT_TIME_HOUR@",
"@PRINT_TIME_MINUTE@",
"@PRINT_TIME_SEC@",
"@USED_FILAMENT_LENGTH@"
};
@@ -1215,22 +1223,76 @@ void GCodeProcessor::run_post_process()
return ret;
};
// Process inline placeholders (print_time_sec and used_filament_length)
// Process inline placeholders (print_time_total_sec, print_time_day, print_time_hour, print_time_minute, print_time_sec and used_filament_length)
auto process_inline_placeholders = [&](std::string& gcode_line) {
bool processed = false;
const std::string& print_time_placeholder = reserved_tag(ETags::Print_Time_Sec_Placeholder);
const std::string& print_time_total_placeholder = reserved_tag(ETags::Print_Time_Total_Sec_Placeholder);
const std::string& print_time_day_placeholder = reserved_tag(ETags::Print_Time_Day_Placeholder);
const std::string& print_time_hour_placeholder = reserved_tag(ETags::Print_Time_Hour_Placeholder);
const std::string& print_time_minute_placeholder = reserved_tag(ETags::Print_Time_Minute_Placeholder);
const std::string& print_time_sec_placeholder = reserved_tag(ETags::Print_Time_Sec_Placeholder);
const std::string& used_filament_placeholder = reserved_tag(ETags::Used_Filament_Length_Placeholder);
// Replace print_time_sec
size_t pos = gcode_line.find(print_time_placeholder);
double print_time_total_sec = m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time;
if (print_time_total_sec < 0.0)
print_time_total_sec = 0.0;
int total_seconds = static_cast<int>(print_time_total_sec);
int print_time_day = total_seconds / 86400;
int day_remainder_seconds = total_seconds % 86400;
int print_time_hour = day_remainder_seconds / 3600;
int print_time_minute = (day_remainder_seconds % 3600) / 60;
int print_time_sec = day_remainder_seconds % 60;
// Replace print_time_total_sec
size_t pos = gcode_line.find(print_time_total_placeholder);
while (pos != std::string::npos) {
double print_time_sec = m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time;
char buf[64];
sprintf(buf, "%.2f", print_time_sec);
gcode_line.replace(pos, print_time_placeholder.length(), buf);
sprintf(buf, "%.2f", print_time_total_sec);
gcode_line.replace(pos, print_time_total_placeholder.length(), buf);
processed = true;
pos = gcode_line.find(print_time_placeholder, pos + strlen(buf));
pos = gcode_line.find(print_time_total_placeholder, pos + strlen(buf));
}
// Replace print_time_day
pos = gcode_line.find(print_time_day_placeholder);
while (pos != std::string::npos) {
char buf[64];
sprintf(buf, "%d", print_time_day);
gcode_line.replace(pos, print_time_day_placeholder.length(), buf);
processed = true;
pos = gcode_line.find(print_time_day_placeholder, pos + strlen(buf));
}
// Replace print_time_hour
pos = gcode_line.find(print_time_hour_placeholder);
while (pos != std::string::npos) {
char buf[64];
sprintf(buf, "%d", print_time_hour);
gcode_line.replace(pos, print_time_hour_placeholder.length(), buf);
processed = true;
pos = gcode_line.find(print_time_hour_placeholder, pos + strlen(buf));
}
// Replace print_time_minute
pos = gcode_line.find(print_time_minute_placeholder);
while (pos != std::string::npos) {
char buf[64];
sprintf(buf, "%d", print_time_minute);
gcode_line.replace(pos, print_time_minute_placeholder.length(), buf);
processed = true;
pos = gcode_line.find(print_time_minute_placeholder, pos + strlen(buf));
}
// Replace print_time_sec
pos = gcode_line.find(print_time_sec_placeholder);
while (pos != std::string::npos) {
char buf[64];
sprintf(buf, "%d", print_time_sec);
gcode_line.replace(pos, print_time_sec_placeholder.length(), buf);
processed = true;
pos = gcode_line.find(print_time_sec_placeholder, pos + strlen(buf));
}
// Replace used_filament_length
@@ -2609,7 +2671,7 @@ bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string
return ret;
}
bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag)
bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag, bool is_bbl_printer)
{
max_count = std::max(max_count, 1U);
@@ -2618,7 +2680,7 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
CNumericLocalesSetter locales_setter;
GCodeReader parser;
auto& _tags = s_IsBBLPrinter ? Reserved_Tags : Reserved_Tags_compatible;
auto& _tags = is_bbl_printer ? Reserved_Tags : Reserved_Tags_compatible;
parser.parse_buffer(gcode, [&ret, &found_tag, max_count, _tags](GCodeReader& parser, const GCodeReader::GCodeLine& line) {
std::string comment = line.raw();
if (comment.length() > 2 && comment.front() == ';') {
+5 -3
View File
@@ -512,6 +512,10 @@ class Print;
Wipe_Tower_Start,
Wipe_Tower_End,
PA_Change,
Print_Time_Total_Sec_Placeholder,
Print_Time_Day_Placeholder,
Print_Time_Hour_Placeholder,
Print_Time_Minute_Placeholder,
Print_Time_Sec_Placeholder,
Used_Filament_Length_Placeholder,
};
@@ -521,7 +525,7 @@ class Print;
static bool contains_reserved_tag(const std::string& gcode, std::string& found_tag);
// checks the given gcode for reserved tags and returns true when finding any
// (the first max_count found tags are returned into found_tag)
static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag);
static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector<std::string>& found_tag, bool is_bbl_printer);
static int get_gcode_last_filament(const std::string &gcode_str);
static bool get_last_z_from_gcode(const std::string& gcode_str, double& z);
@@ -1543,5 +1547,3 @@ class Print;
} /* namespace Slic3r */
#endif /* slic3r_GCodeProcessor_hpp_ */
+10 -9
View File
@@ -107,6 +107,12 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT
// normalize_fdm_2 clearing enable_prime_tower. Its mixed-filament case is not modelled.
const bool need_wipe_tower = smooth_timelapse || wrapping;
// Fewer than two filaments cannot make a tool change, so only wrapping detection or smooth
// timelapse print a tower then. The flush volume is no proof of one: it is read from the
// matrix of every configured slot, nonzero even when a single one of them is used.
if (filaments_cnt < 2 && !need_wipe_tower)
return footprint;
// A tower printed for one of the reasons above has no tool change to purge for; both
// planners give it the idle depth below and nothing more.
const size_t purge_count = filaments_cnt > 1 ? (dual_nozzle ? filaments_cnt : filaments_cnt - 1) : 0;
@@ -151,14 +157,6 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT
purges[longest_ramming].filament_change_length = float(float_at("filament_change_length", filament_ids[longest_ramming], 0.) * double(nozzles.size() - 1));
}
// Both wall types decide this together: over-reserving only wastes bed area, but reporting
// no tower for one that is built collapses the validation hull to a point.
// A tool change is a reason on its own (see the base commit); Type1 already reserves
// per filament, Type2 has only the volume, which can resolve to zero.
const bool has_purge = type1 ? !purges.empty() : volume > EPSILON;
if (!has_purge && filaments_cnt < 2 && !need_wipe_tower)
return footprint;
const double min_depth = WipeTower::get_limit_depth_by_height(float(max_object_height));
const float perimeter_width = float(nozzle_diameter) * 1.25f; // Width_To_Nozzle_Ratio
// With nothing to purge, plan_tower_new sizes the tower for wrapping detection or the
@@ -171,7 +169,10 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeT
if (!purges.empty())
side = WipeTower::estimate_rib_tower_bbox_side(purges, float(width), float(layer_height), float(nozzle_diameter), float(extra_spacing), float(rib_width), float(extra_rib_length), float(max_object_height));
else {
const double square = has_purge ? std::sqrt(volume / layer_height * extra_spacing) : idle_depth;
// Type2 squares the tower from its purge volume; Type1 with no purge list (a lone
// filament kept for timelapse or wrapping) sizes for the idle depth.
const bool has_purge = !type1 && volume > EPSILON;
const double square = has_purge ? std::sqrt(volume / layer_height * extra_spacing) : idle_depth;
side = WipeTower::rib_footprint_side(float(square), float(square), float(rib_width), float(extra_rib_length), float(max_object_height));
}
footprint.width = footprint.depth = side;
+7 -3
View File
@@ -120,9 +120,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
fill_no_overlap
);
if (this->layer()->lower_layer != nullptr)
// Cummulative sum of polygons over all the regions.
g.lower_slices = &this->layer()->lower_layer->lslices;
if (this->layer()->upper_layer != NULL)
g.upper_slices = &this->layer()->upper_layer->lslices;
@@ -135,6 +132,13 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
g.overhang_flow = this->bridging_flow(frPerimeter, object_config.thick_bridges);
g.solid_infill_flow = this->flow(frSolidInfill);
// Cumulative sum of polygons over all the regions, less what the lower layer could not print.
ExPolygons lower_slices;
if (this->layer()->lower_layer != nullptr) {
lower_slices = g.printable_slices(this->layer()->lower_layer->lslices);
g.lower_slices = &lower_slices;
}
if (this->layer()->object()->config().wall_generator.value == PerimeterGeneratorType::Arachne && !spiral_mode)
g.process_arachne();
else
+15
View File
@@ -2885,6 +2885,21 @@ bool PerimeterGeneratorLoop::is_internal_contour() const
return true;
}
// ORCA: Arachne drops features below min_feature_size, classic builds nothing thinner than a third of the
// nozzle. Both describe the layer below, a union of regions sharing neither nozzle nor generator, so every
// ambiguity resolves low: it may keep a sliver that was never printed, but it never drops one that was.
ExPolygons PerimeterGenerator::printable_slices(const ExPolygons &slices) const
{
double min_width = *std::min_element(print_config->nozzle_diameter.values.begin(),
print_config->nozzle_diameter.values.end()) / 3.;
if (object_config->wall_generator.value == PerimeterGeneratorType::Arachne) {
const double min_feature_size = Arachne::make_paths_params(layer_id, *object_config, *print_config).min_feature_size;
// Spiral vase can put a classic layer under an Arachne one, so there both limits apply.
min_width = print_config->spiral_mode ? std::min(min_width, min_feature_size) : min_feature_size;
}
return min_width > EPSILON ? opening_ex(slices, float(scale_(min_width / 2.))) : slices;
}
std::vector<Polygons> PerimeterGenerator::generate_lower_polygons_series(float width)
{
float nozzle_diameter = print_config->nozzle_diameter.get_at(config->outer_wall_filament_id - 1);
+2
View File
@@ -149,6 +149,8 @@ public:
//BBS
double smaller_width_ext_mm3_per_mm() const { return m_ext_mm3_per_mm_smaller_width; }
Polygons lower_slices_polygons() const { return m_lower_slices_polygons; }
// ORCA: the slices less the slivers the wall generator prints nothing for, so they never count as support.
ExPolygons printable_slices(const ExPolygons &slices) const;
private:
std::vector<Polygons> generate_lower_polygons_series(float width);
+22 -3
View File
@@ -152,6 +152,7 @@ static t_config_enum_values s_keys_map_PrintHostType {
{ "octoprint", htOctoPrint },
{ "crealityprint", htCrealityPrint },
{ "duet", htDuet },
{ "ultimaker", htUltiMaker },
{ "flashair", htFlashAir },
{ "astrobox", htAstroBox },
{ "repetier", htRepetier },
@@ -5401,6 +5402,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("prusaconnect");
def->enum_values.push_back("octoprint");
def->enum_values.push_back("duet");
def->enum_values.push_back("ultimaker");
def->enum_values.push_back("flashair");
def->enum_values.push_back("astrobox");
def->enum_values.push_back("repetier");
@@ -5417,6 +5419,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("PrusaConnect");
def->enum_labels.push_back("Octo/Klipper");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("UltiMaker");
def->enum_labels.push_back("FlashAir");
def->enum_labels.push_back("AstroBox");
def->enum_labels.push_back("Repetier");
@@ -6607,7 +6610,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("G-code written at the very top of the output file, before any other content. "
"Useful for adding metadata that printer firmware reads from the first lines of the file "
"(e.g. estimated print time, filament usage). "
"Supports placeholders like {print_time_sec} and {used_filament_length}.");
"Supports placeholders like {print_time_total_sec}, {print_time_day}, {print_time_hour}, {print_time_minute}, {print_time_sec} and {used_filament_length}.");
def->multiline = true;
def->full_width = true;
def->height = 8;
@@ -12556,10 +12559,26 @@ PrintStatisticsConfigDef::PrintStatisticsConfigDef()
def->label = L("Used filament");
def->tooltip = L("Total length of filament used in the print.");
def = this->add("print_time_sec", coString);
def->label = L("Print time (seconds)");
def = this->add("print_time_total_sec", coString);
def->label = L("Print time (total seconds)");
def->tooltip = L("Total estimated print time in seconds. Replaced with actual value during post-processing.");
def = this->add("print_time_day", coString);
def->label = L("Print time (days component)");
def->tooltip = L("Estimated print time day component (normal mode). Replaced with actual value during post-processing.");
def = this->add("print_time_hour", coString);
def->label = L("Print time (hours component)");
def->tooltip = L("Estimated print time hour component (normal mode). Replaced with actual value during post-processing.");
def = this->add("print_time_minute", coString);
def->label = L("Print time (minutes component)");
def->tooltip = L("Estimated print time minute component (normal mode). Replaced with actual value during post-processing.");
def = this->add("print_time_sec", coString);
def->label = L("Print time (seconds component)");
def->tooltip = L("Estimated print time second component (normal mode). Replaced with actual value during post-processing.");
def = this->add("used_filament_length", coString);
def->label = L("Filament length (meters)");
def->tooltip = L("Total filament length used in meters. Replaced with actual value during post-processing.");
+1 -1
View File
@@ -98,7 +98,7 @@ enum class WipeTowerType {
};
enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink, ht3DPrinterOS, htMoonraker
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htUltiMaker, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink, ht3DPrinterOS, htMoonraker
};
enum AuthorizationType {
+14 -3
View File
@@ -11,12 +11,23 @@ namespace Slic3r {
float CalibPressureAdvance::find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height, int extruder_id, int filament_idx)
{
const double general_suggested_min_speed = 100.0;
double filament_max_volumetric_speed = config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(filament_idx);
// Read defensively — CLI callers may hand us a config missing optional keys.
auto vector_at = [&config](const char *key, int idx) -> double {
if (const auto *o = config.option<ConfigOptionFloats>(key)) return o->get_at(idx);
const ConfigOptionDef *d = config.def() ? config.def()->get(key) : nullptr;
return (d && d->default_value) ? d->get_default_value<ConfigOptionFloats>()->get_at(idx) : 0.0;
};
auto nullable_at = [&config](const char *key, int idx) -> double {
if (const auto *o = config.option<ConfigOptionFloatsNullable>(key)) return o->get_at(idx);
const ConfigOptionDef *d = config.def() ? config.def()->get(key) : nullptr;
return (d && d->default_value) ? d->get_default_value<ConfigOptionFloatsNullable>()->get_at(idx) : 0.0;
};
double filament_max_volumetric_speed = vector_at("filament_max_volumetric_speed", filament_idx);
// todo multi_extruders:
const float nozzle_diameter = config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(extruder_id);
const float nozzle_diameter = vector_at("nozzle_diameter", extruder_id);
if (line_width <= 0.) line_width = Flow::auto_extrusion_width(frPerimeter, nozzle_diameter);
Flow pattern_line = Flow(line_width, layer_height, nozzle_diameter);
auto pa_speed = std::min(std::max(general_suggested_min_speed, config.option<ConfigOptionFloatsNullable>("outer_wall_speed")->get_at(extruder_id)),
auto pa_speed = std::min(std::max(general_suggested_min_speed, nullable_at("outer_wall_speed", extruder_id)),
filament_max_volumetric_speed / pattern_line.mm3_per_mm());
return std::floor(pa_speed);