Merge branch 'ys_dbl_sl_pure'

This commit is contained in:
Lukas Matena 2024-04-24 10:54:47 +02:00
commit 65f0806ef5
35 changed files with 3748 additions and 3593 deletions

17
resources/icons/cog_f.svg Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="machine_x2B_cog">
<path fill="#ED6B21" d="M13.77,6.39c-0.13-0.47-0.32-0.92-0.55-1.33l0.43-1.3l-1.41-1.41l-1.3,0.43c-0.42-0.23-0.86-0.42-1.33-0.55
L9,1H7L6.39,2.23C5.92,2.36,5.47,2.54,5.06,2.78l-1.3-0.43L2.34,3.76l0.43,1.3C2.54,5.47,2.36,5.92,2.23,6.39L1,7v2l1.23,0.61
c0.13,0.47,0.32,0.92,0.55,1.33l-0.43,1.3l1.41,1.41l1.3-0.43c0.42,0.23,0.86,0.42,1.33,0.55L7,15h2l0.61-1.23
c0.47-0.13,0.92-0.32,1.33-0.55l1.3,0.43l1.41-1.41l-0.43-1.3c0.23-0.42,0.42-0.86,0.55-1.33L15,9V7L13.77,6.39z M8,13
c-2.76,0-5-2.24-5-5s2.24-5,5-5s5,2.24,5,5S10.76,13,8,13z"/>
<path fill="#808080" d="M11.3,7.08c-0.07-0.27-0.18-0.52-0.31-0.76l0.25-0.74l-0.81-0.81L9.68,5.01C9.45,4.88,9.19,4.78,8.92,4.7
L8.57,4H7.43L7.08,4.7C6.81,4.78,6.55,4.88,6.32,5.01L5.58,4.77L4.77,5.58l0.25,0.74C4.88,6.55,4.78,6.81,4.7,7.08L4,7.43v1.14
l0.7,0.35c0.07,0.27,0.18,0.52,0.31,0.76l-0.25,0.74l0.81,0.81l0.74-0.25c0.24,0.13,0.49,0.24,0.76,0.31L7.43,12h1.14l0.35-0.7
c0.27-0.07,0.52-0.18,0.76-0.31l0.74,0.25l0.81-0.81l-0.25-0.74c0.13-0.24,0.24-0.49,0.31-0.76L12,8.57V7.43L11.3,7.08z M8,10.86
c-1.58,0-2.86-1.28-2.86-2.86S6.42,5.14,8,5.14S10.86,6.42,10.86,8S9.58,10.86,8,10.86z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -4,7 +4,7 @@
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="edit_x5F_Gcode">
<g>
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
</g>
<g>
<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36

Before

Width:  |  Height:  |  Size: 808 B

After

Width:  |  Height:  |  Size: 797 B

View File

@ -4,7 +4,7 @@
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="edit_x5F_Gcode">
<g>
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
</g>
<g>
<path fill="#ED6B21" d="M7.97,7.47h2.65v2.05c0,1.73-0.82,2.48-2.69,2.48S5.3,11.25,5.3,9.55V6.39c0-1.61,0.73-2.36,2.63-2.36

Before

Width:  |  Height:  |  Size: 808 B

After

Width:  |  Height:  |  Size: 797 B

View File

@ -3,7 +3,7 @@
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="error_tick">
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 629 B

View File

@ -3,7 +3,7 @@
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="error_tick">
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
<path fill="none" stroke="#ED6B21" stroke-linecap="round" stroke-width="2" d="M8 4 L8 9" />

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 629 B

View File

@ -4,7 +4,7 @@
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="pause_x5F_print">
<g>
<path fill="#808080" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#808080" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
</g>
<g>
<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6

Before

Width:  |  Height:  |  Size: 833 B

After

Width:  |  Height:  |  Size: 822 B

View File

@ -4,7 +4,7 @@
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="pause_x5F_print">
<g>
<path fill="#ED6B21" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
<path fill="#646464" stroke="#ED6B21" d="M 8 1.85 M 8 1 L 2 5 v 2 v 4 l 6 4 l 6 -4 V 7 V 5 L 8 1 L 8 1 z"/>
</g>
<g>
<path fill="#ED6B21" d="M6,11.71c-0.39,0-0.71-0.32-0.71-0.71V5c0-0.39,0.32-0.71,0.71-0.71S6.71,4.61,6.71,5v6

Before

Width:  |  Height:  |  Size: 833 B

After

Width:  |  Height:  |  Size: 822 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="undo">
<path fill="none" stroke="#ED6B21" stroke-width="2.5" stroke-linecap="round" stroke-miterlimit="10" d="M3,11
c0.91,1.78,2.76,3,4.89,3c3.04,0,5.5-2.46,5.5-5.5c0-3.04-2.46-5.5-5.5-5.5c-0.17,0-0.34,0.01-0.5,0.03"/>
<polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
7.39,1 7.39,5 4.39,3 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 750 B

View File

@ -30,7 +30,8 @@ src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DesktopIntegrationDialog.cpp
src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/TickCodesManager.cpp
src/slic3r/GUI/DoubleSliderForLayers.cpp
src/slic3r/GUI/Downloader.cpp
src/slic3r/GUI/DownloaderFileGet.cpp
src/slic3r/GUI/EditGCodeDialog.cpp

View File

@ -189,6 +189,25 @@ namespace ImGui
const wchar_t InfoMarkerSmall = 0x2716;
const wchar_t CollapseBtn = 0x2715;
// icons for double slider (middle size icons)
const wchar_t Lock = 0x2801;
const wchar_t LockHovered = 0x2802;
const wchar_t Unlock = 0x2803;
const wchar_t UnlockHovered = 0x2804;
const wchar_t DSRevert = 0x2805;
const wchar_t DSRevertHovered = 0x2806;
const wchar_t DSSettings = 0x2807;
const wchar_t DSSettingsHovered = 0x2808;
// icons for double slider (small size icons)
const wchar_t ErrorTick = 0x2809;
const wchar_t ErrorTickHovered = 0x280A;
const wchar_t PausePrint = 0x280B;
const wchar_t PausePrintHovered = 0x280C;
const wchar_t EditGCode = 0x280D;
const wchar_t EditGCodeHovered = 0x280E;
const wchar_t RemoveTick = 0x280F;
const wchar_t RemoveTickHovered = 0x2810;
// void MyFunction(const char* name, const MyMatrix44& v);
}

View File

@ -14,6 +14,11 @@ class DynamicPrintConfig;
namespace CustomGCode {
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
* So, let use same value as a permissible error for layer height.
*/
constexpr double epsilon() { return 0.0011; }
enum Type
{
ColorChange,

View File

@ -252,8 +252,14 @@ set(SLIC3R_GUI_SOURCES
GUI/ProgressStatusBar.cpp
GUI/Mouse3DController.cpp
GUI/Mouse3DController.hpp
GUI/DoubleSlider.cpp
GUI/DoubleSlider.hpp
GUI/ImGuiDoubleSlider.cpp
GUI/ImGuiDoubleSlider.hpp
GUI/TickCodesManager.cpp
GUI/TickCodesManager.hpp
GUI/DoubleSliderForLayers.cpp
GUI/DoubleSliderForLayers.hpp
GUI/DoubleSliderForGcode.cpp
GUI/DoubleSliderForGcode.hpp
GUI/Notebook.cpp
GUI/Notebook.hpp
GUI/TopBar.cpp

File diff suppressed because it is too large Load Diff

View File

@ -1,484 +0,0 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_DoubleSlider_hpp_
#define slic3r_GUI_DoubleSlider_hpp_
#include "libslic3r/CustomGCode.hpp"
#include "wxExtensions.hpp"
#include <wx/window.h>
#include <wx/control.h>
#include <wx/dc.h>
#include <wx/slider.h>
#include <vector>
#include <set>
class wxMenu;
namespace Slic3r {
using namespace CustomGCode;
class PrintObject;
class Layer;
namespace DoubleSlider {
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
* So, let use same value as a permissible error for layer height.
*/
constexpr double epsilon() { return 0.0011; }
// return true when areas are mostly equivalent
bool equivalent_areas(const double& bottom_area, const double& top_area);
// return true if color change was detected
bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs,
// what to do with detected color change
// and return true when detection have to be desturbed
std::function<bool(const Layer*)> break_condition);
// custom message the slider sends to its parent to notify a tick-change:
wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
enum SelectedSlider {
ssUndef,
ssLower,
ssHigher
};
enum FocusedItem {
fiNone,
fiRevertIcon,
fiOneLayerIcon,
fiCogIcon,
fiColorBand,
fiActionIcon,
fiLowerThumb,
fiHigherThumb,
fiSmartWipeTower,
fiTick
};
enum ConflictType
{
ctNone,
ctModeConflict,
ctMeaninglessColorChange,
ctMeaninglessToolChange,
ctRedundant
};
enum MouseAction
{
maNone,
maAddMenu, // show "Add" context menu for NOTexist active tick
maEditMenu, // show "Edit" context menu for exist active tick
maCogIconMenu, // show context for "cog" icon
maForceColorEdit, // force color editing from colored band
maAddTick, // force tick adding
maDeleteTick, // force tick deleting
maCogIconClick, // LeftMouseClick on "cog" icon
maOneLayerIconClick, // LeftMouseClick on "one_layer" icon
maRevertIconClick, // LeftMouseClick on "revert" icon
};
enum DrawMode
{
dmRegular,
dmSlaPrint,
dmSequentialFffPrint,
dmSequentialGCodeView,
};
enum LabelType
{
ltHeightWithLayer,
ltHeight,
ltEstimatedTime,
};
struct TickCode
{
bool operator<(const TickCode& other) const { return other.tick > this->tick; }
bool operator>(const TickCode& other) const { return other.tick < this->tick; }
int tick = 0;
Type type = ColorChange;
int extruder = 0;
std::string color;
std::string extra;
};
class TickCodeInfo
{
std::string custom_gcode;
std::string pause_print_msg;
bool m_suppress_plus = false;
bool m_suppress_minus = false;
bool m_use_default_colors= false;
// int m_default_color_idx = 0;
std::vector<std::string>* m_colors {nullptr};
std::string get_color_for_tick(TickCode tick, Type type, const int extruder);
public:
std::set<TickCode> ticks {};
Mode mode = Undef;
bool empty() const { return ticks.empty(); }
void set_pause_print_msg(const std::string& message) { pause_print_msg = message; }
bool add_tick(const int tick, Type type, int extruder, double print_z);
bool edit_tick(std::set<TickCode>::iterator it, double print_z);
void switch_code(Type type_from, Type type_to);
bool switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder);
void erase_all_ticks_with_code(Type type);
bool has_tick_with_code(Type type);
bool has_tick(int tick);
ConflictType is_conflict_tick(const TickCode& tick, Mode out_mode, int only_extruder, double print_z);
// Get used extruders for tick.
// Means all extruders(tools) which will be used during printing from current tick to the end
std::set<int> get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode = Undef) const;
void suppress_plus (bool suppress) { m_suppress_plus = suppress; }
void suppress_minus(bool suppress) { m_suppress_minus = suppress; }
bool suppressed_plus () { return m_suppress_plus; }
bool suppressed_minus() { return m_suppress_minus; }
void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; }
void set_extruder_colors(std::vector<std::string>* extruder_colors) { m_colors = extruder_colors; }
};
struct ExtrudersSequence
{
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
bool random_sequence { false };
bool color_repetition { false };
std::vector<size_t> extruders = { 0 };
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&
(other.interval_by_mm == this->interval_by_mm ) &&
(other.interval_by_layers == this->interval_by_layers ) &&
(other.random_sequence == this->random_sequence ) &&
(other.color_repetition == this->color_repetition ) &&
(other.extruders == this->extruders ) ;
}
bool operator!=(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals != this->is_mm_intervals ) ||
(other.interval_by_mm != this->interval_by_mm ) ||
(other.interval_by_layers != this->interval_by_layers ) ||
(other.random_sequence != this->random_sequence ) ||
(other.color_repetition != this->color_repetition ) ||
(other.extruders != this->extruders ) ;
}
void add_extruder(size_t pos, size_t extruder_id = size_t(0))
{
extruders.insert(extruders.begin() + pos+1, extruder_id);
}
void delete_extruder(size_t pos)
{
if (extruders.size() == 1)
return;// last item can't be deleted
extruders.erase(extruders.begin() + pos);
}
void init(size_t extruders_count)
{
extruders.clear();
for (size_t extruder = 0; extruder < extruders_count; extruder++)
extruders.push_back(extruder);
}
};
class Control : public wxControl
{
public:
Control(
wxWindow *parent,
wxWindowID id,
int lowerValue,
int higherValue,
int minValue,
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxSL_VERTICAL,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxEmptyString);
~Control() {}
void msw_rescale();
void sys_color_changed();
int GetMinValue() const { return m_min_value; }
int GetMaxValue() const { return m_max_value; }
double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; }
double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; }
int GetLowerValue() const { return m_lower_value; }
int GetHigherValue() const { return m_higher_value; }
int GetActiveValue() const;
double GetLowerValueD() { return get_double_value(ssLower); }
double GetHigherValueD() { return get_double_value(ssHigher); }
wxSize DoGetBestSize() const override;
wxSize get_min_size() const ;
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetLowerValue (const int lower_val);
void SetHigherValue(const int higher_val);
void SetSelectionSpan(const int lower_val, const int higher_val);
void SetMaxValue(const int max_value);
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
void SetSliderValues(const std::vector<double>& values);
void ChangeOneLayerLock();
void SetSliderAlternateValues(const std::vector<double>& values) { m_alternate_values = values; }
Info GetTicksValues() const;
void SetTicksValues(const Info &custom_gcode_per_print_z);
void SetLayersTimes(const std::vector<float>& layers_times, float total_time);
void SetLayersTimes(const std::vector<double>& layers_times);
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
void SetDrawMode(DrawMode mode) { m_draw_mode = mode; }
void SetManipulationMode(Mode mode) { m_mode = mode; }
Mode GetManipulationMode() const { return m_mode; }
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
bool IsNewPrint();
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
void OnPaint(wxPaintEvent& ) { render(); }
void OnLeftDown(wxMouseEvent& event);
void OnMotion(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); }
void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); }
void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); }
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
void OnRightUp(wxMouseEvent& event);
void add_code_as_tick(Type type, int selected_extruder = -1);
// add default action for tick, when press "+"
void add_current_tick(bool call_from_keyboard = false);
// delete current tick, when press "-"
void delete_current_tick();
void edit_tick(int tick = -1);
void switch_one_layer_mode();
void discard_all_thicks();
void move_current_thumb_to_pos(wxPoint pos);
void edit_extruder_sequence();
void jump_to_value();
void enable_action_icon(bool enable) { m_enable_action_icon = enable; }
void show_add_context_menu();
void show_edit_context_menu();
void show_cog_icon_context_menu();
void auto_color_change();
ExtrudersSequence m_extruders_sequence;
protected:
void render();
void draw_focus_rect(wxDC& dc);
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
void draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len);
void draw_ticks(wxDC& dc);
void draw_colored_band(wxDC& dc);
void draw_ruler(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
void draw_revert_icon(wxDC& dc);
void draw_cog_icon(wxDC &dc);
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
void draw_tick_on_mouse_position(wxDC &dc);
void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type = ltHeight, bool right_side = true) const;
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection);
bool is_lower_thumb_editable();
bool detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
void move_current_thumb(const bool condition);
void enter_window(wxMouseEvent& event, const bool enter);
bool is_wipe_tower_layer(int tick) const;
private:
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
int get_tick_near_point(const wxPoint& pt);
double get_scroll_step();
wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const;
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
int get_value_from_position(const wxCoord x, const wxCoord y);
int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); }
wxCoord get_position_from_value(const int value);
wxSize get_size() const;
void get_size(int* w, int* h) const;
double get_double_value(const SelectedSlider& selection);
int get_tick_from_value(double value, bool force_lower_bound = false);
wxString get_tooltip(int tick = -1);
int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange);
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
wxRect get_colored_band_rect();
// Get active extruders for tick.
// Means one current extruder for not existing tick OR
// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick)
// Use those values to disable selection of active extruders
std::array<int, 2> get_active_extruders_for_tick(int tick) const;
void post_ticks_changed_event(Type type = Custom);
bool check_ticks_changed_event(Type type);
void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false);
void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false);
bool is_osx { false };
wxFont m_font;
int m_min_value;
int m_max_value;
int m_lower_value;
int m_higher_value;
bool m_render_as_disabled{ false };
ScalableBitmap m_bmp_thumb_higher;
ScalableBitmap m_bmp_thumb_lower;
ScalableBitmap m_bmp_add_tick_on;
ScalableBitmap m_bmp_add_tick_off;
ScalableBitmap m_bmp_del_tick_on;
ScalableBitmap m_bmp_del_tick_off;
ScalableBitmap m_bmp_one_layer_lock_on;
ScalableBitmap m_bmp_one_layer_lock_off;
ScalableBitmap m_bmp_one_layer_unlock_on;
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
ScalableBitmap m_bmp_cog;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
bool m_is_focused = false;
bool m_force_mode_apply = true;
bool m_enable_action_icon = true;
bool m_is_wipe_tower = false; //This flag indicates that there is multiple extruder print with wipe tower
DrawMode m_draw_mode = dmRegular;
Mode m_mode = SingleExtruder;
int m_only_extruder = -1;
MouseAction m_mouse = maNone;
FocusedItem m_focus = fiNone;
wxPoint m_moving_pos = wxDefaultPosition;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxRect m_rect_revert_icon;
wxRect m_rect_cog_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim;
int m_revert_icon_dim;
int m_cog_icon_dim;
long m_style;
long m_extra_style;
float m_label_koef{ 1.0 };
std::vector<double> m_values;
TickCodeInfo m_ticks;
std::vector<double> m_layers_times;
std::vector<double> m_layers_values;
std::vector<std::string> m_extruder_colors;
std::string m_print_obj_idxs;
std::vector<double> m_alternate_values;
// control's view variables
wxCoord SLIDER_MARGIN; // margin around slider
wxPen DARK_ORANGE_PEN;
wxPen ORANGE_PEN;
wxPen LIGHT_ORANGE_PEN;
wxPen DARK_GREY_PEN;
wxPen GREY_PEN;
wxPen LIGHT_GREY_PEN;
wxPen FOCUS_RECT_PEN;
wxBrush FOCUS_RECT_BRUSH;
std::vector<wxPen*> m_line_pens;
std::vector<wxPen*> m_segm_pens;
class Ruler {
wxWindow* m_parent{nullptr}; // m_parent is nullptr for Unused ruler
// in this case we will not init/update/render it
// values to check if ruler has to be updated
double m_min_val;
double m_max_val;
double m_scroll_step;
size_t m_max_values_cnt;
int m_DPI;
public:
double long_step;
double short_step;
std::vector<double> max_values;// max value for each object/instance in sequence print
// > 1 for sequential print
void set_parent(wxWindow* parent);
void update_dpi();
void init(const std::vector<double>& values, double scroll_step);
void update(const std::vector<double>& values, double scroll_step);
bool is_ok() { return long_step > 0 && short_step > 0; }
size_t count() { return max_values.size(); }
bool can_draw() { return m_parent != nullptr; }
} m_ruler;
};
} // DoubleSlider;
} // Slic3r
#endif // slic3r_GUI_DoubleSlider_hpp_

