mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 18:25:54 +08:00
Merge branch 'master' into fs_dir_per_glyph
This commit is contained in:
commit
5d37932b4d
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -98,7 +98,7 @@ src/slic3r/Utils/Process.cpp
|
||||
src/slic3r/Utils/Repetier.cpp
|
||||
src/slic3r/Config/Snapshot.cpp
|
||||
src/libslic3r/GCode.cpp
|
||||
src/libslic3r/ExtrusionEntity.cpp
|
||||
src/libslic3r/ExtrusionRole.cpp
|
||||
src/libslic3r/Flow.cpp
|
||||
src/libslic3r/Format/3mf.cpp
|
||||
src/libslic3r/Format/AMF.cpp
|
||||
@ -111,6 +111,7 @@ src/libslic3r/SLA/Pad.cpp
|
||||
src/libslic3r/SLA/Hollowing.cpp
|
||||
src/libslic3r/SLAPrint.cpp
|
||||
src/libslic3r/SLAPrintSteps.cpp
|
||||
src/libslic3r/Utils.cpp
|
||||
src/libslic3r/PrintBase.cpp
|
||||
src/libslic3r/PrintConfig.cpp
|
||||
src/libslic3r/Zipper.cpp
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -568,9 +568,9 @@ namespace ImGui
|
||||
// - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible.
|
||||
// - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x
|
||||
IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
|
||||
IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
|
||||
IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const char* current_label = NULL, const char* original_label = NULL);
|
||||
IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
|
||||
IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);
|
||||
IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL, const char* current_label = NULL, const char* original_label = NULL);
|
||||
IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.
|
||||
IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.
|
||||
|
||||
|
@ -4689,7 +4689,7 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag
|
||||
// Edit colors components (each component in 0.0f..1.0f range).
|
||||
// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
|
||||
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.
|
||||
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
|
||||
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags, const char* current_label/* = NULL*/, const char* original_label/* = NULL*/)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
@ -4859,7 +4859,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
||||
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
|
||||
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
|
||||
SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
|
||||
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
|
||||
value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x, current_label, original_label);
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
@ -4952,7 +4952,7 @@ static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2
|
||||
// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.)
|
||||
// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
|
||||
// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0)
|
||||
bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
|
||||
bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col, const char* current_label/*=NULL*/, const char* original_label/*=NULL*/)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
@ -5126,13 +5126,13 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
||||
PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
|
||||
ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
|
||||
if ((flags & ImGuiColorEditFlags_NoLabel))
|
||||
Text("Current");
|
||||
Text(current_label ? current_label : "Current");
|
||||
|
||||
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip;
|
||||
ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2));
|
||||
if (ref_col != NULL)
|
||||
{
|
||||
Text("Original");
|
||||
Text(original_label ? original_label : "Original");
|
||||
ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
|
||||
if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)))
|
||||
{
|
||||
|
@ -168,6 +168,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
||||
|
||||
this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height);
|
||||
|
||||
if (this->insert_wipe_tower_extruder()) {
|
||||
this->reorder_extruders(first_extruder);
|
||||
this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height);
|
||||
}
|
||||
|
||||
this->collect_extruder_statistics(prime_multi_material);
|
||||
|
||||
this->mark_skirt_layers(print.config(), max_layer_height);
|
||||
@ -462,6 +467,27 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
|
||||
}
|
||||
}
|
||||
|
||||
bool ToolOrdering::insert_wipe_tower_extruder()
|
||||
{
|
||||
// In case that wipe_tower_extruder is set to non-zero, we must make sure that the extruder will be in the list.
|
||||
bool changed = false;
|
||||
if (m_print_config_ptr->wipe_tower_extruder != 0) {
|
||||
for (LayerTools& lt : m_layer_tools) {
|
||||
if (lt.wipe_tower_partitions > 0) {
|
||||
lt.extruders.emplace_back(m_print_config_ptr->wipe_tower_extruder - 1);
|
||||
sort_remove_duplicates(lt.extruders);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Now convert the 0-based list to 1-based again.
|
||||
for (LayerTools& lt : m_layer_tools) {
|
||||
for (auto& extruder : lt.extruders)
|
||||
++extruder;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
|
||||
{
|
||||
m_first_printing_extruder = (unsigned int)-1;
|
||||
|
@ -167,6 +167,7 @@ private:
|
||||
void collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches);
|
||||
void reorder_extruders(unsigned int last_extruder_id);
|
||||
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height);
|
||||
bool insert_wipe_tower_extruder();
|
||||
void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height);
|
||||
void collect_extruder_statistics(bool prime_multi_material);
|
||||
|
||||
|
@ -581,7 +581,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_extruder == 0 ? config.filament_soluble.get_at(idx) : (idx != config.wipe_tower_extruder - 1);
|
||||
m_filpar[idx].temperature = config.temperature.get_at(idx);
|
||||
m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
|
||||
|
||||
|
@ -121,7 +121,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
||||
result = load_step(input_file.c_str(), &model);
|
||||
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
|
||||
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||
else if (boost::algorithm::iends_with(input_file, ".3mf") || boost::algorithm::iends_with(input_file, ".zip"))
|
||||
//FIXME options & LoadAttribute::CheckVersion ?
|
||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
||||
else
|
||||
@ -155,7 +155,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
||||
Model model;
|
||||
|
||||
bool result = false;
|
||||
if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||
if (boost::algorithm::iends_with(input_file, ".3mf") || boost::algorithm::iends_with(input_file, ".zip"))
|
||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||
|
@ -456,7 +456,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width"
|
||||
};
|
||||
|
@ -208,6 +208,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "wipe_tower_bridging"
|
||||
|| opt_key == "wipe_tower_extra_spacing"
|
||||
|| opt_key == "wipe_tower_no_sparse_layers"
|
||||
|| opt_key == "wipe_tower_extruder"
|
||||
|| opt_key == "wiping_volumes_matrix"
|
||||
|| opt_key == "parking_pos_retraction"
|
||||
|| opt_key == "cooling_tube_retraction"
|
||||
@ -328,6 +329,15 @@ std::vector<unsigned int> Print::extruders() const
|
||||
std::vector<unsigned int> extruders = this->object_extruders();
|
||||
append(extruders, this->support_material_extruders());
|
||||
sort_remove_duplicates(extruders);
|
||||
|
||||
// The wipe tower extruder can also be set. When the wipe tower is enabled and it will be generated,
|
||||
// append its extruder into the list too.
|
||||
if (has_wipe_tower() && config().wipe_tower_extruder != 0 && extruders.size() > 1) {
|
||||
assert(config().wipe_tower_extruder > 0 && config().wipe_tower_extruder < int(config().nozzle_diameter.size()));
|
||||
extruders.emplace_back(config().wipe_tower_extruder - 1); // the config value is 1-based
|
||||
sort_remove_duplicates(extruders);
|
||||
}
|
||||
|
||||
return extruders;
|
||||
}
|
||||
|
||||
@ -458,10 +468,21 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
|
||||
boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" };
|
||||
|
||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||
std::string Print::validate(std::string* warning) const
|
||||
std::string Print::validate(std::vector<std::string>* warnings) const
|
||||
{
|
||||
std::vector<unsigned int> extruders = this->extruders();
|
||||
|
||||
if (warnings) {
|
||||
for (size_t a=0; a<extruders.size(); ++a)
|
||||
for (size_t b=a+1; b<extruders.size(); ++b)
|
||||
if (std::abs(m_config.bed_temperature.get_at(extruders[a]) - m_config.bed_temperature.get_at(extruders[b])) > 15
|
||||
|| std::abs(m_config.first_layer_bed_temperature.get_at(extruders[a]) - m_config.first_layer_bed_temperature.get_at(extruders[b])) > 15) {
|
||||
warnings->emplace_back("_BED_TEMPS_DIFFER");
|
||||
goto DONE;
|
||||
}
|
||||
DONE:;
|
||||
}
|
||||
|
||||
if (m_objects.empty())
|
||||
return _u8L("All objects are outside of the print volume.");
|
||||
|
||||
@ -681,12 +702,12 @@ std::string Print::validate(std::string* warning) const
|
||||
|
||||
// Do we have custom support data that would not be used?
|
||||
// Notify the user in that case.
|
||||
if (! object->has_support() && warning) {
|
||||
if (! object->has_support() && warnings) {
|
||||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||||
bool has_enforcers = mv->is_support_enforcer() ||
|
||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
if (has_enforcers) {
|
||||
*warning = "_SUPPORTS_OFF";
|
||||
warnings->emplace_back("_SUPPORTS_OFF");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -547,7 +547,7 @@ public:
|
||||
bool has_brim() const;
|
||||
|
||||
// Returns an empty string if valid, otherwise returns an error message.
|
||||
std::string validate(std::string* warning = nullptr) const override;
|
||||
std::string validate(std::vector<std::string>* warnings = nullptr) const override;
|
||||
double skirt_first_layer_height() const;
|
||||
Flow brim_flow() const;
|
||||
Flow skirt_flow() const;
|
||||
|
@ -418,7 +418,7 @@ public:
|
||||
virtual std::vector<ObjectID> print_object_ids() const = 0;
|
||||
|
||||
// Validate the print, return empty string if valid, return error if process() cannot (or should not) be started.
|
||||
virtual std::string validate(std::string* warning = nullptr) const { return std::string(); }
|
||||
virtual std::string validate(std::vector<std::string>* warnings = nullptr) const { return std::string(); }
|
||||
|
||||
enum ApplyStatus {
|
||||
// No change after the Print::apply() call.
|
||||
|
@ -3235,6 +3235,27 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(10.));
|
||||
|
||||
def = this->add("wipe_tower_extruder", coInt);
|
||||
def->label = L("Wipe tower extruder");
|
||||
def->category = L("Extruders");
|
||||
def->tooltip = L("The extruder to use when printing perimeter of the wipe tower. "
|
||||
"Set to 0 to use the one that is available (non-soluble would be preferred).");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
def = this->add("solid_infill_every_layers", coInt);
|
||||
def->label = L("Solid infill every");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("This feature allows to force a solid layer every given number of layers. "
|
||||
"Zero to disable. You can set this to any value (for example 9999); "
|
||||
"Slic3r will automatically choose the maximum possible number of layers "
|
||||
"to combine according to nozzle diameter and layer height.");
|
||||
def->sidetext = L("layers");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
def = this->add("xy_size_compensation", coFloat);
|
||||
def->label = L("XY Size Compensation");
|
||||
def->category = L("Advanced");
|
||||
@ -4325,6 +4346,14 @@ void DynamicPrintConfig::normalize_fdm()
|
||||
}
|
||||
}
|
||||
|
||||
if (this->has("wipe_tower_extruder")) {
|
||||
// If invalid, replace with 0.
|
||||
int extruder = this->opt<ConfigOptionInt>("wipe_tower_extruder")->value;
|
||||
int num_extruders = this->opt<ConfigOptionFloats>("nozzle_diameter")->size();
|
||||
if (extruder < 0 || extruder > num_extruders)
|
||||
this->option("wipe_tower_extruder")->setInt(0);
|
||||
}
|
||||
|
||||
if (!this->has("solid_infill_extruder") && this->has("infill_extruder"))
|
||||
this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt());
|
||||
|
||||
|
@ -829,6 +829,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionFloat, wipe_tower_cone_angle))
|
||||
((ConfigOptionPercent, wipe_tower_extra_spacing))
|
||||
((ConfigOptionFloat, wipe_tower_bridging))
|
||||
((ConfigOptionInt, wipe_tower_extruder))
|
||||
((ConfigOptionFloats, wiping_volumes_matrix))
|
||||
((ConfigOptionFloats, wiping_volumes_extruders))
|
||||
((ConfigOptionFloat, z_offset))
|
||||
|
@ -534,7 +534,7 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const
|
||||
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config);
|
||||
}
|
||||
|
||||
std::string SLAPrint::validate(std::string*) const
|
||||
std::string SLAPrint::validate(std::vector<std::string>*) const
|
||||
{
|
||||
for(SLAPrintObject * po : m_objects) {
|
||||
|
||||
|
@ -494,7 +494,7 @@ public:
|
||||
|
||||
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
|
||||
|
||||
std::string validate(std::string* warning = nullptr) const override;
|
||||
std::string validate(std::vector<std::string>* warnings = nullptr) const override;
|
||||
|
||||
// An aggregation of SliceRecord-s from all the print objects for each
|
||||
// occupied layer. Slice record levels dont have to match exactly.
|
||||
|
@ -307,43 +307,9 @@ public:
|
||||
|
||||
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
|
||||
// and removing spaces.
|
||||
inline std::string short_time(const std::string &time)
|
||||
{
|
||||
// Parse the dhms time format.
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
if (time.find('d') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
|
||||
else if (time.find('h') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
|
||||
else if (time.find('m') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
|
||||
else if (time.find('s') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
// Round to full minutes.
|
||||
if (days + hours + minutes > 0 && seconds >= 30) {
|
||||
if (++minutes == 60) {
|
||||
minutes = 0;
|
||||
if (++hours == 24) {
|
||||
hours = 0;
|
||||
++days;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format the dhm time.
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd%dh%dm", days, hours, minutes);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh%dm", hours, minutes);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm", minutes);
|
||||
else
|
||||
::sprintf(buffer, "%ds", seconds);
|
||||
return buffer;
|
||||
}
|
||||
std::string short_time(const std::string& time, bool force_localization = false);
|
||||
// localized short_time used on UI
|
||||
inline std::string short_time_ui(const std::string& time) { return short_time(time, true); }
|
||||
|
||||
// Returns the given time is seconds in format DDd HHh MMm SSs
|
||||
inline std::string get_time_dhms(float time_in_secs)
|
||||
|
@ -983,6 +983,61 @@ std::string xml_escape_double_quotes_attribute_value(std::string text)
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string short_time(const std::string &time, bool force_localization /*= false*/)
|
||||
{
|
||||
// Parse the dhms time format.
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
if (time.find('d') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
|
||||
else if (time.find('h') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
|
||||
else if (time.find('m') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
|
||||
else if (time.find('s') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
// Round to full minutes.
|
||||
if (days + hours + minutes > 0 && seconds >= 30) {
|
||||
if (++minutes == 60) {
|
||||
minutes = 0;
|
||||
if (++hours == 24) {
|
||||
hours = 0;
|
||||
++days;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format the dhm time
|
||||
|
||||
if (force_localization) {
|
||||
auto get_d = [days]() { return format(_u8L("%1%d"), days); };
|
||||
auto get_h = [hours]() { return format(_u8L("%1%h"), hours); };
|
||||
// TRN "m" means "minutes"
|
||||
auto get_m = [minutes]() { return format(_u8L("%1%m"), minutes); };
|
||||
|
||||
if (days > 0)
|
||||
return get_d() + get_h() + get_m();
|
||||
if (hours > 0)
|
||||
return get_h() + get_m();
|
||||
if (minutes > 0)
|
||||
return get_m();
|
||||
return format(_u8L("%1%s"), seconds);
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd%dh%dm", days, hours, minutes);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh%dm", hours, minutes);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm", minutes);
|
||||
else
|
||||
::sprintf(buffer, "%ds", seconds);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string format_memsize_MB(size_t n)
|
||||
{
|
||||
std::string out;
|
||||
|
@ -549,10 +549,10 @@ bool BackgroundSlicingProcess::empty() const
|
||||
return m_print->empty();
|
||||
}
|
||||
|
||||
std::string BackgroundSlicingProcess::validate(std::string* warning)
|
||||
std::string BackgroundSlicingProcess::validate(std::vector<std::string>* warnings)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
return m_print->validate(warning);
|
||||
return m_print->validate(warnings);
|
||||
}
|
||||
|
||||
// Apply config over the print. Returns false, if the new config values caused any of the already
|
||||
|
@ -132,7 +132,7 @@ public:
|
||||
bool empty() const;
|
||||
// Validate the print. Returns an empty string if valid, returns an error message if invalid.
|
||||
// Call validate before calling start().
|
||||
std::string validate(std::string* warning = nullptr);
|
||||
std::string validate(std::vector<std::string>* warnings = nullptr);
|
||||
|
||||
// Set the export path of the G-code.
|
||||
// Once the path is set, the G-code
|
||||
|
@ -153,15 +153,22 @@ BundleMap BundleMap::load()
|
||||
if (res.find(id) != res.end()) { continue; }
|
||||
|
||||
// Fresh index should be in archive_dir, otherwise look for it in cache
|
||||
// Then if not in archive or cache - it could be 3rd party profile that user just copied to vendor folder (both ini and cache)
|
||||
|
||||
fs::path idx_path (archive_dir / (id + ".idx"));
|
||||
if (!boost::filesystem::exists(idx_path)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << format("Missing index %1% when loading bundle %2%.", idx_path.string(), id);
|
||||
BOOST_LOG_TRIVIAL(error) << format("Missing index %1% when loading bundle %2%. Going to search for it in cache folder.", idx_path.string(), id);
|
||||
idx_path = fs::path(cache_dir / (id + ".idx"));
|
||||
}
|
||||
if (!boost::filesystem::exists(idx_path)) {
|
||||
BOOST_LOG_TRIVIAL(error) << format("Missing index %1% when loading bundle %2%. Going to search for it in vendor folder. Is it a 3rd party profile?", idx_path.string(), id);
|
||||
idx_path = fs::path(vendor_dir / (id + ".idx"));
|
||||
}
|
||||
if (!boost::filesystem::exists(idx_path)) {
|
||||
BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to missing index %2%.", id, idx_path.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
Slic3r::GUI::Config::Index index;
|
||||
try {
|
||||
index.load(idx_path);
|
||||
|
@ -708,28 +708,28 @@ static wxString short_and_splitted_time(const std::string& time)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
|
||||
// Format the dhm time.
|
||||
char buffer[64];
|
||||
auto get_d = [days]() { return format(_u8L("%1%d"), days); };
|
||||
auto get_h = [hours]() { return format(_u8L("%1%h"), hours); };
|
||||
auto get_m = [minutes](){ return format(_u8L("%1%m"), minutes); };
|
||||
auto get_s = [seconds](){ return format(_u8L("%1%s"), seconds); };
|
||||
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd%dh\n%dm", days, hours, minutes);
|
||||
else if (hours > 0) {
|
||||
return format_wxstr("%1%%2%\n%3%", get_d(), get_h(), get_m());
|
||||
if (hours > 0) {
|
||||
if (hours < 10 && minutes < 10 && seconds < 10)
|
||||
::sprintf(buffer, "%dh%dm%ds", hours, minutes, seconds);
|
||||
else if (hours > 10 && minutes > 10 && seconds > 10)
|
||||
::sprintf(buffer, "%dh\n%dm\n%ds", hours, minutes, seconds);
|
||||
else if ((minutes < 10 && seconds > 10) || (minutes > 10 && seconds < 10))
|
||||
::sprintf(buffer, "%dh\n%dm%ds", hours, minutes, seconds);
|
||||
else
|
||||
::sprintf(buffer, "%dh%dm\n%ds", hours, minutes, seconds);
|
||||
return format_wxstr("%1%%2%%3%", get_h(), get_m(), get_s());
|
||||
if (hours > 10 && minutes > 10 && seconds > 10)
|
||||
return format_wxstr("%1%\n%2%\n%3%", get_h(), get_m(), get_s());
|
||||
if ((minutes < 10 && seconds > 10) || (minutes > 10 && seconds < 10))
|
||||
return format_wxstr("%1%\n%2%%3%", get_h(), get_m(), get_s());
|
||||
return format_wxstr("%1%%2%\n%3%", get_h(), get_m(), get_s());
|
||||
}
|
||||
else if (minutes > 0) {
|
||||
if (minutes > 0) {
|
||||
if (minutes > 10 && seconds > 10)
|
||||
::sprintf(buffer, "%dm\n%ds", minutes, seconds);
|
||||
else
|
||||
::sprintf(buffer, "%dm%ds", minutes, seconds);
|
||||
return format_wxstr("%1%\n%2%", get_m(), get_s());
|
||||
return format_wxstr("%1%%2%", get_m(), get_s());
|
||||
}
|
||||
else
|
||||
::sprintf(buffer, "%ds", seconds);
|
||||
return wxString::FromUTF8(buffer);
|
||||
return from_u8(get_s());
|
||||
}
|
||||
|
||||
wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Plater.hpp"
|
||||
@ -3271,6 +3273,11 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
// draw text
|
||||
ImGui::Dummy({ icon_size, icon_size });
|
||||
ImGui::SameLine();
|
||||
|
||||
// localize "g" and "m" units
|
||||
const std::string& grams = _u8L("g");
|
||||
const std::string& inches = _u8L("in");
|
||||
const std::string& metres = _CTX_utf8(L_CONTEXT("m", "Metre"), "Metre");
|
||||
if (callback != nullptr) {
|
||||
if (ImGui::MenuItem(label.c_str()))
|
||||
callback();
|
||||
@ -3306,11 +3313,9 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
::sprintf(buf, "%.1f%%", 100.0f * percent);
|
||||
ImGui::TextUnformatted((percent > 0.0f) ? buf : "");
|
||||
ImGui::SameLine(offsets[2]);
|
||||
::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m);
|
||||
imgui.text(buf);
|
||||
imgui.text(format("%1$.2f %2%", used_filament_m, (imperial_units ? inches : metres)));
|
||||
ImGui::SameLine(offsets[3]);
|
||||
::sprintf(buf, "%.2f g", used_filament_g);
|
||||
imgui.text(buf);
|
||||
imgui.text(format("%1$.2f %2%", used_filament_g, grams));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -3330,13 +3335,10 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
ImGui::TextUnformatted((percent > 0.0f) ? buf : "");
|
||||
}
|
||||
else if (used_filament_m > 0.0) {
|
||||
char buf[64];
|
||||
ImGui::SameLine(offsets[0]);
|
||||
::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m);
|
||||
imgui.text(buf);
|
||||
imgui.text(format("%1$.2f %2%", used_filament_m, (imperial_units ? inches : metres)));
|
||||
ImGui::SameLine(offsets[1]);
|
||||
::sprintf(buf, "%.2f g", used_filament_g);
|
||||
imgui.text(buf);
|
||||
imgui.text(format("%1$.2f %2%", used_filament_g, grams));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3506,7 +3508,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
if (role < GCodeExtrusionRole::Count) {
|
||||
labels.push_back(_u8L(gcode_extrusion_role_to_string(role)));
|
||||
auto [time, percent] = role_time_and_percent(role);
|
||||
times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : "");
|
||||
times.push_back((time > 0.0f) ? short_time_ui(get_time_dhms(time)) : "");
|
||||
percents.push_back(percent);
|
||||
max_time_percent = std::max(max_time_percent, percent);
|
||||
auto [used_filament_m, used_filament_g] = used_filament_per_role(role);
|
||||
@ -3630,7 +3632,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
}
|
||||
|
||||
if (m_buffers[buffer_id(EMoveType::Travel)].visible)
|
||||
append_item(EItemType::Line, Travel_Colors[0], _u8L("Travel"), true, short_time(get_time_dhms(time_mode.travel_time)),
|
||||
append_item(EItemType::Line, Travel_Colors[0], _u8L("Travel"), true, short_time_ui(get_time_dhms(time_mode.travel_time)),
|
||||
time_mode.travel_time / time_mode.time, max_time_percent, offsets, 0.0f, 0.0f);
|
||||
|
||||
break;
|
||||
@ -3807,7 +3809,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
ImGuiWrapper::to_ImU32(color2));
|
||||
|
||||
ImGui::SameLine(offsets[0]);
|
||||
imgui.text(short_time(get_time_dhms(times.second - times.first)));
|
||||
imgui.text(short_time_ui(get_time_dhms(times.second - times.first)));
|
||||
};
|
||||
|
||||
auto append_print = [&imgui, imperial_units](const ColorRGBA& color, const std::array<float, 4>& offsets, const Times& times, std::pair<double, double> used_filament) {
|
||||
@ -3823,9 +3825,9 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
ImGuiWrapper::to_ImU32(color));
|
||||
|
||||
ImGui::SameLine(offsets[0]);
|
||||
imgui.text(short_time(get_time_dhms(times.second)));
|
||||
imgui.text(short_time_ui(get_time_dhms(times.second)));
|
||||
ImGui::SameLine(offsets[1]);
|
||||
imgui.text(short_time(get_time_dhms(times.first)));
|
||||
imgui.text(short_time_ui(get_time_dhms(times.first)));
|
||||
if (used_filament.first > 0.0f) {
|
||||
char buffer[64];
|
||||
ImGui::SameLine(offsets[2]);
|
||||
@ -3850,7 +3852,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; }
|
||||
case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; }
|
||||
}
|
||||
times.push_back(short_time(get_time_dhms(item.times.second)));
|
||||
times.push_back(short_time_ui(get_time_dhms(item.times.second)));
|
||||
}
|
||||
|
||||
|
||||
@ -3883,7 +3885,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
case PartialTime::EType::Pause: {
|
||||
imgui.text(_u8L("Pause"));
|
||||
ImGui::SameLine(offsets[0]);
|
||||
imgui.text(short_time(get_time_dhms(item.times.second - item.times.first)));
|
||||
imgui.text(short_time_ui(get_time_dhms(item.times.second - item.times.first)));
|
||||
break;
|
||||
}
|
||||
case PartialTime::EType::ColorChange: {
|
||||
@ -4001,11 +4003,11 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
if (ImGui::BeginTable("Times", 2)) {
|
||||
if (!time_mode.layers_times.empty()) {
|
||||
add_strings_row_to_table(_u8L("First layer") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
short_time(get_time_dhms(time_mode.layers_times.front())), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
|
||||
short_time_ui(get_time_dhms(time_mode.layers_times.front())), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
|
||||
}
|
||||
|
||||
add_strings_row_to_table(_u8L("Total") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
short_time(get_time_dhms(time_mode.time)), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
|
||||
short_time_ui(get_time_dhms(time_mode.time)), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
@ -335,7 +335,11 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
const ColorRGBA& select_first_color = m_modified_extruders_colors[m_first_selected_extruder_idx];
|
||||
ImVec4 first_color = ImGuiWrapper::to_ImVec4(select_first_color);
|
||||
const std::string first_label = into_u8(m_desc.at("first_color")) + "##color_picker";
|
||||
if (ImGui::ColorEdit4(first_label.c_str(), (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
|
||||
if (ImGui::ColorEdit4(first_label.c_str(), (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel,
|
||||
// TRN Means "current color"
|
||||
_u8L("Current").c_str(),
|
||||
// TRN Means "original color"
|
||||
_u8L("Original").c_str()))
|
||||
m_modified_extruders_colors[m_first_selected_extruder_idx] = ImGuiWrapper::from_ImVec4(first_color);
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
@ -348,7 +352,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
||||
const ColorRGBA& select_second_color = m_modified_extruders_colors[m_second_selected_extruder_idx];
|
||||
ImVec4 second_color = ImGuiWrapper::to_ImVec4(select_second_color);
|
||||
const std::string second_label = into_u8(m_desc.at("second_color")) + "##color_picker";
|
||||
if (ImGui::ColorEdit4(second_label.c_str(), (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
|
||||
if (ImGui::ColorEdit4(second_label.c_str(), (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel,
|
||||
_u8L("Current").c_str(), _u8L("Original").c_str()))
|
||||
m_modified_extruders_colors[m_second_selected_extruder_idx] = ImGuiWrapper::from_ImVec4(second_color);
|
||||
|
||||
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
@ -1370,7 +1370,7 @@ void Sidebar::update_sliced_info_sizer()
|
||||
}
|
||||
p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost");
|
||||
|
||||
wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time));
|
||||
wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : from_u8(short_time_ui(get_time_dhms(float(ps.estimated_print_time))));
|
||||
p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":");
|
||||
|
||||
p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed());
|
||||
@ -1459,14 +1459,14 @@ void Sidebar::update_sliced_info_sizer()
|
||||
new_label = _L("Estimated printing time") + ":";
|
||||
if (ps.estimated_normal_print_time != "N/A") {
|
||||
new_label += format_wxstr("\n - %1%", _L("normal mode"));
|
||||
info_text += format_wxstr("\n%1%", short_time(ps.estimated_normal_print_time));
|
||||
info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_normal_print_time));
|
||||
|
||||
p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed());
|
||||
|
||||
}
|
||||
if (ps.estimated_silent_print_time != "N/A") {
|
||||
new_label += format_wxstr("\n - %1%", _L("stealth mode"));
|
||||
info_text += format_wxstr("\n%1%", short_time(ps.estimated_silent_print_time));
|
||||
info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_silent_print_time));
|
||||
}
|
||||
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
|
||||
}
|
||||
@ -1702,6 +1702,7 @@ struct Plater::priv
|
||||
static const std::regex pattern_zip_amf;
|
||||
static const std::regex pattern_any_amf;
|
||||
static const std::regex pattern_prusa;
|
||||
static const std::regex pattern_zip;
|
||||
|
||||
priv(Plater *q, MainFrame *main_frame);
|
||||
~priv();
|
||||
@ -1848,7 +1849,7 @@ struct Plater::priv
|
||||
void suppress_snapshots() { m_prevent_snapshots++; }
|
||||
void allow_snapshots() { m_prevent_snapshots--; }
|
||||
|
||||
void process_validation_warning(const std::string& warning) const;
|
||||
void process_validation_warning(const std::vector<std::string>& warning) const;
|
||||
|
||||
bool background_processing_enabled() const { return this->get_config_bool("background_processing"); }
|
||||
void update_print_volume_state();
|
||||
@ -1998,6 +1999,7 @@ const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_zip(".*zip", std::regex::icase);
|
||||
|
||||
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
@ -2005,7 +2007,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder",
|
||||
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
@ -2423,7 +2425,7 @@ void Plater::check_selected_presets_visibility(PrinterTechnology loaded_printer_
|
||||
|
||||
std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units/* = false*/)
|
||||
{
|
||||
if (input_files.empty()) { return std::vector<size_t>(); }
|
||||
if (input_files.empty()) { return std::vector<size_t>(); }
|
||||
|
||||
auto *nozzle_dmrs = config->opt<ConfigOptionFloats>("nozzle_diameter");
|
||||
|
||||
@ -2484,7 +2486,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
progress_dlg->Fit();
|
||||
}
|
||||
|
||||
const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
|
||||
const bool type_3mf = std::regex_match(path.string(), pattern_3mf) || std::regex_match(path.string(), pattern_zip);
|
||||
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
|
||||
const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf);
|
||||
const bool type_prusa = std::regex_match(path.string(), pattern_prusa);
|
||||
@ -3196,12 +3198,12 @@ void Plater::priv::update_print_volume_state()
|
||||
this->q->model().update_print_volume_state(this->bed.build_volume());
|
||||
}
|
||||
|
||||
void Plater::priv::process_validation_warning(const std::string& warning) const
|
||||
void Plater::priv::process_validation_warning(const std::vector<std::string>& warnings) const
|
||||
{
|
||||
if (warning.empty())
|
||||
if (warnings.empty())
|
||||
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
|
||||
else {
|
||||
std::string text = warning;
|
||||
|
||||
for (std::string text : warnings) {
|
||||
std::string hypertext = "";
|
||||
std::function<bool(wxEvtHandler*)> action_fn = [](wxEvtHandler*){ return false; };
|
||||
|
||||
@ -3220,6 +3222,8 @@ void Plater::priv::process_validation_warning(const std::string& warning) const
|
||||
return true;
|
||||
};
|
||||
}
|
||||
if (text == "_BED_TEMPS_DIFFER")
|
||||
text = _u8L("Bed temperatures for the used filaments differ significantly.");
|
||||
|
||||
notification_manager->push_notification(
|
||||
NotificationType::ValidateWarning,
|
||||
@ -3277,8 +3281,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
// The delayed error message is no more valid.
|
||||
delayed_error_message.clear();
|
||||
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
|
||||
std::string warning;
|
||||
std::string err = background_process.validate(&warning);
|
||||
std::vector<std::string> warnings;
|
||||
std::string err = background_process.validate(&warnings);
|
||||
if (err.empty()) {
|
||||
notification_manager->set_all_slicing_errors_gray(true);
|
||||
notification_manager->close_notification_of_type(NotificationType::ValidateError);
|
||||
@ -3287,7 +3291,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
|
||||
// Pass a warning from validation and either show a notification,
|
||||
// or hide the old one.
|
||||
process_validation_warning(warning);
|
||||
process_validation_warning(warnings);
|
||||
if (printer_technology == ptFFF) {
|
||||
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||
canvas->reset_sequential_print_clearance();
|
||||
@ -3326,8 +3330,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
std::string warning;
|
||||
std::string err = background_process.validate(&warning);
|
||||
std::vector<std::string> warnings;
|
||||
std::string err = background_process.validate(&warnings);
|
||||
if (!err.empty())
|
||||
return return_state;
|
||||
}
|
||||
@ -3340,7 +3344,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
//actualizate warnings
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED || background_process.empty()) {
|
||||
if (background_process.empty())
|
||||
process_validation_warning(std::string());
|
||||
process_validation_warning(std::vector<std::string>());
|
||||
actualize_slicing_warnings(*this->background_process.current_print());
|
||||
actualize_object_warnings(*this->background_process.current_print());
|
||||
show_warning_dialog = false;
|
||||
@ -4035,11 +4039,11 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
// revert previously selection
|
||||
const std::string& old_name = wxGetApp().preset_bundle->filaments.get_edited_preset().name;
|
||||
wxGetApp().preset_bundle->set_filament_preset(idx, old_name);
|
||||
combo->update();
|
||||
}
|
||||
else
|
||||
// Synchronize config.ini with the current selections.
|
||||
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
|
||||
combo->update();
|
||||
}
|
||||
else if (select_preset) {
|
||||
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
|
||||
@ -5935,7 +5939,13 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*
|
||||
// searches for project files
|
||||
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
|
||||
std::string filename = (*it).filename().string();
|
||||
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
|
||||
|
||||
bool handle_as_project = (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf"));
|
||||
if (boost::algorithm::iends_with(filename, ".zip") && is_project_3mf(it->string())) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "File with .zip extension is 3mf project, opening as it would have .3mf extension: " << *it;
|
||||
handle_as_project = true;
|
||||
}
|
||||
if (handle_as_project) {
|
||||
ProjectDropDialog::LoadType load_type = ProjectDropDialog::LoadType::Unknown;
|
||||
{
|
||||
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) ||
|
||||
|
@ -820,6 +820,8 @@ void PlaterPresetComboBox::update()
|
||||
// Extruder color is not defined.
|
||||
extruder_color.clear();
|
||||
selected_filament_preset = extruder_filaments.get_selected_preset();
|
||||
if (selected_filament_preset->is_dirty)
|
||||
selected_filament_preset = &m_preset_bundle->filaments.get_edited_preset();
|
||||
assert(selected_filament_preset);
|
||||
}
|
||||
|
||||
|
@ -1598,6 +1598,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("solid_infill_extruder");
|
||||
optgroup->append_single_option_line("support_material_extruder");
|
||||
optgroup->append_single_option_line("support_material_interface_extruder");
|
||||
optgroup->append_single_option_line("wipe_tower_extruder");
|
||||
|
||||
optgroup = page->new_optgroup(L("Ooze prevention"));
|
||||
optgroup->append_single_option_line("ooze_prevention");
|
||||
|
@ -32,6 +32,37 @@ namespace pt = boost::property_tree;
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
std::string get_host_from_url(const std::string& url_in)
|
||||
{
|
||||
std::string url = url_in;
|
||||
// add http:// if there is no scheme
|
||||
size_t double_slash = url.find("//");
|
||||
if (double_slash == std::string::npos)
|
||||
url = "http://" + url;
|
||||
std::string out = url;
|
||||
CURLU* hurl = curl_url();
|
||||
if (hurl) {
|
||||
// Parse the input URL.
|
||||
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
// Replace the address.
|
||||
char* host;
|
||||
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
out = host;
|
||||
curl_free(host);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
|
||||
curl_url_cleanup(hurl);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
||||
return out;
|
||||
}
|
||||
#ifdef WIN32
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||
@ -96,38 +127,6 @@ std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
|
||||
return out;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string get_host_from_url(const std::string& url_in)
|
||||
{
|
||||
std::string url = url_in;
|
||||
// add http:// if there is no scheme
|
||||
size_t double_slash = url.find("//");
|
||||
if (double_slash == std::string::npos)
|
||||
url = "http://" + url;
|
||||
std::string out = url;
|
||||
CURLU* hurl = curl_url();
|
||||
if (hurl) {
|
||||
// Parse the input URL.
|
||||
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, url.c_str(), 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
// Replace the address.
|
||||
char* host;
|
||||
rc = curl_url_get(hurl, CURLUPART_HOST, &host, 0);
|
||||
if (rc == CURLUE_OK) {
|
||||
out = host;
|
||||
curl_free(host);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to get host form URL " << url;
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to parse URL " << url;
|
||||
curl_url_cleanup(hurl);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "OctoPrint get_host_from_url: failed to allocate curl_url";
|
||||
return out;
|
||||
}
|
||||
#endif // WIN32
|
||||
std::string escape_string(const std::string& unescaped)
|
||||
{
|
||||
@ -170,7 +169,7 @@ const char* OctoPrint::get_name() const { return "OctoPrint"; }
|
||||
bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
const char* name = get_name();
|
||||
bool res = true;
|
||||
// Msg contains ip string.
|
||||
@ -179,7 +178,15 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
auto http = Http::get(url);//std::move(url));
|
||||
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
http
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
@ -228,7 +235,7 @@ bool OctoPrint::test(wxString& msg) const
|
||||
auto url = make_url("api/version");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
@ -339,7 +346,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
|
||||
return true;
|
||||
} else {
|
||||
// There are multiple addresses - user needs to choose which to use.
|
||||
size_t selected_index = resolved_addr.size();
|
||||
size_t selected_index = resolved_addr.size();
|
||||
IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
|
||||
if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
|
||||
return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
|
||||
@ -378,7 +385,14 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
|
||||
% upload_parent_path.string()
|
||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
auto http = Http::post(url);//std::move(url));
|
||||
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
@ -456,7 +470,15 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
|
||||
% upload_parent_path.string()
|
||||
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
auto http = Http::post(std::move(url));
|
||||
// "Host" header is necessary here. In the workaround above (two mDNS..) we have got IP address from test connection and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
@ -779,7 +801,7 @@ bool PrusaLink::test_with_method_check(wxString& msg, bool& use_put) const
|
||||
auto url = make_url("api/version");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
@ -852,7 +874,15 @@ bool PrusaLink::test_with_resolved_ip_and_method_check(wxString& msg, bool& use_
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
auto http = Http::get(url);//std::move(url));
|
||||
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
http
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
@ -1006,13 +1036,18 @@ bool PrusaLink::put_inner(PrintHostUpload upload_data, std::string url, const st
|
||||
bool res = true;
|
||||
// Percent escape all filenames in on path and add it to the url. This is different from POST.
|
||||
url += "/" + escape_path_by_element(upload_data.upload_path);
|
||||
|
||||
std::string host = get_host_from_url(m_host);
|
||||
Http http = Http::put(std::move(url));
|
||||
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
// This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
|
||||
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
|
||||
http.header("Print-After-Upload", "?1");
|
||||
|
||||
http.set_put_body(upload_data.source_path)
|
||||
.header("Content-Type", "text/x.gcode")
|
||||
.header("Overwrite", "?1")
|
||||
@ -1048,7 +1083,15 @@ bool PrusaLink::post_inner(PrintHostUpload upload_data, std::string url, const s
|
||||
bool res = true;
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
std::string host = get_host_from_url(m_host);
|
||||
|
||||
Http http = Http::post(std::move(url));
|
||||
// "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
|
||||
// And when creating Http object above, libcurl automatically includes "Host" header from address it got.
|
||||
// Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
|
||||
// Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
|
||||
// https://www.rfc-editor.org/rfc/rfc7230#section-5.4
|
||||
http.header("Host", host);
|
||||
set_auth(http);
|
||||
set_http_post_header_args(http, upload_data.post_action);
|
||||
http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
set(SLIC3R_APP_NAME "PrusaSlicer")
|
||||
set(SLIC3R_APP_KEY "PrusaSlicer")
|
||||
set(SLIC3R_VERSION "2.6.0-beta2")
|
||||
set(SLIC3R_VERSION "2.6.0-beta3")
|
||||
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
|
||||
set(SLIC3R_RC_VERSION "2,6,0,0")
|
||||
set(SLIC3R_RC_VERSION_DOTS "2.6.0.0")
|
||||
|
Loading…
x
Reference in New Issue
Block a user