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)); }