View File

@ -0,0 +1,29 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "DoubleSliderForGcode.hpp"
namespace DoubleSlider {
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
static const float HORIZONTAL_SLIDER_HEIGHT = 45.0f;
void DSForGcode::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/, float offset/* = 0.f*/)
{
if (!m_ctrl.IsShown())
return;
m_scale = extra_scale * 0.1f * m_em;
ImVec2 pos = ImVec2{std::max(LEFT_MARGIN, 0.2f * canvas_width), canvas_height - 1.5f * HORIZONTAL_SLIDER_HEIGHT * m_scale};
ImVec2 size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_HEIGHT * m_scale);
m_ctrl.Init(pos, size, m_scale);
if (m_ctrl.render())
process_thumb_move();
}
} // DoubleSlider

View File

@ -0,0 +1,38 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_DoubleSliderForGcode_hpp_
#define slic3r_GUI_DoubleSliderForGcode_hpp_
#include "ImGuiDoubleSlider.hpp"
namespace DoubleSlider {
class DSForGcode : public Manager<unsigned int>
{
public:
DSForGcode() : Manager<unsigned int>() {}
DSForGcode( int lowerPos,
int higherPos,
int minPos,
int maxPos)
{
Init(lowerPos, higherPos, minPos, maxPos, "moves_slider", true);
}
~DSForGcode() {}
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override;
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
private:
bool m_render_as_disabled{ false };
};
} // DoubleSlider;
#endif // slic3r_GUI_DoubleSliderForGcode_hpp_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_DoubleSliderForLayers_hpp_
#define slic3r_GUI_DoubleSliderForLayers_hpp_
#include "ImGuiDoubleSlider.hpp"
#include "TickCodesManager.hpp"
#include <vector>
#include <set>
namespace Slic3r {
class Print;
namespace GUI
{
class ImGuiWrapper;
}
}
using namespace Slic3r::CustomGCode;
namespace DoubleSlider {
enum FocusedItem {
fiNone,
fiRevertIcon,
fiOneLayerIcon,
fiCogIcon,
fiColorBand,
fiActionIcon,
fiLowerThumb,
fiHigherThumb,
fiSmartWipeTower,
fiTick
};
enum DrawMode
{
dmRegular,
dmSlaPrint,
dmSequentialFffPrint,
};
enum LabelType
{
ltHeightWithLayer,
ltHeight,
ltEstimatedTime,
};
class DSForLayers : public Manager<double>
{
public:
DSForLayers() : Manager<double>() {}
DSForLayers(int lowerValue,
int higherValue,
int minValue,
int maxValue,
bool allow_editing);
~DSForLayers() {}
void ChangeOneLayerLock();
Info GetTicksValues() const;
void SetTicksValues(const Info& custom_gcode_per_print_z);
void SetLayersTimes(const std::vector<float>& layers_times, float total_time);
void SetLayersTimes(const std::vector<double>& layers_times);
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override;
// jump to selected layer
void jump_to_value();
// just for editor
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
void UseDefaultColors(bool def_colors_on);
bool is_new_print(const std::string& print_obj_idxs);
void set_imgui_wrapper(Slic3r::GUI::ImGuiWrapper* imgui) { m_imgui = imgui; }
// manipulation with slider from keyboard
// add default action for tick, when press "+"
void add_current_tick();
// delete current tick, when press "-"
void delete_current_tick();
// process adding of auto color change
void auto_color_change();
void set_callback_on_ticks_changed(std::function<void()> cb)
{ m_cb_ticks_changed = cb; };
void set_callback_on_check_gcode(std::function<void(Type)> cb )
{ m_ticks.set_callback_on_check_gcode(cb); }
void set_callback_on_get_extruder_colors(std::function<std::vector<std::string>()> cb)
{ m_cb_get_extruder_colors = cb; }
void set_callback_on_get_print (std::function<const Slic3r::Print& ()> cb)
{ m_cb_get_print = cb; }
void set_callback_on_empty_auto_color_change(std::function<void()> cb)
{ m_ticks.set_callback_on_empty_auto_color_change(cb); }
void set_callback_on_get_custom_code(std::function<std::string(const std::string&, double)> cb)
{ m_ticks.set_callback_on_get_custom_code(cb); }
void set_callback_on_get_pause_print_msg(std::function<std::string(const std::string&, double)> cb)
{ m_ticks.set_callback_on_get_pause_print_msg(cb); }
void set_callback_on_get_new_color(std::function<std::string(const std::string&)> cb)
{ m_ticks.set_callback_on_get_new_color(cb); }
void set_callback_on_show_info_msg(std::function<int(const std::string&, int)> cb)
{ m_ticks.set_callback_on_show_info_msg(cb); }
void set_callback_on_show_warning_msg(std::function<int(const std::string&, int)> cb)
{ m_ticks.set_callback_on_show_warning_msg(cb); }
void set_callback_on_get_extruders_cnt(std::function<int()> cb)
{ m_ticks.set_callback_on_get_extruders_cnt(cb); }
void set_callback_on_get_extruders_sequence(std::function<bool(ExtrudersSequence&)> cb)
{ m_ticks.set_callback_on_get_extruders_sequence(cb); }
std::string gcode(Type type) { return m_ticks.gcode(type); }
private:
bool is_osx { false };
bool m_allow_editing { true };
bool m_show_estimated_times { false };
bool m_show_cog_menu { false };
DrawMode m_draw_mode { dmRegular };
Mode m_mode { SingleExtruder };
FocusedItem m_focus { fiNone };
TickCodeManager m_ticks;
Slic3r::GUI::ImGuiWrapper* m_imgui { nullptr };
std::vector<double> m_layers_times;
std::vector<double> m_layers_values;
bool is_wipe_tower_layer(int tick) const;
std::string get_label(int tick, LabelType label_type) const;
std::string get_tooltip(int tick = -1);
void update_draw_scroll_line_cb();
// functions for extend rendering of m_ctrl
void draw_colored_band(const ImRect& groove, const ImRect& slideable_region);
void draw_ticks(const ImRect& slideable_region);
void render_menu();
void render_cog_menu();
bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1);
void add_code_as_tick(Type type, int selected_extruder = -1);
void edit_tick(int tick = -1);
void discard_all_thicks();
void process_jump_to_value();
std::string get_label(int pos) const override { return get_label(pos, ltHeightWithLayer); }
void process_ticks_changed() {
if (m_cb_ticks_changed)
m_cb_ticks_changed();
}
bool m_show_just_color_change_menu { false };
bool m_show_get_jump_value { false };
bool m_show_color_picker { false };
double m_jump_to_value { 0.0 };
std::string m_print_obj_idxs;
std::string m_selectable_color;
void render_add_tick_menu();
void render_multi_extruders_menu();
bool render_jump_to_window(const ImVec2& pos, double* active_value, double min_z, double max_z);
void render_color_picker();
std::function<void()> m_cb_ticks_changed { nullptr };
std::function<std::vector<std::string>()> m_cb_get_extruder_colors { nullptr };
std::function<const Slic3r::Print&()> m_cb_get_print { nullptr };
};
} // DoubleSlider;
#endif // slic3r_GUI_DoubleSliderForLayers_hpp_

View File

@ -6,7 +6,8 @@
#define slic3r_GUI_ExtruderSequenceDialog_hpp_
#include "GUI_Utils.hpp"
#include "DoubleSlider.hpp"
#include "DoubleSliderForLayers.hpp"
#include "wxExtensions.hpp"
class wxTextCtrl;
class wxFlexGridSizer;

View File

@ -14,6 +14,7 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/CustomGCode.hpp"
#include "slic3r/GUI/format.hpp"
@ -24,7 +25,6 @@
#include "I18N.hpp"
#include "GUI_Utils.hpp"
#include "GUI.hpp"
#include "DoubleSlider.hpp"
#include "GLCanvas3D.hpp"
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
@ -2120,12 +2120,12 @@ void GCodeViewer::render_legend(float& legend_height)
if (extruder_id + 1 != static_cast<unsigned char>(item.extruder))
continue;
if (item.type != ColorChange)
if (item.type != CustomGCode::ColorChange)
continue;
const std::vector<float> zs = m_viewer.get_layers_zs();
auto lower_b = std::lower_bound(zs.begin(), zs.end(),
static_cast<float>(item.print_z - Slic3r::DoubleSlider::epsilon()));
static_cast<float>(item.print_z - Slic3r::CustomGCode::epsilon()));
if (lower_b == zs.end())
continue;

View File

@ -77,7 +77,6 @@
#include <float.h>
#include <algorithm>
#include <cmath>
#include "DoubleSlider.hpp"
#include <imgui/imgui_internal.h>
#include <slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp>
@ -1019,9 +1018,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
@ -1950,6 +1947,8 @@ void GLCanvas3D::render()
wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width());
wxGetApp().plater()->render_sliders(*this);
wxGetApp().imgui()->render();
m_canvas->SwapBuffers();
@ -2820,14 +2819,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case '6': { select_view("right"); break; }
case '+': {
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
else
post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, +1));
break;
}
case '-': {
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
else
post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, -1));
break;
@ -2845,7 +2844,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'g': {
if ((evt.GetModifiers() & shiftMask) != 0) {
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
post_event(wxKeyEvent(EVT_GLCANVAS_JUMP_TO, evt));
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
}
break;
}
@ -3130,7 +3129,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
keyCode == WXK_UP ||
keyCode == WXK_DOWN) {
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_SLIDERS, evt));
post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt));
}
}
}

View File

@ -176,9 +176,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);

View File

