diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 61ef9a1bd..e592466c7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1858,7 +1858,7 @@ void GCode::process_layer( std::string gcode; // add tag for processor - gcode += "; " + GCodeProcessor::Layer_Change_Tag + "\n"; + gcode += ";" + GCodeProcessor::Layer_Change_Tag + "\n"; // export layer z char buf[64]; sprintf(buf, ";Z:%g\n", print_z); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 14bd38a33..4d3e16b47 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -393,7 +393,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename) auto is_temporary_decoration = [](const std::string& gcode_line) { // remove trailing '\n' std::string line = gcode_line.substr(0, gcode_line.length() - 1); - if (line == "; " + Layer_Change_Tag) + if (line == ";" + Layer_Change_Tag) return true; else return false; diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 6e1b6a8cd..3f988acf1 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -4,6 +4,7 @@ #include "libslic3r/GCode.hpp" #else #include "wxExtensions.hpp" +#include "libslic3r/GCode/PreviewData.hpp" #endif // ENABLE_GCODE_VIEWER #include "GUI.hpp" #include "GUI_App.hpp" @@ -12,6 +13,7 @@ #include "ExtruderSequenceDialog.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/AppConfig.hpp" +#include "GUI_Utils.hpp" #include #include @@ -68,7 +70,8 @@ Control::Control( wxWindow *parent, m_higher_value (higherValue), m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), + m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) { #ifdef __WXOSX__ is_osx = true; @@ -240,6 +243,12 @@ void Control::SetMaxValue(const int max_value) Update(); } +void Control::SetSliderValues(const std::vector& values) +{ + m_values = values; + m_ruler.count = std::count(m_values.begin(), m_values.end(), m_values.front()); +} + void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) { int width; @@ -371,6 +380,13 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) Update(); } +void Control::SetLayersTimes(const std::vector& layers_times) +{ + m_layers_times = layers_times; + for (int i = 1; i < m_layers_times.size(); i++) + m_layers_times[i] += m_layers_times[i - 1]; +} + void Control::SetDrawMode(bool is_sla_print, bool is_sequential_print) { m_draw_mode = is_sla_print ? dmSlaPrint : @@ -443,6 +459,9 @@ void Control::render() // and only in a case of no-empty m_values draw_colored_band(dc); + if (m_extra_style & wxSL_AUTOTICKS) + draw_ruler(dc); + if (!m_render_as_disabled) { // draw line draw_scroll_line(dc, lower_pos, higher_pos); @@ -569,9 +588,16 @@ void Control::draw_tick_on_mouse_position(wxDC& dc) //draw info line dc.SetPen(LIGHT_GREY_PEN); draw_ticks(dc, pos); + + if (m_extra_style & wxSL_VALUE_LABEL) { + wxColour old_clr = dc.GetTextForeground(); + dc.SetTextForeground(LIGHT_GREY_PEN.GetColour()); + draw_tick_text(dc, pos, tick, ltEstimatedTime, false); + dc.SetTextForeground(old_clr); + } } -wxString Control::get_label(int tick) const +wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const { const int value = tick; @@ -584,23 +610,35 @@ wxString Control::get_label(int tick) const if (m_draw_mode == dmSequentialGCodeView) return wxString::Format("%d", static_cast(m_values[value])); else { - const wxString str = m_values.empty() ? + if (label_type == ltEstimatedTime) { + if (m_values.size() != m_layers_times.size()) + return "time"; + return Slic3r::short_time(get_time_dhms(m_layers_times[value])); + } + wxString str = m_values.empty() ? wxString::Format("%.*f", 2, m_label_koef * value) : wxString::Format("%.*f", 2, m_values[value]); - return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); + if (label_type == ltHeight) + return str; + if (label_type == ltHeightWithLayer) + return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); } #else const wxString str = m_values.empty() ? wxNumberFormatter::ToString(m_label_koef * value, 2, wxNumberFormatter::Style_None) : wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); - return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); + if (label_type == ltHeight) + return str; + if (label_type == ltHeightWithLayer) + return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1); #endif // ENABLE_GCODE_VIEWER + return wxEmptyString; } -void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const +void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type/* = ltHeight*/, bool right_side/*=true*/) const { wxCoord text_width, text_height; - const wxString label = get_label(tick); + const wxString label = get_label(tick, label_type); dc.GetMultiLineTextExtent(label, &text_width, &text_height); wxPoint text_pos; if (right_side) { @@ -615,9 +653,6 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ } else text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); - - // update text rectangle - m_rect_lower_thumb_text = wxRect(text_pos, wxSize(text_width, text_height)); } else { if (is_horizontal()) { @@ -627,9 +662,6 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ } else text_pos = wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1); - - // update text rectangle - m_rect_higher_thumb_text = wxRect(text_pos, wxSize(text_width, text_height)); } dc.DrawText(label, text_pos); @@ -637,7 +669,7 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_ 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, selection == ssLower); + draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, ltHeightWithLayer, selection == ssLower); } void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) @@ -715,6 +747,15 @@ void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& hig draw_thumb_text(dc, pos_l, ssLower); } +void Control::draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len) +{ + int mid_space = 9; + is_horizontal() ? dc.DrawLine(pos, mid - (mid_space + tick_len), pos, mid - mid_space) : + dc.DrawLine(mid - (mid_space + tick_len), pos, mid - mid_space, pos); + is_horizontal() ? dc.DrawLine(pos, mid + (mid_space + tick_len), pos, mid + mid_space) : + dc.DrawLine(mid + (mid_space + tick_len), pos, mid + mid_space, pos); +}; + void Control::draw_ticks(wxDC& dc) { if (m_draw_mode == dmSlaPrint) @@ -726,11 +767,7 @@ void Control::draw_ticks(wxDC& dc) const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; for (auto tick : m_ticks.ticks) { const wxCoord pos = get_position_from_value(tick.tick); - - is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : - dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); - is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : - dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/); + draw_ticks_pair(dc, pos, mid, 7); // if current tick if focused, we should to use a specific "focused" icon bool focused_tick = m_moving_pos != wxDefaultPosition && tick.tick == get_tick_near_point(m_moving_pos); @@ -866,6 +903,118 @@ void Control::draw_colored_band(wxDC& dc) } } +void Control::Ruler::update(wxWindow* win, const std::vector& values, double scroll_step) +{ + int DPI = GUI::get_dpi_for_window(win); + int pixels_per_sm = lround((double)(DPI) * 5.0/25.4); + + int pow = -2; + int step = 0; + auto end_it = count == 1 ? values.end() : values.begin() + lround(values.size() / count); + + while (pow < 3) { + int tick = 0; + 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(); + + if (lround(tick * scroll_step) > pixels_per_sm) { + step = istep; + + // 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; + // there couldn't be more then 10 short ticks between thicks + short_step = 0.1 * short_ticks_cnt; + break; + } + } + if (step > 0) + break; + pow++; + } + + long_step = step == 0 ? -1.0 : (double)step* std::pow(10, pow); +} + +void Control::draw_ruler(wxDC& dc) +{ + m_ruler.update(this->GetParent(), m_values, get_scroll_step()); + if (!m_ruler.is_ok()) + return; + + int height, width; + get_size(&width, &height); + const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; + + 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)); + draw_ticks_pair(dc, pos, mid, 2); + current_tick += m_ruler.short_step; + if (current_tick > m_max_value) + break; + } + }; + + dc.SetPen(LIGHT_GREY_PEN); + wxColour old_clr = dc.GetTextForeground(); + dc.SetTextForeground(LIGHT_GREY_PEN.GetColour()); + + double short_tick; + int tick = 0; + double value = 0.0; + int sequence = 0; + + while (tick <= m_max_value) { + value += m_ruler.long_step; + if (value > m_values.back() && sequence < m_ruler.count) { + value = m_ruler.long_step; + for (tick; tick < m_values.size(); tick++) + if (m_values[tick] < value) + break; + // short ticks from the last tick to the end of current sequence + draw_short_ticks(dc, short_tick, tick); + sequence++; + } + short_tick = tick; + + for (tick; tick < m_values.size(); tick++) { + if (m_values[tick] == value) + break; + if (m_values[tick] > value) { + if (tick > 0) + tick--; + break; + } + } + if (tick > m_max_value) + break; + + wxCoord pos = get_position_from_value(tick); + draw_ticks_pair(dc, pos, mid, 5); + draw_tick_text(dc, wxPoint(mid, pos), tick); + + draw_short_ticks(dc, short_tick, tick); + + if (value == m_values.back() && sequence < m_ruler.count) { + value = 0.0; + sequence++; + tick++; + } + } + // short ticks from the last tick to the end + draw_short_ticks(dc, short_tick, m_max_value); + + dc.SetTextForeground(old_clr); +} + void Control::draw_one_layer_icon(wxDC& dc) { #if ENABLE_GCODE_VIEWER @@ -1083,9 +1232,9 @@ wxString Control::get_tooltip(int tick/*=-1*/) else #endif // ENABLE_GCODE_VIEWER return m_mode == MultiAsSingle ? - GUI::from_u8((boost::format(_u8L("Jump to height %s or " - "Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) : - _L("Jump to height") + " (Shift + G)"; + GUI::from_u8((boost::format(_u8L("Jump to height %s Set ruler mode\n or " + "Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) : + GUI::from_u8((boost::format(_u8L("Jump to height %s or Set ruler mode")) % " (Shift + G)\n").str()); #if ENABLE_GCODE_VIEWER } #endif // ENABLE_GCODE_VIEWER @@ -1339,14 +1488,7 @@ void Control::OnLeftUp(wxMouseEvent& event) add_current_tick(); break; case maCogIconClick : - if (m_mode == MultiAsSingle && m_draw_mode == dmRegular) - show_cog_icon_context_menu(); - else -#if ENABLE_GCODE_VIEWER - jump_to_value(); -#else - jump_to_print_z(); -#endif // ENABLE_GCODE_VIEWER + show_cog_icon_context_menu(); break; case maOneLayerIconClick: switch_one_layer_mode(); @@ -1736,8 +1878,27 @@ void Control::show_cog_icon_context_menu() [this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu); #endif // ENABLE_GCODE_VIEWER - append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "", - [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); + wxMenu* ruler_mode_menu = new wxMenu(); + if (ruler_mode_menu) { + append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("None"), _L("Supprese show the ruler"), + [this](wxCommandEvent&) { if (m_extra_style != 0) m_extra_style = 0; }, ruler_mode_menu, + []() { return true; }, [this]() { return m_extra_style == 0; }, GUI::wxGetApp().plater()); + + append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show object height"), _L("Show object height on the ruler"), + [this](wxCommandEvent&) { m_extra_style & wxSL_AUTOTICKS ? m_extra_style &= wxSL_AUTOTICKS : m_extra_style |= wxSL_AUTOTICKS; }, ruler_mode_menu, + []() { return true; }, [this]() { return m_extra_style & wxSL_AUTOTICKS; }, GUI::wxGetApp().plater()); + + append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show estimated print time"), _L("Show estimated print time on the ruler"), + [this](wxCommandEvent&) { m_extra_style & wxSL_VALUE_LABEL ? m_extra_style &= wxSL_VALUE_LABEL : m_extra_style |= wxSL_VALUE_LABEL; }, ruler_mode_menu, + []() { return true; }, [this]() { return m_extra_style & wxSL_VALUE_LABEL; }, GUI::wxGetApp().plater()); + + append_submenu(&menu, ruler_mode_menu, wxID_ANY, _L("Ruler mode"), _L("Set ruler mode"), "", + [this]() { return true; }, this); + } + + if (m_mode == MultiAsSingle && m_draw_mode == dmRegular) + append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "", + [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); GUI::wxGetApp().plater()->PopupMenu(&menu); } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 511858dd5..509f6ce1e 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -84,6 +84,13 @@ enum DrawMode #endif // ENABLE_GCODE_VIEWER }; +enum LabelType +{ + ltHeightWithLayer, + ltHeight, + ltEstimatedTime, +}; + struct TickCode { bool operator<(const TickCode& other) const { return other.tick > this->tick; } @@ -212,11 +219,12 @@ public: void SetMaxValue(const int max_value); void SetKoefForLabels(const double koef) { m_label_koef = koef; } - void SetSliderValues(const std::vector& values) { m_values = values; } + void SetSliderValues(const std::vector& values); void ChangeOneLayerLock(); - Info GetTicksValues() const; - void SetTicksValues(const Info &custom_gcode_per_print_z); + Info GetTicksValues() const; + void SetTicksValues(const Info &custom_gcode_per_print_z); + void SetLayersTimes(const std::vector& layers_times); void SetDrawMode(bool is_sla_print, bool is_sequential_print); #if ENABLE_GCODE_VIEWER @@ -281,15 +289,17 @@ protected: void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); + void draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len); void draw_ticks(wxDC& dc); void draw_colored_band(wxDC& dc); + void draw_ruler(wxDC& dc); void draw_one_layer_icon(wxDC& dc); void draw_revert_icon(wxDC& dc); void draw_cog_icon(wxDC &dc); void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); void draw_tick_on_mouse_position(wxDC &dc); - void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side = true) const; + void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type = ltHeight, bool right_side = true) const; void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection); @@ -306,7 +316,7 @@ private: int get_tick_near_point(const wxPoint& pt); double get_scroll_step(); - wxString get_label(int tick) const; + 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); } @@ -387,10 +397,12 @@ private: int m_revert_icon_dim; int m_cog_icon_dim; long m_style; + long m_extra_style; float m_label_koef = 1.0; std::vector m_values; TickCodeInfo m_ticks; + std::vector m_layers_times; std::vector m_extruder_colors; @@ -407,6 +419,15 @@ private: std::vector m_line_pens; std::vector m_segm_pens; + + struct Ruler { + double long_step; + double short_step; + int count { 1 }; // > 1 for sequential print + + void update(wxWindow* win, const std::vector& values, double scroll_step); + bool is_ok() { return long_step > 0 && short_step > 0; } + } m_ruler; }; } // DoubleSlider; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c108f6946..637231aae 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -999,6 +999,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee } m_layers_slider->SetSelectionSpan(idx_low, idx_high); m_layers_slider->SetTicksValues(ticks_info_from_model); + m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes[0].layers_times); bool sla_print_technology = wxGetApp().plater()->printer_technology() == ptSLA; bool sequential_print = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects");