diff --git a/src/imgui/imgui.h b/src/imgui/imgui.h index 06a610d4085..f6646cfd7e3 100644 --- a/src/imgui/imgui.h +++ b/src/imgui/imgui.h @@ -552,7 +552,7 @@ namespace ImGui IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0, float p_data_void = FLT_MAX); IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); @@ -561,7 +561,7 @@ namespace ImGui IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0); IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0); IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); - IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0, void* p_data_void = nullptr); IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.) diff --git a/src/imgui/imgui_internal.h b/src/imgui/imgui_internal.h index 8006246b6d0..98469f9c02b 100644 --- a/src/imgui/imgui_internal.h +++ b/src/imgui/imgui_internal.h @@ -2580,7 +2580,7 @@ namespace ImGui IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type); IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format); IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2); - IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format); + IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_default_data = nullptr); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); diff --git a/src/imgui/imgui_widgets.cpp b/src/imgui/imgui_widgets.cpp index faf616307e6..65981d9db44 100644 --- a/src/imgui/imgui_widgets.cpp +++ b/src/imgui/imgui_widgets.cpp @@ -1909,7 +1909,7 @@ void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const // User can input math operators (e.g. +100) to edit a numerical values. // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format) +bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_default_data) { while (ImCharIsBlankA(*buf)) buf++; @@ -1927,8 +1927,6 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b { op = 0; } - if (!buf[0]) - return false; // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); @@ -1937,6 +1935,15 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (format == NULL) format = type_info->ScanFmt; + + if (!buf[0]) + if (p_default_data && data_type == ImGuiDataType_Float) { + float* v = (float*)p_data; + *v = (*(const float*)p_default_data); + return memcmp(&data_backup, p_data, type_info->Size) != 0; + } else { + return false; + } // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. int arg1i = 0; @@ -3339,7 +3346,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG // Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional. // Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly. -bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags) +bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags, void* p_data_void) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -3353,6 +3360,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); + if (p_data_void && data_type == ImGuiDataType_Float && (*(const float*)p_data) == (*(const float*)p_data_void)) + buf[0] = 0; bool value_changed = false; if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) @@ -3368,7 +3377,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data PushID(label); SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format, p_data_void); // Step buttons const ImVec2 backup_frame_padding = style.FramePadding; @@ -3402,8 +3411,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data } else { - if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); + if (InputText(label, buf, flags)) + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format, p_data_void); } if (value_changed) MarkItemEdited(window->DC.LastItemId); @@ -3446,10 +3455,10 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_dat return value_changed; } -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags, float p_data_void) { flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags); + return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags, p_data_void == FLT_MAX ? (void*)nullptr : (void*)&p_data_void); } bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) diff --git a/src/libslic3r/GCode/FanMover.cpp b/src/libslic3r/GCode/FanMover.cpp index 989e08bdb8a..a4796c38eaf 100644 --- a/src/libslic3r/GCode/FanMover.cpp +++ b/src/libslic3r/GCode/FanMover.cpp @@ -208,7 +208,7 @@ void FanMover::_remove_slow_fan(int16_t min_speed, float past_sec) { } std::string FanMover::_set_fan(int16_t speed) { - const Tool* tool = m_writer.get_tool(m_currrent_extruder < 20 ? m_currrent_extruder : 0); + const Tool* tool = m_writer.get_tool(m_current_extruder < 20 ? m_current_extruder : 0); std::string str = GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, m_writer.config.gcode_comments.value, speed, tool ? tool->fan_offset() : 0, m_writer.config.fan_percentage.value); if(!str.empty() && str.back() == '\n') return str.substr(0,str.size()-1); @@ -248,13 +248,51 @@ void FanMover::_process_T(const std::string_view command) // T-1 is a valid gcode line for RepRap Firmwares (used to deselects all tools) see https://github.com/prusa3d/PrusaSlicer/issues/5677 if ((flavor != gcfRepRap && flavor != gcfSprinter) || eid != -1) - m_currrent_extruder = static_cast(0); + m_current_extruder = static_cast(0); } else { - m_currrent_extruder = static_cast(eid); + m_current_extruder = static_cast(eid); } } } + +void FanMover::_process_ACTIVATE_EXTRUDER(const std::string_view cmd) +{ + if (size_t cmd_end = cmd.find("ACTIVATE_EXTRUDER"); cmd_end != std::string::npos) { + bool error = false; + size_t extruder_pos_start = cmd.find("EXTRUDER", cmd_end + std::string_view("ACTIVATE_EXTRUDER").size()) + std::string_view("EXTRUDER").size(); + assert(cmd[extruder_pos_start - 1] == 'R'); + if (extruder_pos_start != std::string::npos) { + //remove next char until '-' or [0-9] + while (extruder_pos_start < cmd.size() && (cmd[extruder_pos_start] == ' ' || cmd[extruder_pos_start] == '=' || cmd[extruder_pos_start] == '\t')) + ++extruder_pos_start; + size_t extruder_pos_end = extruder_pos_start + 1; + while (extruder_pos_end < cmd.size() && cmd[extruder_pos_end] != ' ' && cmd[extruder_pos_end] != '\t' && cmd[extruder_pos_end] != '\r' && cmd[extruder_pos_end] != '\n') + ++extruder_pos_end; + std::string_view extruder_name = cmd.substr(extruder_pos_start, extruder_pos_end-extruder_pos_start); + // we have a "name". It may be whatever or "extruder" + X + for (const Extruder &extruder : m_writer.extruders()) { + if (m_writer.config.tool_name.values[extruder.id()] == extruder_name) { + m_current_extruder = static_cast(extruder.id()); + return; + } + } + std::string extruder_str("extruder"); + if (extruder_str == extruder_name) { + m_current_extruder = static_cast(0); + return; + } + for (const Extruder &extruder : m_writer.extruders()) { + if (extruder_str + std::to_string(extruder.id()) == extruder_name) { + m_current_extruder = static_cast(extruder.id()); + return; + } + } + } + BOOST_LOG_TRIVIAL(error) << "invalid ACTIVATE_EXTRUDER gcode command: '" << cmd << "', ignored by the fam mover post-process."; + } +} + void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line) { // processes 'normal' gcode lines @@ -266,6 +304,9 @@ void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCode if (line.has_f()) m_current_speed = line.f() / 60.0f; switch (::toupper(cmd[0])) { + case 'A': + _process_ACTIVATE_EXTRUDER(line.raw()); + break; case 'T': case 't': _process_T(cmd); @@ -442,17 +483,20 @@ void FanMover::_process_gcode_line(GCodeReader& reader, const GCodeReader::GCode if (line.has(Axis::E)) { new_data.e = reader.e(); if (relative_e) { - assert(new_data.e == 0); new_data.de = line.e(); + // GCode reader doesn't know it's relative extrusion, we have to do it ourself. + //assert(new_data.e == 0); + new_data.e = 0; } else new_data.de = line.dist_E(reader); } assert(new_data.dx == 0 || reader.x() == new_data.x); - assert(new_data.dx == 0 || reader.x() + new_data.dx == line.x()); - assert(new_data.dy == 0 ||reader.y() == new_data.y); - assert(new_data.dy == 0 || reader.y() + new_data.dy == line.y()); - assert(new_data.de == 0 || reader.e() == new_data.e); - assert(new_data.de == 0 || reader.e() + new_data.de == line.e()); + assert(new_data.dx == 0 || std::abs(reader.x() + new_data.dx - line.x()) < 0.00001f); + assert(new_data.dy == 0 || reader.y() == new_data.y); + assert(new_data.dy == 0 || std::abs(reader.y() + new_data.dy - line.y()) < 0.00001f); + assert(new_data.de == 0 || (relative_e?0:reader.e()) == new_data.e); + assert(new_data.de == 0 || std::abs((relative_e?0.f:reader.e()) + new_data.de - line.e()) < 0.00001f); + //assert(new_data.de == 0 ||(relative_e?0.f:reader.e()) + new_data.de == line.e()); if (m_current_kickstart.time > 0 && time > 0) { m_current_kickstart.time -= time; diff --git a/src/libslic3r/GCode/FanMover.hpp b/src/libslic3r/GCode/FanMover.hpp index a5598df4bfe..6b651529ee7 100644 --- a/src/libslic3r/GCode/FanMover.hpp +++ b/src/libslic3r/GCode/FanMover.hpp @@ -49,7 +49,7 @@ class FanMover // in unit/second double m_current_speed = 1000 / 60.0; bool m_is_custom_gcode = false; - uint16_t m_currrent_extruder = 0; + uint16_t m_current_extruder = 0; // variable for when you add a line (front of the buffer) int m_front_buffer_fan_speed = 1; @@ -86,6 +86,7 @@ class FanMover } // Processes the given gcode line void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line); + void _process_ACTIVATE_EXTRUDER(const std::string_view command); void _process_T(const std::string_view command); void _put_in_middle_G1(std::list::iterator item_to_split, float nb_sec, BufferData&& line_to_write); void _print_in_middle_G1(BufferData& line_to_split, float nb_sec, const std::string& line_to_write); diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index fd3a8998914..763df78c286 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -49,6 +49,11 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_ m_filament_crossections.push_back(float(a)); } + m_extruder_names.clear(); + for (const std::string & str: config.tool_name.values) { + m_extruder_names.push_back(str); + } + // Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min // Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min // Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2 @@ -235,7 +240,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo gcode = parse_int(line); } catch (Slic3r::InvalidArgument &) { // Ignore invalid GCodes. - eatws(line); + eatws(line); break; } @@ -368,16 +373,26 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo // Ignore the rest of the M-codes. break; } + case 'A': { + parse_activate_extruder(str_line); + break; + } case 'T': { // Activate an extruder head. - int new_extruder = parse_int(line); - if (new_extruder != int(m_current_extruder)) { - m_current_extruder = new_extruder; - m_retracted = true; - buf.type = GCODELINETYPE_TOOL_CHANGE; - } else { - buf.type = GCODELINETYPE_NOOP; + try { + int new_extruder = parse_int(line); + if (new_extruder != int(m_current_extruder)) { + m_current_extruder = new_extruder; + //m_retracted = true; // why? + buf.type = GCODELINETYPE_TOOL_CHANGE; + } else { + buf.type = GCODELINETYPE_NOOP; + } + } catch (Slic3r::InvalidArgument &) { + // Ignore invalid GCodes. + eatws(line); + break; } break; } @@ -393,6 +408,47 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo return true; } +void PressureEqualizer::parse_activate_extruder(const std::string &line_str) +{ + if (size_t cmd_end = line_str.find("CTIVATE_EXTRUDER"); cmd_end != std::string::npos) { + bool error = false; + size_t extruder_pos_start = line_str.find("EXTRUDER", cmd_end + std::string_view("CTIVATE_EXTRUDER").size()) + + std::string_view("EXTRUDER").size(); + assert(line_str[extruder_pos_start - 1] == 'R'); + if (extruder_pos_start != std::string::npos) { + // remove next char until '-' or [0-9] + while (extruder_pos_start < line_str.size() && + (line_str[extruder_pos_start] == ' ' || line_str[extruder_pos_start] == '=' || + line_str[extruder_pos_start] == '\t')) + ++extruder_pos_start; + size_t extruder_pos_end = extruder_pos_start + 1; + while (extruder_pos_end < line_str.size() && line_str[extruder_pos_end] != ' ' && + line_str[extruder_pos_end] != '\t' && line_str[extruder_pos_end] != '\r' && + line_str[extruder_pos_end] != '\n') + ++extruder_pos_end; + std::string extruder_name = line_str.substr(extruder_pos_start, extruder_pos_end - extruder_pos_start); + // we have a "name". It may be whatever or "extruder" + X + for (size_t extruder_idx = 0; extruder_idx < m_extruder_names.size(); ++extruder_idx) { + if (m_extruder_names[extruder_idx] == extruder_name) { + m_current_extruder = extruder_idx; + return; + } + } + std::string extruder_str("extruder"); + if (extruder_str == extruder_name) { + m_current_extruder = 0; + return; + } + for (size_t extruder_idx = 0; extruder_idx < m_extruder_names.size(); ++extruder_idx) { + if (extruder_str + std::to_string(extruder_idx) == extruder_name) { + m_current_extruder = extruder_idx; + return; + } + } + } + } +} + void PressureEqualizer::output_gcode_line(const size_t line_idx) { GCodeLine &line = m_gcode_lines[line_idx]; diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index d596ebe9ebc..f1a025e8fbd 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1446,7 +1446,7 @@ static inline std::vector> mmu_segmentation_top_and_bott // color_idx == 0 means "don't know" extruder aka the underlying extruder. // As this region may split existing regions, we collect statistics over all regions for color_idx == 0. color_idx == 0 || config.perimeter_extruder == int(color_idx)) { - double perimeter_extrusion_width = config.get_computed_value("perimeter_extrusion_width"); + double perimeter_extrusion_width = config.get_computed_value("perimeter_extrusion_width", config.perimeter_extruder); out.extrusion_width = std::max(out.extrusion_width, perimeter_extrusion_width); out.top_solid_layers = std::max(out.top_solid_layers, config.top_solid_layers); out.bottom_solid_layers = std::max(out.bottom_solid_layers, config.bottom_solid_layers); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index a07a74390ab..724995e2d37 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1659,14 +1659,7 @@ void PlaceholderParser::append_custom_variables(std::map& values = entry.second; - //check if all values are empty - bool is_not_string = false; - for (uint16_t extruder_id = 0; extruder_id < nb_extruders; ++extruder_id) { - if (!values[extruder_id].empty()) { - is_not_string = true; - break; - } - } + bool is_not_string = true; std::vector string_values; //check if all values are strings if (is_not_string) { @@ -1679,6 +1672,7 @@ void PlaceholderParser::append_custom_variables(std::map -void _deserialize_maybe_from_prusa(std::map settings, +void _deserialize_maybe_from_prusa(const std::map settings, CONFIG_CLASS & config, const DynamicPrintConfig & global_config, ConfigSubstitutionContext & config_substitutions, @@ -7889,15 +7889,16 @@ void _deserialize_maybe_from_prusa(std::map se { std::map unknown_keys; const ConfigDef *def = config.def(); - for (auto [key, value] : settings) { + for (const auto &[key, value] : settings) { try { t_config_option_key opt_key = key; - PrintConfigDef::handle_legacy(opt_key, value, false); + std::string opt_value = value; + PrintConfigDef::handle_legacy(opt_key, opt_value, false); if (!opt_key.empty()) if (!def->has(opt_key)) { unknown_keys.emplace(key, value); } else { - config.set_deserialize(opt_key, value, config_substitutions); + config.set_deserialize(opt_key, opt_value, config_substitutions); } } catch (UnknownOptionException & /* e */) { // log & ignore @@ -7918,7 +7919,7 @@ void _deserialize_maybe_from_prusa(std::map se // from prusa: try again with from_prusa before handle_legacy if (check_prusa) { std::map settings_to_change; - for (auto [key, value] : unknown_keys) { + for (auto& [key, value] : unknown_keys) { t_config_option_key opt_key = key; std::map result = PrintConfigDef::from_prusa(opt_key, value, global_config); settings_to_change.insert(result.begin(), result.end()); @@ -7945,10 +7946,10 @@ void _deserialize_maybe_from_prusa(std::map se } } } - for (auto entry : settings_to_change) + for (const auto &entry : settings_to_change) config.set_deserialize(entry.first, entry.second, config_substitutions); } else { - for (auto [key, value] : unknown_keys) { + for (const auto& [key, value] : unknown_keys) { if (config_substitutions.rule != ForwardCompatibilitySubstitutionRule::Disable) { config_substitutions.add(ConfigSubstitution(key, value)); } @@ -7958,7 +7959,7 @@ void _deserialize_maybe_from_prusa(std::map se // set phony entries if (with_phony) { const ConfigDef *def = config.def(); - for (auto &[opt_key_width, opt_key_spacing] : + for (const auto& [opt_key_width, opt_key_spacing] : {std::pair{"extrusion_width", "extrusion_spacing"}, std::pair{"perimeter_extrusion_width", "perimeter_extrusion_spacing"}, std::pair{"external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing"}, @@ -7966,12 +7967,38 @@ void _deserialize_maybe_from_prusa(std::map se std::pair{"infill_extrusion_width", "infill_extrusion_spacing"}, std::pair{"solid_infill_extrusion_width", "solid_infill_extrusion_spacing"}, std::pair{"top_infill_extrusion_width", "top_infill_extrusion_spacing"}}) { - // if prusa has defined a width, or if the config has a default spacing that need to be overwritten - if (config.option(opt_key_width) != nullptr || config.option(opt_key_spacing) != nullptr) { - ConfigOption *opt_new = def->get(opt_key_spacing)->default_value.get()->clone(); - opt_new->deserialize(""); // note: deserialize don't set phony, only the ConfigBase::set_deserialize* - opt_new->set_phony(true); - config.set_key_value(opt_key_spacing, opt_new); + const ConfigOption *opt_width = config.option(opt_key_width); + const ConfigOption *opt_spacing = config.option(opt_key_spacing); + if (opt_width && opt_spacing) { + // if the config has a default spacing that need to be overwritten (if the width wasn't deserialized as phony) + if (settings.find(opt_key_spacing) == settings.end()) { + if (opt_width->is_phony()) { + if (opt_spacing->is_phony()) { + ConfigOption *opt_new = opt_spacing->clone(); + opt_new->set_phony(false); + config.set_key_value(opt_key_spacing, opt_new); + } + } else { + if (!opt_spacing->is_phony()) { + ConfigOption *opt_new = opt_spacing->clone(); + opt_new->set_phony(true); + config.set_key_value(opt_key_spacing, opt_new); + } + } + } else { + //spacing exist in the config, make sure one if phony + if (opt_spacing->is_phony() && opt_width->is_phony()) { + ConfigOption *opt_new = opt_width->clone(); + opt_new->set_phony(false); + config.set_key_value(opt_key_width, opt_new); + } + if (!opt_spacing->is_phony() && !opt_width->is_phony()) { + ConfigOption *opt_new = opt_width->clone(); + opt_new->set_phony(true); + config.set_key_value(opt_key_width, opt_new); + } + } + assert(config.option(opt_key_width)->is_phony() != config.option(opt_key_spacing)->is_phony()); } } } diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index e3f405662d2..2c89d1ff01c 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -1095,7 +1095,7 @@ void set_header_generate_with_date(bool with_date) std::string header_slic3r_generated() { if (config_file_header_with_date) - return std::string("generated by " SLIC3R_APP_KEY " " SLIC3R_VERSION " on ") + Utils::utc_timestamp(); + return std::string("generated by " SLIC3R_APP_KEY " " SLIC3R_VERSION_FULL " on ") + Utils::utc_timestamp(); else return std::string("generated by " SLIC3R_APP_KEY " " SLIC3R_VERSION " on"); } @@ -1103,7 +1103,7 @@ std::string header_slic3r_generated() std::string header_gcodeviewer_generated() { if (config_file_header_with_date) - return std::string("generated by " GCODEVIEWER_APP_KEY " " SLIC3R_VERSION " on ") + Utils::utc_timestamp(); + return std::string("generated by " GCODEVIEWER_APP_KEY " " SLIC3R_VERSION_FULL " on ") + Utils::utc_timestamp(); else return std::string("generated by " GCODEVIEWER_APP_KEY " " SLIC3R_VERSION " on"); } diff --git a/src/slic3r/GUI/CalibrationTempDialog.cpp b/src/slic3r/GUI/CalibrationTempDialog.cpp index 7ca7edfaf74..b76abb26ac9 100644 --- a/src/slic3r/GUI/CalibrationTempDialog.cpp +++ b/src/slic3r/GUI/CalibrationTempDialog.cpp @@ -102,21 +102,41 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { xyzScale = 1; model.objects[objs_idx[0]]->scale(xyzScale, xyzScale * 0.5, xyzScale); } + + // it's rotated but not around the good origin: correct that + double init_z_rotate_angle = Geometry::deg2rad(plat->config()->opt_float("init_z_rotate")); + Matrix3d rot_matrix = Eigen::Quaterniond(Eigen::AngleAxisd(init_z_rotate_angle, Vec3d{0,0,1})).toRotationMatrix(); + auto translate_from_rotation = [&rot_matrix, &model, &objs_idx](int idx, const Vec3d &translation) { + ModelVolume *vol_parent = model.objects[objs_idx[idx]]->volumes[model.objects[objs_idx[idx]]->volumes.size()-2]; + ModelVolume *vol = model.objects[objs_idx[idx]]->volumes[model.objects[objs_idx[idx]]->volumes.size()-1]; + //Geometry::Transformation trsf = vol->get_transformation(); + //Vec3d rotxtrans = rot_matrix * translation; + //Vec3d offset_reste = trsf.get_offset()- translation; + //Vec3d tot = rot_matrix * translation + trsf.get_offset()- translation; + //Vec3d tot2 = translation + trsf.get_offset()- rot_matrix * translation; + //trsf.set_offset( (rot_matrix *translation) - translation + trsf.get_offset()); + Geometry::Transformation trsf = vol->get_transformation(); + trsf.set_offset( (rot_matrix *translation) + vol_parent->get_offset()); + vol->set_transformation(trsf); + }; //add 8 others float zshift = (1 - xyzScale) / 2; if (temperature > 175 && temperature < 290 && temperature%5==0) { + Vec3d translate{ 0 - xyzScale * 3.75, -xyzScale * 2.7, xyzScale * (0 * 10 - 2.45) }; add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("t"+std::to_string(temperature)+".amf")).string(), - //Vec3d{ xyzScale * 5, - xyzScale * 2.5, zshift - xyzScale * 2.5}, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); - Vec3d{ 8 - xyzScale * 5, -xyzScale * 2.3, xyzScale * (0 * 10 - 2.45) }, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 }); + translate, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 }); + translate_from_rotation(0, translate); } for (int16_t i = 1; i < nb_items; i++) { add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("Smart_compact_temperature_calibration_item.amf")).string(), Vec3d{ 0,0, i * 10 * xyzScale }, Vec3d{ xyzScale, xyzScale * 0.5, xyzScale }); int sub_temp = temperature - i * step_temp; if (sub_temp > 175 && sub_temp < 290 && sub_temp % 5 == 0) { + Vec3d translate{ 0 - xyzScale * 3.75, -xyzScale * 2.7, xyzScale * (0 * 10 - 2.45) }; add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("t" + std::to_string(sub_temp) + ".amf")).string(), - Vec3d{ 8 - xyzScale * 5, -xyzScale * 2.3, xyzScale * (i * 10 - 2.5) }, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 }); + translate, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 }); + translate_from_rotation(0, translate); } } diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index b45070e5cd7..bc347621b42 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -74,10 +74,10 @@ Control::Control( wxWindow *parent, const wxValidator& val, const wxString& name) : wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), - m_higher_value (higherValue), - m_min_value(minValue), - m_max_value(maxValue), + m_lower_tick(lowerValue), + m_higher_tick (higherValue), + m_min_tick(minValue), + m_max_tick(maxValue), m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) { @@ -218,8 +218,8 @@ void Control::sys_color_changed() int Control::GetActiveValue() const { return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; + m_lower_tick : m_selection == ssHigher ? + m_higher_tick : -1; } wxSize Control::get_min_size() const @@ -236,10 +236,10 @@ wxSize Control::DoGetBestSize() const return get_min_size(); } -void Control::SetLowerValue(const int lower_val) +void Control::SetLowerValue(const int lower_tick) { m_selection = ssLower; - m_lower_value = lower_val; + m_lower_tick = lower_tick; correct_lower_value(); Refresh(); Update(); @@ -249,10 +249,10 @@ void Control::SetLowerValue(const int lower_val) ProcessWindowEvent(e); } -void Control::SetHigherValue(const int higher_val) +void Control::SetHigherValue(const int higher_tick) { m_selection = ssHigher; - m_higher_value = higher_val; + m_higher_tick = higher_tick; correct_higher_value(); Refresh(); Update(); @@ -262,11 +262,11 @@ void Control::SetHigherValue(const int higher_val) ProcessWindowEvent(e); } -void Control::SetSelectionSpan(const int lower_val, const int higher_val) +void Control::SetSelectionSpan(const int lower_tick, const int higher_tick) { - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) + m_lower_tick = std::max(lower_tick, m_min_tick); + m_higher_tick = std::max(std::min(higher_tick, m_max_tick), m_lower_tick); + if (m_lower_tick < m_higher_tick) m_is_one_layer = false; Refresh(); @@ -277,9 +277,9 @@ void Control::SetSelectionSpan(const int lower_val, const int higher_val) ProcessWindowEvent(e); } -void Control::SetMaxValue(const int max_value) +void Control::SetMaxValue(const int max_tick) { - m_max_value = max_value; + m_max_tick = max_tick; Refresh(); Update(); } @@ -322,14 +322,14 @@ double Control::get_scroll_step() { const wxSize sz = get_size(); const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); + return double(slider_len - SLIDER_MARGIN * 2) / (m_max_tick - m_min_tick); } // get position on the slider line from entered value -wxCoord Control::get_position_from_value(const int value) +wxCoord Control::get_position_from_tick(const int tick) { const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; + const int val = is_horizontal() ? tick : m_max_tick - tick; return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); } @@ -350,13 +350,13 @@ void Control::get_size(int* w, int* h) const double Control::get_double_value(const SelectedSlider& selection) { - if (m_values.empty() || m_lower_value<0) + if (m_values.empty() || m_lower_tick<0) return 0.0; - if (m_values.size() <= size_t(m_higher_value)) { + if (m_values.size() <= size_t(m_higher_tick)) { correct_higher_value(); return m_values.back(); } - return m_values[selection == ssLower ? m_lower_value : m_higher_value]; + return m_values[selection == ssLower ? m_lower_tick : m_higher_tick]; } int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) @@ -506,12 +506,12 @@ void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) { const double step = get_scroll_step(); if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); + lower_pos = SLIDER_MARGIN + int(m_lower_tick*step + 0.5); + higher_pos = SLIDER_MARGIN + int(m_higher_tick*step + 0.5); } else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); + lower_pos = SLIDER_MARGIN + int((m_max_tick - m_lower_tick)*step + 0.5); + higher_pos = SLIDER_MARGIN + int((m_max_tick - m_higher_tick)*step + 0.5); } } @@ -539,8 +539,8 @@ void Control::render() wxPaintDC dc(this); dc.SetFont(m_font); - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); + const wxCoord lower_pos = get_position_from_tick(m_lower_tick); + const wxCoord higher_pos = get_position_from_tick(m_higher_tick); // draw colored band on the background of a scroll line // and only in a case of no-empty m_values @@ -588,7 +588,7 @@ bool Control::is_wipe_tower_layer(int tick) const void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; if (!m_enable_action_icon) return; @@ -650,7 +650,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc) get_size(&width, &height); int tick = get_tick_near_point(m_moving_pos); - if (tick == m_higher_value || tick == m_lower_value) + if (tick == m_higher_tick || tick == m_lower_tick) return ; auto draw_ticks = [this](wxDC& dc, wxPoint pos, int margin=0 ) @@ -674,7 +674,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc) if (tick > 0) // this tick exists and should be marked as a focused { - wxCoord new_pos = get_position_from_value(tick); + wxCoord new_pos = get_position_from_tick(tick); const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); dc.SetPen(DARK_COLOR_PEN); @@ -687,11 +687,11 @@ void Control::draw_tick_on_mouse_position(wxDC& dc) return; } - tick = get_value_from_position(m_moving_pos); - if (tick > m_max_value || tick < m_min_value || tick == m_higher_value || tick == m_lower_value) + tick = get_tick_from_position(m_moving_pos); + if (tick > m_max_tick || tick < m_min_tick || tick == m_higher_tick || tick == m_lower_tick) return; - wxCoord new_pos = get_position_from_value(tick); + wxCoord new_pos = get_position_from_tick(tick); const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); //draw info line @@ -869,7 +869,7 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType l } wxColour old_clr = dc.GetTextForeground(); - const wxPen& pen = is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? DARK_COLOR_PEN : wxPen(old_clr); + const wxPen& pen = is_wipe_tower_layer(tick) && (tick == m_lower_tick || tick == m_higher_tick) ? DARK_COLOR_PEN : wxPen(old_clr); dc.SetPen(pen); dc.SetTextForeground(pen.GetColour()); @@ -883,7 +883,7 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType l void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const { - draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, ltHeightWithLayer, selection == ssLower); + draw_tick_text(dc, pos, selection == ssLower ? m_lower_tick : m_higher_tick, ltHeightWithLayer, selection == ssLower); } void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) @@ -979,7 +979,7 @@ void Control::draw_ticks(wxDC& dc) break; } - const wxCoord pos = get_position_from_value(tick.tick); + const wxCoord pos = get_position_from_tick(tick.tick); draw_ticks_pair(dc, pos, mid, 7); // if current tick if focused, we should to use a specific "focused" icon @@ -1092,7 +1092,7 @@ void Control::draw_colored_band(wxDC& dc) if ( (m_mode == SingleExtruder && tick_it->type == ColorChange ) || (m_mode == MultiAsSingle && (tick_it->type == ToolChange || tick_it->type == ColorChange)) ) { - const wxCoord pos = get_position_from_value(tick_it->tick); + const wxCoord pos = get_position_from_tick(tick_it->tick); is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : main_band.SetBottom(pos - 1); @@ -1110,15 +1110,21 @@ void Control::draw_colored_band(wxDC& dc) void Control::Ruler::init(const std::vector& values) { - max_values.clear(); - max_values.reserve(std::count(values.begin(), values.end(), values.front())); + sequences.clear(); + if(values.empty()) + return; - auto it = std::find(values.begin() + 1, values.end(), values.front()); - while (it != values.end()) { - max_values.push_back(*(it - 1)); - it = std::find(it + 1, values.end(), values.front()); + sequences.reserve(std::count(values.begin(), values.end(), values.front())); + double start = values.front(); + double last = start; + for(size_t idx = 1; idx < values.size(); ++idx) { + if (values[idx] <= last) { + sequences.emplace_back(start, last); + start = values[idx]; + } + last = values[idx]; } - max_values.push_back(*(it - 1)); + sequences.emplace_back(start, last); } void Control::Ruler::update(wxWindow* win, const std::vector& values, double scroll_step) @@ -1129,46 +1135,29 @@ void Control::Ruler::update(wxWindow* win, const std::vector& values, do long_step = -1.0; return; } - int DPI = GUI::get_dpi_for_window(win); - int pixels_per_sm = lround((double)(DPI) * 5.0/25.4); - - if (lround(scroll_step) > pixels_per_sm) { - long_step = -1.0; - return; - } - int pow = -2; - int step = 0; - auto end_it = std::find(values.begin() + 2, values.end(), values.front()); - - while (pow < 3) { - for (int istep : {1, 2, 5}) { - double val = (double)istep * std::pow(10,pow); - auto val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); - - if (val_it == values.end()) - break; - int tick = val_it - values.begin(); - - // find next tick with istep - val *= 2; - val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); - // count of short ticks between ticks - int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick; - - if (lround(short_ticks_cnt * scroll_step) > pixels_per_sm) { - step = istep; - // there couldn't be more then 10 short ticks between ticks - short_step = 0.1 * short_ticks_cnt; - break; + int DPI = GUI::get_dpi_for_window(win); + int pixels_per_long_step = lround((double)(DPI) * 5.0/25.4); + int pixels_per_small_step = lround((double)(DPI) * 1/25.4); + + //compute max number of visible steps + if (pixels_per_long_step <= scroll_step) { + // enough space to show all layers numbers + short_step = 0; + long_step = 1; + } else { + // not enough space to show all layers numbers, use small ticks in-between + long_step = 1 + pixels_per_long_step / scroll_step; + short_step = 1; + if (pixels_per_small_step > scroll_step) { + // not enough space to show small ticks, let some space + short_step = 1 + pixels_per_small_step / scroll_step; + if (long_step != short_step * int(long_step / short_step)) { + // long step can't be divided by short_step, lengthen long_step + long_step = short_step * ( 1 + int(long_step / short_step)); } } - if (step > 0) - break; - pow++; } - - long_step = step == 0 ? -1.0 : (double)step* std::pow(10, pow); } void Control::draw_ruler(wxDC& dc) @@ -1186,79 +1175,82 @@ void Control::draw_ruler(wxDC& dc) dc.SetPen(GREY_PEN); wxColour old_clr = dc.GetTextForeground(); dc.SetTextForeground(GREY_PEN.GetColour()); - if (m_ruler.long_step < 0) { for (size_t tick = 1; tick < m_values.size(); tick++) { - wxCoord pos = get_position_from_value(tick); + wxCoord pos = get_position_from_tick(tick); draw_ticks_pair(dc, pos, mid, 5); draw_tick_text(dc, wxPoint(mid, pos), tick); } } else { - auto draw_short_ticks = [this, mid](wxDC& dc, double& current_tick, int max_tick) { - while (current_tick < max_tick) { - wxCoord pos = get_position_from_value(lround(current_tick)); + auto draw_short_ticks = [this, mid](wxDC& dc, int& current_tick, int max_tick) { + if (current_tick >= max_tick || m_ruler.short_step <= 0) { + return; + } + assert(current_tick < m_values.size()); + assert(max_tick < m_values.size()); + assert(max_tick <= m_max_tick); + double current_value = m_values[current_tick]; + while (current_tick + m_ruler.short_step <= max_tick) { + wxCoord pos = get_position_from_tick(current_tick); draw_ticks_pair(dc, pos, mid, 2); + // go to next value current_tick += m_ruler.short_step; - if (current_tick > m_max_value) - break; } }; - double short_tick = std::nan(""); + int short_tick = 0; int tick = 0; - double value = 0.0; size_t sequence = 0; + //get the ticks of all sequence jump (start pos) + std::vector sequence_tick_start; + { + double start = m_values.front(); + double last = start; + sequence_tick_start.push_back(0); + for(size_t idx = 1; idx < m_values.size(); ++idx) { + if (m_values[idx] <= last) { + sequence_tick_start.push_back(idx); + } + last = m_values[idx]; + } + } int prev_y_pos = -1; wxCoord label_height = dc.GetMultiLineTextExtent("0").y - 2; int values_size = (int)m_values.size(); - - while (tick <= m_max_value) { - value += m_ruler.long_step; - if (value > m_ruler.max_values[sequence]) { - value = m_ruler.long_step; - for (; tick < values_size; tick++) - if (m_values[tick] < value) - break; - // short ticks from the last tick to the end of current sequence + assert(m_values.size() > m_max_tick); + //iterate on all layer z values + while (tick <= m_max_tick) { + // if we will go over the next sequence next time, don't print this full tick, go to the next start + if (sequence + 1 < sequence_tick_start.size() && sequence_tick_start[sequence+1] < tick + m_ruler.long_step) { + sequence++; + tick = sequence_tick_start[sequence]; //note: first sequence can be empty. - if(!std::isnan(short_tick)) + if (short_tick < tick) { draw_short_ticks(dc, short_tick, tick); - if (sequence < m_ruler.count() - 1) sequence++; - } - short_tick = tick; - - for (; tick < values_size; tick++) { - if (m_values[tick] == value) - break; - if (m_values[tick] > value) { - if (tick > 0) - tick--; - break; + short_tick = tick; } } - if (tick > m_max_value) - break; - wxCoord pos = get_position_from_value(tick); + wxCoord pos = get_position_from_tick(tick); draw_ticks_pair(dc, pos, mid, 5); if (prev_y_pos < 0 || prev_y_pos - pos >= label_height) { draw_tick_text(dc, wxPoint(mid, pos), tick); prev_y_pos = pos; } - assert(!std::isnan(short_tick)); + assert(short_tick >= 0); draw_short_ticks(dc, short_tick, tick); - - if (value == m_ruler.max_values[sequence]) { - value = 0.0; - if (sequence < m_ruler.count() - 1) sequence++; - tick++; - } + short_tick = tick; + + //go to next step + tick += m_ruler.long_step; } // short ticks from the last tick to the end - assert(!std::isnan(short_tick)); - draw_short_ticks(dc, short_tick, m_max_value); + assert(short_tick >= 0); + draw_short_ticks(dc, short_tick, m_max_tick); + //big tick at the end + draw_ticks_pair(dc, get_position_from_tick(m_max_tick), mid, 5); } dc.SetTextForeground(old_clr); @@ -1349,7 +1341,7 @@ void Control::update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, co m_rect_higher_thumb = rect; } -int Control::get_value_from_position(const wxCoord x, const wxCoord y) +int Control::get_tick_from_position(const wxCoord x, const wxCoord y) { const int height = get_size().y; const double step = get_scroll_step(); @@ -1357,7 +1349,7 @@ int Control::get_value_from_position(const wxCoord x, const wxCoord y) if (is_horizontal()) return int(double(x - SLIDER_MARGIN) / step + 0.5); - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); + return int(m_min_tick + double(height - SLIDER_MARGIN - y) / step + 0.5); } bool Control::is_lower_thumb_editable() @@ -1387,7 +1379,7 @@ bool Control::is_point_in_rect(const wxPoint& pt, const wxRect& rect) int Control::get_tick_near_point(const wxPoint& pt) { for (auto tick : m_ticks.ticks) { - const wxCoord pos = get_position_from_value(tick.tick); + const wxCoord pos = get_position_from_tick(tick.tick); if (is_horizontal()) { if (pos - 4 <= pt.x && pt.x <= pos + 4) @@ -1431,7 +1423,7 @@ void Control::OnLeftDown(wxMouseEvent& event) m_mouse = maCogIconClick; else if (m_draw_mode == dmRegular) { if (is_point_in_rect(pos, m_rect_tick_action)) { - auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_tick : m_higher_tick }); m_mouse = it == m_ticks.ticks.end() ? maAddTick : maDeleteTick; } else if (is_point_in_rect(pos, m_rect_revert_icon)) @@ -1450,24 +1442,24 @@ void Control::OnLeftDown(wxMouseEvent& event) void Control::correct_lower_value() { - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; + if (m_lower_tick < m_min_tick) + m_lower_tick = m_min_tick; + else if (m_lower_tick > m_max_tick) + m_lower_tick = m_max_tick; - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) - m_higher_value = m_lower_value; + if ((m_lower_tick >= m_higher_tick && m_lower_tick <= m_max_tick) || m_is_one_layer) + m_higher_tick = m_lower_tick; } void Control::correct_higher_value() { - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; + if (m_higher_tick > m_max_tick) + m_higher_tick = m_max_tick; + else if (m_higher_tick < m_min_tick) + m_higher_tick = m_min_tick; - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) - m_lower_value = m_higher_value; + if ((m_higher_tick <= m_lower_tick && m_higher_tick >= m_min_tick) || m_is_one_layer) + m_lower_tick = m_higher_tick; } wxString Control::get_tooltip(int tick/*=-1*/) @@ -1606,7 +1598,7 @@ int Control::get_edited_tick_for_position(const wxPoint pos, Type type /*= Color if (m_ticks.empty()) return -1; - int tick = get_value_from_position(pos); + int tick = get_tick_from_position(pos); auto it = std::lower_bound(m_ticks.ticks.begin(), m_ticks.ticks.end(), TickCode{ tick }); while (it != m_ticks.ticks.begin()) { @@ -1630,7 +1622,7 @@ void Control::OnMotion(wxMouseEvent& event) m_focus = fiOneLayerIcon; else if (is_point_in_rect(pos, m_rect_tick_action)) { m_focus = fiActionIcon; - tick = m_selection == ssLower ? m_lower_value : m_higher_value; + tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; } else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) m_focus = fiRevertIcon; @@ -1646,8 +1638,8 @@ void Control::OnMotion(wxMouseEvent& event) else { tick = get_tick_near_point(pos); if (tick < 0 && m_is_wipe_tower) { - tick = get_value_from_position(pos); - m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? + tick = get_tick_from_position(pos); + m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_tick || tick == m_higher_tick) ? fiSmartWipeTower : fiTick; } else @@ -1657,16 +1649,16 @@ void Control::OnMotion(wxMouseEvent& event) } else if (m_is_left_down || m_is_right_down) { if (m_selection == ssLower) { - int current_value = m_lower_value; - m_lower_value = get_value_from_position(pos.x, pos.y); + int current_value = m_lower_tick; + m_lower_tick = get_tick_from_position(pos.x, pos.y); correct_lower_value(); - action = (current_value != m_lower_value); + action = (current_value != m_lower_tick); } else if (m_selection == ssHigher) { - int current_value = m_higher_value; - m_higher_value = get_value_from_position(pos.x, pos.y); + int current_value = m_higher_tick; + m_higher_tick = get_tick_from_position(pos.x, pos.y); correct_higher_value(); - action = (current_value != m_higher_value); + action = (current_value != m_higher_tick); } m_moving_pos = wxDefaultPosition; } @@ -1690,7 +1682,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_tick : m_higher_tick); std::vector icons = get_extruder_color_icons(true); @@ -1721,7 +1713,7 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); wxMenu* add_color_change_menu = new wxMenu(); @@ -1817,11 +1809,11 @@ void Control::move_current_thumb(const bool condition) m_selection = ssHigher; if (m_selection == ssLower) { - m_lower_value -= delta; + m_lower_tick -= delta; correct_lower_value(); } else if (m_selection == ssHigher) { - m_higher_value -= delta; + m_higher_tick -= delta; correct_higher_value(); } Refresh(); @@ -1949,7 +1941,7 @@ void Control::OnRightDown(wxMouseEvent& event) m_mouse = maNone; if (m_draw_mode == dmRegular) { if (is_point_in_rect(pos, m_rect_tick_action)) { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; m_mouse = m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end() ? maAddMenu : maEditMenu; } @@ -1962,9 +1954,9 @@ void Control::OnRightDown(wxMouseEvent& event) return; if (m_selection == ssLower) - m_higher_value = m_lower_value; + m_higher_tick = m_lower_tick; else - m_lower_value = m_higher_value; + m_lower_tick = m_higher_tick; // set slider to "one layer" mode m_is_right_down = m_is_one_layer = true; @@ -2094,7 +2086,7 @@ void Control::show_edit_context_menu() { wxMenu menu; - std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_tick : m_higher_tick }); if (it->type == ToolChange) { if (m_mode == MultiAsSingle) @@ -2371,7 +2363,7 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { if (m_selection == ssUndef) return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; if ( !check_ticks_changed_event(type) ) return; @@ -2402,7 +2394,7 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) { if (m_selection == ssUndef) return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; auto it = m_ticks.ticks.find(TickCode{ tick }); if (it != m_ticks.ticks.end() || // this tick is already exist @@ -2432,8 +2424,8 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) this->GetPosition(&width, &height); pos = is_horizontal() ? - wxPoint(get_position_from_value(tick), height + coord) : - wxPoint(width + coord, get_position_from_value(tick)); + wxPoint(get_position_from_tick(tick), height + coord) : + wxPoint(width + coord, get_position_from_tick(tick)); } GUI::wxGetApp().plater()->PopupMenu(&menu, pos); @@ -2445,7 +2437,7 @@ void Control::delete_current_tick() if (m_selection == ssUndef) return; - auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_tick : m_higher_tick }); if (it == m_ticks.ticks.end() || !check_ticks_changed_event(it->type)) return; @@ -2458,7 +2450,7 @@ void Control::delete_current_tick() void Control::edit_tick(int tick/* = -1*/) { if (tick < 0) - tick = m_selection == ssLower ? m_lower_value : m_higher_value; + tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); if (it == m_ticks.ticks.end() || @@ -2475,8 +2467,8 @@ void Control::switch_one_layer_mode() { m_is_one_layer = !m_is_one_layer; if (!m_is_one_layer) { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); + SetLowerValue(m_min_tick); + SetHigherValue(m_max_tick); } m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (m_selection == ssUndef) m_selection = ssHigher; @@ -2485,8 +2477,8 @@ void Control::switch_one_layer_mode() // discard all custom changes on DoubleSlider void Control::discard_all_thicks() { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); + SetLowerValue(m_min_tick); + SetHigherValue(m_max_tick); m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (m_selection == ssUndef) m_selection = ssHigher; @@ -2502,7 +2494,7 @@ void Control::move_current_thumb_to_pos(wxPoint pos) { const int tick_val = get_tick_near_point(pos); const int mouse_val = tick_val >= 0 && m_draw_mode == dmRegular ? tick_val : - get_value_from_position(pos); + get_tick_from_position(pos); if (mouse_val >= 0) { if (m_selection == ssLower) { SetLowerValue(mouse_val); @@ -2539,7 +2531,7 @@ void Control::edit_extruder_sequence() std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() std::uniform_int_distribution<> distrib(0, extr_cnt-1); - while (tick <= m_max_value) + while (tick <= m_max_tick) { bool color_repetition = false; if (m_extruders_sequence.random_sequence) { @@ -2578,8 +2570,8 @@ void Control::edit_extruder_sequence() void Control::jump_to_value() { - double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value], - m_values[m_min_value], m_values[m_max_value], m_draw_mode); + double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_tick : m_higher_tick], + m_values[m_min_tick], m_values[m_max_tick], m_draw_mode); if (value < 0.0) return; diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 016903b26cf..10c1a050076 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -222,12 +222,12 @@ class Control : public wxControl void msw_rescale(); void sys_color_changed(); - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } + int GetMinValue() const { return m_min_tick; } + int GetMaxValue() const { return m_max_tick; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_tick]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_tick]; } + int GetLowerValue() const { return m_lower_tick; } + int GetHigherValue() const { return m_higher_tick; } int GetActiveValue() const; double GetLowerValueD() { return get_double_value(ssLower); } double GetHigherValueD() { return get_double_value(ssHigher); } @@ -235,11 +235,11 @@ class Control : public wxControl wxSize get_min_size() const ; // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetLowerValue (const int lower_val); - void SetHigherValue(const int higher_val); - void SetSelectionSpan(const int lower_val, const int higher_val); + void SetLowerValue (const int lower_tick); + void SetHigherValue(const int higher_tick); + void SetSelectionSpan(const int lower_tick, const int higher_tick); - void SetMaxValue(const int max_value); + void SetMaxValue(const int max_tick); void SetKoefForLabels(const double koef) { m_label_koef = koef; } void SetSliderValues(const std::vector& values); void ChangeOneLayerLock(); @@ -265,8 +265,8 @@ class Control : public wxControl bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } bool is_one_layer() const { return m_is_one_layer; } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_lower_at_min() const { return m_lower_tick == m_min_tick; } + bool is_higher_at_max() const { return m_higher_tick == m_max_tick; } bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } void OnPaint(wxPaintEvent& ) { render(); } @@ -340,9 +340,9 @@ class Control : public wxControl double get_scroll_step(); wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const; void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); } - wxCoord get_position_from_value(const int value); + int get_tick_from_position(const wxCoord x, const wxCoord y); + int get_tick_from_position(const wxPoint pos) { return get_tick_from_position(pos.x, pos.y); } + wxCoord get_position_from_tick(const int tick); wxSize get_size() const; void get_size(int* w, int* h) const; double get_double_value(const SelectedSlider& selection); @@ -368,10 +368,10 @@ class Control : public wxControl bool is_osx { false }; wxFont m_font; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; + int m_min_tick; + int m_max_tick; + int m_lower_tick; + int m_higher_tick; bool m_render_as_disabled{ false }; @@ -449,15 +449,17 @@ class Control : public wxControl std::vector m_segm_pens; struct Ruler { - double long_step; - double short_step; - std::vector max_values;// max value for each object/instance in sequence print - // > 1 for sequential print + //double long_step; + //double short_step; + int long_step; //in tick + int short_step; //in tick + // max value for each object/instance in sequence print, > 1 for sequential print (not used anymore) + std::vector> sequences; void init(const std::vector& values); void update(wxWindow* win, const std::vector& values, double scroll_step); bool is_ok() { return long_step > 0 && short_step > 0; } - size_t count() { return max_values.size(); } + size_t count() { return sequences.size(); } } m_ruler; }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index f1853972f13..61757e19deb 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -191,13 +191,56 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move move.volumetric_rate(), move.mm3_per_mm, move.extruder_id, move.cp_color_id, { { endpoint, endpoint } }, move.layer_duration, move.time }); } +namespace quick_pow10 +{ + const int pow10[10] = { + 1, 10, 100, 1000, 10000, + 100000, 1000000, 10000000, 100000000, 1000000000 + }; +} -void GCodeViewer::Extrusions::Range::update_from(const float value) +void GCodeViewer::Extrusions::Range::update_from(const float value, const uint8_t decimal_precision) { - if (value != max && value != min) - ++count; - min = std::min(min, value); - max = std::max(max, value); + if (has_min_max != max && has_min_max != min && has_min_max < 3) + ++has_min_max; + + if (full_precision_min > value) { + full_precision_min = value; + // rounding min to lowest value + min = (int32_t(value * quick_pow10::pow10[decimal_precision])) / float(quick_pow10::pow10[decimal_precision]); + assert(min <= full_precision_min); + } + if (full_precision_max < value) { + full_precision_max = value; + // rounding max to highest value + max = (int32_t(value * quick_pow10::pow10[decimal_precision])) / float(quick_pow10::pow10[decimal_precision]); + if (max < full_precision_max) + max = (int32_t(0.5f + value * quick_pow10::pow10[decimal_precision])) / float(quick_pow10::pow10[decimal_precision]); + //assert(max >= full_precision_max); + } + + // if you want to keep every different values + //++values[value]; + //if (values.size() >= 10000) { + // // Too many items, remove some + // double min_distance = (max - min) / 1000; + // int min_items = total_count / 1000; + // float prev_value = min; + // float midway = min + (max - min) / 2; + // assert(values.begin()->first == min); + // for (auto it = std::next(values.begin()); it != values.end(); ++it) { + // if (it->second < min_items && it->first - prev_value < min_distance && it->first != max) { + // // too little, too near, eliminate. + // if(it->first < midway) + // std::prev(it)->second += it->second; + // else + // std::next(it)->second += it->second; + // it = values.erase(it); + // } else + // prev_value = it->first; + // } + // BOOST_LOG_TRIVIAL(debug) << "Too many range items, reducing from 10000 to " << values.size(); + //} total_count++; // don't keep outliers @@ -206,11 +249,14 @@ void GCodeViewer::Extrusions::Range::update_from(const float value) mins[idx] = std::min(mins[idx], value); maxs[idx] = std::max(maxs[idx], value); } + void GCodeViewer::Extrusions::Range::reset() { - min = FLT_MAX; - max = -FLT_MAX; - count = 0; + min = FLT_MAX; + max = -FLT_MAX; + full_precision_min = FLT_MAX; + full_precision_max = -FLT_MAX; + has_min_max = 0; total_count = 0; for (size_t idx = 0; idx < 20; idx++) { counts[idx] = 0; @@ -219,40 +265,43 @@ void GCodeViewer::Extrusions::Range::reset() } } -float GCodeViewer::Extrusions::Range::get_max_no_outliers(float ratio_outlier) const +float GCodeViewer::Extrusions::Range::get_current_max() const { - size_t min_number = ratio_outlier * total_count / 20; - for (size_t idx = 19; idx < 20; --idx) { - if (counts[idx] > min_number) { - return maxs[idx]; + float current_max = max; + if (ratio_outlier > 0) { + size_t min_number = ratio_outlier * total_count / 20; + for (size_t idx = 19; idx < 20; --idx) { + if (counts[idx] > min_number) { + current_max = maxs[idx]; + break; + } } } - assert(false); - return max; + if (this->user_max > 0) + current_max = std::min(current_max, this->user_max); + return current_max; } -float GCodeViewer::Extrusions::Range::get_min_no_outliers(float ratio_outlier) const +float GCodeViewer::Extrusions::Range::get_current_min() const { - size_t min_number = ratio_outlier * total_count / 20; - for (size_t idx = 0; idx < 20; ++idx) { - if (counts[idx] > min_number) { - return mins[idx]; + float current_min = min; + if (ratio_outlier > 0) { + size_t min_number = this->ratio_outlier * total_count / 20; + for (size_t idx = 0; idx < 20; ++idx) { + if (counts[idx] > min_number) { + current_min = mins[idx]; + break; + } } } - assert(false); - return min; + if (this->user_min > 0) + current_min = std::max(current_min, this->user_min); + return current_min; } -float GCodeViewer::Extrusions::Range::step_size_no_outliers(float ratio_outlier, bool log) const +float GCodeViewer::Extrusions::Range::step_size() const { - return step_size(get_min_no_outliers(ratio_outlier), get_max_no_outliers(ratio_outlier), log); -} - -GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at_no_outliers(float value, - float ratio_outlier, - bool log) const -{ - return get_color_at(get_min_no_outliers(ratio_outlier), get_max_no_outliers(ratio_outlier), value, log); + return step_size(get_current_min(), get_current_max(), this->log_scale); } float GCodeViewer::Extrusions::Range::step_size(float min, float max, bool log) @@ -267,19 +316,27 @@ float GCodeViewer::Extrusions::Range::step_size(float min, float max, bool log) return (max - min) / (static_cast(GCodeViewer::Range_Colors.size()) - 1.0f); } -GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float min, float max, float value, bool log) +GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value) const { + const float current_min = get_current_min(); + const float current_max = get_current_max(); + + if (value < current_min) + return Too_Low_Value_Color; + if (value > current_max) + return Too_High_Value_Color; + // Input value scaled to the colors range - const float step = step_size(min, max, log); + const float step = step_size(current_min, current_max, this->log_scale); float global_t; - if (log) + if (this->log_scale) { - float min_range = min; + float min_range = current_min; if (min_range == 0) min_range = 0.001f; global_t = (step != 0.0f) ? std::max(0.0f, std::log(value / min_range)) / step : 0.0f; // lower limit of 0.0f } else - global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f + global_t = (step != 0.0f) ? std::max(0.0f, value - current_min) / step : 0.0f; // lower limit of 0.0f const size_t color_max_idx = Range_Colors.size() - 1; @@ -298,6 +355,23 @@ GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float min, float return ret; } +GCodeViewer::Extrusions::Range* GCodeViewer::Extrusions::Ranges::get(EViewType type){ + switch (type) + { + case EViewType::Height: return &this->height; + case EViewType::Width: return &this->width; + case EViewType::Feedrate: return &this->feedrate; + case EViewType::FanSpeed: return &this->fan_speed; + case EViewType::Temperature: return &this->temperature; + case EViewType::LayerTime: return &this->layer_duration; + case EViewType::Chronology: return &this->elapsed_time; + case EViewType::VolumetricRate: return &this->volumetric_rate; + case EViewType::VolumetricFlow: return &this->volumetric_flow; + default: return nullptr; + } +} + + GCodeViewer::SequentialRangeCap::~SequentialRangeCap() { if (ibo > 0) glsafe(::glDeleteBuffers(1, &ibo)); @@ -652,6 +726,8 @@ const std::vector GCodeViewer::Range_Colors{ { const GCodeViewer::Color GCodeViewer::Wipe_Color = { 1.0f, 1.0f, 0.0f, 1.0f }; const GCodeViewer::Color GCodeViewer::Neutral_Color = { 0.25f, 0.25f, 0.25f, 1.0f }; +const GCodeViewer::Color GCodeViewer::Too_Low_Value_Color = { 0.62f, 0.537f, 0.784f, 0.5f }; +const GCodeViewer::Color GCodeViewer::Too_High_Value_Color = { 0.784f, 0.537f, 0.668f, 0.5f }; GCodeViewer::GCodeViewer() { @@ -919,23 +995,21 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v { case EMoveType::Extrude: { - m_extrusions.ranges.height.update_from(round_to_bin(curr.height)); - m_extrusions.ranges.width.update_from(round_to_bin(curr.width)); - m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); - m_extrusions.ranges.temperature.update_from(curr.temperature); - if (curr.extrusion_role != erCustom || is_visible(erCustom)) { - m_extrusions.ranges.volumetric_rate.update_from(round_to_bin(curr.volumetric_rate())); - m_extrusions.ranges.volumetric_flow.update_from(round_to_bin(curr.mm3_per_mm)); - } + m_extrusions.ranges.height.update_from(curr.height, 3); + m_extrusions.ranges.width.update_from(curr.width, 3); + m_extrusions.ranges.fan_speed.update_from(curr.fan_speed, 0); + m_extrusions.ranges.temperature.update_from(curr.temperature, 0); + m_extrusions.ranges.volumetric_rate.update_from(curr.volumetric_rate(), 3); + m_extrusions.ranges.volumetric_flow.update_from(curr.mm3_per_mm, 3); if (curr.layer_duration > 0.f) - m_extrusions.ranges.layer_duration.update_from(curr.layer_duration); - m_extrusions.ranges.elapsed_time.update_from(curr.time); + m_extrusions.ranges.layer_duration.update_from(curr.layer_duration, 0); + m_extrusions.ranges.elapsed_time.update_from(curr.time, 0); [[fallthrough]]; } case EMoveType::Travel: { if (m_buffers[buffer_id(curr.type)].visible) - m_extrusions.ranges.feedrate.update_from(curr.feedrate); + m_extrusions.ranges.feedrate.update_from(curr.feedrate, 1); break; } @@ -2328,26 +2402,15 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool switch (m_view_type) { case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } - case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at_with_outliers(path.height); break; } - case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at_with_outliers(path.width); break; } - case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at_with_outliers(path.feedrate); break; } - case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at_with_outliers(path.fan_speed); break; } - case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at_with_outliers(path.temperature); break; } - case EViewType::LayerTime: { color = m_extrusions.ranges.layer_duration.get_color_at_with_outliers(path.layer_time); break; } - case EViewType::LayerTimeLog: { color = m_extrusions.ranges.layer_duration.get_color_at_with_outliers(path.layer_time, true); break; } - case EViewType::Chronology: { color = m_extrusions.ranges.elapsed_time.get_color_at_with_outliers(path.elapsed_time); break; } - case EViewType::VolumetricRate: { - color = m_outliers_allowed ? - m_extrusions.ranges.volumetric_rate.get_color_at_with_outliers(path.volumetric_rate) : - m_extrusions.ranges.volumetric_rate.get_color_at_no_outliers(path.volumetric_rate); - break; - } - case EViewType::VolumetricFlow: { - color = m_outliers_allowed ? - m_extrusions.ranges.volumetric_flow.get_color_at_with_outliers(path.volumetric_flow) : - m_extrusions.ranges.volumetric_flow.get_color_at_no_outliers(path.volumetric_flow); - break; - } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } + case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } + case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } + case EViewType::LayerTime: { color = m_extrusions.ranges.layer_duration.get_color_at(path.layer_time); break; } + case EViewType::Chronology: { color = m_extrusions.ranges.elapsed_time.get_color_at(path.elapsed_time); break; } + case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } + case EViewType::VolumetricFlow: { color = m_extrusions.ranges.volumetric_flow.get_color_at(path.volumetric_flow); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::Filament: { color = m_filament_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { @@ -3203,6 +3266,9 @@ void GCodeViewer::render_legend(float& legend_height) bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); bool show_switch_show_outliers = (m_view_type == EViewType::VolumetricFlow || m_view_type == EViewType::VolumetricRate); + bool show_switch_log_scale = (m_view_type == EViewType::LayerTime); + bool show_min_max_field = m_extrusions.ranges.get(m_view_type) != nullptr; + bool show_min_max_field_same_line = (m_view_type == EViewType::VolumetricFlow || m_view_type == EViewType::VolumetricRate || m_view_type == EViewType::LayerTime || m_view_type == EViewType::Chronology); const float icon_size = ImGui::GetTextLineHeight(); const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); @@ -3303,43 +3369,58 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PopStyleVar(); }; - auto append_range = [append_item](const Extrusions::Range& range, unsigned int decimals, bool ignore_outliers) { - auto append_range_item = [append_item](int i, float value, unsigned int decimals) { + auto append_range = [append_item](const Extrusions::Range& range, unsigned int decimals) { + auto append_range_item = [append_item](const GCodeViewer::Color &color, float value, unsigned int decimals) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); - append_item(EItemType::Rect, Range_Colors[i], buf); + append_item(EItemType::Rect, color, buf); }; - if (range.count == 1) + if (range.has_min_max == 1) // single item use case - append_range_item(0, range.min, decimals); - else if (range.count == 2) { - append_range_item(static_cast(Range_Colors.size()) - 1, range.max, decimals); - append_range_item(0, range.min, decimals); + append_range_item(Range_Colors[0], range.min, decimals); + else if (range.has_min_max == 2) { + append_range_item(Range_Colors[static_cast(Range_Colors.size()) - 1], range.max, decimals); + append_range_item(Range_Colors[0], range.min, decimals); } else { - const float step_size = ignore_outliers ? range.step_size_no_outliers() : range.step_size_with_outliers(); + const float step_size = range.step_size(); + const float current_min = range.get_current_min(); + const float current_max = range.get_current_max(); + if( current_max != range.max) + append_range_item(Too_High_Value_Color, range.max, decimals); for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { - append_range_item(i, range.min + static_cast(i) * step_size, decimals); + if (!range.log_scale) + append_range_item(Range_Colors[i], current_min + static_cast(i) * step_size, decimals); + else + append_range_item(Range_Colors[i], std::exp(std::log(current_min) + static_cast(i) * step_size), decimals); } + if( current_min != range.min) + append_range_item(Too_Low_Value_Color, range.min, decimals); } }; - auto append_range_time = [this, append_item](const Extrusions::Range& range, bool is_log) { - if (range.count == 1) + auto append_range_time = [this, append_item](const Extrusions::Range& range) { + if (range.has_min_max == 1) // single item use case append_item(EItemType::Rect, Range_Colors[0], get_time_dhms(range.min)); - else if (range.count == 2) { + else if (range.has_min_max == 2) { append_item(EItemType::Rect, Range_Colors[static_cast(Range_Colors.size()) - 1], get_time_dhms(range.max)); append_item(EItemType::Rect, Range_Colors[0], get_time_dhms(range.min)); } else { - float step_size = range.step_size_with_outliers(is_log); + const float step_size = range.step_size(); + const float current_min = range.get_current_min(); + const float current_max = range.get_current_max(); + if( current_max != range.max) + append_item(EItemType::Rect, Too_High_Value_Color, get_time_dhms(range.max)); for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { - if (!is_log) - append_item(EItemType::Rect, Range_Colors[i], get_time_dhms(range.min + static_cast(i) * step_size)); + if (!range.log_scale) + append_item(EItemType::Rect, Range_Colors[i], get_time_dhms(range.get_current_min() + static_cast(i) * step_size)); else - append_item(EItemType::Rect, Range_Colors[i], get_time_dhms(std::exp(std::log(range.min) + static_cast(i) * step_size))); + append_item(EItemType::Rect, Range_Colors[i], get_time_dhms(std::exp(std::log(range.get_current_min()) + static_cast(i) * step_size))); } + if( current_min != range.min) + append_item(EItemType::Rect, Too_Low_Value_Color, get_time_dhms(range.min)); } }; @@ -3524,7 +3605,6 @@ void GCodeViewer::render_legend(float& legend_height) case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } case EViewType::LayerTime: { imgui.title(_u8L("Layer Time")); break; } - case EViewType::LayerTimeLog: { imgui.title(_u8L("Layer Time (log)")); break; } case EViewType::Chronology: { imgui.title(_u8L("Chronology")); break; } case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } case EViewType::VolumetricFlow: { imgui.title(_u8L("Extrusion section (mm³/mm)")); break; } @@ -3561,16 +3641,15 @@ void GCodeViewer::render_legend(float& legend_height) } break; } - case EViewType::Height: { append_range(m_extrusions.ranges.height, 3, false); break; } - case EViewType::Width: { append_range(m_extrusions.ranges.width, 3, false); break; } - case EViewType::Feedrate: { append_range(m_extrusions.ranges.feedrate, 1, false); break; } - case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0, false); break; } - case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0, false); break; } - case EViewType::LayerTime: { append_range_time(m_extrusions.ranges.layer_duration, false); break; } - case EViewType::LayerTimeLog: { append_range_time(m_extrusions.ranges.layer_duration, true); break; } - case EViewType::Chronology: { append_range_time(m_extrusions.ranges.elapsed_time, false); break; } - case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3, !m_outliers_allowed); break; } - case EViewType::VolumetricFlow: { append_range(m_extrusions.ranges.volumetric_flow, 3, !m_outliers_allowed); break; } + case EViewType::Height: { append_range(m_extrusions.ranges.height, 3); break; } + case EViewType::Width: { append_range(m_extrusions.ranges.width, 3); break; } + case EViewType::Feedrate: { append_range(m_extrusions.ranges.feedrate, 1); break; } + case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } + case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; } + case EViewType::LayerTime: { append_range_time(m_extrusions.ranges.layer_duration); break; } + case EViewType::Chronology: { append_range_time(m_extrusions.ranges.elapsed_time); break; } + case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } + case EViewType::VolumetricFlow: { append_range(m_extrusions.ranges.volumetric_flow, 3); break; } case EViewType::Tool: { // shows only extruders actually used @@ -3982,8 +4061,12 @@ void GCodeViewer::render_legend(float& legend_height) } if (show_switch_show_outliers) { + + Extrusions::Range *range = m_extrusions.ranges.get(m_view_type); + assert(range); + ImGui::Spacing(); - const bool outliers_allowed = m_outliers_allowed; + const bool outliers_allowed = range->ratio_outlier > 0.f; if (!outliers_allowed) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); @@ -3993,7 +4076,7 @@ void GCodeViewer::render_legend(float& legend_height) // draw text ImGui::SameLine(); if (ImGui::MenuItem((_u8L("Allow outliers")).c_str())) { - m_outliers_allowed = !m_outliers_allowed; + range->ratio_outlier = range->ratio_outlier > 0.f ? 0.f : 0.01f; // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->update_preview_moves_slider(); @@ -4015,6 +4098,115 @@ void GCodeViewer::render_legend(float& legend_height) if (!outliers_allowed) ImGui::PopStyleVar(); } + + if (show_switch_log_scale) { + ImGui::Spacing(); + + Extrusions::Range *range = m_extrusions.ranges.get(m_view_type); + assert(range); + + const bool log_scale = range->log_scale; + if (!log_scale) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + + // draw text + ImGui::SameLine(); + if (ImGui::MenuItem((_u8L("Use log scale")).c_str())) { + range->log_scale = !range->log_scale; + // update buffers' render paths + refresh_render_paths(false, false); + wxGetApp().plater()->update_preview_moves_slider(); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->update_preview_bottom_toolbar(); + } else { + // show tooltip + if (ImGui::IsItemHovered()) { + if (!log_scale) + ImGui::PopStyleVar(); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(log_scale ? _u8L("Click to return to linear scale") : _u8L("Click to switch to logarithmic scale")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + if (!log_scale) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + + // to avoid the tooltip to change size when moving the mouse + imgui.set_requires_extra_frame(); + } + } + if (!log_scale) + ImGui::PopStyleVar(); + } + + if (show_min_max_field) { + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + + Extrusions::Range *range = m_extrusions.ranges.get(m_view_type); + assert(range); + + float old_min = range->user_min; + float old_max = range->user_max; + float mod_min = range->user_min; + float mod_max = range->user_max; + // adapt box to values + std::string format = "%.0f"; + float size = 36.f; + if( (range->min > 0 && range->min < 0.01) || (range->max > 0 && range->max < 0.1)) { + format = "%.4f"; + size = 54.f; + }else if( (range->min > 0 && range->min < 1) || (range->max > 0 && range->max < 10)) { + format = "%.2f"; + size = 45.f; + } + std::string min_label = _u8L("min"); + std::string max_label = _u8L("max"); + float max_label_size = std::max(imgui.calc_text_size(min_label).x, imgui.calc_text_size(min_label).y); + max_label_size += imgui.scaled(1.f); + // draw text + ImGui::AlignTextToFramePadding(); + imgui.text(min_label); + if (show_min_max_field_same_line) + ImGui::SameLine(); + else + ImGui::SameLine(max_label_size); + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat("##min", &mod_min, 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + if (show_min_max_field_same_line) + ImGui::SameLine(); + else + ImGui::AlignTextToFramePadding(); + imgui.text(max_label); + if (show_min_max_field_same_line) + ImGui::SameLine(); + else + ImGui::SameLine(max_label_size); + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat("##max", &mod_max, 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + + if (mod_min != old_min) { + range->user_min = mod_min; + } + + if (mod_max != old_max) { + range->user_max = mod_max; + } + + if (old_min != range->user_min || old_max != range->user_max) { + + // update buffers' render paths + refresh_render_paths(false, false); + wxGetApp().plater()->update_preview_moves_slider(); + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->update_preview_bottom_toolbar(); + } + + } // total estimated printing time section if (show_estimated_time) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 2b465077cd6..2865436a7fa 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -37,6 +37,8 @@ class GCodeViewer static const std::vector Range_Colors; static const Color Wipe_Color; static const Color Neutral_Color; + static const Color Too_Low_Value_Color; + static const Color Too_High_Value_Color; enum class EOptionsColors : unsigned char { @@ -382,6 +384,9 @@ class GCodeViewer bool visible{ false }; }; + public: + enum class EViewType : unsigned char; + private: // helper to render extrusion paths struct Extrusions { @@ -389,23 +394,33 @@ class GCodeViewer { float min; float max; - unsigned int count; - uint32_t counts[20]; + float full_precision_min; + float full_precision_max; + + // 0 if no values, 1 if only one different value, 2 if only min max, 3 if more values. + uint8_t has_min_max = 0; + + // count of all item passed into update uint64_t total_count; + // total_count per log item + uint32_t counts[20]; float maxs[20]; float mins[20]; + + // set 0 or lower to disable + float user_min = 0; + float user_max = 0; + bool log_scale = false; + float ratio_outlier = 0.f; Range() { reset(); } - void update_from(const float value); + void update_from(const float value, const uint8_t decimal_precision); void reset(); - float get_max_no_outliers(float ratio_outlier = 0.01) const; - float get_min_no_outliers(float ratio_outlier = 0.01) const; - float step_size_no_outliers(float ratio_outlier = 0.01, bool log = false) const; - Color get_color_at_no_outliers(float value, float ratio_outlier = 0.01, bool log = false) const; - float step_size_with_outliers(float ratio_outlier = 0.01, bool log = false) const { return step_size(min, max, log); } - Color get_color_at_with_outliers(float value, float ratio_outlier = 0.01, bool log = false) const { return get_color_at(min, max, value, log); } + float get_current_max() const; + float get_current_min() const; + float step_size() const; + Color get_color_at(float value) const; static float step_size(float min, float max, bool log = false); - static Color get_color_at(float min, float max, float value, bool log = false); }; struct Ranges @@ -429,7 +444,7 @@ class GCodeViewer // Color mapping by time. Range elapsed_time; - + Range* get(EViewType type); void reset() { height.reset(); @@ -697,7 +712,6 @@ class GCodeViewer FanSpeed, Temperature, LayerTime, - LayerTimeLog, Chronology, VolumetricRate, VolumetricFlow, @@ -731,7 +745,6 @@ class GCodeViewer Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; - bool m_outliers_allowed{ true }; PrintEstimatedStatistics m_print_statistics; PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; #if ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 1f4fe58a5c1..ded6e93b6e7 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -251,7 +251,6 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_choice_view_label[GCodeViewer::EViewType::FanSpeed] = m_width_screen == tiny ? _L("Fan") : _L("Fan speed"); m_choice_view_label[GCodeViewer::EViewType::Temperature] = m_width_screen == tiny ? _L("Temp") : _L("Temperature"); m_choice_view_label[GCodeViewer::EViewType::LayerTime] = m_width_screen == tiny ? _L("time") : _L("Layer time"); - m_choice_view_label[GCodeViewer::EViewType::LayerTimeLog] = m_width_screen == tiny ? _L("Log time") : _L("Layer time (log)"); m_choice_view_label[GCodeViewer::EViewType::Chronology] = m_width_screen == tiny ? _L("Chrono") : _L("Chronology"); m_choice_view_label[GCodeViewer::EViewType::VolumetricRate] = m_width_screen == tiny ? _L("Vol. flow") : _L("Volumetric flow rate"); m_choice_view_label[GCodeViewer::EViewType::VolumetricFlow] = _L("Section"); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 4197c3cc612..84dcbf1e5e7 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -123,8 +123,10 @@ std::string PresetHints::cooling_description(const Preset &preset_fil, const Pre out += _L("By default, there won't be any fan speed command."); else if (default_fan_speed == 0) out += _L("Fan will be turned off by default."); + else if(min_fan_speed == 0) + out += format_wxstr(_L("Fan will run at %1%%% by default."), std::max(default_fan_speed, min_fan_speed)); else - out += format_wxstr(_L("Fan will run at %1%%% by default."), default_fan_speed); + out += format_wxstr(_L("Fan will run at %1%%% by default."), std::max(default_fan_speed, min_fan_speed))+" "+format_wxstr(_L("The minimum speed is %1%%%."), min_fan_speed); format_double_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Internal perimeters"), peri_fan_speed, _L("External perimeters"), ext_peri_fan_speed); format_simple_fan_min_speed(out, min_fan_speed, default_fan_speed, _L("Sparse infill"), infill_fan_speed);