@ -19,9 +19,12 @@
#include "OpenGLManager.hpp"
#include "GLCanvas3D.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "DoubleSlider.hpp"
#include "DoubleSliderForGcode.hpp"
#include "DoubleSliderForLayers.hpp"
#include "ExtruderSequenceDialog.hpp"
#include "Plater.hpp"
#include "MainFrame.hpp"
#include "MsgDialog.hpp"
#include "format.hpp"
#include <wx/listbook.h>
@ -33,6 +36,7 @@
#include <wx/combo.h>
#include <wx/combobox.h>
#include <wx/checkbox.h>
#include <wx/colordlg.h>
// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421
#include "libslic3r/Print.hpp"
@ -197,8 +201,8 @@ Preview::Preview(
void Preview::set_layers_slider_values_range(int bottom, int top)
{
m_layers_slider->SetHigherValue(std::min(top, m_layers_slider->GetMaxValue()));
m_layers_slider->SetLowerValue(std::max(bottom, m_layers_slider->GetMinValue()));
m_layers_slider->SetHigherPos(std::min(top, m_layers_slider->GetMaxPos()));
m_layers_slider->SetLowerPos(std::max(bottom, m_layers_slider->GetMinPos()));
}
bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
@ -231,29 +235,13 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
m_canvas->show_legend(true);
m_canvas->enable_dynamic_background(true);
m_layers_slider_sizer = create_layers_slider_sizer();
wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this));
create_sliders();
m_left_sizer = new wxBoxSizer(wxVERTICAL);
m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL);
right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0);
m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView);
wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL);
bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0);
m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer);
m_left_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0);
m_left_sizer->Hide(m_bottom_toolbar_panel);
wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL);
main_sizer->Add(m_left_sizer, 1, wxALL | wxEXPAND, 0);
main_sizer->Add(right_sizer, 0, wxALL | wxEXPAND, 0);
SetSizer(main_sizer);
SetMinSize(GetSize());
@ -325,10 +313,8 @@ void Preview::reload_print()
void Preview::msw_rescale()
{
// rescale slider
if (m_layers_slider != nullptr) m_layers_slider->msw_rescale();
if (m_moves_slider != nullptr) m_moves_slider->msw_rescale();
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
m_moves_slider->SetEmUnit(wxGetApp().em_unit());
// rescale warning legend on the canvas
get_canvas3d()->msw_rescale();
@ -336,53 +322,49 @@ void Preview::msw_rescale()
reload_print();
}
void Preview::sys_color_changed()
void Preview::render_sliders(GLCanvas3D& canvas)
{
#ifdef _WIN32
wxWindowUpdateLocker noUpdates(this);
wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel);
#endif // _WIN32
const Size cnv_size = canvas.get_canvas_size();
const int canvas_width = cnv_size.get_width();
const int canvas_height = cnv_size.get_height();
const float extra_scale = cnv_size.get_scale_factor();
if (m_layers_slider != nullptr)
m_layers_slider->sys_color_changed();
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
const bool is_collapse_btn_shown = collapse_toolbar.is_enabled();
if (m_layers_slider)
m_layers_slider->Render(canvas_width, canvas_height, extra_scale, is_collapse_btn_shown ? collapse_toolbar.get_height() : 0.f);
if (m_moves_slider)
m_moves_slider->Render(canvas_width, canvas_height, extra_scale);
}
void Preview::jump_layers_slider(wxKeyEvent& evt)
float Preview::get_moves_slider_height()
{
if (m_layers_slider) m_layers_slider->OnChar(evt);
if (m_moves_slider && m_moves_slider->IsShown())
return m_moves_slider->GetHeight();
return 0.0f;
}
void Preview::move_layers_slider(wxKeyEvent& evt)
float Preview::get_layers_slider_width()
{
if (m_layers_slider != nullptr) m_layers_slider->OnKeyDown(evt);
}
void Preview::edit_layers_slider(wxKeyEvent& evt)
{
if (m_layers_slider != nullptr) m_layers_slider->OnChar(evt);
if (m_layers_slider && m_layers_slider->IsShown())
return m_layers_slider->GetWidth();
return 0.0f;
}
void Preview::bind_event_handlers()
{
Bind(wxEVT_SIZE, &Preview::on_size, this);
m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
}
void Preview::unbind_event_handlers()
{
Unbind(wxEVT_SIZE, &Preview::on_size, this);
m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
}
void Preview::move_moves_slider(wxKeyEvent& evt)
{
if (m_moves_slider != nullptr) m_moves_slider->OnKeyDown(evt);
}
void Preview::hide_layers_slider()
{
m_layers_slider_sizer->Hide((size_t)0);
Layout();
m_layers_slider->Hide();
}
void Preview::on_size(wxSizeEvent& evt)
@ -391,37 +373,167 @@ void Preview::on_size(wxSizeEvent& evt)
Refresh();
}
wxBoxSizer* Preview::create_layers_slider_sizer()
/* To avoid get an empty string from wxTextEntryDialog
* Let disable OK button, if TextCtrl is empty
* */
static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, double max = -1.0)
{
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100);
GUI::wxGetApp().UpdateDlgDarkUI(dlg);
// detect TextCtrl and OK button
wxWindowList& dlg_items = dlg->GetChildren();
for (auto item : dlg_items) {
if (wxTextCtrl* textctrl = dynamic_cast<wxTextCtrl*>(item)) {
textctrl->SetInsertionPointEnd();
wxButton* btn_OK = static_cast<wxButton*>(dlg->FindWindowById(wxID_OK));
btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl](wxUpdateUIEvent& evt) {
evt.Enable(!textctrl->IsEmpty());
}, btn_OK->GetId());
break;
}
}
}
void Preview::create_sliders()
{
// Layers Slider
m_layers_slider = std::make_unique<DoubleSlider::DSForLayers>(0, 0, 0, 100, wxGetApp().is_editor());
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
m_layers_slider->set_imgui_wrapper(wxGetApp().imgui());
m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA,
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"));
m_layers_slider->enable_action_icon(wxGetApp().is_editor());
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"));
sizer->Add(m_layers_slider, 0, wxEXPAND, 0);
m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } );
// sizer, m_canvas_widget
m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this);
m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) {
if (event.GetKeyCode() == WXK_SHIFT)
m_layers_slider->UseDefaultColors(true);
event.Skip();
if (wxGetApp().is_editor()) {
m_layers_slider->set_callback_on_ticks_changed([this]() -> void {
Model& model = wxGetApp().plater()->model();
model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues();
m_schedule_background_process();
m_keep_current_preview_type = false;
reload_print();
});
m_layers_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_layers_slider_scroll_changed, this);
m_layers_slider->set_callback_on_check_gcode([this](CustomGCode::Type type) -> void {
if (type == ColorChange && m_layers_slider->gcode(ColorChange).empty())
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyColorChangeCode);
});
Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
Model& model = wxGetApp().plater()->model();
model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues();
m_schedule_background_process();
m_layers_slider->set_callback_on_empty_auto_color_change([]() -> void {
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange);
});
m_keep_current_preview_type = false;
reload_print();
});
m_layers_slider->set_callback_on_get_extruder_colors([]() -> std::vector<std::string> {
return wxGetApp().plater()->get_extruder_colors_from_plater_config();
});
return sizer;
m_layers_slider->set_callback_on_get_print([]() -> const Print& {
return GUI::wxGetApp().plater()->fff_print();
});
m_layers_slider->set_callback_on_get_custom_code([](const std::string& code_in, double height) -> std::string
{
wxString msg_text = _L("Enter custom G-code used on current layer") + ":";
wxString msg_header = format_wxstr(_L("Custom G-code on current layer (%1% mm)."), height);
// get custom gcode
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in,
wxTextEntryDialogStyle | wxTE_MULTILINE);
upgrade_text_entry_dialog(&dlg);
bool valid = true;
std::string value;
do {
if (dlg.ShowModal() != wxID_OK)
return "";
value = into_u8(dlg.GetValue());
valid = true;// GUI::Tab::validate_custom_gcode("Custom G-code", value); // !ysFIXME validate_custom_gcode
} while (!valid);
return value;
});
m_layers_slider->set_callback_on_get_pause_print_msg([](const std::string& msg_in, double height) -> std::string
{
wxString msg_text = _L("Enter short message shown on Printer display when a print is paused") + ":";
wxString msg_header = format_wxstr(_L("Message for pause print on current layer (%1% mm)."), height);
// get custom gcode
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in),
wxTextEntryDialogStyle);
upgrade_text_entry_dialog(&dlg);
if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty())
return "";
return into_u8(dlg.GetValue());
});
m_layers_slider->set_callback_on_get_new_color([](const std::string& color) -> std::string
{
wxColour clr(color);
if (!clr.IsOk())
clr = wxColour(0, 0, 0); // Don't set alfa to transparence
auto data = new wxColourData();
data->SetChooseFull(1);
data->SetColour(clr);
wxColourDialog dialog(GUI::wxGetApp().GetTopWindow(), data);
dialog.CenterOnParent();
if (dialog.ShowModal() == wxID_OK)
return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
return "";
});
m_layers_slider->set_callback_on_show_info_msg([this](const std::string& message, int btns_flag) -> int
{
GUI::MessageDialog msg(this, from_u8(message), _L("Notice"), btns_flag);
int ret = msg.ShowModal();
return ret == wxID_YES ? wxYES :
ret == wxID_NO ? wxNO :
ret == wxID_CANCEL ? wxCANCEL : -1;
});
m_layers_slider->set_callback_on_show_warning_msg([this](const std::string& message, int btns_flag) -> int
{
GUI::WarningDialog msg(this, from_u8(message), _L("Warning"), btns_flag);
int ret = msg.ShowModal();
return ret == wxID_YES ? wxYES :
ret == wxID_NO ? wxNO :
ret == wxID_CANCEL ? wxCANCEL : -1;
});
m_layers_slider->set_callback_on_get_extruders_cnt([]() -> int
{
return GUI::wxGetApp().extruders_edited_cnt();
});
m_layers_slider->set_callback_on_get_extruders_sequence([](DoubleSlider::ExtrudersSequence& extruders_sequence) -> bool
{
GUI::ExtruderSequenceDialog dlg(extruders_sequence);
if (dlg.ShowModal() != wxID_OK)
return false;
extruders_sequence = dlg.GetValue();
return true;
});
}
// Move Gcode Slider
m_moves_slider = std::make_unique<DoubleSlider::DSForGcode>(0, 0, 0, 100);
m_moves_slider->SetEmUnit(wxGetApp().em_unit());
m_moves_slider->set_callback_on_thumb_move([this]() ->void { on_moves_slider_scroll_changed(); });
// m_canvas_widget
m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_sliders_from_canvas, this);
m_canvas_widget->Bind(EVT_GLCANVAS_SLIDERS_MANIPULATION, &Preview::update_sliders_from_canvas, this);
}
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
@ -460,7 +572,7 @@ void Preview::check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_f
ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(),
[layers_z](CustomGCode::Item val)
{
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon());
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - CustomGCode::epsilon());
return it == layers_z.end();
}),
ticks_from_model.end());
@ -471,17 +583,17 @@ void Preview::check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_f
void Preview::update_layers_slider(const std::vector<double>& layers_z, bool keep_z_range)
{
// Save the initial slider span.
double z_low = m_layers_slider->GetLowerValueD();
double z_high = m_layers_slider->GetHigherValueD();
bool was_empty = m_layers_slider->GetMaxValue() == 0;
double z_low = m_layers_slider->GetLowerValue();
double z_high = m_layers_slider->GetHigherValue();
bool was_empty = m_layers_slider->GetMaxPos() == 0;
bool force_sliders_full_range = was_empty;
if (!keep_z_range) {
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/;
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValue()) > CustomGCode::epsilon()/*1e-6*/;
force_sliders_full_range |= span_changed;
}
bool snap_to_min = force_sliders_full_range || m_layers_slider->is_lower_at_min();
bool snap_to_max = force_sliders_full_range || m_layers_slider->is_higher_at_max();
bool snap_to_min = force_sliders_full_range || m_layers_slider->IsLowerAtMin();
bool snap_to_max = force_sliders_full_range || m_layers_slider->IsHigherAtMax();
// Detect and set manipulation mode for double slider
update_layers_slider_mode();
@ -499,19 +611,19 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
//first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
m_layers_slider->SetSliderValues(layers_z);
assert(m_layers_slider->GetMinValue() == 0);
m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1);
assert(m_layers_slider->GetMinPos() == 0);
m_layers_slider->SetMaxPos(layers_z.empty() ? 0 : layers_z.size() - 1);
int idx_low = 0;
int idx_high = m_layers_slider->GetMaxValue();
int idx_high = m_layers_slider->GetMaxPos();
if (!layers_z.empty()) {
if (!snap_to_min) {
int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/);
int idx_new = find_close_layer_idx(layers_z, z_low, CustomGCode::epsilon()/*1e-6*/);
if (idx_new != -1)
idx_low = idx_new;
}
if (!snap_to_max) {
int idx_new = find_close_layer_idx(layers_z, z_high, DoubleSlider::epsilon()/*1e-6*/);
int idx_new = find_close_layer_idx(layers_z, z_high, CustomGCode::epsilon()/*1e-6*/);
if (idx_new != -1)
idx_high = idx_new;
}
@ -535,10 +647,20 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
break;
}
auto get_print_obj_idxs = [plater]() ->std::string {
if (plater->printer_technology() == ptSLA)
return "sla";
const Print& print = GUI::wxGetApp().plater()->fff_print();
std::string idxs;
for (auto object : print.objects())
idxs += std::to_string(object->id().id) + "_";
return idxs;
};
// Suggest the auto color change, if model looks like sign
if (!color_change_already_exists &&
wxGetApp().app_config->get_bool("allow_auto_color_change") &&
m_layers_slider->IsNewPrint())
m_layers_slider->is_new_print(get_print_obj_idxs()))
{
const Print& print = wxGetApp().plater()->fff_print();
@ -593,9 +715,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
break;
}
}
m_layers_slider_sizer->Show((size_t)0);
Layout();
m_layers_slider->Show();
}
void Preview::update_layers_slider_mode()
@ -654,33 +774,54 @@ void Preview::update_layers_slider_mode()
void Preview::reset_layers_slider()
{
m_layers_slider->SetHigherValue(0);
m_layers_slider->SetLowerValue(0);
m_layers_slider->SetHigherPos(0);
m_layers_slider->SetLowerPos(0);
}
void Preview::update_layers_slider_from_canvas(wxKeyEvent& event)
void Preview::update_sliders_from_canvas(wxKeyEvent& event)
{
if (event.HasModifiers()) {
const auto key = event.GetKeyCode();
const bool can_edit = wxGetApp().is_editor();
if (can_edit && (key == WXK_NUMPAD_ADD || key == '+'))
m_layers_slider->add_current_tick();
else if (can_edit && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK || key == '-'))
m_layers_slider->delete_current_tick();
else if (key == 'G' || key == 'g')
m_layers_slider->jump_to_value();
else if (key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) {
int delta = 1;
// accelerators
int accelerator = 0;
if (wxGetKeyState(WXK_SHIFT))
accelerator += 5;
if (wxGetKeyState(WXK_CONTROL))
accelerator += 5;
if (accelerator > 0)
delta *= accelerator;
if (key == WXK_LEFT || key == WXK_RIGHT)
m_moves_slider->move_current_thumb(delta * (key == WXK_LEFT ? 1 : -1));
else if (key == WXK_UP || key == WXK_DOWN)
m_layers_slider->move_current_thumb(delta * (key == WXK_DOWN ? 1 : -1));
}
else if (event.HasModifiers()) {
event.Skip();
return;
}
const auto key = event.GetKeyCode();
if (key == 'S' || key == 'W') {
const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1;
m_layers_slider->SetHigherValue(new_pos);
if (event.ShiftDown() || m_layers_slider->is_one_layer()) m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue());
else if (key == 'S' || key == 'W') {
const int new_pos = key == 'W' ? m_layers_slider->GetHigherPos() + 1 : m_layers_slider->GetHigherPos() - 1;
m_layers_slider->SetHigherPos(new_pos);
}
else if (key == 'A' || key == 'D') {
const int new_pos = key == 'D' ? m_moves_slider->GetHigherValue() + 1 : m_moves_slider->GetHigherValue() - 1;
m_moves_slider->SetHigherValue(new_pos);
if (event.ShiftDown() || m_moves_slider->is_one_layer()) m_moves_slider->SetLowerValue(m_moves_slider->GetHigherValue());
const int new_pos = key == 'D' ? m_moves_slider->GetHigherPos() + 1 : m_moves_slider->GetHigherPos() - 1;
m_moves_slider->SetHigherPos(new_pos);
}
else if (key == 'X')
m_layers_slider->ChangeOneLayerLock();
else if (key == WXK_SHIFT)
m_layers_slider->UseDefaultColors(false);
else
event.Skip();
}
@ -698,9 +839,9 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
std::optional<uint32_t>{ m_canvas->get_gcode_vertex_at(*visible_range_max).gcode_id } : std::nullopt;
const size_t range_size = range[1] - range[0] + 1;
std::vector<double> values;
std::vector<unsigned int> values;
values.reserve(range_size);
std::vector<double> alternate_values;
std::vector<unsigned int> alternate_values;
alternate_values.reserve(range_size);
std::optional<uint32_t> visible_range_min_id;
@ -713,7 +854,7 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
if (i > range[0]) {
// skip consecutive moves with same gcode id (resulting from processing G2 and G3 lines)
if (last_gcode_id == gcode_id) {
values.back() = static_cast<double>(i + 1);
values.back() = i + 1;
skip = true;
}
else
@ -721,11 +862,11 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
}
if (!skip) {
values.emplace_back(static_cast<double>(i + 1));
alternate_values.emplace_back(static_cast<double>(gcode_id));
if (gcode_id_min.has_value() && alternate_values.back() == static_cast<double>(*gcode_id_min))
values.emplace_back(i + 1);
alternate_values.emplace_back(gcode_id);
if (gcode_id_min.has_value() && alternate_values.back() == *gcode_id_min)
visible_range_min_id = counter;
else if (gcode_id_max.has_value() && alternate_values.back() == static_cast<double>(*gcode_id_max))
else if (gcode_id_max.has_value() && alternate_values.back() == *gcode_id_max)
visible_range_max_id = counter;
++counter;
}
@ -736,8 +877,10 @@ void Preview::update_moves_slider(std::optional<int> visible_range_min, std::opt
m_moves_slider->SetSliderValues(values);
m_moves_slider->SetSliderAlternateValues(alternate_values);
m_moves_slider->SetMaxValue(static_cast<int>(values.size()) - 1);
m_moves_slider->SetMaxPos(static_cast<int>(values.size()) - 1);
m_moves_slider->SetSelectionSpan(span_min_id, span_max_id);
m_moves_slider->ShowLowerThumb(get_app_config()->get("seq_top_layer_only") == "0");
}
void Preview::enable_moves_slider(bool enable)
@ -745,7 +888,6 @@ void Preview::enable_moves_slider(bool enable)
bool render_as_disabled = !enable;
if (m_moves_slider != nullptr && m_moves_slider->is_rendering_as_disabled() != render_as_disabled) {
m_moves_slider->set_render_as_disabled(render_as_disabled);
m_moves_slider->Refresh();
}
}
@ -781,9 +923,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
if (wxGetApp().is_editor() && !has_layers) {
m_canvas->reset_gcode_layers_times_cache();
hide_layers_slider();
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
m_moves_slider->Hide();
m_canvas_widget->Refresh();
return;
}
@ -810,11 +950,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
m_canvas->load_gcode_preview(*m_gcode_result, tool_colors, color_print_colors);
// the view type may have been changed by the call m_canvas->load_gcode_preview()
gcode_view_type = m_canvas->get_gcode_view_type();
m_left_sizer->Layout();
Refresh();
zs = m_canvas->get_gcode_layers_zs();
if (!zs.empty())
m_left_sizer->Show(m_bottom_toolbar_panel);
m_loaded = true;
}
else if (is_pregcode_preview) {
@ -823,16 +959,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
// the view type has been changed by the call m_canvas->load_gcode_preview()
if (gcode_view_type == libvgcode::EViewType::ColorPrint && !color_print_values.empty())
m_canvas->set_gcode_view_type(gcode_view_type);
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
zs = m_canvas->get_gcode_layers_zs();
}
else {
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
}
m_moves_slider->Show(gcode_preview_data_valid && !zs.empty());
if (!zs.empty() && !m_keep_current_preview_type) {
const unsigned int number_extruders = wxGetApp().is_editor() ?
@ -893,9 +1022,7 @@ void Preview::load_print_as_sla()
if (IsShown()) {
m_canvas->load_sla_preview();
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
m_moves_slider->Hide();
if (n_layers > 0)
update_layers_slider(zs);
@ -904,26 +1031,26 @@ void Preview::load_print_as_sla()
}
}
void Preview::on_layers_slider_scroll_changed(wxCommandEvent& event)
void Preview::on_layers_slider_scroll_changed()
{
if (IsShown()) {
PrinterTechnology tech = m_process->current_printer_technology();
if (tech == ptFFF) {
m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValueD(), m_layers_slider->GetHigherValueD() });
m_canvas->set_toolpaths_z_range({ static_cast<unsigned int>(m_layers_slider->GetLowerValue()), static_cast<unsigned int>(m_layers_slider->GetHigherValue()) });
m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValue(), m_layers_slider->GetHigherValue() });
m_canvas->set_toolpaths_z_range({ static_cast<unsigned int>(m_layers_slider->GetLowerPos()), static_cast<unsigned int>(m_layers_slider->GetHigherPos()) });
m_canvas->set_as_dirty();
}
else if (tech == ptSLA) {
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValueD()));
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValueD()));
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValue()));
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValue()));
m_canvas->render();
}
}
}
void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event)
void Preview::on_moves_slider_scroll_changed()
{
m_canvas->update_gcode_sequential_view_current(static_cast<unsigned int>(m_moves_slider->GetLowerValueD() - 1.0), static_cast<unsigned int>(m_moves_slider->GetHigherValueD() - 1.0));
m_canvas->update_gcode_sequential_view_current(static_cast<unsigned int>(m_moves_slider->GetLowerValue() - 1), static_cast<unsigned int>(m_moves_slider->GetHigherValue() - 1));
m_canvas->set_as_dirty();
m_canvas->request_extra_frame();
}

