diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 172db0ec95..0ca4e8fbf7 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -153,8 +153,8 @@ namespace ImGui const wchar_t PlugMarker = 0x1C; const wchar_t DowelMarker = 0x1D; const wchar_t SnapMarker = 0x1E; - const wchar_t HorizontalHide = 0xB1; - const wchar_t HorizontalShow = 0xB2; + const wchar_t HorizontalHide = 0xB4; + const wchar_t HorizontalShow = 0xB6; // Do not forget use following letters only in wstring const wchar_t DocumentationButton = 0x2600; const wchar_t DocumentationHoverButton = 0x2601; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 4148745f88..93d94b016c 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -412,7 +412,8 @@ struct SLAPrintStatistics size_t fast_layers_count; double total_cost; double total_weight; - std::vector layers_times; + std::vector layers_times_running_total; + std::vector layers_areas; // Config with the filled in print statistics. DynamicConfig config() const; @@ -429,7 +430,8 @@ struct SLAPrintStatistics fast_layers_count = 0; total_cost = 0.; total_weight = 0.; - layers_times.clear(); + layers_times_running_total.clear(); + layers_areas.clear(); } }; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4b67975a87..bc792e1f42 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -942,8 +942,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { double models_volume(0.0); double estim_time(0.0); - std::vector layers_times; + std::vector> layers_times; // level and time + std::vector> layers_areas; // level and area layers_times.reserve(printer_input.size()); + layers_areas.reserve(printer_input.size()); size_t slow_layers = 0; size_t fast_layers = 0; @@ -961,7 +963,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // write vars &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, - &fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt) + &fast_layers, &fade_layer_time, &layers_times, &layers_areas](size_t sliced_layer_cnt) { PrintLayer &layer = m_print->m_printer_input[sliced_layer_cnt]; @@ -1029,6 +1031,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { Lock lck(mutex); supports_volume += layer_support_area * l_height; } + double layer_area = layer_model_area + layer_support_area; + // Here we can save the expensively calculated polygons for printing ExPolygons trslices; trslices.reserve(model_polygons.size() + supports_polygons.size()); @@ -1039,7 +1043,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // Calculation of the slow and fast layers to the future controlling those values on FW - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; + const bool is_fast_layer = layer_area <= display_area*area_fill; const double tilt_time = material_config.material_print_speed == slamsSlow ? slow_tilt : material_config.material_print_speed == slamsHighViscosity ? hv_tilt : is_fast_layer ? fast_tilt : slow_tilt; @@ -1082,13 +1086,14 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { + 120 / 1000 // Magical constant to compensate remaining computation delay in exposure thread ); - layers_times.push_back(layer_times); + layers_times.emplace_back(layer.level(), layer_times); estim_time += layer_times; + layers_areas.emplace_back(layer.level(), layer_area * SCALING_FACTOR * SCALING_FACTOR); } }; // sequential version for debugging: - // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); + // for(size_t i = 0; i < printer_input.size(); ++i) printlayerfn(i); execution::for_each(ex_tbb, size_t(0), printer_input.size(), printlayerfn, execution::max_concurrency(ex_tbb)); @@ -1102,7 +1107,17 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { print_statistics.estimated_print_time = NaNd; else { print_statistics.estimated_print_time = estim_time; - print_statistics.layers_times = layers_times; + + // Times and areas vectors were filled in parallel, they need to be sorted first. + // The print statistics will contain only the values (in the correct order). + std::sort(layers_times.begin(), layers_times.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + std::sort(layers_areas.begin(), layers_areas.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + print_statistics.layers_times_running_total.clear(); + for (size_t i=0; i& areas = print.print_statistics().layers_areas; + const std::vector& times = print.print_statistics().layers_times_running_total; + const double display_area = print.printer_config().display_height * print.printer_config().display_width; + if (layer_idx >= 0 && layer_idx < areas.size()) { + const double area = areas[layer_idx]; + const double time = times[layer_idx] - (layer_idx == 0 ? 0. : times[layer_idx-1]); + const double time_until_layer = times[layer_idx]; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImGuiPureWrap::set_next_window_pos(float(cnv_width) - imgui.get_style_scaling() * 5.f, imgui.get_style_scaling() * 55.f, ImGuiCond_Always, 1.0f, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + + ImGuiPureWrap::begin(_u8L("Layer statistics"), ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoFocusOnAppearing); + ImGui::Text(_u8L("Layer area: %.0f mm²").c_str(), area); + int area_percent_int = int(std::round(100. * area/display_area)); + ImGui::Text(GUI::format(_u8L("Area fill: %1% %%%%"), area_percent_int == 0 ? "<1" : std::to_string(area_percent_int)).c_str()); + ImGui::Separator(); + ImGui::Text(GUI::format(_u8L("Layer time: %1%"), get_time_dhms(time)).c_str()); + std::string buffer_str = _u8L("Time since start: %1%"); + ImGui::Text(GUI::format(buffer_str, get_time_dhms(time_until_layer)).c_str()); + + // The dummy control below uses the assumption that the total time string will be the longest + // and forces the width of the window large enough so it does not resize depending on the current value. + ImGui::Dummy(ImVec2(ImGui::CalcTextSize(GUI::format(buffer_str, get_time_dhms(82799)).c_str()).x, 0.)); + ImGuiPureWrap::end(); + } +} + + + void GLCanvas3D::_render_sla_slices() { if (!m_use_clipping_planes || current_printer_technology() != ptSLA) @@ -6261,6 +6295,13 @@ void GLCanvas3D::_render_sla_slices() // nothing to render, return return; + if (print->finished()) { + double slider_width = 0.; + if (const Preview* preview = dynamic_cast(m_canvas->GetParent())) + slider_width = preview->get_layers_slider_width(); + render_sla_layer_legend(*print, m_layer_slider_index, get_canvas_size().get_width() - slider_width); + } + double clip_min_z = -m_clipping_planes[0].get_data()[3]; double clip_max_z = m_clipping_planes[1].get_data()[3]; for (unsigned int i = 0; i < (unsigned int)print_objects.size(); ++i) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3975b02b08..783feb3a16 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -501,6 +501,7 @@ private: ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; std::array m_sla_caps; + int m_layer_slider_index = -1; std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() @@ -763,6 +764,8 @@ public: void bed_shape_changed(); + void set_layer_slider_index(int i) { m_layer_slider_index = i; } + void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { if (id < 2) { m_clipping_planes[id] = plane; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c66763ede8..c36e780c4e 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -328,14 +328,14 @@ void Preview::render_sliders(GLCanvas3D& canvas) m_moves_slider->Render(canvas_width, canvas_height, extra_scale); } -float Preview::get_moves_slider_height() +float Preview::get_moves_slider_height() const { if (m_moves_slider && m_moves_slider->IsShown()) return m_moves_slider->GetHeight(); return 0.0f; } -float Preview::get_layers_slider_width() +float Preview::get_layers_slider_width() const { if (m_layers_slider && m_layers_slider->IsShown()) return m_layers_slider->GetWidth(); @@ -629,7 +629,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee bool sequential_print = wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"); m_layers_slider->SetDrawMode(sla_print_technology, sequential_print); if (sla_print_technology) - m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times); + m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times_running_total); else m_layers_slider->SetLayersTimes(m_canvas->get_gcode_layers_times_cache(), m_gcode_result->print_statistics.modes.front().time); @@ -1037,6 +1037,7 @@ void Preview::on_layers_slider_scroll_changed() else if (tech == ptSLA) { m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValue())); m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValue())); + m_canvas->set_layer_slider_index(m_layers_slider->GetHigherPos()); m_canvas->render(); } } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 9d8f85304d..35f4d2bbe2 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -136,8 +136,8 @@ public: void msw_rescale(); void render_sliders(GLCanvas3D& canvas); - float get_layers_slider_width(); - float get_moves_slider_height(); + float get_layers_slider_width() const; + float get_moves_slider_height() const; bool is_loaded() const { return m_loaded; }