diff --git a/resources/icons/edit_button_pressed.svg b/resources/icons/edit_button_pressed.svg new file mode 100644 index 0000000000..6e4058d10f --- /dev/null +++ b/resources/icons/edit_button_pressed.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_colorchanges.svg b/resources/icons/legend_colorchanges.svg new file mode 100644 index 0000000000..cb95ef4676 --- /dev/null +++ b/resources/icons/legend_colorchanges.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_customgcodes.svg b/resources/icons/legend_customgcodes.svg new file mode 100644 index 0000000000..96e0be69e3 --- /dev/null +++ b/resources/icons/legend_customgcodes.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/icons/legend_deretract.svg b/resources/icons/legend_deretract.svg new file mode 100644 index 0000000000..4b636df9de --- /dev/null +++ b/resources/icons/legend_deretract.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_pauseprints.svg b/resources/icons/legend_pauseprints.svg new file mode 100644 index 0000000000..954bc00e97 --- /dev/null +++ b/resources/icons/legend_pauseprints.svg @@ -0,0 +1,76 @@ + + diff --git a/resources/icons/legend_retract.svg b/resources/icons/legend_retract.svg new file mode 100644 index 0000000000..494e2f7286 --- /dev/null +++ b/resources/icons/legend_retract.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_seams.svg b/resources/icons/legend_seams.svg new file mode 100644 index 0000000000..724414119d --- /dev/null +++ b/resources/icons/legend_seams.svg @@ -0,0 +1,45 @@ + + + + + + + diff --git a/resources/icons/legend_shells.svg b/resources/icons/legend_shells.svg new file mode 100644 index 0000000000..b0a93effb2 --- /dev/null +++ b/resources/icons/legend_shells.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_toolchanges.svg b/resources/icons/legend_toolchanges.svg new file mode 100644 index 0000000000..85b6218a9b --- /dev/null +++ b/resources/icons/legend_toolchanges.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/icons/legend_toolmarker.svg b/resources/icons/legend_toolmarker.svg new file mode 100644 index 0000000000..3cd5cf8d96 --- /dev/null +++ b/resources/icons/legend_toolmarker.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/legend_travel.svg b/resources/icons/legend_travel.svg new file mode 100644 index 0000000000..553e90a743 --- /dev/null +++ b/resources/icons/legend_travel.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/legend_wipe.svg b/resources/icons/legend_wipe.svg new file mode 100644 index 0000000000..decfcd6011 --- /dev/null +++ b/resources/icons/legend_wipe.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 09c80a9d9b..db0e54e60d 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -155,6 +155,18 @@ namespace ImGui const wchar_t ClippyMarker = 0x2602; const wchar_t InfoMarker = 0x2603; const wchar_t SliderFloatEditBtnIcon = 0x2604; + const wchar_t SliderFloatEditBtnPressedIcon = 0x2605; + const wchar_t LegendTravel = 0x2606; + const wchar_t LegendWipe = 0x2607; + const wchar_t LegendRetract = 0x2608; + const wchar_t LegendDeretract = 0x2609; + const wchar_t LegendSeams = 0x2610; + const wchar_t LegendToolChanges = 0x2611; + const wchar_t LegendColorChanges = 0x2612; + const wchar_t LegendPausePrints = 0x2613; + const wchar_t LegendCustomGCodes = 0x2614; + const wchar_t LegendShells = 0x2615; + const wchar_t LegendToolMarker = 0x2616; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f54e38eec8..1f19a548e7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -189,6 +189,9 @@ void GCodeProcessor::TimeMachine::reset() max_travel_acceleration = 0.0f; extrude_factor_override_percentage = 1.0f; time = 0.0f; +#if ENABLE_TRAVEL_TIME + travel_time = 0.0f; +#endif // ENABLE_TRAVEL_TIME stop_times = std::vector(); curr.reset(); prev.reset(); @@ -304,9 +307,17 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa block_time += additional_time; time += block_time; +#if ENABLE_TRAVEL_TIME + if (block.move_type == EMoveType::Travel) + travel_time += block_time; + else + roles_time[static_cast(block.role)] += block_time; +#endif // ENABLE_TRAVEL_TIME gcode_time.cache += block_time; moves_time[static_cast(block.move_type)] += block_time; +#if !ENABLE_TRAVEL_TIME roles_time[static_cast(block.role)] += block_time; +#endif // !ENABLE_TRAVEL_TIME if (block.layer_id >= layers_time.size()) { const size_t curr_size = layers_time.size(); layers_time.resize(block.layer_id); @@ -1363,6 +1374,18 @@ std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mod return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].time)) : std::string("N/A"); } +#if ENABLE_TRAVEL_TIME +float GCodeProcessor::get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const +{ + return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast(mode)].travel_time : 0.0f; +} + +std::string GCodeProcessor::get_travel_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const +{ + return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast(mode)].travel_time)) : std::string("N/A"); +} +#endif // ENABLE_TRAVEL_TIME + std::vector>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const { std::vector>> ret; @@ -3372,6 +3395,9 @@ void GCodeProcessor::update_estimated_times_stats() auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast(mode)]; data.time = get_time(mode); +#if ENABLE_TRAVEL_TIME + data.travel_time = get_travel_time(mode); +#endif // ENABLE_TRAVEL_TIME data.custom_gcode_times = get_custom_gcode_times(mode, true); data.moves_times = get_moves_time(mode); data.roles_times = get_roles_time(mode); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 2b6ee9cea9..33b9a23f38 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -44,6 +44,9 @@ namespace Slic3r { struct Mode { float time; +#if ENABLE_TRAVEL_TIME + float travel_time; +#endif // ENABLE_TRAVEL_TIME std::vector>> custom_gcode_times; std::vector> moves_times; std::vector> roles_times; @@ -51,6 +54,9 @@ namespace Slic3r { void reset() { time = 0.0f; +#if ENABLE_TRAVEL_TIME + travel_time = 0.0f; +#endif // ENABLE_TRAVEL_TIME custom_gcode_times.clear(); moves_times.clear(); roles_times.clear(); @@ -290,6 +296,9 @@ namespace Slic3r { float max_travel_acceleration; // mm/s^2 float extrude_factor_override_percentage; float time; // s +#if ENABLE_TRAVEL_TIME + float travel_time; // s +#endif // ENABLE_TRAVEL_TIME struct StopTime { unsigned int g1_line_id; @@ -596,6 +605,10 @@ namespace Slic3r { float get_time(PrintEstimatedStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; +#if ENABLE_TRAVEL_TIME + float get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const; + std::string get_travel_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; +#endif // ENABLE_TRAVEL_TIME std::vector>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const; std::vector> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index f2dddca079..f82e5edcf8 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -45,4 +45,19 @@ #define ENABLE_SPIRAL_VASE_LAYERS (1 && ENABLE_2_4_1_RC) +//==================== +// 2.5.0.alpha1 techs +//==================== +#define ENABLE_2_5_0_ALPHA1 1 + +// Enable changes in preview layout +#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_5_0_ALPHA1) +// Enable drawing the items in legend toolbar using icons +#define ENABLE_LEGEND_TOOLBAR_ICONS (1 && ENABLE_PREVIEW_LAYOUT) +// Enable coloring of toolpaths in preview by layer time +#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_5_0_ALPHA1) +// Enable showing time estimate for travel moves in legend +#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_5_0_ALPHA1) + + #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index d9419495ee..2562d1913a 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -360,7 +361,7 @@ inline std::string get_time_dhms(float time_in_secs) else if (minutes > 0) ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); else - ::sprintf(buffer, "%ds", (int)time_in_secs); + ::sprintf(buffer, "%ds", (int)std::round(time_in_secs)); return buffer; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 9b413b4d6f..5edc321f7b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -160,16 +160,43 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } }); } +#if ENABLE_PREVIEW_LAYER_TIME +float GCodeViewer::Extrusions::Range::step_size(EType type) const +{ + switch (type) + { + default: + case EType::Linear: { return (max > min) ? (max - min) / (static_cast(Range_Colors.size()) - 1.0f) : 0.0f; } + case EType::Logarithmic: { return (max > min && min > 0.0f) ? ::log(max / min) / (static_cast(Range_Colors.size()) - 1.0f) : 0.0f; } + } +} + +ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value, EType type) const +#else ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value) const +#endif // ENABLE_PREVIEW_LAYER_TIME { // Input value scaled to the colors range +#if ENABLE_PREVIEW_LAYER_TIME + float global_t = 0.0f; + const float step = step_size(type); + if (step > 0.0f) { + switch (type) + { + default: + case EType::Linear: { global_t = (value > min) ? (value - min) / step : 0.0f; break; } + case EType::Logarithmic: { global_t = (value > min && min > 0.0f) ? ::log(value / min) / step : 0.0f; break; } + } + } +#else const float step = step_size(); const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f +#endif // ENABLE_PREVIEW_LAYER_TIME const size_t color_max_idx = Range_Colors.size() - 1; // Compute the two colors just below (low) and above (high) the input value - const size_t color_low_idx = std::clamp(static_cast(global_t), 0, color_max_idx); + const size_t color_low_idx = std::clamp(static_cast(global_t), 0, color_max_idx); const size_t color_high_idx = std::clamp(color_low_idx + 1, 0, color_max_idx); // Interpolate between the low and high colors to find exactly which color the input value should get @@ -740,19 +767,37 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v } } +#if ENABLE_PREVIEW_LAYER_TIME + for (size_t i = 0; i < gcode_result.print_statistics.modes.size(); ++i) { + m_layers_times[i] = gcode_result.print_statistics.modes[i].layers_times; + } + + for (size_t i = 0; i < m_layers_times.size(); ++i) { + for (float time : m_layers_times[i]) { + m_extrusions.ranges.layer_time[i].update_from(time); + } + } +#endif // ENABLE_PREVIEW_LAYER_TIME + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS // update buffers' render paths +#if ENABLE_PREVIEW_LAYOUT + refresh_render_paths(false, false); +#else refresh_render_paths(); +#endif // ENABLE_PREVIEW_LAYOUT log_memory_used("Refreshed G-code extrusion paths, "); } +#if !ENABLE_PREVIEW_LAYOUT void GCodeViewer::refresh_render_paths() { refresh_render_paths(false, false); } +#endif // !ENABLE_PREVIEW_LAYOUT void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig* config) { @@ -781,12 +826,20 @@ void GCodeViewer::reset() m_layers_z_range = { 0, 0 }; m_roles = std::vector(); m_print_statistics.reset(); +#if ENABLE_PREVIEW_LAYER_TIME + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + m_layers_times[i] = std::vector(); + } +#endif // ENABLE_PREVIEW_LAYER_TIME m_custom_gcode_per_print_z = std::vector(); m_sequential_view.gcode_window.reset(); #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); #endif // ENABLE_GCODE_VIEWER_STATISTICS m_contained_in_bed = true; +#if ENABLE_PREVIEW_LAYOUT + m_legend_resizer.reset(); +#endif // ENABLE_PREVIEW_LAYOUT } void GCodeViewer::render() @@ -896,7 +949,9 @@ unsigned int GCodeViewer::get_options_visibility_flags() const flags = set_flag(flags, static_cast(Preview::OptionType::CustomGCodes), is_toolpath_move_type_visible(EMoveType::Custom_GCode)); flags = set_flag(flags, static_cast(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); +#if !ENABLE_PREVIEW_LAYOUT flags = set_flag(flags, static_cast(Preview::OptionType::Legend), is_legend_enabled()); +#endif // !ENABLE_PREVIEW_LAYOUT return flags; } @@ -917,7 +972,9 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags) set_toolpath_move_type_visible(EMoveType::Custom_GCode, is_flag_set(static_cast(Preview::OptionType::CustomGCodes))); m_shells.visible = is_flag_set(static_cast(Preview::OptionType::Shells)); m_sequential_view.marker.set_visible(is_flag_set(static_cast(Preview::OptionType::ToolMarker))); +#if !ENABLE_PREVIEW_LAYOUT enable_legend(is_flag_set(static_cast(Preview::OptionType::Legend))); +#endif // !ENABLE_PREVIEW_LAYOUT } void GCodeViewer::set_layers_z_range(const std::array& layers_z_range) @@ -1982,7 +2039,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) if (last_z == nullptr || z < *last_z - EPSILON || *last_z + EPSILON < z) m_layers.append(z, { last_travel_s_id, move_id }); else - m_layers.get_endpoints().back().last = move_id; + m_layers.get_ranges().back().last = move_id; // extruder ids m_extruder_ids.emplace_back(move.extruder_id); // roles @@ -1991,7 +2048,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) } else if (move.type == EMoveType::Travel) { if (move_id - last_travel_s_id > 1 && !m_layers.empty()) - m_layers.get_endpoints().back().last = move_id; + m_layers.get_ranges().back().last = move_id; last_travel_s_id = move_id; } @@ -2118,6 +2175,26 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool 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; } +#if ENABLE_PREVIEW_LAYER_TIME + case EViewType::LayerTimeLinear: + case EViewType::LayerTimeLogarithmic: { + const Path::Sub_Path& sub_path = path.sub_paths.front(); + double z = static_cast(sub_path.first.position.z()); + const std::vector& zs = m_layers.get_zs(); + const std::vector& ranges = m_layers.get_ranges(); + size_t time_mode_id = static_cast(m_time_estimate_mode); + for (size_t i = 0; i < zs.size(); ++i) { + if (std::abs(zs[i] - z) < EPSILON) { + if (ranges[i].contains(sub_path.first.s_id)) { + color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], + (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); + break; + } + } + } + break; + } +#endif // ENABLE_PREVIEW_LAYER_TIME case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { @@ -2142,7 +2219,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool auto is_in_layers_range = [this](const Path& path, size_t min_id, size_t max_id) { auto in_layers_range = [this, min_id, max_id](size_t id) { - return m_layers.get_endpoints_at(min_id).first <= id && id <= m_layers.get_endpoints_at(max_id).last; + return m_layers.get_range_at(min_id).first <= id && id <= m_layers.get_range_at(max_id).last; }; return in_layers_range(path.sub_paths.front().first.s_id) && in_layers_range(path.sub_paths.back().last.s_id); @@ -2167,8 +2244,8 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool path.sub_paths.back().last = buffer.paths[last].sub_paths.back().last; } - const size_t min_s_id = m_layers.get_endpoints_at(min_id).first; - const size_t max_s_id = m_layers.get_endpoints_at(max_id).last; + const size_t min_s_id = m_layers.get_range_at(min_id).first; + const size_t max_s_id = m_layers.get_range_at(max_id).last; return (min_s_id <= path.sub_paths.front().first.s_id && path.sub_paths.front().first.s_id <= max_s_id) || (min_s_id <= path.sub_paths.back().last.s_id && path.sub_paths.back().last.s_id <= max_s_id); @@ -2201,14 +2278,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel || buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { for (size_t id : buffer.model.instances.s_ids) { - if (id < m_layers.get_endpoints_at(m_layers_z_range[0]).first || m_layers.get_endpoints_at(m_layers_z_range[1]).last < id) + if (id < m_layers.get_range_at(m_layers_z_range[0]).first || m_layers.get_range_at(m_layers_z_range[1]).last < id) continue; global_endpoints.first = std::min(global_endpoints.first, id); global_endpoints.last = std::max(global_endpoints.last, id); if (top_layer_only) { - if (id < m_layers.get_endpoints_at(m_layers_z_range[1]).first || m_layers.get_endpoints_at(m_layers_z_range[1]).last < id) + if (id < m_layers.get_range_at(m_layers_z_range[1]).first || m_layers.get_range_at(m_layers_z_range[1]).last < id) continue; top_layer_endpoints.first = std::min(top_layer_endpoints.first, id); @@ -2950,7 +3027,11 @@ void GCodeViewer::render_legend(float& legend_height) const float max_height = 0.75f * static_cast(cnv_size.get_height()); const float child_height = 0.3333f * max_height; ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height }); +#if ENABLE_PREVIEW_LAYOUT + imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); +#else imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove); +#endif // ENABLE_PREVIEW_LAYOUT enum class EItemType : unsigned char { @@ -2961,8 +3042,14 @@ void GCodeViewer::render_legend(float& legend_height) }; const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast(m_time_estimate_mode)]; +#if ENABLE_PREVIEW_LAYER_TIME + bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || + m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic || + (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); +#else 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())); +#endif // ENABLE_PREVIEW_LAYER_TIME const float icon_size = ImGui::GetTextLineHeight(); const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); @@ -3048,7 +3135,25 @@ void GCodeViewer::render_legend(float& legend_height) } else { imgui.text(label); +#if ENABLE_TRAVEL_TIME + if (!time.empty()) { + ImGui::SameLine(offsets[0]); + imgui.text(time); + ImGui::SameLine(offsets[1]); + pos = ImGui::GetCursorScreenPos(); + const float width = std::max(1.0f, percent_bar_size * percent / max_percent); + draw_list->AddRectFilled({ pos.x, pos.y + 2.0f }, { pos.x + width, pos.y + icon_size - 2.0f }, + ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT)); + ImGui::Dummy({ percent_bar_size, icon_size }); + ImGui::SameLine(); + char buf[64]; + ::sprintf(buf, "%.1f%%", 100.0f * percent); + ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); + } + else if (used_filament_m > 0.0) { +#else if (used_filament_m > 0.0) { +#endif // ENABLE_TRAVEL_TIME char buf[64]; ImGui::SameLine(offsets[0]); ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m); @@ -3074,6 +3179,7 @@ void GCodeViewer::render_legend(float& legend_height) // single item use case append_range_item(0, range.min, decimals); else if (range.count == 2) { + // two items use case append_range_item(static_cast(Range_Colors.size()) - 1, range.max, decimals); append_range_item(0, range.min, decimals); } @@ -3085,6 +3191,39 @@ void GCodeViewer::render_legend(float& legend_height) } }; +#if ENABLE_PREVIEW_LAYER_TIME + auto append_time_range = [append_item](const Extrusions::Range& range, Extrusions::Range::EType type) { + auto append_range_item = [append_item](int i, float value) { + std::string str_value = get_time_dhms(value); + if (str_value == "0s") + str_value = "< 1s"; + append_item(EItemType::Rect, Range_Colors[i], str_value); + }; + + if (range.count == 1) + // single item use case + append_range_item(0, range.min); + else if (range.count == 2) { + // two items use case + append_range_item(static_cast(Range_Colors.size()) - 1, range.max); + append_range_item(0, range.min); + } + else { + float step_size = range.step_size(type); + for (int i = static_cast(Range_Colors.size()) - 1; i >= 0; --i) { + float value = 0.0f; + switch (type) + { + default: + case Extrusions::Range::EType::Linear: { value = range.min + static_cast(i) * step_size; break; } + case Extrusions::Range::EType::Logarithmic: { value = ::exp(::log(range.min) + static_cast(i) * step_size); break; } + } + append_range_item(i, value); + } + } + }; +#endif // ENABLE_PREVIEW_LAYER_TIME + auto append_headers = [&imgui](const std::array& texts, const std::array& offsets) { size_t i = 0; for (; i < offsets.size(); i++) { @@ -3184,7 +3323,7 @@ void GCodeViewer::render_legend(float& legend_height) std::vector percents; std::vector used_filaments_m; std::vector used_filaments_g; - float max_percent = 0.0f; + float max_time_percent = 0.0f; if (m_view_type == EViewType::FeatureType) { // calculate offsets to align time/percentage data @@ -3195,7 +3334,7 @@ void GCodeViewer::render_legend(float& legend_height) auto [time, percent] = role_time_and_percent(role); times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); percents.push_back(percent); - max_percent = std::max(max_percent, percent); + max_time_percent = std::max(max_time_percent, percent); auto [used_filament_m, used_filament_g] = used_filament_per_role(role); used_filaments_m.push_back(used_filament_m); used_filaments_g.push_back(used_filament_g); @@ -3255,6 +3394,47 @@ void GCodeViewer::render_legend(float& legend_height) offsets = calculate_offsets(labels, times, { "Extruder NNN", longest_used_filament_string }, icon_size); } +#if ENABLE_PREVIEW_LAYOUT + // selection section + bool view_type_changed = false; + int old_view_type = static_cast(get_view_type()); + int view_type = old_view_type; + + if (!m_legend_resizer.dirty) + ImGui::SetNextItemWidth(-1.0f); + + ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f }); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f }); + imgui.combo("", { _u8L("Feature type"), + _u8L("Height (mm)"), + _u8L("Width (mm)"), + _u8L("Speed (mm/s)"), + _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), + _u8L("Volumetric flow rate (mm³/s)"), +#if ENABLE_PREVIEW_LAYER_TIME + _u8L("Layer time (linear)"), + _u8L("Layer time (logarithmic)"), +#endif // ENABLE_PREVIEW_LAYER_TIME + _u8L("Tool"), + _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest); + ImGui::PopStyleColor(2); + + if (old_view_type != view_type) { + set_view_type(static_cast(view_type)); + wxGetApp().plater()->set_keep_current_preview_type(true); + wxGetApp().plater()->refresh_print(); + view_type_changed = true; + } + + // extrusion paths section -> title + if (m_view_type == EViewType::FeatureType) + append_headers({ _u8L(""), _u8L("Time"), _u8L("Percentage"), _u8L("Used filament") }, offsets); + else if (m_view_type == EViewType::Tool) + append_headers({ _u8L(""), _u8L("Used filament"), _u8L(""), _u8L("") }, offsets); + else + ImGui::Separator(); +#else // extrusion paths section -> title switch (m_view_type) { @@ -3263,58 +3443,81 @@ void GCodeViewer::render_legend(float& legend_height) append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage"), _u8L("Used filament") }, offsets); break; } - case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } - case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } - case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } - case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } - case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } - case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } - case EViewType::Tool: - { + case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; } + case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } + case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } + case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } + case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } + case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } +#if ENABLE_PREVIEW_LAYER_TIME + case EViewType::LayerTimeLinear: { imgui.title(_u8L("Layer time (linear)")); break; } + case EViewType::LayerTimeLogarithmic: { imgui.title(_u8L("Layer time (logarithmic)")); break; } +#endif // ENABLE_PREVIEW_LAYER_TIME + case EViewType::Tool: { append_headers({ _u8L("Tool"), _u8L("Used filament") }, offsets); break; } - case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } + case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } default: { break; } } +#endif // ENABLE_PREVIEW_LAYOUT +#if ENABLE_PREVIEW_LAYOUT + if (!view_type_changed) { +#endif // ENABLE_PREVIEW_LAYOUT // extrusion paths section -> items switch (m_view_type) { case EViewType::FeatureType: { +#if ENABLE_TRAVEL_TIME + max_time_percent = std::max(max_time_percent, time_mode.travel_time / time_mode.time); +#endif // ENABLE_TRAVEL_TIME + for (size_t i = 0; i < m_roles.size(); ++i) { ExtrusionRole role = m_roles[i]; if (role >= erCount) continue; const bool visible = is_visible(role); append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], - visible, times[i], percents[i], max_percent, offsets, used_filaments_m[i], used_filaments_g[i], [this, role, visible]() { + visible, times[i], percents[i], max_time_percent, offsets, used_filaments_m[i], used_filaments_g[i], [this, role, visible]() { m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->update_preview_moves_slider(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); +#if !ENABLE_PREVIEW_LAYOUT wxGetApp().plater()->update_preview_bottom_toolbar(); +#endif // !ENABLE_PREVIEW_LAYOUT } ); } + +#if ENABLE_TRAVEL_TIME + if (m_buffers[buffer_id(EMoveType::Travel)].visible) + append_item(EItemType::Line, Travel_Colors[0], _u8L("Travel"), true, short_time(get_time_dhms(time_mode.travel_time)), + time_mode.travel_time / time_mode.time, max_time_percent, offsets, 0.0f, 0.0f); +#endif // ENABLE_TRAVEL_TIME + 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::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } - case EViewType::Tool: - { + 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::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } +#if ENABLE_PREVIEW_LAYER_TIME + case EViewType::LayerTimeLinear: { append_time_range(m_extrusions.ranges.layer_time[static_cast(m_time_estimate_mode)], Extrusions::Range::EType::Linear); break; } + case EViewType::LayerTimeLogarithmic: { append_time_range(m_extrusions.ranges.layer_time[static_cast(m_time_estimate_mode)], Extrusions::Range::EType::Logarithmic); break; } +#endif // ENABLE_PREVIEW_LAYER_TIME + case EViewType::Tool: { // shows only extruders actually used size_t i = 0; for (unsigned char extruder_id : m_extruder_ids) { append_item(EItemType::Rect, m_tool_colors[extruder_id], _u8L("Extruder") + " " + std::to_string(extruder_id + 1), true, "", 0.0f, 0.0f, offsets, used_filaments_m[i], used_filaments_g[i]); - i++; + ++i; } break; } @@ -3326,7 +3529,7 @@ void GCodeViewer::render_legend(float& legend_height) total_items += color_print_ranges(i, custom_gcode_per_print_z).size(); } - const bool need_scrollable = static_cast(total_items) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + const bool need_scrollable = static_cast(total_items) * icon_size + (static_cast(total_items) - 1.0f) * ImGui::GetStyle().ItemSpacing.y > child_height; // add scrollable region, if needed if (need_scrollable) @@ -3387,6 +3590,9 @@ void GCodeViewer::render_legend(float& legend_height) } default: { break; } } +#if ENABLE_PREVIEW_LAYOUT + } +#endif // ENABLE_PREVIEW_LAYOUT // partial estimated printing time section if (m_view_type == EViewType::ColorPrint) { @@ -3534,7 +3740,7 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::Spacing(); append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets); - const bool need_scrollable = static_cast(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height; + const bool need_scrollable = static_cast(partial_times.size()) * icon_size + (static_cast(partial_times.size()) - 1.0f) * ImGui::GetStyle().ItemSpacing.y > child_height; if (need_scrollable) // add scrollable region ImGui::BeginChild("events", { -1.0f, child_height }, false); @@ -3564,6 +3770,7 @@ void GCodeViewer::render_legend(float& legend_height) } } +#if !ENABLE_PREVIEW_LAYOUT // travel paths section if (m_buffers[buffer_id(EMoveType::Travel)].visible) { switch (m_view_type) @@ -3644,6 +3851,7 @@ void GCodeViewer::render_legend(float& legend_height) add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses")); add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom G-codes")); } +#endif // !ENABLE_PREVIEW_LAYOUT // settings section bool has_settings = false; @@ -3725,7 +3933,7 @@ void GCodeViewer::render_legend(float& legend_height) if (can_show_mode_button(m_time_estimate_mode)) { switch (m_time_estimate_mode) { - case PrintEstimatedStatistics::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]"; break; } + case PrintEstimatedStatistics::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]"; break; } case PrintEstimatedStatistics::ETimeMode::Stealth: { time_title += " [" + _u8L("Stealth mode") + "]"; break; } default: { assert(false); break; } } @@ -3756,6 +3964,10 @@ void GCodeViewer::render_legend(float& legend_height) if (can_show_mode_button(mode)) { if (imgui.button(label)) { m_time_estimate_mode = mode; +#if ENABLE_PREVIEW_LAYER_TIME + if (m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic) + refresh_render_paths(false, false); +#endif // ENABLE_PREVIEW_LAYER_TIME imgui.set_requires_extra_frame(); } } @@ -3763,18 +3975,216 @@ void GCodeViewer::render_legend(float& legend_height) switch (m_time_estimate_mode) { case PrintEstimatedStatistics::ETimeMode::Normal: { - show_mode_button(_L("Show stealth mode"), PrintEstimatedStatistics::ETimeMode::Stealth); + show_mode_button(_u8L("Show stealth mode"), PrintEstimatedStatistics::ETimeMode::Stealth); break; } case PrintEstimatedStatistics::ETimeMode::Stealth: { - show_mode_button(_L("Show normal mode"), PrintEstimatedStatistics::ETimeMode::Normal); + show_mode_button(_u8L("Show normal mode"), PrintEstimatedStatistics::ETimeMode::Normal); break; } default : { assert(false); break; } } } - legend_height = ImGui::GetCurrentWindow()->Size.y; +#if ENABLE_PREVIEW_LAYOUT + // toolbar section + auto toggle_button = [this, &imgui, icon_size](Preview::OptionType type, const std::string& name, + std::function draw_callback) { + auto is_flag_set = [](unsigned int flags, unsigned int flag) { + return (flags & (1 << flag)) != 0; + }; + + auto set_flag = [](unsigned int flags, unsigned int flag, bool active) { + return active ? (flags | (1 << flag)) : (flags & ~(1 << flag)); + }; + + unsigned int flags = get_options_visibility_flags(); + unsigned int flag = static_cast(type); + bool active = is_flag_set(flags, flag); + + if (imgui.draw_radio_button(name, 1.5f * icon_size, active, draw_callback)) { + unsigned int new_flags = set_flag(flags, flag, !active); + set_options_visibility_from_flags(new_flags); + + const unsigned int diff_flags = flags ^ new_flags; + if (m_view_type == GCodeViewer::EViewType::Feedrate && is_flag_set(diff_flags, static_cast(Preview::OptionType::Travel))) + wxGetApp().plater()->refresh_print(); + else { + bool keep_first = m_sequential_view.current.first != m_sequential_view.global.first; + bool keep_last = m_sequential_view.current.last != m_sequential_view.global.last; + wxGetApp().plater()->get_current_canvas3D()->refresh_gcode_preview_render_paths(keep_first, keep_last); + } + wxGetApp().plater()->update_preview_moves_slider(); + } + + if (ImGui::IsItemHovered()) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(name); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + }; + +#if ENABLE_LEGEND_TOOLBAR_ICONS +// auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { +// const float margin = 3.0f; +// const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size)); +// window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); +// }; +// auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { +// const float margin = 3.0f; +// window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); +// }; + auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) { + ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + const float tex_w = static_cast(io.Fonts->TexWidth); + const float tex_h = static_cast(io.Fonts->TexHeight); + const ImFontAtlas::CustomRect* const rect = imgui.GetTextureCustomRect(icon_id); + const ImVec2 uv0 = { static_cast(rect->X) / tex_w, static_cast(rect->Y) / tex_h }; + const ImVec2 uv1 = { static_cast(rect->X + rect->Width) / tex_w, static_cast(rect->Y + rect->Height) / tex_h }; + window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f })); + }; +#else + auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { + const float margin = 3.0f; + const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size)); + window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); + }; + auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const Color& color) { + const float margin = 3.0f; + window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 3.0f); + }; +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); + ImGui::Spacing(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendTravel); +#else + toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [line_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + line_icon(window, pos, size, Travel_Colors[0]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendWipe); +#else + toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [line_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + line_icon(window, pos, size, Wipe_Color); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendRetract); +#else + toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::Retractions)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendDeretract); +#else + toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::Unretractions)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendSeams); +#else + toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::Seams)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendToolChanges); +#else + toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::ToolChanges)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendColorChanges); +#else + toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::ColorChanges)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendPausePrints); +#else + toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::PausePrints)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendCustomGCodes); +#else + toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + circle_icon(window, pos, size, Options_Colors[static_cast(EOptionsColors::CustomGCodes)]); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendShells); +#else + toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [](ImGuiWindow& window, const ImVec2& pos, float size) { + const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 1.0f }); + const float margin = 3.0f; + const float proj = 0.25f * size; + window.DrawList->AddRect({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin - proj, pos.y + margin + proj }, color); + window.DrawList->AddLine({ pos.x + margin, pos.y + margin + proj }, { pos.x + margin + proj, pos.y + margin }, color); + window.DrawList->AddLine({ pos.x + size - margin - proj, pos.y + margin + proj }, { pos.x + size - margin, pos.y + margin }, color); + window.DrawList->AddLine({ pos.x + size - margin - proj, pos.y + size - margin }, { pos.x + size - margin, pos.y + size - margin - proj }, color); + window.DrawList->AddLine({ pos.x + margin + proj, pos.y + margin }, { pos.x + size - margin, pos.y + margin }, color); + window.DrawList->AddLine({ pos.x + size - margin, pos.y + margin }, { pos.x + size - margin, pos.y + size - margin - proj }, color); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + ImGui::SameLine(); +#if ENABLE_LEGEND_TOOLBAR_ICONS + toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { + image_icon(window, pos, size, ImGui::LegendToolMarker); +#else + toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [](ImGuiWindow& window, const ImVec2& pos, float size) { + const ImU32 color = ImGui::GetColorU32({ 1.0f, 1.0f, 1.0f, 0.8f }); + const float margin = 3.0f; + const ImVec2 p1(0.5f * (pos.x + pos.x + size), pos.y + size - margin); + const ImVec2 p2 = ImVec2(p1.x + 0.25f * size, p1.y - 0.25f * size); + const ImVec2 p3 = ImVec2(p1.x - 0.25f * size, p1.y - 0.25f * size); + window.DrawList->AddTriangleFilled(p1, p2, p3, color); + const float mid_x = 0.5f * (pos.x + pos.x + size); + window.DrawList->AddRectFilled({ mid_x - 0.09375f * size, p1.y - 0.25f * size }, { mid_x + 0.09375f * size, pos.y + margin }, color); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + }); + + bool size_dirty = !ImGui::GetCurrentWindow()->ScrollbarY && ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x != ImGui::GetWindowWidth(); + if (m_legend_resizer.dirty || size_dirty != m_legend_resizer.dirty) { + wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + } + m_legend_resizer.dirty = size_dirty; +#endif // ENABLE_PREVIEW_LAYOUT + + legend_height = ImGui::GetWindowHeight(); imgui.end(); ImGui::PopStyleVar(); @@ -3896,7 +4306,7 @@ void GCodeViewer::log_memory_used(const std::string& label, int64_t additional) } } int64_t layers_size = SLIC3R_STDVEC_MEMSIZE(m_layers.get_zs(), double); - layers_size += SLIC3R_STDVEC_MEMSIZE(m_layers.get_endpoints(), Layers::Endpoints); + layers_size += SLIC3R_STDVEC_MEMSIZE(m_layers.get_ranges(), Layers::Range); BOOST_LOG_TRIVIAL(trace) << label << "(" << format_memsize_MB(additional + paths_size + render_paths_size + layers_size) << ");" << log_memory_info(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 2a59747177..f7adcc9eb3 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -381,6 +381,14 @@ class GCodeViewer { struct Range { +#if ENABLE_PREVIEW_LAYER_TIME + enum class EType : unsigned char + { + Linear, + Logarithmic + }; +#endif // ENABLE_PREVIEW_LAYER_TIME + float min; float max; unsigned int count; @@ -395,8 +403,13 @@ class GCodeViewer } void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; } +#if ENABLE_PREVIEW_LAYER_TIME + float step_size(EType type = EType::Linear) const; + ColorRGBA get_color_at(float value, EType type = EType::Linear) const; +#else float step_size() const { return (max - min) / (static_cast(Range_Colors.size()) - 1.0f); } ColorRGBA get_color_at(float value) const; +#endif // ENABLE_PREVIEW_LAYER_TIME }; struct Ranges @@ -413,6 +426,10 @@ class GCodeViewer Range volumetric_rate; // Color mapping by extrusion temperature. Range temperature; +#if ENABLE_PREVIEW_LAYER_TIME + // Color mapping by layer time. + std::array(PrintEstimatedStatistics::ETimeMode::Count)> layer_time; +#endif // ENABLE_PREVIEW_LAYER_TIME void reset() { height.reset(); @@ -421,6 +438,11 @@ class GCodeViewer fan_speed.reset(); volumetric_rate.reset(); temperature.reset(); +#if ENABLE_PREVIEW_LAYER_TIME + for (auto& range : layer_time) { + range.reset(); + } +#endif // ENABLE_PREVIEW_LAYER_TIME } }; @@ -440,42 +462,43 @@ class GCodeViewer class Layers { public: - struct Endpoints + struct Range { size_t first{ 0 }; size_t last{ 0 }; - bool operator == (const Endpoints& other) const { return first == other.first && last == other.last; } - bool operator != (const Endpoints& other) const { return !operator==(other); } + bool operator == (const Range& other) const { return first == other.first && last == other.last; } + bool operator != (const Range& other) const { return !operator==(other); } + bool contains(size_t id) const { return first <= id && id <= last; } }; private: std::vector m_zs; - std::vector m_endpoints; + std::vector m_ranges; public: - void append(double z, Endpoints endpoints) { + void append(double z, const Range& range) { m_zs.emplace_back(z); - m_endpoints.emplace_back(endpoints); + m_ranges.emplace_back(range); } void reset() { m_zs = std::vector(); - m_endpoints = std::vector(); + m_ranges = std::vector(); } size_t size() const { return m_zs.size(); } bool empty() const { return m_zs.empty(); } const std::vector& get_zs() const { return m_zs; } - const std::vector& get_endpoints() const { return m_endpoints; } - std::vector& get_endpoints() { return m_endpoints; } + const std::vector& get_ranges() const { return m_ranges; } + std::vector& get_ranges() { return m_ranges; } double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; } - Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); } + Range get_range_at(unsigned int id) const { return (id < m_ranges.size()) ? m_ranges[id] : Range(); } bool operator != (const Layers& other) const { if (m_zs != other.m_zs) return true; - if (m_endpoints != other.m_endpoints) + if (m_ranges != other.m_ranges) return true; return false; } @@ -677,6 +700,10 @@ public: FanSpeed, Temperature, VolumetricRate, +#if ENABLE_PREVIEW_LAYER_TIME + LayerTimeLinear, + LayerTimeLogarithmic, +#endif // ENABLE_PREVIEW_LAYER_TIME Tool, ColorPrint, Count @@ -705,6 +732,14 @@ private: Shells m_shells; EViewType m_view_type{ EViewType::FeatureType }; bool m_legend_enabled{ true }; +#if ENABLE_PREVIEW_LAYOUT + struct LegendResizer + { + bool dirty{ true }; + void reset() { dirty = true; } + }; + LegendResizer m_legend_resizer; +#endif // ENABLE_PREVIEW_LAYOUT PrintEstimatedStatistics m_print_statistics; PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -713,6 +748,9 @@ private: std::array m_detected_point_sizes = { 0.0f, 0.0f }; GCodeProcessorResult::SettingsIds m_settings_ids; std::array m_sequential_range_caps; +#if ENABLE_PREVIEW_LAYER_TIME + std::array, static_cast(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times; +#endif // ENABLE_PREVIEW_LAYER_TIME std::vector m_custom_gcode_per_print_z; @@ -728,7 +766,11 @@ public: void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized); // recalculate ranges in dependence of what is visible and sets tool/print colors void refresh(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors); +#if ENABLE_PREVIEW_LAYOUT + void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; +#else void refresh_render_paths(); +#endif // ENABLE_PREVIEW_LAYOUT void update_shells_color_by_extruder(const DynamicPrintConfig* config); void reset(); @@ -772,10 +814,16 @@ public: std::vector& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; } size_t get_extruders_count() { return m_extruders_count; } +#if ENABLE_PREVIEW_LAYOUT + void invalidate_legend() { m_legend_resizer.reset(); } +#endif // ENABLE_PREVIEW_LAYOUT + private: void load_toolpaths(const GCodeProcessorResult& gcode_result); void load_shells(const Print& print, bool initialized); +#if !ENABLE_PREVIEW_LAYOUT void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; +#endif // !ENABLE_PREVIEW_LAYOUT void render_toolpaths(); void render_shells(); void render_legend(float& legend_height); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e28b8e5bae..fb22f9099c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2110,12 +2110,21 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co request_extra_frame(); } +#if ENABLE_PREVIEW_LAYOUT +void GLCanvas3D::refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) +{ + m_gcode_viewer.refresh_render_paths(keep_sequential_current_first, keep_sequential_current_last); + set_as_dirty(); + request_extra_frame(); +} +#else void GLCanvas3D::refresh_gcode_preview_render_paths() { m_gcode_viewer.refresh_render_paths(); set_as_dirty(); request_extra_frame(); } +#endif // ENABLE_PREVIEW_LAYOUT void GLCanvas3D::load_sla_preview() { @@ -2419,12 +2428,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'i': { _update_camera_zoom(1.0); break; } case 'K': case 'k': { wxGetApp().plater()->get_camera().select_next_type(); m_dirty = true; break; } - case 'L': - case 'l': { - if (!m_main_toolbar.is_enabled()) { + case 'L': + case 'l': { + if (!m_main_toolbar.is_enabled()) { +#if ENABLE_PREVIEW_LAYOUT + show_legend(!is_legend_shown()); +#else m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled()); m_dirty = true; wxGetApp().plater()->update_preview_bottom_toolbar(); +#endif // ENABLE_PREVIEW_LAYOUT } break; } @@ -3751,6 +3764,9 @@ void GLCanvas3D::set_cursor(ECursorType type) void GLCanvas3D::msw_rescale() { +#if ENABLE_PREVIEW_LAYOUT + m_gcode_viewer.invalidate_legend(); +#endif // ENABLE_PREVIEW_LAYOUT } void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9e4815ac37..ebffe46957 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -731,7 +731,11 @@ public: void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector& str_tool_colors); +#if ENABLE_PREVIEW_LAYOUT + void refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last); +#else void refresh_gcode_preview_render_paths(); +#endif // ENABLE_PREVIEW_LAYOUT void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); } GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); } void load_sla_preview(); @@ -824,6 +828,11 @@ public: bool are_labels_shown() const { return m_labels.is_shown(); } void show_labels(bool show) { m_labels.show(show); } +#if ENABLE_PREVIEW_LAYOUT + bool is_legend_shown() const { return m_gcode_viewer.is_legend_enabled(); } + void show_legend(bool show) { m_gcode_viewer.enable_legend(show); m_dirty = true; } +#endif // ENABLE_PREVIEW_LAYOUT + bool is_using_slope() const { return m_slope.is_used(); } void use_slope(bool use) { m_slope.use(use); } void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); } diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 156444a514..2cac5e3566 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -208,6 +208,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_layers_slider_sizer = create_layers_slider_sizer(); wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this)); +#if !ENABLE_PREVIEW_LAYOUT m_label_view_type = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("View")); #ifdef _WIN32 wxGetApp().UpdateDarkUI(m_choice_view_type = new BitmapComboBox(m_bottom_toolbar_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY)); @@ -221,6 +222,10 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_choice_view_type->Append(_L("Fan speed")); m_choice_view_type->Append(_L("Temperature")); m_choice_view_type->Append(_L("Volumetric flow rate")); +#if ENABLE_PREVIEW_LAYER_TIME + m_choice_view_type->Append(_L("Layer time (linear)")); + m_choice_view_type->Append(_L("Layer time (logarithmic)")); +#endif // ENABLE_PREVIEW_LAYER_TIME m_choice_view_type->Append(_L("Tool")); m_choice_view_type->Append(_L("Color Print")); m_choice_view_type->SetSelection(0); @@ -232,6 +237,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) #else long combo_style = wxCB_READONLY; #endif + m_combochecklist_features = new wxComboCtrl(); m_combochecklist_features->Create(m_bottom_toolbar_panel, wxID_ANY, _L("Feature types"), wxDefaultPosition, wxDefaultSize, combo_style); std::string feature_items = GUI::into_u8( @@ -270,6 +276,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) get_option_type_string(OptionType::Legend) + "|1" ); Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items); +#endif // !ENABLE_PREVIEW_LAYOUT m_left_sizer = new wxBoxSizer(wxVERTICAL); m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); @@ -281,6 +288,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); +#if !ENABLE_PREVIEW_LAYOUT bottom_toolbar_sizer->AddSpacer(5); bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 0); @@ -292,6 +300,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5); bottom_toolbar_sizer->Hide(m_combochecklist_features); bottom_toolbar_sizer->AddSpacer(5); +#endif // !ENABLE_PREVIEW_LAYOUT bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0); m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer); @@ -353,7 +362,9 @@ void Preview::load_print(bool keep_z_range) else if (tech == ptSLA) load_print_as_sla(); +#if !ENABLE_PREVIEW_LAYOUT update_bottom_toolbar(); +#endif // !ENABLE_PREVIEW_LAYOUT Layout(); } @@ -396,10 +407,12 @@ void Preview::refresh_print() void Preview::msw_rescale() { +#if !ENABLE_PREVIEW_LAYOUT #ifdef _WIN32 m_choice_view_type->Rescale(); m_choice_view_type->SetMinSize(m_choice_view_type->GetSize()); #endif +#endif // !ENABLE_PREVIEW_LAYOUT // rescale slider if (m_layers_slider != nullptr) m_layers_slider->msw_rescale(); if (m_moves_slider != nullptr) m_moves_slider->msw_rescale(); @@ -417,11 +430,13 @@ void Preview::sys_color_changed() wxWindowUpdateLocker noUpdates(this); wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel); +#if !ENABLE_PREVIEW_LAYOUT wxGetApp().UpdateDarkUI(m_choice_view_type); wxGetApp().UpdateDarkUI(m_combochecklist_features); wxGetApp().UpdateDarkUI(static_cast(m_combochecklist_features->GetPopupControl())); wxGetApp().UpdateDarkUI(m_combochecklist_options); wxGetApp().UpdateDarkUI(static_cast(m_combochecklist_options->GetPopupControl())); +#endif // !ENABLE_PREVIEW_LAYOUT #endif if (m_layers_slider != nullptr) @@ -445,19 +460,23 @@ void Preview::edit_layers_slider(wxKeyEvent& evt) void Preview::bind_event_handlers() { - this->Bind(wxEVT_SIZE, &Preview::on_size, this); + Bind(wxEVT_SIZE, &Preview::on_size, this); +#if !ENABLE_PREVIEW_LAYOUT m_choice_view_type->Bind(wxEVT_COMBOBOX, &Preview::on_choice_view_type, this); m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); m_combochecklist_options->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); +#endif // !ENABLE_PREVIEW_LAYOUT m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); } void Preview::unbind_event_handlers() { - this->Unbind(wxEVT_SIZE, &Preview::on_size, this); + Unbind(wxEVT_SIZE, &Preview::on_size, this); +#if !ENABLE_PREVIEW_LAYOUT m_choice_view_type->Unbind(wxEVT_COMBOBOX, &Preview::on_choice_view_type, this); m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this); m_combochecklist_options->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this); +#endif // !ENABLE_PREVIEW_LAYOUT m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); } @@ -478,6 +497,7 @@ void Preview::on_size(wxSizeEvent& evt) Refresh(); } +#if !ENABLE_PREVIEW_LAYOUT void Preview::on_choice_view_type(wxCommandEvent& evt) { int selection = m_choice_view_type->GetCurrentSelection(); @@ -544,6 +564,7 @@ void Preview::update_bottom_toolbar() } } } +#endif // !ENABLE_PREVIEW_LAYOUT wxBoxSizer* Preview::create_layers_slider_sizer() { @@ -953,10 +974,20 @@ void Preview::load_print_as_fff(bool keep_z_range) std::vector gcodes = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_canvas->get_custom_gcode_per_print_z(); +#if ENABLE_PREVIEW_LAYOUT + const GCodeViewer::EViewType choice = !gcodes.empty() ? + GCodeViewer::EViewType::ColorPrint : + (number_extruders > 1) ? GCodeViewer::EViewType::Tool : GCodeViewer::EViewType::FeatureType; + if (choice != gcode_view_type) { + m_canvas->set_gcode_view_preview_type(choice); + if (wxGetApp().is_gcode_viewer()) + m_keep_current_preview_type = true; + refresh_print(); + } +#else const wxString choice = !gcodes.empty() ? _L("Color Print") : (number_extruders > 1) ? _L("Tool") : _L("Feature type"); - int type = m_choice_view_type->FindString(choice); if (m_choice_view_type->GetSelection() != type) { if (0 <= type && type < static_cast(GCodeViewer::EViewType::Count)) { @@ -967,6 +998,7 @@ void Preview::load_print_as_fff(bool keep_z_range) refresh_print(); } } +#endif // ENABLE_PREVIEW_LAYOUT } if (zs.empty()) { @@ -1042,6 +1074,7 @@ void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event) m_canvas->render(); } +#if !ENABLE_PREVIEW_LAYOUT wxString Preview::get_option_type_string(OptionType type) const { switch (type) @@ -1061,6 +1094,7 @@ wxString Preview::get_option_type_string(OptionType type) const default: { return ""; } } } +#endif // !ENABLE_PREVIEW_LAYOUT } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 42246aa181..d9c32e8293 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -80,16 +80,18 @@ class Preview : public wxPanel wxBoxSizer* m_left_sizer { nullptr }; wxBoxSizer* m_layers_slider_sizer { nullptr }; wxPanel* m_bottom_toolbar_panel { nullptr }; +#if !ENABLE_PREVIEW_LAYOUT wxStaticText* m_label_view_type { nullptr }; #ifdef _WIN32 BitmapComboBox* m_choice_view_type { nullptr }; #else wxComboBox* m_choice_view_type { nullptr }; #endif - wxStaticText* m_label_show { nullptr }; + wxStaticText* m_label_show{ nullptr }; wxComboCtrl* m_combochecklist_features { nullptr }; size_t m_combochecklist_features_pos { 0 }; wxComboCtrl* m_combochecklist_options { nullptr }; +#endif // !ENABLE_PREVIEW_LAYOUT DynamicPrintConfig* m_config; BackgroundSlicingProcess* m_process; @@ -126,7 +128,9 @@ public: CustomGCodes, Shells, ToolMarker, +#if !ENABLE_PREVIEW_LAYOUT Legend +#endif // !ENABLE_PREVIEW_LAYOUT }; Preview(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, @@ -154,12 +158,18 @@ public: bool is_loaded() const { return m_loaded; } +#if !ENABLE_PREVIEW_LAYOUT void update_bottom_toolbar(); +#endif // !ENABLE_PREVIEW_LAYOUT void update_moves_slider(); void enable_moves_slider(bool enable); void move_moves_slider(wxKeyEvent& evt); void hide_layers_slider(); +#if ENABLE_PREVIEW_LAYOUT + void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; } +#endif // ENABLE_PREVIEW_LAYOUT + private: bool init(wxWindow* parent, Bed3D& bed, Model* model); @@ -167,9 +177,11 @@ private: void unbind_event_handlers(); void on_size(wxSizeEvent& evt); +#if !ENABLE_PREVIEW_LAYOUT void on_choice_view_type(wxCommandEvent& evt); void on_combochecklist_features(wxCommandEvent& evt); void on_combochecklist_options(wxCommandEvent& evt); +#endif // !ENABLE_PREVIEW_LAYOUT // Create/Update/Reset double slider on 3dPreview wxBoxSizer* create_layers_slider_sizer(); @@ -186,7 +198,9 @@ private: void on_layers_slider_scroll_changed(wxCommandEvent& event); void on_moves_slider_scroll_changed(wxCommandEvent& event); +#if !ENABLE_PREVIEW_LAYOUT wxString get_option_type_string(OptionType type) const; +#endif // !ENABLE_PREVIEW_LAYOUT }; } // namespace GUI diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fdb2cdce8a..01eeef342c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -53,7 +53,22 @@ static const std::map font_icons = { {ImGui::PreferencesButton , "notification_preferences" }, {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, + {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, +#if ENABLE_LEGEND_TOOLBAR_ICONS + {ImGui::LegendTravel , "legend_travel" }, + {ImGui::LegendWipe , "legend_wipe" }, + {ImGui::LegendRetract , "legend_retract" }, + {ImGui::LegendDeretract , "legend_deretract" }, + {ImGui::LegendSeams , "legend_seams" }, + {ImGui::LegendToolChanges , "legend_toolchanges" }, + {ImGui::LegendColorChanges , "legend_colorchanges" }, + {ImGui::LegendPausePrints , "legend_pauseprints" }, + {ImGui::LegendCustomGCodes , "legend_customgcodes" }, + {ImGui::LegendShells , "legend_shells" }, + {ImGui::LegendToolMarker , "legend_toolmarker" }, +#endif // ENABLE_LEGEND_TOOLBAR_ICONS }; + static const std::map font_icons_large = { {ImGui::CloseNotifButton , "notification_close" }, {ImGui::CloseNotifHoverButton , "notification_close_hover" }, @@ -371,10 +386,41 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active) return ImGui::RadioButton(label_utf8.c_str(), active); } -bool ImGuiWrapper::image_button() +#if ENABLE_PREVIEW_LAYOUT +bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool active, + std::function draw_callback) { - return false; + ImGuiWindow& window = *ImGui::GetCurrentWindow(); + if (window.SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window.GetID(name.c_str()); + + const ImVec2 pos = window.DC.CursorPos; + const ImRect total_bb(pos, pos + ImVec2(size, size + style.FramePadding.y * 2.0f)); + ImGui::ItemSize(total_bb, style.FramePadding.y); + if (!ImGui::ItemAdd(total_bb, id)) + return false; + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + ImGui::MarkItemEdited(id); + + if (hovered) + window.DrawList->AddRect({ pos.x - 1.0f, pos.y - 1.0f }, { pos.x + size + 1.0f, pos.y + size + 1.0f }, ImGui::GetColorU32(ImGuiCol_CheckMark)); + + if (active) + window.DrawList->AddRect(pos, { pos.x + size, pos.y + size }, ImGui::GetColorU32(ImGuiCol_CheckMark)); + + draw_callback(window, pos, size); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window.DC.LastItemStatusFlags); + return pressed; } +#endif // ENABLE_PREVIEW_LAYOUT bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) { @@ -498,6 +544,9 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float if (pos != std::string::npos) str_label = str_label.substr(0, pos) + str_label.substr(pos + 2); + // the current slider edit state needs to be detected here before calling SliderFloat() + bool slider_editing = ImGui::GetCurrentWindow()->GetID(str_label.c_str()) == ImGui::GetActiveID(); + bool ret = ImGui::SliderFloat(str_label.c_str(), v, v_min, v_max, format, power); m_last_slider_status.hovered = ImGui::IsItemHovered(); @@ -515,15 +564,42 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float if (show_edit_btn) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); ImGui::SameLine(); - std::wstring btn_name = ImGui::SliderFloatEditBtnIcon + boost::nowide::widen(str_label); + +#if ENABLE_LEGEND_TOOLBAR_ICONS + ImGuiIO& io = ImGui::GetIO(); + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(slider_editing ? ImGui::SliderFloatEditBtnPressedIcon : ImGui::SliderFloatEditBtnIcon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.5f, 0.5f, 0.5f, 1.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.5f, 0.5f, 0.5f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f }); + +#if ENABLE_LEGEND_TOOLBAR_ICONS + const ImTextureID tex_id = io.Fonts->TexID; + if (image_button(tex_id, size, uv0, uv1, -1, ImVec4(0.0, 0.0, 0.0, 0.0), ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags_PressedOnClick)) { + if (!slider_editing) + ImGui::SetKeyboardFocusHere(-1); + else + ImGui::ClearActiveID(); + this->set_requires_extra_frame(); + } +#else + std::wstring btn_name = ImGui::SliderFloatEditBtnIcon + boost::nowide::widen(str_label); if (ImGui::Button(into_u8(btn_name).c_str())) { ImGui::SetKeyboardFocusHere(-1); this->set_requires_extra_frame(); } +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + ImGui::PopStyleColor(3); + if (ImGui::IsItemHovered()) this->tooltip(into_u8(_L("Edit")).c_str(), max_tooltip_width); @@ -557,17 +633,65 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn); } -bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection) +static bool image_button_ex(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + ImGui::ItemSize(bb); + if (!ImGui::ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImGui::RenderNavHighlight(bb, id); + ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui::GetColorU32(bg_col)); + window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui::GetColorU32(tint_col)); + + return pressed; +} + +bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + ImGui::PushID((void*)(intptr_t)user_texture_id); + const ImGuiID id = window->GetID("#image"); + ImGui::PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding; + return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); +} + +bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags) { // this is to force the label to the left of the widget: - text(label); - ImGui::SameLine(); +#if ENABLE_PREVIEW_LAYOUT + if (!label.empty()) { +#endif // ENABLE_PREVIEW_LAYOUT + text(label); + ImGui::SameLine(); +#if ENABLE_PREVIEW_LAYOUT + } +#endif // ENABLE_PREVIEW_LAYOUT int selection_out = selection; bool res = false; const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : ""; - if (ImGui::BeginCombo("", selection_str)) { + if (ImGui::BeginCombo("", selection_str, flags)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { selection_out = i; @@ -1011,6 +1135,14 @@ bool ImGuiWrapper::want_any_input() const return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; } +#if ENABLE_LEGEND_TOOLBAR_ICONS +ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) +{ + auto item = m_custom_glyph_rects_ids.find(tex_id); + return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr; +} +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) { return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); @@ -1119,12 +1251,27 @@ void ImGuiWrapper::init_font(bool compress) int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next // add rectangles for the icons to the font atlas +#if ENABLE_LEGEND_TOOLBAR_ICONS + for (auto& icon : font_icons) { + m_custom_glyph_rects_ids[icon.first] = + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); + } + for (auto& icon : font_icons_large) { + m_custom_glyph_rects_ids[icon.first] = + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); + } + for (auto& icon : font_icons_extra_large) { + m_custom_glyph_rects_ids[icon.first] = + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); +} +#else for (auto& icon : font_icons) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); for (auto& icon : font_icons_large) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); for (auto& icon : font_icons_extra_large) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS // Build texture atlas unsigned char* pixels; diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 6f8097fc48..f461bc970a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -21,6 +21,9 @@ class wxString; class wxMouseEvent; class wxKeyEvent; +#if ENABLE_PREVIEW_LAYOUT +struct IMGUI_API ImGuiWindow; +#endif // ENABLE_PREVIEW_LAYOUT namespace Slic3r { namespace GUI { @@ -37,6 +40,9 @@ class ImGuiWrapper bool m_disabled{ false }; bool m_new_frame_open{ false }; bool m_requires_extra_frame{ false }; +#if ENABLE_LEGEND_TOOLBAR_ICONS + std::map m_custom_glyph_rects_ids; +#endif // ENABLE_LEGEND_TOOLBAR_ICONS std::string m_clipboard_text; public: @@ -86,7 +92,9 @@ public: bool button(const wxString &label); bool button(const wxString& label, float width, float height); bool radio_button(const wxString &label, bool active); - bool image_button(); +#if ENABLE_PREVIEW_LAYOUT + bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); +#endif // ENABLE_PREVIEW_LAYOUT bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); @@ -109,7 +117,10 @@ public: bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); - bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected + bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + + // Use selection = -1 to not mark any option as selected + bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0); bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel); void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized); @@ -132,6 +143,10 @@ public: static ColorRGBA from_ImU32(const ImU32& color); static ColorRGBA from_ImVec4(const ImVec4& color); +#if ENABLE_LEGEND_TOOLBAR_ICONS + ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id); +#endif // ENABLE_LEGEND_TOOLBAR_ICONS + static const ImVec4 COL_GREY_DARK; static const ImVec4 COL_GREY_LIGHT; static const ImVec4 COL_ORANGE_DARK; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index c330f7cc92..ba3f6675fd 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -223,7 +223,7 @@ void KBShortcutsDialog::fill_shortcuts() { "A", L("Horizontal slider - Move active thumb Left") }, { "D", L("Horizontal slider - Move active thumb Right") }, { "X", L("On/Off one layer mode of the vertical slider") }, - { "L", L("Show/Hide Legend and Estimated printing time") }, + { "L", L("Show/Hide legend") }, { "C", L("Show/Hide G-code window") }, }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 1f8875643d..dd6f45318f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1430,6 +1430,11 @@ void MainFrame::init_menubar_as_editor() append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + sep + "E", _L("Show object/instance labels in 3D scene"), [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this, [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this); +#if ENABLE_PREVIEW_LAYOUT + append_menu_check_item(viewMenu, wxID_ANY, _L("Show Legen&d") + sep + "L", _L("Show legend in preview"), + [this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this, + [this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this); +#endif // ENABLE_PREVIEW_LAYOUT append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse Sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"), [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); @@ -1547,6 +1552,12 @@ void MainFrame::init_menubar_as_gcodeviewer() if (m_plater != nullptr) { viewMenu = new wxMenu(); add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this)); +#if ENABLE_PREVIEW_LAYOUT + viewMenu->AppendSeparator(); + append_menu_check_item(viewMenu, wxID_ANY, _L("Show legen&d") + sep + "L", _L("Show legend"), + [this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this, + [this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this); +#endif // ENABLE_PREVIEW_LAYOUT } // helpmenu diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 35821ff021..21aa5fc124 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1739,6 +1739,11 @@ struct Plater::priv bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); } void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); } +#if ENABLE_PREVIEW_LAYOUT + bool is_legend_shown() const { return (current_panel == preview) && preview->get_canvas3d()->is_legend_shown(); } + void show_legend(bool show) { if (current_panel == preview) preview->get_canvas3d()->show_legend(show); } +#endif // ENABLE_PREVIEW_LAYOUT + bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); } void collapse_sidebar(bool collapse); @@ -1752,7 +1757,9 @@ struct Plater::priv bool init_view_toolbar(); bool init_collapse_toolbar(); +#if !ENABLE_PREVIEW_LAYOUT void update_preview_bottom_toolbar(); +#endif // !ENABLE_PREVIEW_LAYOUT void update_preview_moves_slider(); void enable_preview_moves_slider(bool enable); @@ -4461,10 +4468,12 @@ bool Plater::priv::init_collapse_toolbar() return true; } +#if !ENABLE_PREVIEW_LAYOUT void Plater::priv::update_preview_bottom_toolbar() { preview->update_bottom_toolbar(); } +#endif // !ENABLE_PREVIEW_LAYOUT void Plater::priv::update_preview_moves_slider() { @@ -5408,6 +5417,11 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); } bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); } void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); } +#if ENABLE_PREVIEW_LAYOUT +bool Plater::is_legend_shown() const { return p->is_legend_shown(); } +void Plater::show_legend(bool show) { p->show_legend(show); } +#endif // ENABLE_PREVIEW_LAYOUT + bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); } void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); } @@ -6736,10 +6750,12 @@ GLToolbar& Plater::get_collapse_toolbar() return p->collapse_toolbar; } +#if !ENABLE_PREVIEW_LAYOUT void Plater::update_preview_bottom_toolbar() { p->update_preview_bottom_toolbar(); } +#endif // !ENABLE_PREVIEW_LAYOUT void Plater::update_preview_moves_slider() { @@ -6849,6 +6865,12 @@ bool Plater::is_render_statistic_dialog_visible() const return p->show_render_statistic_dialog; } +#if ENABLE_PREVIEW_LAYOUT +void Plater::set_keep_current_preview_type(bool value) +{ + p->preview->set_keep_current_preview_type(value); +} +#endif // ENABLE_PREVIEW_LAYOUT Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name) : TakeSnapshot(plater, from_u8(snapshot_name)) {} diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 8b6bff624a..0b26f09e11 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -223,6 +223,11 @@ public: bool are_view3D_labels_shown() const; void show_view3D_labels(bool show); +#if ENABLE_PREVIEW_LAYOUT + bool is_legend_shown() const; + void show_legend(bool show); +#endif // ENABLE_PREVIEW_LAYOUT + bool is_sidebar_collapsed() const; void collapse_sidebar(bool show); @@ -382,7 +387,9 @@ public: const GLToolbar& get_collapse_toolbar() const; GLToolbar& get_collapse_toolbar(); +#if !ENABLE_PREVIEW_LAYOUT void update_preview_bottom_toolbar(); +#endif // !ENABLE_PREVIEW_LAYOUT void update_preview_moves_slider(); void enable_preview_moves_slider(bool enable); @@ -448,6 +455,10 @@ public: void toggle_render_statistic_dialog(); bool is_render_statistic_dialog_visible() const; +#if ENABLE_PREVIEW_LAYOUT + void set_keep_current_preview_type(bool value); +#endif // ENABLE_PREVIEW_LAYOUT + // Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu. bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition); bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); }