View File

@ -20,6 +20,11 @@ class wxComboBox;
class wxComboCtrl;
class wxCheckBox;
namespace DoubleSlider {
class DSForGcode;
class DSForLayers;
};
namespace Slic3r {
class DynamicPrintConfig;
@ -27,10 +32,6 @@ class Print;
class BackgroundSlicingProcess;
class Model;
namespace DoubleSlider {
class Control;
};
namespace GUI {
class GLCanvas3D;
@ -82,8 +83,6 @@ class Preview : public wxPanel
wxGLCanvas* m_canvas_widget { nullptr };
GLCanvas3D* m_canvas { nullptr };
wxBoxSizer* m_left_sizer { nullptr };
wxBoxSizer* m_layers_slider_sizer { nullptr };
wxPanel* m_bottom_toolbar_panel { nullptr };
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
@ -97,8 +96,8 @@ class Preview : public wxPanel
bool m_loaded { false };
DoubleSlider::Control* m_layers_slider{ nullptr };
DoubleSlider::Control* m_moves_slider{ nullptr };
std::unique_ptr<DoubleSlider::DSForLayers> m_layers_slider{ nullptr };
std::unique_ptr<DoubleSlider::DSForGcode> m_moves_slider { nullptr };
public:
enum class OptionType : unsigned int
@ -135,16 +134,15 @@ public:
void reload_print();
void msw_rescale();
void sys_color_changed();
void jump_layers_slider(wxKeyEvent& evt);
void move_layers_slider(wxKeyEvent& evt);
void edit_layers_slider(wxKeyEvent& evt);
void render_sliders(GLCanvas3D& canvas);
float get_layers_slider_width();
float get_moves_slider_height();
bool is_loaded() const { return m_loaded; }
void update_moves_slider(std::optional<int> visible_range_min = std::nullopt, std::optional<int> visible_range_max = std::nullopt);
void enable_moves_slider(bool enable);
void move_moves_slider(wxKeyEvent& evt);
void hide_layers_slider();
void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; }
@ -160,20 +158,20 @@ private:
void on_size(wxSizeEvent& evt);
// Create/Update/Reset double slider on 3dPreview
wxBoxSizer* create_layers_slider_sizer();
void create_sliders();
void check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_from_model,
const std::vector<double>& layers_z);
void reset_layers_slider();
void update_layers_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void update_layers_slider_mode();
// update vertical DoubleSlider after keyDown in canvas
void update_layers_slider_from_canvas(wxKeyEvent& event);
void update_sliders_from_canvas(wxKeyEvent& event);
void load_print_as_fff(bool keep_z_range = false);
void load_print_as_sla();
void on_layers_slider_scroll_changed(wxCommandEvent& event);
void on_moves_slider_scroll_changed(wxCommandEvent& event);
void on_layers_slider_scroll_changed();
void on_moves_slider_scroll_changed();
};
} // namespace GUI

View File

