diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9dacd594be7..b6a2ddb6658 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5726,6 +5726,9 @@ std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref, // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case const double seam_gap = scale_(m_config.seam_gap.get_abs_value(nozzle_diameter)); + const bool seam_gap_applied = enable_seam_slope || m_enable_loop_clipping; + const double seam_gap_distance_mm = seam_gap_applied ? unscale_(seam_gap) : 0.0; + double seam_scarf_distance_mm = 0.0; const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0; // get paths @@ -5874,6 +5877,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref, const double slope_min_length = slope_entire_loop ? loop_length : std::min(m_config.seam_slope_min_length.value, loop_length); const int slope_steps = m_config.seam_slope_steps; const double slope_max_segment_length = scale_(slope_min_length / slope_steps); + seam_scarf_distance_mm = slope_min_length; // Calculate the sloped loop ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role()); @@ -5898,6 +5902,11 @@ std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref, } } + if (description == "perimeter") { + m_processor.result().print_statistics.total_seam_gap_distance += static_cast(seam_gap_distance_mm); + m_processor.result().print_statistics.total_seam_scarf_distance += static_cast(seam_scarf_distance_mm); + } + // BBS if (m_wipe.enable && FILAMENT_CONFIG(wipe)) { m_wipe.path = Polyline(); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 598ae5bcd21..0ab32a67801 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3821,9 +3821,10 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes return; EMoveType type = move_type(delta_pos); + const float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); + m_travel_dist = delta_xyz; + if (type == EMoveType::Extrude) { - const float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); - m_travel_dist = delta_xyz; float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; float area_toolpath_cross_section = volume_extruded_filament / delta_xyz; @@ -5445,6 +5446,11 @@ void GCodeProcessor::process_filament_change(int id) int next_extruder_id = m_filament_maps[id]; int next_filament_id = id; float extra_time = 0; + unsigned int filament_changes_delta = 0; + unsigned int extruder_changes_delta = 0; + float filament_load_time_delta = 0.0f; + float filament_unload_time_delta = 0.0f; + float tool_change_time_delta = 0.0f; if (prev_filament_id == next_filament_id) return; @@ -5457,12 +5463,14 @@ void GCodeProcessor::process_filament_change(int id) assert(prev_extruder_id != -1); process_filaments(CustomGCode::ToolChange); m_filament_id[next_extruder_id] = next_filament_id; - m_result.lock(); - m_result.print_statistics.total_filament_changes += 1; - m_result.unlock(); - extra_time += get_filament_unload_time(static_cast(prev_filament_id)); + filament_changes_delta += 1; + const float filament_unload_time = get_filament_unload_time(static_cast(prev_filament_id)); + extra_time += filament_unload_time; + filament_unload_time_delta += filament_unload_time; m_time_processor.extruder_unloaded = false; - extra_time += get_filament_load_time(static_cast(next_filament_id)); + const float filament_load_time = get_filament_load_time(static_cast(next_filament_id)); + extra_time += filament_load_time; + filament_load_time_delta += filament_load_time; } else { if (prev_extruder_id == -1) { @@ -5470,7 +5478,9 @@ void GCodeProcessor::process_filament_change(int id) m_extruder_id = next_extruder_id; m_filament_id[next_extruder_id] = next_filament_id; m_time_processor.extruder_unloaded = false; - extra_time += get_filament_load_time(static_cast(next_filament_id)); + const float filament_load_time = get_filament_load_time(static_cast(next_filament_id)); + extra_time += filament_load_time; + filament_load_time_delta += filament_load_time; } else { //first process cache generated by last extruder @@ -5481,24 +5491,39 @@ void GCodeProcessor::process_filament_change(int id) //no filament in current extruder m_filament_id[next_extruder_id] = next_filament_id; m_time_processor.extruder_unloaded = false; - extra_time += get_filament_load_time(static_cast(next_filament_id)); + const float filament_load_time = get_filament_load_time(static_cast(next_filament_id)); + extra_time += filament_load_time; + filament_load_time_delta += filament_load_time; } else if (m_last_filament_id[next_extruder_id] != next_filament_id) { //need to change filament m_filament_id[next_extruder_id] = next_filament_id; - m_result.lock(); - m_result.print_statistics.total_filament_changes += 1; - m_result.unlock(); - extra_time += get_filament_unload_time(static_cast(prev_filament_id)); + filament_changes_delta += 1; + const float filament_unload_time = get_filament_unload_time(static_cast(prev_filament_id)); + extra_time += filament_unload_time; + filament_unload_time_delta += filament_unload_time; m_time_processor.extruder_unloaded = false; - extra_time += get_filament_load_time(static_cast(next_filament_id)); + const float filament_load_time = get_filament_load_time(static_cast(next_filament_id)); + extra_time += filament_load_time; + filament_load_time_delta += filament_load_time; } - m_result.lock(); - m_result.print_statistics.total_extruder_changes++; - m_result.unlock(); - extra_time += get_extruder_change_time(next_extruder_id); + extruder_changes_delta += 1; + const float tool_change_time = get_extruder_change_time(next_extruder_id); + extra_time += tool_change_time; + tool_change_time_delta += tool_change_time; } } + + if (filament_changes_delta > 0 || extruder_changes_delta > 0 || filament_load_time_delta > 0.0f || filament_unload_time_delta > 0.0f || tool_change_time_delta > 0.0f) { + m_result.lock(); + m_result.print_statistics.total_filament_changes += filament_changes_delta; + m_result.print_statistics.total_extruder_changes += extruder_changes_delta; + m_result.print_statistics.total_filament_load_time += filament_load_time_delta; + m_result.print_statistics.total_filament_unload_time += filament_unload_time_delta; + m_result.print_statistics.total_tool_change_time += tool_change_time_delta; + m_result.unlock(); + } + m_cp_color.current = m_extruder_colors[next_filament_id]; simulate_st_synchronize(extra_time); // store tool change move @@ -5543,6 +5568,11 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type, m_line_id + 1 : ((type == EMoveType::Seam) ? m_last_line_id : m_line_id); + if (type == EMoveType::Travel) { + m_result.print_statistics.total_travel_moves++; + m_result.print_statistics.total_travel_distance += m_travel_dist; + } + m_result.moves.push_back({ m_last_line_id, type, diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 867c8561b1e..546df6fbb25 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -78,6 +78,13 @@ class Print; std::array(ETimeMode::Count)> modes; unsigned int total_filament_changes; unsigned int total_extruder_changes; + float total_filament_load_time; + float total_filament_unload_time; + float total_tool_change_time; + float total_travel_distance; + unsigned int total_travel_moves; + float total_seam_gap_distance; + float total_seam_scarf_distance; PrintEstimatedStatistics() { reset(); } @@ -95,6 +102,13 @@ class Print; used_filaments_per_role.clear(); total_filament_changes = 0; total_extruder_changes = 0; + total_filament_load_time = 0.0f; + total_filament_unload_time = 0.0f; + total_tool_change_time = 0.0f; + total_travel_distance = 0.0f; + total_travel_moves = 0; + total_seam_gap_distance = 0.0f; + total_seam_scarf_distance = 0.0f; } }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 01635e85b8b..5dac81b7fe1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -43,6 +43,7 @@ #include #include +#include #include @@ -94,7 +95,7 @@ static std::string get_view_type_string(libvgcode::EViewType view_type) return _u8L("Filament"); else if (view_type == libvgcode::EViewType::LayerTimeLinear) return _u8L("Layer Time"); -else if (view_type == libvgcode::EViewType::LayerTimeLogarithmic) + else if (view_type == libvgcode::EViewType::LayerTimeLogarithmic) return _u8L("Layer Time (log)"); // ORCA: Add Pressure Advance visualization support else if (view_type == libvgcode::EViewType::PressureAdvance) @@ -124,6 +125,29 @@ static int find_close_layer_idx(const std::vector &zs, double &z, double return -1; } +static std::string format_compact_weight(double value_in_grams, bool imperial_units) +{ + char buffer[64]; + if (imperial_units) { + ::sprintf(buffer, "%.2f oz", value_in_grams / GizmoObjectManipulation::oz_to_g); + return buffer; + } + + const double abs_value = value_in_grams < 0.0 ? -value_in_grams : value_in_grams; + const char* unit = "g"; + double scaled_value = abs_value; + if (scaled_value >= 1000000.0) { + scaled_value /= 1000000.0; + unit = "t"; + } else if (scaled_value >= 1000.0) { + scaled_value /= 1000.0; + unit = "kg"; + } + + ::sprintf(buffer, "%s%.2f%s", value_in_grams < 0.0 ? "-" : "", scaled_value, unit); + return buffer; +} + #if ENABLE_ACTUAL_SPEED_DEBUG int GCodeViewer::SequentialView::ActualSpeedImguiWidget::plot(const char* label, const std::array& frame_size) { @@ -1287,6 +1311,25 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const //BBS: move the id to the end of reset m_last_result_id = gcode_result.id; m_gcode_result = &gcode_result; + m_move_type_counts.fill(0); + for (auto& move_type_times : m_move_type_times) + move_type_times.fill(0.0f); + m_move_type_distances.fill(0.0f); + for (const GCodeProcessorResult::MoveVertex& move : gcode_result.moves) { + if (move.internal_only) + continue; + + const size_t move_type = static_cast(move.type); + if (move_type < m_move_type_counts.size()) { + ++m_move_type_counts[move_type]; + for (size_t mode = 0; mode < move.time.size(); ++mode) + m_move_type_times[move_type][mode] += move.time[mode]; + if (move.type == EMoveType::Retract || move.type == EMoveType::Unretract) + m_move_type_distances[move_type] += std::fabs(move.delta_extruder); + else + m_move_type_distances[move_type] += move.travel_dist; + } + } m_only_gcode_in_preview = only_gcode; m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); @@ -1449,6 +1492,29 @@ void GCodeViewer::load_as_preview(libvgcode::GCodeInputData&& data) { m_loaded_as_preview = true; + m_move_type_counts.fill(0); + for (auto& move_type_times : m_move_type_times) + move_type_times.fill(0.0f); + m_move_type_distances.fill(0.0f); + const size_t normal_time_mode_idx = static_cast(PrintEstimatedStatistics::ETimeMode::Normal); + for (size_t i = 0; i < data.vertices.size(); ++i) { + const libvgcode::PathVertex& vertex = data.vertices[i]; + const size_t move_type = static_cast(vertex.type); + if (move_type < m_move_type_counts.size()) { + ++m_move_type_counts[move_type]; + for (size_t mode = 0; mode < vertex.times.size(); ++mode) + m_move_type_times[move_type][mode] += vertex.times[mode]; + if (vertex.type == libvgcode::EMoveType::Retract || vertex.type == libvgcode::EMoveType::Unretract) { + m_move_type_distances[move_type] += std::fabs(vertex.feedrate) * vertex.times[normal_time_mode_idx]; + } else if (i > 0) { + const float dx = vertex.position[0] - data.vertices[i - 1].position[0]; + const float dy = vertex.position[1] - data.vertices[i - 1].position[1]; + const float dz = vertex.position[2] - data.vertices[i - 1].position[2]; + m_move_type_distances[move_type] += std::sqrt(dx * dx + dy * dy + dz * dz); + } + } + } + m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::Skirt, { 127, 255, 127 }); m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::ExternalPerimeter, { 255, 255, 0 }); m_viewer.set_extrusion_role_color(libvgcode::EGCodeExtrusionRole::SupportMaterial, { 127, 255, 127 }); @@ -1496,6 +1562,10 @@ void GCodeViewer::reset() m_extruders_count = 0; m_filament_diameters = std::vector(); m_filament_densities = std::vector(); + m_move_type_counts.fill(0); + for (auto& move_type_times : m_move_type_times) + move_type_times.fill(0.0f); + m_move_type_distances.fill(0.0f); m_print_statistics.reset(); m_custom_gcode_per_print_z = std::vector(); m_left_extruder_filament.clear(); @@ -2676,39 +2746,42 @@ void GCodeViewer::render_all_plates_stats(const std::vectorfirst + 1), offsets[_u8L("Filament")]}); char buf[64]; - double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1.0; - float column_sum_m = 0.0f; float column_sum_g = 0.0f; if (displayed_columns & ColumnData::Model) { + const std::string weight_text = format_compact_weight(model_used_filaments_g_all_plates[i], imperial_units); if ((displayed_columns & ~ColumnData::Model) > 0) - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i] / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", model_used_filaments_m_all_plates[i], weight_text.c_str()); else - ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m_all_plates[i], model_used_filaments_g_all_plates[i] / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m_all_plates[i], weight_text.c_str()); columns_offsets.push_back({ buf, offsets[_u8L("Model")] }); column_sum_m += model_used_filaments_m_all_plates[i]; column_sum_g += model_used_filaments_g_all_plates[i]; } if (displayed_columns & ColumnData::Support) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m_all_plates[i], support_used_filaments_g_all_plates[i] / unit_conver); + const std::string weight_text = format_compact_weight(support_used_filaments_g_all_plates[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", support_used_filaments_m_all_plates[i], weight_text.c_str()); columns_offsets.push_back({ buf, offsets[_u8L("Support")] }); column_sum_m += support_used_filaments_m_all_plates[i]; column_sum_g += support_used_filaments_g_all_plates[i]; } if (displayed_columns & ColumnData::Flushed) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m_all_plates[i], flushed_filaments_g_all_plates[i] / unit_conver); + const std::string weight_text = format_compact_weight(flushed_filaments_g_all_plates[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", flushed_filaments_m_all_plates[i], weight_text.c_str()); columns_offsets.push_back({ buf, offsets[_u8L("Flushed")] }); column_sum_m += flushed_filaments_m_all_plates[i]; column_sum_g += flushed_filaments_g_all_plates[i]; } if (displayed_columns & ColumnData::WipeTower) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m_all_plates[i], wipe_tower_used_filaments_g_all_plates[i] / unit_conver); + const std::string weight_text = format_compact_weight(wipe_tower_used_filaments_g_all_plates[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", wipe_tower_used_filaments_m_all_plates[i], weight_text.c_str()); columns_offsets.push_back({ buf, offsets[_u8L("Tower")] }); column_sum_m += wipe_tower_used_filaments_m_all_plates[i]; column_sum_g += wipe_tower_used_filaments_g_all_plates[i]; } if ((displayed_columns & ~ColumnData::Model) > 0) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver); + const std::string weight_text = format_compact_weight(column_sum_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", column_sum_m, weight_text.c_str()); columns_offsets.push_back({ buf, offsets[_u8L("Total")] }); } @@ -3072,7 +3145,9 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast(m_viewer.get_time_mode())]; const libvgcode::EViewType curr_view_type = m_viewer.get_view_type(); const int curr_view_type_i = static_cast(curr_view_type); - bool show_estimated_time = time_mode.time > 0.0f && (curr_view_type == libvgcode::EViewType::FeatureType || + const size_t current_time_mode = static_cast(m_viewer.get_time_mode()); + const float total_estimated_time = time_mode.time > 0.0f ? time_mode.time : m_viewer.get_estimated_time(); + bool show_estimated_time = total_estimated_time > 0.0f && (curr_view_type == libvgcode::EViewType::FeatureType || curr_view_type == libvgcode::EViewType::LayerTimeLinear || curr_view_type == libvgcode::EViewType::LayerTimeLogarithmic || (curr_view_type == libvgcode::EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); @@ -3086,6 +3161,39 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImVec2 pos_rect = ImGui::GetCursorScreenPos(); float window_padding = 4.0f * m_scale; + auto format_compact_count = [](unsigned long long value) { + static constexpr const char* suffixes[] = { "", "K", "M", "B", "T", "P", "E" }; + constexpr size_t suffix_count = sizeof(suffixes) / sizeof(suffixes[0]); + + if (value < 1000) + return std::to_string(value); + + size_t suffix_index = 0; + unsigned long long divisor = 1; + while (suffix_index + 1 < suffix_count && value / divisor >= 1000) { + divisor *= 1000; + ++suffix_index; + } + + const unsigned long long whole = value / divisor; + const unsigned long long tenths = (value % divisor) * 10 / divisor; + + std::string ret = std::to_string(whole); + if (tenths != 0) + ret += "." + std::to_string(tenths); + ret += suffixes[suffix_index]; + return ret; + }; + + auto format_percent = [](float percent) { + if (percent == 0.0f) + return std::string("0"); + + char buffer[64]; + percent > 0.001f ? ::sprintf(buffer, "%.1f", percent * 100.0f) : ::sprintf(buffer, "<0.1"); + return std::string(buffer); + }; + // ORCA dont use background on top bar to give modern look //draw_list->AddRectFilled(ImVec2(pos_rect.x,pos_rect.y - ImGui::GetStyle().WindowPadding.y), //ImVec2(pos_rect.x + ImGui::GetWindowWidth() + ImGui::GetFrameHeight(),pos_rect.y + ImGui::GetFrameHeight() + window_padding * 2.5), @@ -3122,10 +3230,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv case EItemType::Line: { draw_list->AddLine({ pos.x + 1, pos.y + icon_size + 2 }, { pos.x + icon_size - 1, pos.y + 4 }, ImGuiWrapper::to_ImU32(color), 3.0f); break; + } case EItemType::None: break; } - } ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20.0 * m_scale, 6.0 * m_scale)); @@ -3297,14 +3405,26 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); }; - auto role_time_and_percent = [this, time_mode](libvgcode::EGCodeExtrusionRole role) { + auto role_time_and_percent = [this, total_estimated_time](libvgcode::EGCodeExtrusionRole role) { const float time = m_viewer.get_extrusion_role_estimated_time(role); - return std::make_pair(time, time / time_mode.time); + return std::make_pair(time, total_estimated_time > 0.0f ? time / total_estimated_time : 0.0f); }; - auto travel_time_and_percent = [this, time_mode]() { + auto travel_time_and_percent = [this, total_estimated_time]() { const float time = m_viewer.get_travels_estimated_time(); - return std::make_pair(time, time / time_mode.time); + return std::make_pair(time, total_estimated_time > 0.0f ? time / total_estimated_time : 0.0f); + }; + + auto format_distance = [imperial_units](float distance_mm) { + char buffer[64]; + if (imperial_units) { + ::sprintf(buffer, "%.2fin", distance_mm / GizmoObjectManipulation::in_to_mm); + } else if (std::fabs(distance_mm) < 1000.0f) { + ::sprintf(buffer, "%.0fmm", distance_mm); + } else { + ::sprintf(buffer, "%.2fm", distance_mm / 1000.0f); + } + return std::string(buffer); }; auto used_filament_per_role = [this, imperial_units](ExtrusionRole role) { @@ -3409,6 +3529,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv std::vector used_filaments_length; std::vector used_filaments_weight; std::string travel_percent; + std::string travel_distance; + std::string travel_moves; std::vector model_used_filaments_m; std::vector model_used_filaments_g; double total_model_used_filament_m = 0, total_model_used_filament_g = 0; @@ -3528,8 +3650,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv auto [model_used_filament_m, model_used_filament_g] = used_filament_per_role(convert(role)); ::sprintf(buffer, imperial_units ? "%.2fin" : "%.2fm", model_used_filament_m); // ORCA dont use spacing between value and unit used_filaments_length.push_back(buffer); - ::sprintf(buffer, imperial_units ? "%.2foz" : "%.2fg", model_used_filament_g); // ORCA dont use spacing between value and unit - used_filaments_weight.push_back(buffer); + used_filaments_weight.push_back(format_compact_weight(model_used_filament_g, imperial_units)); } } @@ -3542,10 +3663,19 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv else percent > 0.001 ? ::sprintf(buffer, "%.1f", percent * 100) : ::sprintf(buffer, "<0.1"); travel_percent = buffer; + percents.push_back(travel_percent); + + // Set travel distance and moves for the Travel row Usage columns + travel_distance = format_distance(m_print_statistics.total_travel_distance); + used_filaments_length.push_back(travel_distance); + + travel_moves = format_compact_count(m_print_statistics.total_travel_moves); + used_filaments_weight.push_back(travel_moves); } // ORCA use % symbol for percentage and use "Usage" for "Used filaments" offsets = calculate_offsets({ {_u8L("Line Type"), labels}, {_u8L("Time"), times}, {"%", percents}, {"", used_filaments_length}, {"", used_filaments_weight}, {_u8L("Display"), {""}}}, icon_size); + percents.pop_back(); append_headers({{_u8L("Line Type"), offsets[0]}, {_u8L("Time"), offsets[1]}, {"%", offsets[2]}, {_u8L("Usage"), offsets[3]}, {_u8L("Display"), offsets[5]}}); break; } @@ -3609,7 +3739,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv { std::vector total_filaments; char buffer[64]; - ::sprintf(buffer, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", ps.total_used_filament / /*1000*/koef, ps.total_weight / unit_conver); + const std::string total_weight_text = format_compact_weight(ps.total_weight, imperial_units); + ::sprintf(buffer, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", ps.total_used_filament / /*1000*/koef, total_weight_text.c_str()); total_filaments.push_back(buffer); @@ -3644,9 +3775,54 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv default: { break; } } - auto append_option_item = [this, append_item](libvgcode::EOptionType type, std::vector offsets) { - auto append_option_item_with_type = [this, offsets, append_item](libvgcode::EOptionType type, const ColorRGBA& color, const std::string& label, bool visible) { - append_item(EItemType::Rect, color, {{ label , offsets[0] }}, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, type, visible]() { + auto append_option_item = [this, append_item, current_time_mode, total_estimated_time, &format_compact_count, &format_percent, &format_distance](libvgcode::EOptionType type, std::vector offsets) { + const bool full_layout = offsets.size() > 4; + auto option_stats = [this, current_time_mode, total_estimated_time, &format_compact_count, &format_percent, &format_distance, full_layout](libvgcode::EOptionType option_type) -> std::array { + libvgcode::EMoveType move_type; + bool has_move_type = true; + switch (option_type) { + case libvgcode::EOptionType::Wipes: { move_type = libvgcode::EMoveType::Wipe; break; } + case libvgcode::EOptionType::Retractions: { move_type = libvgcode::EMoveType::Retract; break; } + case libvgcode::EOptionType::Unretractions: { move_type = libvgcode::EMoveType::Unretract; break; } + case libvgcode::EOptionType::Seams: { move_type = libvgcode::EMoveType::Seam; break; } + case libvgcode::EOptionType::ToolChanges: { move_type = libvgcode::EMoveType::ToolChange; break; } + default: { has_move_type = false; break; } + } + + if (!has_move_type) + return { "", "", "", "" }; + + const size_t move_type_idx = static_cast(move_type); + float time = m_move_type_times[move_type_idx][current_time_mode]; + if (option_type == libvgcode::EOptionType::ToolChanges) { + // Toolchange delays are injected via synchronize() and are not attributed to ToolChange move vertices. + time = m_print_statistics.total_filament_load_time + m_print_statistics.total_filament_unload_time + m_print_statistics.total_tool_change_time; + } + const std::string time_text = full_layout && time > 0.0f ? short_time(get_time_dhms(time)) : ""; + const std::string percent_text = full_layout && total_estimated_time > 0.0f ? format_percent(time / total_estimated_time) : ""; + const float seam_distance = m_print_statistics.total_seam_gap_distance + m_print_statistics.total_seam_scarf_distance; + const float distance = (option_type == libvgcode::EOptionType::Seams && seam_distance > 0.0f) ? seam_distance : m_move_type_distances[move_type_idx]; + const std::string distance_text = full_layout && (option_type == libvgcode::EOptionType::Wipes || option_type == libvgcode::EOptionType::Retractions || option_type == libvgcode::EOptionType::Unretractions || option_type == libvgcode::EOptionType::Seams) + ? format_distance(distance) + : ""; + const std::string count_text = full_layout ? format_compact_count(m_move_type_counts[move_type_idx]) : ""; + + return { time_text, percent_text, distance_text, count_text }; + }; + + auto append_option_item_with_type = [this, offsets, append_item, full_layout](libvgcode::EOptionType type, const ColorRGBA& color, const std::string& label, bool visible, + const std::string& time_text, const std::string& percent_text, const std::string& distance_text, const std::string& count_text) { + std::vector> columns_offsets; + columns_offsets.push_back({ label , offsets[0] }); + if (full_layout && !time_text.empty()) + columns_offsets.push_back({ time_text, offsets[1] }); + if (full_layout && !percent_text.empty()) + columns_offsets.push_back({ percent_text, offsets[2] }); + if (full_layout && !distance_text.empty()) + columns_offsets.push_back({ distance_text, offsets[3] }); + if (full_layout && !count_text.empty()) + columns_offsets.push_back({ count_text, distance_text.empty() ? offsets[3] : offsets[4] }); + append_item(EItemType::Rect, color, columns_offsets, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, type, visible]() { m_viewer.toggle_option_visibility(type); update_moves_slider(); }); @@ -3654,18 +3830,33 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv const bool visible = m_viewer.is_option_visible(type); if (type == libvgcode::EOptionType::Travels) { //BBS: only display travel time in FeatureType view - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), _u8L("Travel"), visible); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), _u8L("Travel"), visible, "", "", "", ""); + } + else if (type == libvgcode::EOptionType::Seams) { + const auto option_values = option_stats(type); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Seams)), _u8L("Seams"), visible, + option_values[0], option_values[1], option_values[2], option_values[3]); + } + else if (type == libvgcode::EOptionType::Retractions) { + const auto option_values = option_stats(type); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Retractions)), _u8L("Retract"), visible, + option_values[0], option_values[1], option_values[2], option_values[3]); + } + else if (type == libvgcode::EOptionType::Unretractions) { + const auto option_values = option_stats(type); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Unretractions)), _u8L("Unretract"), visible, + option_values[0], option_values[1], option_values[2], option_values[3]); + } + else if (type == libvgcode::EOptionType::ToolChanges) { + const auto option_values = option_stats(type); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::ToolChanges)), _u8L("Filament Changes"), visible, + option_values[0], option_values[1], option_values[2], option_values[3]); + } + else if (type == libvgcode::EOptionType::Wipes) { + const auto option_values = option_stats(type); + append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Wipes)), _u8L("Wipe"), visible, + option_values[0], option_values[1], option_values[2], option_values[3]); } - else if (type == libvgcode::EOptionType::Seams) - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Seams)), _u8L("Seams"), visible); - else if (type == libvgcode::EOptionType::Retractions) - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Retractions)), _u8L("Retract"), visible); - else if (type == libvgcode::EOptionType::Unretractions) - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Unretractions)), _u8L("Unretract"), visible); - else if (type == libvgcode::EOptionType::ToolChanges) - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::ToolChanges)), _u8L("Filament Changes"), visible); - else if (type == libvgcode::EOptionType::Wipes) - append_option_item_with_type(type, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Wipes)), _u8L("Wipe"), visible); }; const libvgcode::EViewType new_view_type = curr_view_type; @@ -3705,6 +3896,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv columns_offsets.push_back({ _u8L("Travel"), offsets[0] }); columns_offsets.push_back({ travel_time, offsets[1] }); columns_offsets.push_back({ travel_percent, offsets[2] }); + columns_offsets.push_back({ travel_distance, offsets[3] }); // Usage column + columns_offsets.push_back({ travel_moves, offsets[4] }); // Usage column append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), columns_offsets, true, offsets.back()/*ORCA checkbox_pos*/, visible, [this, item, visible]() { m_viewer.toggle_option_visibility(item); update_moves_slider(); @@ -3796,7 +3989,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv size_t i = 0; const std::vector& used_extruders_ids = m_viewer.get_used_extruders_ids(); for (uint8_t extruder_id : used_extruders_ids) { - ::sprintf(buf, imperial_units ? "%.2f in %.2f g" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i]); + const std::string weight_text = format_compact_weight(model_used_filaments_g[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m[i], weight_text.c_str()); append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), { { _u8L("Extruder") + " " + std::to_string(extruder_id + 1), offsets[0]}, {buf, offsets[1]} }); // append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), _u8L("Extruder") + " " + std::to_string(extruder_id + 1), // true, "", 0.0f, 0.0f, offsets, used_filaments_m[extruder_id], used_filaments_g[extruder_id]); @@ -3809,7 +4003,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv char buf[64]; imgui.text(_u8L("Total") + ":"); ImGui::SameLine(); - ::sprintf(buf, imperial_units ? "%.2f in / %.2f oz" : "%.2f m / %.2f g", ps.total_used_filament / koef, ps.total_weight / unit_conver); + const std::string total_weight_text = format_compact_weight(ps.total_weight, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in / %s" : "%.2f m / %s", ps.total_used_filament / koef, total_weight_text.c_str()); imgui.text(buf); ImGui::Dummy({window_padding, window_padding}); @@ -3855,34 +4050,39 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv float column_sum_m = 0.0f; float column_sum_g = 0.0f; if (displayed_columns & ColumnData::Model) { + const std::string weight_text = format_compact_weight(model_used_filaments_g[i], imperial_units); if ((displayed_columns & ~ColumnData::Model) > 0) - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", model_used_filaments_m[i], weight_text.c_str()); else - ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i] / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", model_used_filaments_m[i], weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); column_sum_m += model_used_filaments_m[i]; column_sum_g += model_used_filaments_g[i]; } if (displayed_columns & ColumnData::Support) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", support_used_filaments_m[i], support_used_filaments_g[i] / unit_conver); + const std::string weight_text = format_compact_weight(support_used_filaments_g[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", support_used_filaments_m[i], weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); column_sum_m += support_used_filaments_m[i]; column_sum_g += support_used_filaments_g[i]; } if (displayed_columns & ColumnData::Flushed) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", flushed_filaments_m[i], flushed_filaments_g[i] / unit_conver); + const std::string weight_text = format_compact_weight(flushed_filaments_g[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", flushed_filaments_m[i], weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")]}); column_sum_m += flushed_filaments_m[i]; column_sum_g += flushed_filaments_g[i]; } if (displayed_columns & ColumnData::WipeTower) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", wipe_tower_used_filaments_m[i], wipe_tower_used_filaments_g[i] / unit_conver); + const std::string weight_text = format_compact_weight(wipe_tower_used_filaments_g[i], imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", wipe_tower_used_filaments_m[i], weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); column_sum_m += wipe_tower_used_filaments_m[i]; column_sum_g += wipe_tower_used_filaments_g[i]; } if ((displayed_columns & ~ColumnData::Model) > 0) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver); + const std::string weight_text = format_compact_weight(column_sum_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", column_sum_m, weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); } @@ -3908,27 +4108,32 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv std::vector> columns_offsets; columns_offsets.push_back({ _u8L("Total"), color_print_offsets[_u8L("Filament")]}); if (displayed_columns & ColumnData::Model) { + const std::string weight_text = format_compact_weight(total_model_used_filament_g, imperial_units); if ((displayed_columns & ~ColumnData::Model) > 0) - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_model_used_filament_m, weight_text.c_str()); else - ::sprintf(buf, imperial_units ? "%.2f in %.2f oz" : "%.2f m %.2f g", total_model_used_filament_m, total_model_used_filament_g / unit_conver); + ::sprintf(buf, imperial_units ? "%.2f in %s" : "%.2f m %s", total_model_used_filament_m, weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Model")] }); } if (displayed_columns & ColumnData::Support) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_support_used_filament_m, total_support_used_filament_g / unit_conver); + const std::string weight_text = format_compact_weight(total_support_used_filament_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_support_used_filament_m, weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Support")] }); } if (displayed_columns & ColumnData::Flushed) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_flushed_filament_m, total_flushed_filament_g / unit_conver); + const std::string weight_text = format_compact_weight(total_flushed_filament_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_flushed_filament_m, weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Flushed")] }); } if (displayed_columns & ColumnData::WipeTower) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_wipe_tower_used_filament_m, total_wipe_tower_used_filament_g / unit_conver); + const std::string weight_text = format_compact_weight(total_wipe_tower_used_filament_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_wipe_tower_used_filament_m, weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Tower")] }); } if ((displayed_columns & ~ColumnData::Model) > 0) { - ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m, - (total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g) / unit_conver); + const std::string weight_text = format_compact_weight(total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g, imperial_units); + ::sprintf(buf, imperial_units ? "%.2f in\n%s" : "%.2f m\n%s", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m, + weight_text.c_str()); columns_offsets.push_back({ buf, color_print_offsets[_u8L("Total")] }); } append_item(EItemType::None, libvgcode::convert(tool_colors[0]), columns_offsets); @@ -3939,16 +4144,14 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImGui::SameLine(); imgui.text(_u8L("Filament change times") + ":"); ImGui::SameLine(); - ::sprintf(buf, "%d", m_print_statistics.total_filament_changes); - imgui.text(buf); + imgui.text(format_compact_count(m_print_statistics.total_filament_changes)); //display tool change times ImGui::Dummy({window_padding, window_padding}); ImGui::SameLine(); imgui.text(_u8L("Tool changes") + ":"); ImGui::SameLine(); - ::sprintf(buf, "%d", m_print_statistics.total_extruder_changes); - imgui.text(buf); + imgui.text(format_compact_count(m_print_statistics.total_extruder_changes)); //BBS display cost ImGui::Dummy({ window_padding, window_padding }); @@ -4075,8 +4278,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv imgui.text(buffer); ImGui::SameLine(offsets[3]); - ::sprintf(buffer, "%.2f g", used_filament.second); - imgui.text(buffer); + imgui.text(format_compact_weight(used_filament.second, imperial_units)); } }; @@ -4401,8 +4603,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", ps.total_used_filament / koef); imgui.text(buf); ImGui::SameLine(); - ::sprintf(buf, imperial_units ? " %.2f oz" : " %.2f g", ps.total_weight / unit_conver); - imgui.text(buf); + imgui.text(" " + format_compact_weight(ps.total_weight, imperial_units)); ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); imgui.text(model_filament_str + ":"); @@ -4412,8 +4613,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", ps.total_used_filament / koef - exlude_m); imgui.text(buf); ImGui::SameLine(); - ::sprintf(buf, imperial_units ? " %.2f oz" : " %.2f g", (ps.total_weight - exlude_g) / unit_conver); - imgui.text(buf); + imgui.text(" " + format_compact_weight(ps.total_weight - exlude_g, imperial_units)); //BBS: display cost of filaments ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 727e2b3b986..600c59d829e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -14,6 +14,7 @@ // needed for tech VGCODE_ENABLE_COG_AND_TOOL_MARKERS #include +#include #include #include #include @@ -184,6 +185,9 @@ private: unsigned int m_last_result_id{ 0 }; //BBS: save m_gcode_result as well const GCodeProcessorResult* m_gcode_result; + std::array(EMoveType::Count)> m_move_type_counts{}; + std::array(PrintEstimatedStatistics::ETimeMode::Count)>, static_cast(EMoveType::Count)> m_move_type_times{}; + std::array(EMoveType::Count)> m_move_type_distances{}; //BBS: add only gcode mode bool m_only_gcode_in_preview {false};