diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 089b92d5f7b..4850b9a91b1 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -913,7 +913,9 @@ void AppConfig::save() // Returns "undefined" if the thread naming functionality is not supported by the operating system. std::optional current_thread_name = get_current_thread_name(); if (current_thread_name && *current_thread_name != "slic3r_main") - throw CriticalException("Calling AppConfig::save() from a worker thread!"); + //in win11, it seems that the gui event thread isn't named 'slic3r_main' + BOOST_LOG_TRIVIAL(warning) << "AppConfig::save() from thread '" << *current_thread_name << "' instead of 'slic3r_main'\n"; + //throw CriticalException("Calling AppConfig::save() from a worker thread!"); } // The config is first written to a file with a PID suffix and then moved diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a65eca98d3e..95ad0c59714 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -79,6 +79,7 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(config->bridge_speed); RETURN_COMPARE_NON_EQUAL(config->bridge_speed_internal); RETURN_COMPARE_NON_EQUAL(config->gap_fill_speed); + RETURN_COMPARE_NON_EQUAL(config->print_extrusion_multiplier); } assert(*this == rhs); return false; diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 6195d21941c..7cfc8d48086 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -205,6 +205,7 @@ void Layer::make_perimeters() && config.perimeter_loop_seam == other_config.perimeter_loop_seam && config.perimeter_overlap == other_config.perimeter_overlap && config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp? + && config.print_extrusion_multiplier == other_config.print_extrusion_multiplier && config.small_perimeter_speed == other_config.small_perimeter_speed && config.small_perimeter_min_length == other_config.small_perimeter_min_length && config.small_perimeter_max_length == other_config.small_perimeter_max_length diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 72d3fe3d2cc..68bdad07e45 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -3056,6 +3056,25 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions std::vector paths; const Point* first_point = nullptr; coordf_t resolution_sqr; + //TODO real travel with role & width + void ensure_travel_to(const Point &pt) { + assert(!paths.empty()); + Point last_point = paths.back().last_point(); + if (last_point != pt) { + if (last_point.distance_to_square(pt) < resolution_sqr) { + paths.back().polyline.set_points().back() = pt; + } else { + //add travel + ExtrusionPath travel(paths.back().role(), false); + travel.width = paths.back().width; + travel.height = paths.back().height; + travel.mm3_per_mm = 0; + travel.polyline.append(last_point); + travel.polyline.append(pt); + paths.push_back(travel); + } + } + } virtual void use(ExtrusionPath &path) override { //ensure the loop is continue. if (first_point != nullptr) { @@ -3256,16 +3275,19 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions change_flow.first_point = &point; change_flow.percent_extrusion = 1; change_flow.use(tws); - //add move back + // ChangeFlow added the first move if needed, now add the second + change_flow.ensure_travel_to(point); + //add move around searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, change_flow.paths.begin(), change_flow.paths.end()); - //add move to - if (poly_after.first_point() != point) { - assert(poly_after.first_point().coincides_with_epsilon(point)); - assert(searcher.search_result.loop->paths.size() > idx_path_to_add); - assert(poly_after.first_point().coincides_with_epsilon(searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front())); - searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front() = poly_after.first_point(); - } + ////add move to -> ??? i don't remember why i wrote that, so here it's removed. + assert(poly_after.first_point() == point); + //if (poly_after.first_point() != point) { + // assert(poly_after.first_point().coincides_with_epsilon(point)); + // assert(searcher.search_result.loop->paths.size() > idx_path_to_add); + // assert(poly_after.first_point().coincides_with_epsilon(searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front())); + // searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front() = poly_after.first_point(); + //} #if _DEBUG searcher.search_result.loop->visit(LoopAssertVisitor{}); #endif diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 41e086aea10..f1853972f13 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -192,22 +192,85 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move } -float GCodeViewer::Extrusions::Range::step_size(bool log) const +void GCodeViewer::Extrusions::Range::update_from(const float value) +{ + if (value != max && value != min) + ++count; + min = std::min(min, value); + max = std::max(max, value); + + total_count++; + // don't keep outliers + uint8_t idx = std::min(19, std::max(0, int(9 + log2(min)))); + counts[idx]++; + 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; + total_count = 0; + for (size_t idx = 0; idx < 20; idx++) { + counts[idx] = 0; + maxs[idx] = -FLT_MAX; + mins[idx] = FLT_MAX; + } +} + +float GCodeViewer::Extrusions::Range::get_max_no_outliers(float ratio_outlier) 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]; + } + } + assert(false); + return max; +} + +float GCodeViewer::Extrusions::Range::get_min_no_outliers(float ratio_outlier) 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]; + } + } + assert(false); + return min; +} + +float GCodeViewer::Extrusions::Range::step_size_no_outliers(float ratio_outlier, bool log) 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); +} + +float GCodeViewer::Extrusions::Range::step_size(float min, float max, bool log) { if (log) { float min_range = min; if (min_range == 0) min_range = 0.001f; - return (std::log(max / min_range) / (static_cast(Range_Colors.size()) - 1.0f)); + return (std::log(max / min_range) / (static_cast(GCodeViewer::Range_Colors.size()) - 1.0f)); } else - return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); + return (max - min) / (static_cast(GCodeViewer::Range_Colors.size()) - 1.0f); } -GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float value, bool log) const +GCodeViewer::Color GCodeViewer::Extrusions::Range::get_color_at(float min, float max, float value, bool log) { // Input value scaled to the colors range - const float step = step_size(log); + const float step = step_size(min, max, log); float global_t; if (log) { @@ -2265,16 +2328,26 @@ 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(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::LayerTimeLog: { color = m_extrusions.ranges.layer_duration.get_color_at(path.layer_time, true); 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::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::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::Filament: { color = m_filament_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { @@ -3129,6 +3202,7 @@ void GCodeViewer::render_legend(float& legend_height) const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast(m_time_estimate_mode)]; 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); const float icon_size = ImGui::GetTextLineHeight(); const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); @@ -3229,7 +3303,7 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PopStyleVar(); }; - auto append_range = [append_item](const Extrusions::Range& range, unsigned int decimals) { + 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) { char buf[1024]; ::sprintf(buf, "%.*f", decimals, value); @@ -3244,7 +3318,7 @@ void GCodeViewer::render_legend(float& legend_height) append_range_item(0, range.min, decimals); } else { - const float step_size = range.step_size(); + const float step_size = ignore_outliers ? range.step_size_no_outliers() : range.step_size_with_outliers(); for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { append_range_item(i, range.min + static_cast(i) * step_size, decimals); } @@ -3259,7 +3333,7 @@ void GCodeViewer::render_legend(float& legend_height) 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(is_log); + float step_size = range.step_size_with_outliers(is_log); 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)); @@ -3487,16 +3561,16 @@ void GCodeViewer::render_legend(float& legend_height) } 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::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); break; } - case EViewType::VolumetricFlow: { append_range(m_extrusions.ranges.volumetric_flow, 3); 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::Tool: { // shows only extruders actually used @@ -3906,6 +3980,41 @@ void GCodeViewer::render_legend(float& legend_height) } } } + + if (show_switch_show_outliers) { + ImGui::Spacing(); + const bool outliers_allowed = m_outliers_allowed; + if (!outliers_allowed) + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f); + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + + // draw text + ImGui::SameLine(); + if (ImGui::MenuItem((_u8L("Allow outliers")).c_str())) { + m_outliers_allowed = !m_outliers_allowed; + // 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()) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(outliers_allowed ? _u8L("Click to disable") : _u8L("Click to enable")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + + // to avoid the tooltip to change size when moving the mouse + imgui.set_requires_extra_frame(); + } + } + if (!outliers_allowed) + ImGui::PopStyleVar(); + } // total estimated printing time section if (show_estimated_time) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 3be21e953a5..2b465077cd6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -390,19 +390,22 @@ class GCodeViewer float min; float max; unsigned int count; + uint32_t counts[20]; + uint64_t total_count; + float maxs[20]; + float mins[20]; Range() { reset(); } - - void update_from(const float value) { - if (value != max && value != min) - ++count; - min = std::min(min, value); - max = std::max(max, value); - } - void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; } - - float step_size(bool log = false) const; - Color get_color_at(float value, bool log = false) const; + void update_from(const float value); + 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); } + 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 @@ -728,6 +731,7 @@ 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/version.inc b/version.inc index 7735d667daa..97ea7b75483 100644 --- a/version.inc +++ b/version.inc @@ -9,9 +9,9 @@ set(SLIC3R_APP_KEY "SuperSlicer") set(SLIC3R_APP_CMD "superslicer") # versions set(SLIC3R_VERSION "2.5.59") -set(SLIC3R_VERSION_FULL "2.5.59.6") +set(SLIC3R_VERSION_FULL "2.5.59.7") set(SLIC3R_BUILD_ID "${SLIC3R_APP_KEY}_${SLIC3R_VERSION_FULL}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,5,59,6") +set(SLIC3R_RC_VERSION "2,5,59,7") set(SLIC3R_RC_VERSION_DOTS "${SLIC3R_VERSION_FULL}") # Same as the slicer name but for gcodeviewer