@ -0,0 +1,554 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "ImGuiDoubleSlider.hpp"
namespace DoubleSlider {
const ImU32 tooltip_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT);
const ImU32 thumb_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT);
const ImU32 groove_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND);
const ImU32 border_clr = IM_COL32(255, 255, 255, 255);
static bool behavior(ImGuiID id, const ImRect& region,
const ImS32 v_min, const ImS32 v_max,
ImS32* out_value, ImRect* out_thumb,
ImGuiSliderFlags flags = 0,
bool change_on_mouse_move = false)
{
ImGuiContext& context = *GImGui;
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
const ImVec2 thumb_sz = out_thumb->GetSize();
ImS32 v_range = (v_min < v_max ? v_max - v_min : v_min - v_max);
const float region_usable_sz = (region.Max[axis] - region.Min[axis]);
const float region_usable_pos_min = region.Min[axis];
const float region_usable_pos_max = region.Max[axis];
const float mouse_abs_pos = context.IO.MousePos[axis];
float mouse_pos_ratio = (region_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - region_usable_pos_min) / region_usable_sz, 0.0f, 1.0f) : 0.0f;
if (axis == ImGuiAxis_Y)
mouse_pos_ratio = 1.0f - mouse_pos_ratio;
// Process interacting with the slider
ImS32 v_new = *out_value;
bool value_changed = false;
// wheel behavior
ImRect mouse_wheel_responsive_region;
if (axis == ImGuiAxis_X)
mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(thumb_sz.x / 2, 0), region.Max + ImVec2(thumb_sz.x / 2, 0));
if (axis == ImGuiAxis_Y)
mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(0, thumb_sz.y), region.Max + ImVec2(0, thumb_sz.y));
if (ImGui::ItemHoverable(mouse_wheel_responsive_region, id)) {
if (change_on_mouse_move)
v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f);
else
v_new = ImClamp(*out_value + (ImS32)(context.IO.MouseWheel/* * accer*/), v_min, v_max);
}
// drag behavior
if (context.ActiveId == id)
{
if (context.ActiveIdSource == ImGuiInputSource_Mouse)
{
if (context.IO.MouseReleased[0])
ImGui::ClearActiveID();
if (context.IO.MouseDown[0])
v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f);
}
}
// apply result, output value
if (*out_value != v_new)
{
*out_value = v_new;
value_changed = true;
}
// Output thumb position so it can be displayed by the caller
const ImS32 v_clamped = (v_min < v_max) ? ImClamp(*out_value, v_min, v_max) : ImClamp(*out_value, v_max, v_min);
float thumb_pos_ratio = v_range != 0 ? ((float)(v_clamped - v_min) / (float)v_range) : 0.0f;
thumb_pos_ratio = axis == ImGuiAxis_Y ? 1.0f - thumb_pos_ratio : thumb_pos_ratio;
const float thumb_pos = region_usable_pos_min + (region_usable_pos_max - region_usable_pos_min) * thumb_pos_ratio;
ImVec2 new_thumb_center = axis == ImGuiAxis_Y ? ImVec2(out_thumb->GetCenter().x, thumb_pos) : ImVec2(thumb_pos, out_thumb->GetCenter().y);
*out_thumb = ImRect(new_thumb_center - thumb_sz * 0.5f, new_thumb_center + thumb_sz * 0.5f);
return value_changed;
}
ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const
{
ImVec2 groove_start = is_horizontal ?
ImVec2(pos.x + thumb_dummy_sz().x, pos.y + size.y - groove_sz().y - dummy_sz().y) :
ImVec2(pos.x + size.x - groove_sz().x - dummy_sz().x, pos.y + text_dummy_sz().y);
ImVec2 groove_size = is_horizontal ?
ImVec2(size.x - 2 * thumb_dummy_sz().x - text_dummy_sz().x, groove_sz().y) :
ImVec2(groove_sz().x, size.y - 1.6 * text_dummy_sz().y);
return ImRect(groove_start, groove_start + groove_size);
}
ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is_horizontal) const
{
ImRect draggable_region = is_horizontal ?
ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y) :
ImRect(groove.GetCenter().x, groove.Min.y, groove.GetCenter().x, groove.Max.y);
draggable_region.Expand(is_horizontal ?
ImVec2(/*thumb_radius()*/0, draggable_region_sz().y) :
ImVec2(draggable_region_sz().x, 0));
return draggable_region;
}
ImRect ImGuiControl::DrawOptions::slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const
{
ImVec2 mid = draggable_region.GetCenter();
ImRect scroll_line = is_horizontal ?
ImRect(ImVec2(l_thumb_center.x, mid.y - groove_sz().y / 2), ImVec2(h_thumb_center.x, mid.y + groove_sz().y / 2)) :
ImRect(ImVec2(mid.x - groove_sz().x / 2, h_thumb_center.y), ImVec2(mid.x + groove_sz().x / 2, l_thumb_center.y));
return scroll_line;
}
ImGuiControl::ImGuiControl( int lowerValue,
int higherValue,
int minValue,
int maxValue,
ImGuiSliderFlags flags,
std::string name,
bool use_lower_thumb) :
m_selection(ssUndef),
m_name(name),
m_lower_pos(lowerValue),
m_higher_pos (higherValue),
m_min_pos(minValue),
m_max_pos(maxValue),
m_flags(flags),
m_draw_lower_thumb(use_lower_thumb)
{
}
int ImGuiControl::GetActivePos() const
{
return m_selection == ssLower ? m_lower_pos :
m_selection == ssHigher ? m_higher_pos : -1;
}
void ImGuiControl::SetLowerPos(const int lower_pos)
{
m_selection = ssLower;
m_lower_pos = lower_pos;
correct_lower_pos();
}
void ImGuiControl::SetHigherPos(const int higher_pos)
{
m_selection = ssHigher;
m_higher_pos = higher_pos;
correct_higher_pos();
}
void ImGuiControl::SetSelectionSpan(const int lower_pos, const int higher_pos)
{
m_lower_pos = std::max(lower_pos, m_min_pos);
m_higher_pos = std::max(std::min(higher_pos, m_max_pos), m_lower_pos);
if (m_lower_pos < m_higher_pos)
m_combine_thumbs = false;
}
void ImGuiControl::SetMaxPos(const int max_pos)
{
m_max_pos = max_pos;
correct_higher_pos();
}
void ImGuiControl::MoveActiveThumb(int delta)
{
if (m_selection == ssUndef)
m_selection = ssHigher;
if (m_selection == ssLower) {
m_lower_pos -= delta;
correct_lower_pos();
}
else if (m_selection == ssHigher) {
m_higher_pos -= delta;
correct_higher_pos();
}
}
void ImGuiControl::correct_lower_pos()
{
if (m_lower_pos < m_min_pos)
m_lower_pos = m_min_pos;
else if (m_lower_pos > m_max_pos)
m_lower_pos = m_max_pos;
if ((m_lower_pos >= m_higher_pos && m_lower_pos <= m_max_pos) || m_combine_thumbs) {
m_higher_pos = m_lower_pos;
}
}
void ImGuiControl::correct_higher_pos()
{
if (m_higher_pos > m_max_pos)
m_higher_pos = m_max_pos;
else if (m_higher_pos < m_min_pos)
m_higher_pos = m_min_pos;
if ((m_higher_pos <= m_lower_pos && m_higher_pos >= m_min_pos) || m_combine_thumbs) {
m_lower_pos = m_higher_pos;
}
}
void ImGuiControl::CombineThumbs(bool combine)
{
m_combine_thumbs = combine;
if (combine) {
m_selection = ssHigher;
correct_higher_pos();
}
else
ResetPositions();
}
void ImGuiControl::ResetPositions()
{
SetLowerPos(m_min_pos);
SetHigherPos(m_max_pos);
m_selection == ssLower ? correct_lower_pos() : correct_higher_pos();
}
std::string ImGuiControl::get_label(int pos) const
{
if (m_cb_get_label)
return m_cb_get_label(pos);
if (pos >= m_max_pos || pos < m_min_pos)
return "ErrVal";
return std::to_string(pos);
}
float ImGuiControl::GetPositionInRect(int pos, const ImRect& rect) const
{
int v_min = m_min_pos;
int v_max = m_max_pos;
float pos_ratio = (v_max - v_min) != 0 ? ((float)(pos - v_min) / (float)(v_max - v_min)) : 0.0f;
float thumb_pos;
if (is_horizontal()) {
thumb_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio;
}
else {
pos_ratio = 1.0f - pos_ratio;
thumb_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio;
}
return thumb_pos;
}
ImRect ImGuiControl::GetActiveThumbRect() const
{
return m_selection == ssLower ? m_regions.lower_thumb : m_regions.higher_thumb;
}
void ImGuiControl::draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region)
{
if (m_cb_draw_scroll_line)
m_cb_draw_scroll_line(scroll_line, slideable_region);
else
ImGui::RenderFrame(scroll_line.Min, scroll_line.Max, thumb_bg_clr, false, m_draw_opts.rounding());
}
void ImGuiControl::draw_background(const ImRect& slideable_region)
{
ImVec2 groove_sz = m_draw_opts.groove_sz() * 0.55f;
auto groove_center = slideable_region.GetCenter();
ImRect groove = is_horizontal() ?
ImRect(slideable_region.Min.x, groove_center.y - groove_sz.y, slideable_region.Max.x, groove_center.y + groove_sz.y) :
ImRect(groove_center.x - groove_sz.x, slideable_region.Min.y, groove_center.x + groove_sz.x, slideable_region.Max.y);
ImVec2 groove_padding = (is_horizontal() ? ImVec2(2.0f, 2.0f) : ImVec2(3.0f, 4.0f)) * m_draw_opts.scale;
ImRect bg_rect = groove;
bg_rect.Expand(groove_padding);
// draw bg of slider
ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, border_clr, false, 0.5 * bg_rect.GetWidth());
// draw bg of scroll
ImGui::RenderFrame(groove.Min, groove.Max, groove_bg_clr, false, 0.5 * groove.GetWidth());
}
void ImGuiControl::draw_label(std::string label, const ImRect& thumb)
{
if (label.empty() || label == "ErrVal")
return;
const ImVec2 thumb_center = thumb.GetCenter();
ImVec2 text_padding = m_draw_opts.text_padding();
float rounding = m_draw_opts.rounding();
ImVec2 triangle_offsets[3] = { ImVec2(2.0f, 0.0f) * m_draw_opts.scale, ImVec2(0.0f, 8.0f) * m_draw_opts.scale, ImVec2(9.0f, 0.0f) * m_draw_opts.scale };
ImVec2 text_content_size = ImGui::CalcTextSize(label.c_str());
ImVec2 text_size = text_content_size + text_padding * 2;
ImVec2 text_start = is_horizontal() ?
ImVec2(thumb.Max.x + triangle_offsets[2].x, thumb_center.y - text_size.y) :
ImVec2(thumb.Min.x - text_size.x - triangle_offsets[2].x, thumb_center.y - text_size.y) ;
ImRect text_rect(text_start, text_start + text_size);
ImVec2 pos_1 = is_horizontal() ?
ImVec2(text_rect.Min.x + triangle_offsets[0].x, text_rect.Max.y - triangle_offsets[0].y) :
text_rect.Max - triangle_offsets[0];
ImVec2 pos_2 = is_horizontal() ? pos_1 - triangle_offsets[2] : pos_1 - triangle_offsets[1];
ImVec2 pos_3 = is_horizontal() ? pos_1 - triangle_offsets[1] : pos_1 + triangle_offsets[2];
ImGui::RenderFrame(text_rect.Min, text_rect.Max, tooltip_bg_clr, true, rounding);
ImGui::GetCurrentWindow()->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, tooltip_bg_clr);
ImGui::RenderText(text_start + text_padding, label.c_str());
};
void ImGuiControl::draw_thumb(const ImVec2& center, bool mark/* = false*/)
{
const float line_width = 1.5f * m_draw_opts.scale;
const float radius = m_draw_opts.thumb_radius();
const float line_offset = 0.5f * radius;
const float hexagon_angle = is_horizontal() ? 0.f : IM_PI * 0.5f;
ImGuiPureWrap::draw_hexagon(center, radius, border_clr, hexagon_angle);
ImGuiPureWrap::draw_hexagon(center, radius - line_width, thumb_bg_clr, hexagon_angle);
if (mark) {
ImGuiWindow* window = ImGui::GetCurrentWindow();
window->DrawList->AddLine(center + ImVec2(-line_offset, 0.0f), center + ImVec2(line_offset, 0.0f), border_clr, line_width);
window->DrawList->AddLine(center + ImVec2(0.0f, -line_offset), center + ImVec2(0.0f, line_offset), border_clr, line_width);
}
}
void ImGuiControl::apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region)
{
ImVec2 mid = draggable_region.GetCenter();
float thumb_radius = m_draw_opts.thumb_radius();
// set slideable region
m_regions.higher_slideable_region = is_horizontal() ?
ImRect(draggable_region.Min + ImVec2(m_draw_lower_thumb ? thumb_radius : 0, 0), draggable_region.Max) :
ImRect(draggable_region.Min, draggable_region.Max - ImVec2(0, m_combine_thumbs ? 0 : thumb_radius));
m_regions.lower_slideable_region = is_horizontal() ?
ImRect(draggable_region.Min, draggable_region.Max - ImVec2(thumb_radius, 0)) :
ImRect(draggable_region.Min + ImVec2(0, thumb_radius), draggable_region.Max);
// initialize the thumbs.
float higher_thumb_pos = GetPositionInRect(higher_pos, m_regions.higher_slideable_region);
m_regions.higher_thumb = is_horizontal() ?
ImRect(higher_thumb_pos - thumb_radius, mid.y - thumb_radius, higher_thumb_pos + thumb_radius, mid.y + thumb_radius) :
ImRect(mid.x - thumb_radius, higher_thumb_pos - thumb_radius, mid.x + thumb_radius, higher_thumb_pos + thumb_radius);
float lower_thumb_pos = GetPositionInRect(lower_pos, m_regions.lower_slideable_region);
m_regions.lower_thumb = is_horizontal() ?
ImRect(lower_thumb_pos - thumb_radius, mid.y - thumb_radius, lower_thumb_pos + thumb_radius, mid.y + thumb_radius) :
ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius);
}
void ImGuiControl::check_and_correct_thumbs(int* higher_pos, int* lower_pos)
{
if (!m_draw_lower_thumb || m_combine_thumbs)
return;
const ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter();
const ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter();
const float thumb_radius = m_draw_opts.thumb_radius();
const float higher_thumb_center_pos = is_horizontal() ? higher_thumb_center.x : higher_thumb_center.y;
const float lower_thumb_center_pos = is_horizontal() ? lower_thumb_center.x : lower_thumb_center.y;
if (is_horizontal()) {
if (lower_thumb_center_pos + thumb_radius > higher_thumb_center_pos) {
if (m_selection == ssHigher) {
m_regions.lower_thumb = m_regions.higher_thumb;
m_regions.higher_thumb.TranslateX(thumb_radius);
*lower_pos = *higher_pos;
}
else {
m_regions.higher_thumb = m_regions.lower_thumb;
m_regions.lower_thumb.TranslateX(-thumb_radius);
*higher_pos = *lower_pos;
}
}
}
else {
if (higher_thumb_center_pos + thumb_radius > lower_thumb_center_pos) {
if (m_selection == ssHigher) {
m_regions.lower_thumb = m_regions.higher_thumb;
m_regions.lower_thumb.TranslateY(thumb_radius);
*lower_pos = *higher_pos;
}
else {
m_regions.higher_thumb = m_regions.lower_thumb;
m_regions.lower_thumb.TranslateY(-thumb_radius);
*higher_pos = *lower_pos;
}
}
}
}
bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
std::string& higher_label, std::string& lower_label,
const ImVec2& pos, const ImVec2& size, float scale)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& context = *GImGui;
const ImGuiID id = window->GetID(m_name.c_str());
const ImRect item_size(pos, pos + size);
ImGui::ItemSize(item_size);
// get slider groove size
ImRect groove = m_draw_opts.groove(pos, size, is_horizontal());
// get active(draggable) region.
ImRect draggable_region = m_draw_opts.draggable_region(groove, is_horizontal());
if (ImGui::ItemHoverable(draggable_region, id) && context.IO.MouseDown[0]) {
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
// set slideable regions and thumbs.
apply_regions(*higher_pos, *lower_pos, draggable_region);
// select and mark higher thumb by default
if (m_selection == ssUndef)
m_selection = ssHigher;
// Processing interacting
if (ImGui::ItemHoverable(m_regions.higher_thumb, id) && context.IO.MouseClicked[0])
m_selection = ssHigher;
if (m_draw_lower_thumb && !m_combine_thumbs &&
ImGui::ItemHoverable(m_regions.lower_thumb, id) && context.IO.MouseClicked[0])
m_selection = ssLower;
// update thumb position
bool pos_changed = false;
if (m_selection == ssHigher) {
pos_changed = behavior(id, m_regions.higher_slideable_region, m_min_pos, m_max_pos,
higher_pos, &m_regions.higher_thumb, m_flags);
}
else if (m_draw_lower_thumb && !m_combine_thumbs) {
pos_changed = behavior(id, m_regions.lower_slideable_region, m_min_pos, m_max_pos,
lower_pos, &m_regions.lower_thumb, m_flags);
}
// check thumbs poss and correct them if needed
check_and_correct_thumbs(higher_pos, lower_pos);
const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region;
const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb;
bool show_move_label = false;
ImRect mouse_pos_rc = active_thumb;
if (!pos_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) {
behavior(id, slideable_region, m_min_pos, m_max_pos,
&m_mouse_pos, &mouse_pos_rc, m_flags, true);
show_move_label = true;
}
// detect right click on selected thumb
if (ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1])
m_rclick_on_selected_thumb = true;
if ((!ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1]) ||
context.IO.MouseClicked[0])
m_rclick_on_selected_thumb = false;
// render slider
ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter();
ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter();
ImRect scroll_line = m_draw_opts.slider_line(slideable_region, higher_thumb_center, lower_thumb_center, is_horizontal());
if (m_cb_extra_draw)
m_cb_extra_draw(slideable_region);
// draw background
draw_background(slideable_region);
// draw scroll line
draw_scroll_line(m_combine_thumbs ? groove : scroll_line, slideable_region);
// draw thumbs with label
draw_thumb(higher_thumb_center, m_selection == ssHigher && m_draw_lower_thumb);
draw_label(higher_label, m_regions.higher_thumb);
if (m_draw_lower_thumb && !m_combine_thumbs) {
draw_thumb(lower_thumb_center, m_selection == ssLower);
draw_label(lower_label, m_regions.lower_thumb);
}
// draw label on mouse move
if (show_move_label)
draw_label(get_label_on_move(m_mouse_pos), mouse_pos_rc);
return pos_changed;
}
bool ImGuiControl::render()
{
bool result = false;
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0);
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text));
int windows_flag = ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoScrollWithMouse;
ImGuiPureWrap::set_next_window_pos(m_pos.x, m_pos.y, ImGuiCond_Always);
ImGuiPureWrap::begin(m_name, windows_flag);
float scale = 1.f;
int higher_pos = m_higher_pos;
int lower_pos = m_lower_pos;
std::string higher_label = get_label(m_higher_pos);
std::string lower_label = get_label(m_lower_pos);
int temp_higher_pos = m_higher_pos;
int temp_lower_pos = m_lower_pos;
if (draw_slider(&higher_pos, &lower_pos, higher_label, lower_label, m_pos, m_size, scale)) {
if (temp_higher_pos != higher_pos) {
m_higher_pos = higher_pos;
if (m_combine_thumbs)
m_lower_pos = m_higher_pos;
}
if (temp_lower_pos != lower_pos)
m_lower_pos = lower_pos;
result = true;
}
ImGuiPureWrap::end();
ImGui::PopStyleColor(2);
ImGui::PopStyleVar(3);
return result;
}
} // DoubleSlider

View File

@ -0,0 +1,287 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_ImGUI_DoubleSlider_hpp_
#define slic3r_ImGUI_DoubleSlider_hpp_
#include "ImGuiPureWrap.hpp"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui/imgui_internal.h"
#include <sstream>
// this code is borrowed from https://stackoverflow.com/questions/16605967/set-precision-of-stdto-string-when-converting-floating-point-values
template <typename T>
std::string to_string_with_precision(const T a_value, const int n = 2)
{
std::ostringstream out;
out.precision(n);
out << std::fixed << a_value;
return std::move(out).str();
}
namespace DoubleSlider {
enum SelectedSlider {
ssUndef,
ssLower,
ssHigher
};
class ImGuiControl
{
public:
ImGuiControl(int lowerValue,
int higherValue,
int minValue,
int maxValue,
ImGuiSliderFlags flags = ImGuiSliderFlags_None,
std::string name = "d_slider",
bool use_lower_thumb = true);
ImGuiControl() {}
~ImGuiControl() {}
int GetMinPos() const { return m_min_pos; }
int GetMaxPos() const { return m_max_pos; }
int GetLowerPos() const { return m_lower_pos; }
int GetHigherPos() const { return m_higher_pos; }
int GetActivePos() const;
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetLowerPos (const int lower_pos);
void SetHigherPos(const int higher_pos);
void SetSelectionSpan(const int lower_pos, const int higher_pos);
void SetMaxPos(const int max_pos);
void CombineThumbs(bool combine);
void ResetPositions();
void SetCtrlPos(ImVec2 pos) { m_pos = pos; }
void SetCtrlSize(ImVec2 size) { m_size = size; }
void SetCtrlScale(float scale) { m_draw_opts.scale = scale; }
void Init(const ImVec2& pos, const ImVec2& size, float scale) {
m_pos = pos;
m_size = size;
m_draw_opts.scale = scale;
}
ImVec2 GetCtrlSize() { return m_size; }
ImVec2 GetCtrlPos() { return m_pos; }
void Show(bool show) { m_is_shown = show; }
void Hide() { m_is_shown = false; }
bool IsShown() const { return m_is_shown; }
bool IsCombineThumbs() const { return m_combine_thumbs; }
bool IsActiveHigherThumb() const { return m_selection == ssHigher; }
void MoveActiveThumb(int delta);
void ShowLowerThumb(bool show) { m_draw_lower_thumb = show; }
void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; }
ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); }
float GetPositionInRect(int pos, const ImRect& rect) const;
ImRect GetActiveThumbRect() const;
bool IsRClickOnThumb() const { return m_rclick_on_selected_thumb; }
bool is_horizontal() const { return !(m_flags & ImGuiSliderFlags_Vertical); }
bool render();
std::string get_label(int pos) const;
void set_get_label_on_move_cb(std::function<std::string(int)> cb) { m_cb_get_label_on_move = cb; }
void set_get_label_cb(std::function<std::string(int)> cb) { m_cb_get_label = cb; }
void set_draw_scroll_line_cb(std::function<void(const ImRect&, const ImRect&)> cb) { m_cb_draw_scroll_line = cb; }
void set_extra_draw_cb(std::function<void(const ImRect&)> cb) { m_cb_extra_draw = cb; }
private:
struct DrawOptions {
float scale { 1.f }; // used for Retina on osx
ImVec2 dummy_sz() const { return ImVec2(24.0f, 22.0f) * scale; }
ImVec2 thumb_dummy_sz() const { return ImVec2(17.0f, 17.0f) * scale; }
ImVec2 groove_sz() const { return ImVec2(4.0f, 4.0f) * scale; }
ImVec2 draggable_region_sz()const { return ImVec2(20.0f, 19.0f) * scale; }
ImVec2 text_dummy_sz() const { return ImVec2(50.0f, 34.0f) * scale; }
ImVec2 text_padding() const { return ImVec2( 5.0f, 2.0f) * scale; }
float thumb_radius() const { return 10.0f * scale; }
float thumb_border() const { return 2.0f * scale; }
float rounding() const { return 2.0f * scale; }
ImRect groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const;
ImRect draggable_region(const ImRect& groove, bool is_horizontal) const;
ImRect slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const;
};
struct Regions {
ImRect higher_slideable_region;
ImRect lower_slideable_region;
ImRect higher_thumb;
ImRect lower_thumb;
};
SelectedSlider m_selection;
ImVec2 m_pos;
ImVec2 m_size;
std::string m_name;
ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None };
bool m_is_shown{ true };
int m_min_pos;
int m_max_pos;
int m_lower_pos;
int m_higher_pos;
// slider's position of the mouse cursor
int m_mouse_pos;
bool m_rclick_on_selected_thumb{ false };
bool m_draw_lower_thumb{ true };
bool m_combine_thumbs { false };
bool m_show_move_label { false };
DrawOptions m_draw_opts;
Regions m_regions;
std::function<std::string(int)> m_cb_get_label { nullptr };
std::function<std::string(int)> m_cb_get_label_on_move { nullptr };
std::function<void(const ImRect&, const ImRect&)> m_cb_draw_scroll_line { nullptr };
std::function<void(const ImRect&)> m_cb_extra_draw { nullptr };
void correct_lower_pos();
void correct_higher_pos();
std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); }
void apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region);
void check_and_correct_thumbs(int* higher_pos, int* lower_pos);
void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region);
void draw_background(const ImRect& slideable_region);
void draw_label(std::string label, const ImRect& thumb);
void draw_thumb(const ImVec2& center, bool mark = false);
bool draw_slider(int* higher_pos, int* lower_pos,
std::string& higher_label, std::string& lower_label,
const ImVec2& pos, const ImVec2& size, float scale = 1.0f);
};
// VatType = a typ of values, related to the each position in slider
template<typename ValType>
class Manager
{
public:
void Init( int lowerPos,
int higherPos,
int minPos,
int maxPos,
const std::string& name,
bool is_horizontal)
{
m_ctrl = ImGuiControl( lowerPos, higherPos,
minPos, maxPos,
is_horizontal ? 0 : ImGuiSliderFlags_Vertical,
name, !is_horizontal);
m_ctrl.set_get_label_cb([this](int pos) {return get_label(pos); });
};
Manager() {}
Manager(int lowerPos,
int higherPos,
int minPos,
int maxPos,
const std::string& name,
bool is_horizontal)
{
Init (lowerPos, higherPos, minPos, maxPos, name, is_horizontal);
}
~Manager() {}
int GetMinPos() const { return m_ctrl.GetMinPos(); }
int GetMaxPos() const { return m_ctrl.GetMaxPos(); }
int GetLowerPos() const { return m_ctrl.GetLowerPos(); }
int GetHigherPos()const { return m_ctrl.GetHigherPos(); }
ValType GetMinValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetMinPos()]; }
ValType GetMaxValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetMaxPos()]; }
ValType GetLowerValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetLowerPos()];}
ValType GetHigherValue() { return m_values.empty() ? static_cast<ValType>(0) : m_values[GetHigherPos()]; }
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetLowerPos(const int lower_pos) {
m_ctrl.SetLowerPos(lower_pos);
process_thumb_move();
}
void SetHigherPos(const int higher_pos) {
m_ctrl.SetHigherPos(higher_pos);
process_thumb_move();
}
void SetSelectionSpan(const int lower_pos, const int higher_pos) {
m_ctrl.SetSelectionSpan(lower_pos, higher_pos);
process_thumb_move();
}
void SetMaxPos(const int max_pos) {
m_ctrl.SetMaxPos(max_pos);
process_thumb_move();
}
void SetSliderValues(const std::vector<ValType>& values) { m_values = values; }
// values used to show thumb labels
void SetSliderAlternateValues(const std::vector<ValType>& values) { m_alternate_values = values; }
bool IsLowerAtMin() const { return m_ctrl.GetLowerPos() == m_ctrl.GetMinPos(); }
bool IsHigherAtMax() const { return m_ctrl.GetHigherPos() == m_ctrl.GetMaxPos(); }
void Show(bool show = true) { m_ctrl.Show(show); }
void Hide() { m_ctrl.Show(false); }
bool IsShown() { return m_ctrl.IsShown(); }
void SetEmUnit(int em_unit) { m_em = em_unit; }
void ShowLowerThumb(bool show) { m_ctrl.ShowLowerThumb(show); }
float GetWidth() { return m_ctrl.GetCtrlSize().x; }
float GetHeight() { return m_ctrl.GetCtrlSize().y; }
virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) = 0;
void set_callback_on_thumb_move(std::function<void()> cb) { m_cb_thumb_move = cb; };
void move_current_thumb(const int delta)
{
m_ctrl.MoveActiveThumb(delta);
process_thumb_move();
}
protected:
std::vector<ValType> m_values;
std::vector<ValType> m_alternate_values;
ImGuiControl m_ctrl;
int m_em{ 10 };
float m_scale{ 1.f };
virtual std::string get_label(int pos) const {
if (m_values.empty())
return std::to_string(pos);
if (pos >= int(m_values.size()))
return "ErrVal";
return to_string_with_precision(static_cast<ValType>(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos]));
}
void process_thumb_move() {
if (m_cb_thumb_move)
m_cb_thumb_move();
}
private:
std::function<void()> m_cb_thumb_move{ nullptr };
};
} // DoubleSlider
#endif // slic3r_ImGUI_DoubleSlider_hpp_

View File

@ -296,6 +296,20 @@ bool combo(const std::string& label, const std::vector<std::string>& options, in
return res;
}
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
ImGuiWindow* window = ImGui::GetCurrentWindow();
float a_min = start_angle;
float a_max = start_angle + 2.f * IM_PI;
window->DrawList->PathArcTo(center, radius, a_min, a_max, 6);
window->DrawList->PathFillConvex(col);
}
// Scroll up for one item
void scroll_up()
{
@ -603,4 +617,232 @@ bool is_chars_in_ranges(const ImWchar *ranges,
return true;
}
bool begin_menu(const char* label, bool enabled)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems) return false;
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None);
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow;
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
// We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
// If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.
if (g.MenusIdSubmittedThisFrame.contains(id)) {
if (menu_is_open)
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
else
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values
return menu_is_open;
}
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
g.MenusIdSubmittedThisFrame.push_back(id);
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
bool pressed;
bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) &&
(g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
ImGuiWindow* backed_nav_window = g.NavWindow;
if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
// e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
ImVec2 popup_pos, pos = window->DC.CursorPos;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
// Menu inside an horizontal menu bar
// Selectable extend their highlight by half ItemSpacing in each direction.
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
float w = label_size.x;
pressed = /*selectable*/ImGui::Selectable(label, menu_is_open,
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
(!enabled ? ImGuiSelectableFlags_Disabled : 0),
ImVec2(w, 0.0f));
ImGui::PopStyleVar();
window->DC.CursorPos.x += IM_FLOOR(
style.ItemSpacing.x *
(-1.0f +
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else {
// Menu inside a menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame
float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w);
pressed = /*selectable*/ImGui::Selectable(label, menu_is_open,
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0),
ImVec2(min_w, 0.0f));
ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right);
}
const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id);
if (menuset_is_open) g.NavWindow = backed_nav_window;
bool want_open = false;
bool want_close = false;
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
{
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
bool moving_toward_other_child_menu = false;
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ?
g.OpenPopupStack[g.BeginPopupStack.Size].Window :
NULL;
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) {
// FIXME-DPI: Values should be derived from a master "scale" factor.
ImRect next_window_rect = child_menu_window->Rect();
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
tb.y = ta.y +
ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
// GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
}
if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
want_close = true;
if (!menu_is_open && hovered && pressed) // Click to open
want_open = true;
else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
want_open = true;
if (g.NavActivateId == id) {
want_close = menu_is_open;
want_open = !menu_is_open;
}
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
{
want_open = true;
ImGui::NavMoveRequestCancel();
}
}
else {
// Menu bar
if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
{
want_close = true;
want_open = menu_is_open = false;
}
else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
{
want_open = true;
}
else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
{
want_open = true;
ImGui::NavMoveRequestCancel();
}
}
if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
want_close = true;
if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) {
// Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
ImGui::OpenPopup(label);
return false;
}
menu_is_open |= want_open;
if (want_open) ImGui::OpenPopup(label);
if (menu_is_open) {
ImGui::SetNextWindowPos(popup_pos,
ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
}
else {
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
}
return menu_is_open;
}
void end_menu()
{
ImGui::EndMenu();
}
bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems) return false;
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
bool pressed;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
float w = label_size.x;
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f));
ImGui::PopStyleVar();
window->DC.CursorPos.x += IM_FLOOR(
style.ItemSpacing.x *
(-1.0f +
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
}
else {
// Menu item inside a vertical menu
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f;
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame
float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w);
pressed = /*selectable*/ImGui::Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
if (icon_size.x != 0 && icon_size.y != 0) {
float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y;
float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2;
float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f;
ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y);
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
}
if (shortcut_w > 0.0f) {
ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
ImGui::PopStyleColor();
}
if (selected) {
ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f),
ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
}
}
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
return pressed;
}
} // ImGuiPureWrap

View File

@ -53,6 +53,8 @@ namespace ImGuiPureWrap
// Use selection = -1 to not mark any option as selected
bool combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle = 0.f);
void text(const char* label);
void text(const std::string& label);
void text(const std::wstring& label);
@ -146,6 +148,10 @@ namespace ImGuiPureWrap
bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr);
bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
bool begin_menu(const char* label, bool enabled = true);
void end_menu();
bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true);
const ImVec4 COL_GREY_DARK = { 0.33f, 0.33f, 0.33f, 1.0f };
const ImVec4 COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f };
const ImVec4 COL_ORANGE_DARK = { 0.67f, 0.36f, 0.19f, 1.0f };

View File

@ -116,6 +116,26 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
{ImGui::SlaViewProcessed , "sla_view_processed" },
};
static const std::map<const wchar_t, std::string> font_icons_medium = {
{ImGui::Lock , "lock_closed" },
{ImGui::LockHovered , "lock_closed_f" },
{ImGui::Unlock , "lock_open" },
{ImGui::UnlockHovered , "lock_open_f" },
{ImGui::DSRevert , "undo" },
{ImGui::DSRevertHovered , "undo_f" },
{ImGui::DSSettings , "cog" },
{ImGui::DSSettingsHovered , "cog_f" },
{ImGui::ErrorTick , "error_tick" },
{ImGui::ErrorTickHovered , "error_tick_f" },
{ImGui::PausePrint , "pause_print" },
{ImGui::PausePrintHovered , "pause_print_f" },
{ImGui::EditGCode , "edit_gcode" },
{ImGui::EditGCodeHovered , "edit_gcode_f" },
{ImGui::RemoveTick , "colorchange_del" },
{ImGui::RemoveTickHovered , "colorchange_del_f" },
};
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
{ImGui::ClippyMarker , "notification_clippy" },
};
@ -470,7 +490,7 @@ 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::image_button(const wchar_t icon, const std::string& tooltip)
bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip, bool highlight_on_hover/* = true*/)
{
const ImGuiIO& io = ImGui::GetIO();
const ImTextureID tex_id = io.Fonts->TexID;
@ -481,9 +501,9 @@ bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip)
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);
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f });
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, highlight_on_hover ? 1.0f : 0.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, highlight_on_hover ? 1.0f : 0.0f });
const bool res = ImGuiPureWrap::image_button(tex_id, size, uv0, uv1);
ImGui::PopStyleColor(3);
@ -1125,6 +1145,11 @@ void ImGuiWrapper::init_font(bool compress)
m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
}
const int icon_sz_m = int(1.25 * icon_sz); // default size of medium icon is 20 px
for (auto& icon : font_icons_medium) {
m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz_m, icon_sz_m, 3.0 * font_scale + icon_sz_m);
}
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);
@ -1161,6 +1186,10 @@ void ImGuiWrapper::init_font(bool compress)
load_icon_from_svg(icon, icon_sz);
}
for (auto icon : font_icons_medium) {
load_icon_from_svg(icon, icon_sz_m);
}
icon_sz *= 2; // default size of large icon is 32 px
for (auto icon : font_icons_large) {
load_icon_from_svg(icon, icon_sz);
@ -1524,5 +1553,6 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text)
}
} // namespace GUI
} // namespace Slic3r

View File

@ -22,6 +22,7 @@ struct OptionViewParameters;
class wxString;
class wxMouseEvent;
class wxKeyEvent;
struct ImRect;
struct IMGUI_API ImGuiWindow;
@ -85,8 +86,7 @@ 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 image_button(const wchar_t icon, const std::string& tooltip = {});
bool image_button(const wchar_t icon, const std::string& tooltip = {}, bool highlight_on_hover = true);
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);

View File

@ -174,6 +174,14 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
m_top_y = initial_y + m_window_height;
ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - right_gap, 1.0f * (float)cnv_size.get_height() - m_top_y);
if (wxGetApp().plater()->is_preview_shown()) {
if (Preview* preview = dynamic_cast<Preview*>(canvas.get_wxglcanvas()->GetParent())) {
win_pos.y -= 1.5f * preview->get_moves_slider_height();
win_pos.x -= preview->get_layers_slider_width();
}
}
ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
ImGuiPureWrap::set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);

View File

@ -385,6 +385,7 @@ struct Plater::priv
void set_current_canvas_as_dirty();
GLCanvas3D* get_current_canvas3D();
void render_sliders(GLCanvas3D& canvas);
void unbind_canvas_event_handlers();
void reset_canvas_volumes();
@ -735,12 +736,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); });
}
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_JUMP_TO, [this](wxKeyEvent& evt) { preview->jump_layers_slider(evt); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_SLIDERS, [this](wxKeyEvent& evt) {
preview->move_layers_slider(evt);
preview->move_moves_slider(evt);
});
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); });
if (wxGetApp().is_gcode_viewer())
preview->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->q->reload_gcode_from_disk(); });
@ -3189,6 +3185,12 @@ GLCanvas3D* Plater::priv::get_current_canvas3D()
return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
}
void Plater::priv::render_sliders(GLCanvas3D& canvas)
{
if (current_panel == preview)
preview->render_sliders(canvas);
}
void Plater::priv::unbind_canvas_event_handlers()
{
if (view3D != nullptr)
@ -6402,6 +6404,11 @@ GLCanvas3D* Plater::get_current_canvas3D()
return p->get_current_canvas3D();
}
void Plater::render_sliders(GLCanvas3D& canvas)
{
p->render_sliders(canvas);
}
static std::string concat_strings(const std::set<std::string> &strings,
const std::string &delim = "\n")
{
@ -6686,7 +6693,6 @@ void Plater::msw_rescale()
void Plater::sys_color_changed()
{
p->preview->sys_color_changed();
p->sidebar->sys_color_changed();
p->menus.sys_color_changed();

View File

@ -271,6 +271,8 @@ public:
GLCanvas3D* canvas3D();
const GLCanvas3D * canvas3D() const;
GLCanvas3D* get_current_canvas3D();
void render_sliders(GLCanvas3D& canvas);
void arrange();
void arrange(Worker &w, bool selected);

View File

@ -0,0 +1,704 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "TickCodesManager.hpp"
#include "I18N.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Color.hpp"
using namespace Slic3r;
using namespace CustomGCode;
namespace DoubleSlider {
constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2
constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2
static const int YES = 0x00000002; // an analogue of wxYES
static const int NO = 0x00000008; // an analogue of wxNO
static const int CANCEL = 0x00000010; // an analogue of wxCANCEL
bool equivalent_areas(const double& bottom_area, const double& top_area)
{
return fabs(bottom_area - top_area) <= miscalculation;
}
TickCodeManager::TickCodeManager()
{
m_pause_print_msg = _u8L("Place bearings in slots and resume printing");
}
std::string TickCodeManager::gcode(CustomGCode::Type type)
{
if (m_print) {
const Slic3r::PrintConfig& config = m_print->config();
switch (type) {
case CustomGCode::ColorChange: return config.color_change_gcode;
case CustomGCode::PausePrint: return config.pause_print_gcode;
case CustomGCode::Template: return config.template_custom_gcode;
default: return std::string();
}
}
return std::string();
}
int TickCodeManager::get_tick_from_value(double value, bool force_lower_bound/* = false*/)
{
if (!m_values)
return -1;
std::vector<double>::const_iterator it;
if (is_wipe_tower && !force_lower_bound)
it = std::find_if(m_values->begin(), m_values->end(),
[value](const double & val) { return fabs(value - val) <= epsilon(); });
else
it = std::lower_bound(m_values->begin(), m_values->end(), value - epsilon());
if (it == m_values->end())
return -1;
return int(it - m_values->begin());
}
void TickCodeManager::set_ticks(const Info& custom_gcode_per_print_z)
{
ticks.clear();
const std::vector<CustomGCode::Item>& heights = custom_gcode_per_print_z.gcodes;
for (auto h : heights) {
int tick = get_tick_from_value(h.print_z);
if (tick >=0)
ticks.emplace(TickCode{ tick, h.type, h.extruder, h.color, h.extra });
}
if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty())
mode = custom_gcode_per_print_z.mode;
}
// Get active extruders for tick.
// Means one current extruder for not existing tick OR
// 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick)
// Use those values to disable selection of active extruders
std::array<int, 2> TickCodeManager::get_active_extruders_for_tick(int tick, Mode main_mode) const
{
int default_initial_extruder = main_mode == MultiAsSingle ? std::max<int>(1, only_extruder_id) : 1;
std::array<int, 2> extruders = { default_initial_extruder, -1 };
if (empty())
return extruders;
auto it = ticks.lower_bound(TickCode{tick});
if (it != ticks.end() && it->tick == tick) // current tick exists
extruders[1] = it->extruder;
while (it != ticks.begin()) {
--it;
if(it->type == ToolChange) {
extruders[0] = it->extruder;
break;
}
}
return extruders;
}
bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, std::function<bool(const Layer*)> break_condition)
{
double prev_area = area(object->get_layer(frst_layer_id)->lslices);
bool detected = false;
for (size_t i = frst_layer_id+1; i < layers_cnt; i++) {
const Layer* layer = object->get_layer(i);
double cur_area = area(layer->lslices);
// check for overhangs
if (check_overhangs && cur_area > prev_area && !equivalent_areas(prev_area, cur_area))
break;
// Check percent of the area decrease.
// This value have to be more than min_delta_area and more then 10%
if ((prev_area - cur_area > min_delta_area) && (cur_area / prev_area < 0.9)) {
detected = true;
if (break_condition(layer))
break;
}
prev_area = cur_area;
}
return detected;
}
bool TickCodeManager::auto_color_change(Mode main_mode)
{
if (!m_print)
return false;
if (!empty()) {
if (m_cb_show_warning_msg) {
std::string msg_text = _u8L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" +
_u8L("This action is not revertible.\nDo you want to proceed?");
if (m_cb_show_warning_msg(msg_text, YES | NO) == NO)
return false;
}
ticks.clear();
}
int extruders_cnt = m_cb_get_extruders_cnt ? m_cb_get_extruders_cnt() : 0;
for (auto object : m_print->objects()) {
// An object should to have at least 2 layers to apply an auto color change
if (object->layer_count() < 2)
continue;
check_color_change(object, 1, object->layers().size(), false, [this, extruders_cnt, main_mode](const Layer* layer)
{
int tick = get_tick_from_value(layer->print_z);
if (tick >= 0 && !has_tick(tick)) {
if (main_mode == SingleExtruder) {
set_default_colors(true);
add_tick(tick, ColorChange, 1, layer->print_z);
}
else {
int extruder = 2;
if (!empty()) {
auto it = ticks.end();
it--;
extruder = it->extruder + 1;
if (extruder > extruders_cnt)
extruder = 1;
}
add_tick(tick, ToolChange, extruder, layer->print_z);
}
}
// allow max 3 auto color changes
return ticks.size() > 2;
});
}
if (empty() && m_cb_notify_empty_color_change)
m_cb_notify_empty_color_change();
return true;
}
std::string TickCodeManager::get_new_color(const std::string& color)
{
if (m_cb_get_new_color)
return m_cb_get_new_color(color);
return std::string();
}
std::string TickCodeManager::get_custom_code(const std::string& code_in, double height)
{
if (m_cb_get_custom_code)
return m_cb_get_custom_code(code_in, height);
return std::string();
}
std::string TickCodeManager::get_pause_print_msg(const std::string& msg_in, double height)
{
if (m_cb_get_pause_print_msg)
return m_cb_get_pause_print_msg(msg_in, height);
return std::string();
}
bool TickCodeManager::edit_extruder_sequence(const int max_tick, Mode main_mode)
{
if (!check_ticks_changed_event(ToolChange, main_mode) || !m_cb_get_extruders_sequence)
return false;
// init extruder sequence in respect to the extruders count
if (empty())
m_extruders_sequence.init(colors.size());
if(!m_cb_get_extruders_sequence(m_extruders_sequence))
return false;
erase_all_ticks_with_code(ToolChange);
const int extr_cnt = m_extruders_sequence.extruders.size();
if (extr_cnt == 1)
return true;
int tick = 0;
double value = 0.0;
int extruder = -1;
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<> distrib(0, extr_cnt-1);
while (tick <= max_tick)
{
bool color_repetition = false;
if (m_extruders_sequence.random_sequence) {
int rand_extr = distrib(gen);
if (m_extruders_sequence.color_repetition)
color_repetition = rand_extr == extruder;
else
while (rand_extr == extruder)
rand_extr = distrib(gen);
extruder = rand_extr;
}
else {
extruder++;
if (extruder == extr_cnt)
extruder = 0;
}
const int cur_extruder = m_extruders_sequence.extruders[extruder];
bool meaningless_tick = tick == 0.0 && cur_extruder == extruder;
if (!meaningless_tick && !color_repetition)
ticks.emplace(TickCode{tick, ToolChange,cur_extruder + 1, colors[cur_extruder]});
if (m_extruders_sequence.is_mm_intervals) {
value += m_extruders_sequence.interval_by_mm;
tick = get_tick_from_value(value, true);
if (tick < 0)
break;
}
else
tick += m_extruders_sequence.interval_by_layers;
}
return true;
}
bool TickCodeManager::check_ticks_changed_event(Type type, Mode main_mode)
{
if ( mode == main_mode ||
(type != ColorChange && type != ToolChange) ||
(mode == SingleExtruder && main_mode == MultiAsSingle) || // All ColorChanges will be applied for 1st extruder
(mode == MultiExtruder && main_mode == MultiAsSingle) ) // Just mark ColorChanges for all unused extruders
return true;
if ((mode == SingleExtruder && main_mode == MultiExtruder ) ||
(mode == MultiExtruder && main_mode == SingleExtruder) )
{
if (!has_tick_with_code(ColorChange))
return true;
if (m_cb_show_info_msg) {
std::string message = (mode == SingleExtruder ?
_u8L("The last color change data was saved for a single extruder printing.") :
_u8L("The last color change data was saved for a multi extruder printing.")
) + "\n" +
_u8L("Your current changes will delete all saved color changes.") + "\n\n\t" +
_u8L("Are you sure you want to continue?");
if ( m_cb_show_info_msg(message, YES | NO) == YES)
erase_all_ticks_with_code(ColorChange);
}
return false;
}
// m_ticks_mode == MultiAsSingle
if( has_tick_with_code(ToolChange) ) {
if (m_cb_show_info_msg) {
std::string message = main_mode == SingleExtruder ? (
_u8L("The last color change data was saved for a multi extruder printing.") + "\n\n" +
_u8L("Select YES if you want to delete all saved tool changes, \n"
"NO if you want all tool changes switch to color changes, \n"
"or CANCEL to leave it unchanged.") + "\n\n\t" +
_u8L("Do you want to delete all saved tool changes?")
): ( // MultiExtruder
_u8L("The last color change data was saved for a multi extruder printing with tool changes for whole print.") + "\n\n" +
_u8L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" +
_u8L("Are you sure you want to continue?") ) ;
const int answer = m_cb_show_info_msg(message, YES | NO | (main_mode == SingleExtruder ? CANCEL : 0));
if (answer == YES) {
erase_all_ticks_with_code(ToolChange);
}
else if (main_mode == SingleExtruder && answer == NO) {
switch_code(ToolChange, ColorChange);
}
}
return false;
}
if (m_cb_check_gcode_and_notify)
m_cb_check_gcode_and_notify(type);
return true;
}
// Get used extruders for tick.
// Means all extruders(tools) which will be used during printing from current tick to the end
std::set<int> TickCodeManager::get_used_extruders_for_tick(int tick, double print_z, Mode force_mode/* = Undef*/) const
{
if (!m_print)
return {};
Mode e_mode = !force_mode ? mode : force_mode;
if (e_mode == MultiExtruder) {
const ToolOrdering& tool_ordering = m_print->get_tool_ordering();
if (tool_ordering.empty())
return {};
std::set<int> used_extruders;
auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools& lhs, double rhs) { return lhs.print_z < rhs; });
for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) {
const std::vector<unsigned>& extruders = it_layer_tools->extruders;
for (const auto& extruder : extruders)
used_extruders.emplace(extruder + 1);
}
return used_extruders;
}
const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder_id, 1) : 1;
if (ticks.empty() || e_mode == SingleExtruder)
return { default_initial_extruder };
std::set<int> used_extruders;
auto it_start = ticks.lower_bound(TickCode{ tick });
auto it = it_start;
if (it == ticks.begin() && it->type == ToolChange &&
tick != it->tick) // In case of switch of ToolChange to ColorChange, when tick exists,
// we shouldn't change color for extruder, which will be deleted
{
used_extruders.emplace(it->extruder);
if (tick < it->tick)
used_extruders.emplace(default_initial_extruder);
}
while (it != ticks.begin()) {
--it;
if (it->type == ToolChange && tick != it->tick) {
used_extruders.emplace(it->extruder);
break;
}
}
if (it == ticks.begin() && used_extruders.empty())
used_extruders.emplace(default_initial_extruder);
for (it = it_start; it != ticks.end(); ++it)
if (it->type == ToolChange && tick != it->tick)
used_extruders.emplace(it->extruder);
return used_extruders;
}
std::string TickCodeManager::get_color_for_tick(TickCode tick, Type type, const int extruder)
{
auto opposite_one_color = [](const std::string& color) {
ColorRGB rgb;
decode_color(color, rgb);
return encode_color(opposite(rgb));
};
auto opposite_two_colors = [](const std::string& a, const std::string& b) {
ColorRGB rgb1; decode_color(a, rgb1);
ColorRGB rgb2; decode_color(b, rgb2);
return encode_color(opposite(rgb1, rgb2));
};
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) {
if (ticks.empty())
return opposite_one_color(colors[0]);
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick);
if (before_tick_it == ticks.end()) {
while (before_tick_it != ticks.begin())
if (--before_tick_it; before_tick_it->type == ColorChange)
break;
if (before_tick_it->type == ColorChange)
return opposite_one_color(before_tick_it->color);
return opposite_one_color(colors[0]);
}
if (before_tick_it == ticks.begin()) {
const std::string& frst_color = colors[0];
if (before_tick_it->type == ColorChange)
return opposite_two_colors(frst_color, before_tick_it->color);
auto next_tick_it = before_tick_it;
while (next_tick_it != ticks.end())
if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange)
break;
if (next_tick_it != ticks.end() && next_tick_it->type == ColorChange)
return opposite_two_colors(frst_color, next_tick_it->color);
return opposite_one_color(frst_color);
}
std::string frst_color = "";
if (before_tick_it->type == ColorChange)
frst_color = before_tick_it->color;
else {
auto next_tick_it = before_tick_it;
while (next_tick_it != ticks.end())
if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange) {
frst_color = next_tick_it->color;
break;
}
}
while (before_tick_it != ticks.begin())
if (--before_tick_it; before_tick_it->type == ColorChange)
break;
if (before_tick_it->type == ColorChange) {
if (frst_color.empty())
return opposite_one_color(before_tick_it->color);
return opposite_two_colors(before_tick_it->color, frst_color);
}
if (frst_color.empty())
return opposite_one_color(colors[0]);
return opposite_two_colors(colors[0], frst_color);
}
std::string color = colors[extruder - 1];
if (type == ColorChange) {
if (!ticks.empty()) {
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick );
while (before_tick_it != ticks.begin()) {
--before_tick_it;
if (before_tick_it->type == ColorChange && before_tick_it->extruder == extruder) {
color = before_tick_it->color;
break;
}
}
}
color = get_new_color(color);
}
return color;
}
bool TickCodeManager::add_tick(const int tick, Type type, const int extruder, double print_z)
{
std::string color;
std::string extra;
if (type == Custom) // custom Gcode
{
extra = get_custom_code(m_custom_gcode, print_z);
if (extra.empty())
return false;
m_custom_gcode = extra;
}
else if (type == PausePrint) {
extra = get_pause_print_msg(m_pause_print_msg, print_z);
if (extra.empty())
return false;
m_pause_print_msg = extra;
}
else {
color = get_color_for_tick(TickCode{ tick }, type, extruder);
if (color.empty())
return false;
}
ticks.emplace(TickCode{ tick, type, extruder, color, extra });
return true;
}
bool TickCodeManager::edit_tick(std::set<TickCode>::iterator it, double print_z)
{
// Save previously value of the tick before the call a Dialog from get_... functions,
// otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close
// and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941)
TickCode changed_tick = *it;
std::string edited_value;
if (it->type == ColorChange)
edited_value = get_new_color(it->color);
else if (it->type == PausePrint)
edited_value = get_pause_print_msg(it->extra, print_z);
else
edited_value = get_custom_code(it->type == Template ? gcode(Template) : it->extra, print_z);
if (edited_value.empty())
return false;
// Update iterator. For this moment its value can be invalid
if (it = ticks.find(changed_tick); it == ticks.end())
return false;
if (it->type == ColorChange) {
if (it->color == edited_value)
return false;
changed_tick.color = edited_value;
}
else if (it->type == Template) {
if (gcode(Template) == edited_value)
return false;
changed_tick.extra = edited_value;
changed_tick.type = Custom;
}
else if (it->type == Custom || it->type == PausePrint) {
if (it->extra == edited_value)
return false;
changed_tick.extra = edited_value;
if (it->type == Template)
changed_tick.type = Custom;
}
ticks.erase(it);
ticks.emplace(changed_tick);
return true;
}
void TickCodeManager::switch_code(Type type_from, Type type_to)
{
for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; )
if (it->type == type_from) {
TickCode tick = *it;
tick.type = type_to;
tick.extruder = 1;
ticks.erase(it);
it = ticks.emplace(tick).first;
}
else
++it;
}
bool TickCodeManager::switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder)
{
const std::string color = get_color_for_tick(*it, type_to, extruder);
if (color.empty())
return false;
TickCode changed_tick = *it;
changed_tick.type = type_to;
changed_tick.extruder = extruder;
changed_tick.color = color;
ticks.erase(it);
ticks.emplace(changed_tick);
return true;
}
void TickCodeManager::erase_all_ticks_with_code(Type type)
{
for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) {
if (it->type == type)
it = ticks.erase(it);
else
++it;
}
}
bool TickCodeManager::has_tick_with_code(Type type)
{
for (const TickCode& tick : ticks)
if (tick.type == type)
return true;
return false;
}
bool TickCodeManager::has_tick(int tick)
{
return ticks.find(TickCode{ tick }) != ticks.end();
}
ConflictType TickCodeManager::is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z)
{
if ((tick.type == ColorChange && (
(mode == SingleExtruder && main_mode == MultiExtruder ) ||
(mode == MultiExtruder && main_mode == SingleExtruder) )) ||
(tick.type == ToolChange &&
(mode == MultiAsSingle && main_mode != MultiAsSingle)) )
return ctModeConflict;
// check ColorChange tick
if (tick.type == ColorChange) {
// We should mark a tick as a "MeaninglessColorChange",
// if it has a ColorChange for unused extruder from current print to end of the print
std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, print_z, main_mode);
if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end())
return ctMeaninglessColorChange;
// We should mark a tick as a "Redundant",
// if it has a ColorChange for extruder that has not been used before
if (mode == MultiAsSingle && tick.extruder != std::max<int>(only_extruder_id, 1) )
{
auto it = ticks.lower_bound( tick );
if (it == ticks.begin() && it->type == ToolChange && tick.extruder == it->extruder)
return ctNone;
while (it != ticks.begin()) {
--it;
if (it->type == ToolChange && tick.extruder == it->extruder)
return ctNone;
}
return ctRedundant;
}
}
// check ToolChange tick
if (mode == MultiAsSingle && tick.type == ToolChange) {
// We should mark a tick as a "MeaninglessToolChange",
// if it has a ToolChange to the same extruder
auto it = ticks.find(tick);
if (it == ticks.begin())
return tick.extruder == std::max<int>(only_extruder_id, 1) ? ctMeaninglessToolChange : ctNone;
while (it != ticks.begin()) {
--it;
if (it->type == ToolChange)
return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone;
}
}
return ctNone;
}
std::string TickCodeManager::get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const
{
const int current_extruder = it->extruder == 0 ? std::max<int>(only_extruder_id, 1) : it->extruder;
auto it_n = it;
while (it_n != ticks.begin()) {
--it_n;
if (it_n->type == ColorChange && it_n->extruder == current_extruder)
return it_n->color;
}
return colors[current_extruder-1]; // return a color for a specific extruder from the colors list
}
std::string TickCodeManager::get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const
{
const int def_extruder = std::max<int>(1, only_extruder_id);
auto it_n = it;
bool is_tool_change = false;
while (it_n != ticks.begin()) {
--it_n;
if (it_n->type == ToolChange) {
is_tool_change = true;
if (it_n->extruder == it->extruder)
return it->color;
break;
}
if (it_n->type == ColorChange && it_n->extruder == it->extruder)
return it->color;
}
if (!is_tool_change && it->extruder == def_extruder)
return it->color;
return "";
}
} // DoubleSlider

View File

@ -0,0 +1,221 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_TickCodesManager_hpp_
#define slic3r_GUI_TickCodesManager_hpp_
#include "libslic3r/CustomGCode.hpp"
#include <vector>
#include <set>
using namespace Slic3r::CustomGCode;
namespace Slic3r {
class PrintObject;
class Print;
class Layer;
}
namespace DoubleSlider {
// return true when areas are mostly equivalent
bool equivalent_areas(const double& bottom_area, const double& top_area);
// return true if color change was detected
bool check_color_change(const Slic3r::PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs,
// what to do with detected color change
// and return true when detection have to be desturbed
std::function<bool(const Slic3r::Layer*)> break_condition);
enum ConflictType
{
ctNone,
ctModeConflict,
ctMeaninglessColorChange,
ctMeaninglessToolChange,
ctRedundant
};
struct ExtrudersSequence
{
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
bool random_sequence { false };
bool color_repetition { false };
std::vector<size_t> extruders = { 0 };
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&
(other.interval_by_mm == this->interval_by_mm ) &&
(other.interval_by_layers == this->interval_by_layers ) &&
(other.random_sequence == this->random_sequence ) &&
(other.color_repetition == this->color_repetition ) &&
(other.extruders == this->extruders ) ;
}
bool operator!=(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals != this->is_mm_intervals ) ||
(other.interval_by_mm != this->interval_by_mm ) ||
(other.interval_by_layers != this->interval_by_layers ) ||
(other.random_sequence != this->random_sequence ) ||
(other.color_repetition != this->color_repetition ) ||
(other.extruders != this->extruders ) ;
}
void add_extruder(size_t pos, size_t extruder_id = size_t(0))
{
extruders.insert(extruders.begin() + pos+1, extruder_id);
}
void delete_extruder(size_t pos)
{
if (extruders.size() == 1)
return;// last item can't be deleted
extruders.erase(extruders.begin() + pos);
}
void init(size_t extruders_count)
{
extruders.clear();
for (size_t extruder = 0; extruder < extruders_count; extruder++)
extruders.push_back(extruder);
}
};
struct TickCode
{
bool operator<(const TickCode& other) const { return other.tick > this->tick; }
bool operator>(const TickCode& other) const { return other.tick < this->tick; }
int tick = 0;
Type type = ColorChange;
int extruder = 0;
std::string color;
std::string extra;
};
class TickCodeManager
{
std::string m_custom_gcode;
std::string m_pause_print_msg;
bool m_use_default_colors { true };
const Slic3r::Print* m_print{ nullptr };
// pointer to the m_values from DSForLayers
const std::vector<double>* m_values{ nullptr };
ExtrudersSequence m_extruders_sequence;
bool has_tick_with_code(Type type);
bool has_tick(int tick);
std::string get_color_for_tick(TickCode tick, Type type, const int extruder);
std::string get_custom_code(const std::string& code_in, double height);
std::string get_pause_print_msg(const std::string& msg_in, double height);
std::string get_new_color(const std::string& color);
std::function<void()> m_cb_notify_empty_color_change { nullptr };
std::function<void(Type type)> m_cb_check_gcode_and_notify { nullptr };
std::function<std::string(const std::string&, double)> m_cb_get_custom_code { nullptr };
std::function<std::string(const std::string&, double)> m_cb_get_pause_print_msg { nullptr };
std::function<std::string(const std::string&)> m_cb_get_new_color { nullptr };
std::function<int(const std::string&, int)> m_cb_show_info_msg { nullptr };
std::function<int(const std::string&, int)> m_cb_show_warning_msg { nullptr };
std::function<int()> m_cb_get_extruders_cnt { nullptr };
std::function<bool(ExtrudersSequence&)> m_cb_get_extruders_sequence { nullptr };
public:
TickCodeManager();
~TickCodeManager() {}
std::set<TickCode> ticks {};
Mode mode { Undef };
bool is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower
int only_extruder_id{ -1 };
// colors per extruder
std::vector<std::string> colors {};
bool empty() const { return ticks.empty(); }
void set_ticks(const Info& custom_gcode_per_print_z);
bool add_tick(const int tick, Type type, int extruder, double print_z);
bool edit_tick(std::set<TickCode>::iterator it, double print_z);
void switch_code(Type type_from, Type type_to);
bool switch_code_for_tick(std::set<TickCode>::iterator it, Type type_to, const int extruder);
void erase_all_ticks_with_code(Type type);
ConflictType is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z);
int get_tick_from_value(double value, bool force_lower_bound = false);
std::string gcode(Slic3r::CustomGCode::Type type);
// Get used extruders for tick.
// Means all extruders(tools) which will be used during printing from current tick to the end
std::set<int> get_used_extruders_for_tick(int tick, double print_z, Mode force_mode = Undef) const;
// Get active extruders for tick.
// Means one current extruder for not existing tick OR
// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick)
// Use those values to disable selection of active extruders
std::array<int, 2> get_active_extruders_for_tick(int tick, Mode main_mode) const;
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
// true -> if manipulation with ticks with selected type and in respect to the main_mode (slider mode) is possible
// false -> otherwise
bool check_ticks_changed_event(Type type, Mode main_mode);
// return true, if extruder sequence was changed
bool edit_extruder_sequence(const int max_tick, Mode main_mode);
// return true, if auto color change was successfully processed
bool auto_color_change(Mode main_mode);
void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; }
bool used_default_colors() const { return m_use_default_colors; }
void set_print(const Slic3r::Print& print) { if (!m_print) m_print = &print; }
void set_values(const std::vector<double>* values) { m_values = values; }
void set_callback_on_empty_auto_color_change(std::function<void()> cb)
{ m_cb_notify_empty_color_change = cb; }
void set_callback_on_check_gcode(std::function<void(Type)> cb )
{ m_cb_check_gcode_and_notify = cb; }
void set_callback_on_get_custom_code(std::function<std::string(const std::string&, double)> cb)
{ m_cb_get_custom_code = cb; }
void set_callback_on_get_pause_print_msg(std::function<std::string(const std::string&, double)> cb)
{ m_cb_get_pause_print_msg = cb; }
void set_callback_on_get_new_color(std::function<std::string(const std::string&)> cb)
{ m_cb_get_new_color = cb; }
void set_callback_on_show_info_msg(std::function<int(const std::string&, int)> cb)
{ m_cb_show_info_msg = cb; }
void set_callback_on_show_warning_msg(std::function<int(const std::string&, int)> cb)
{ m_cb_show_warning_msg = cb; }
void set_callback_on_get_extruders_cnt(std::function<int()> cb)
{ m_cb_get_extruders_cnt = cb; }
void set_callback_on_get_extruders_sequence(std::function<bool(ExtrudersSequence&)> cb)
{ m_cb_get_extruders_sequence = cb; }
};
} // DoubleSlider;
#endif // slic3r_GUI_TickCodesManager_hpp_