mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-24 04:54:29 +08:00
ImguiDoubleSlider: WIP: Functions which was related to manipulate with ticks are moved into TickCodeInfo
* TickCodeInfo is renamed to TickCodesManager and moved to separate files * Added more callbacks to avoid use of wxWidgets * Added function for color picker rendering * For string formatting is used Slic3r::format instead of GUI::format (To avoid including wx/string) * All code related to DoubleSlider extracted from Slic3r name space
This commit is contained in:
parent
6b83f135c9
commit
785ad4ce95
@ -30,6 +30,7 @@ src/slic3r/GUI/ConfigManipulation.cpp
|
||||
src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||
src/slic3r/GUI/ConfigWizard.cpp
|
||||
src/slic3r/GUI/DesktopIntegrationDialog.cpp
|
||||
src/slic3r/GUI/TickCodesManager.cpp
|
||||
src/slic3r/GUI/DoubleSliderForLayers.cpp
|
||||
src/slic3r/GUI/Downloader.cpp
|
||||
src/slic3r/GUI/DownloaderFileGet.cpp
|
||||
|
@ -254,6 +254,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Mouse3DController.hpp
|
||||
GUI/ImGuiDoubleSlider.cpp
|
||||
GUI/ImGuiDoubleSlider.hpp
|
||||
GUI/TickCodesManager.cpp
|
||||
GUI/TickCodesManager.hpp
|
||||
GUI/DoubleSliderForLayers.cpp
|
||||
GUI/DoubleSliderForLayers.hpp
|
||||
GUI/DoubleSliderForGcode.cpp
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
#include "DoubleSliderForGcode.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
|
||||
@ -28,6 +26,4 @@ void DSForGcode::Render(const int canvas_width, const int canvas_height, float e
|
||||
|
||||
} // DoubleSlider
|
||||
|
||||
} // Slic3r
|
||||
|
||||
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
class DSForGcode : public Manager<unsigned int>
|
||||
@ -36,7 +34,5 @@ private:
|
||||
|
||||
} // DoubleSlider;
|
||||
|
||||
} // Slic3r
|
||||
|
||||
|
||||
#endif // slic3r_GUI_DoubleSliderForGcode_hpp_
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,34 +5,25 @@
|
||||
#ifndef slic3r_GUI_DoubleSliderForLayers_hpp_
|
||||
#define slic3r_GUI_DoubleSliderForLayers_hpp_
|
||||
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
#include "TickCodesManager.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
class wxMenu;
|
||||
|
||||
namespace Slic3r {
|
||||
class Print;
|
||||
|
||||
using namespace CustomGCode;
|
||||
class PrintObject;
|
||||
class Layer;
|
||||
namespace GUI
|
||||
{
|
||||
class ImGuiWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Slic3r::CustomGCode;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
using namespace GUI;
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
enum FocusedItem {
|
||||
fiNone,
|
||||
fiRevertIcon,
|
||||
@ -46,15 +37,6 @@ enum FocusedItem {
|
||||
fiTick
|
||||
};
|
||||
|
||||
enum ConflictType
|
||||
{
|
||||
ctNone,
|
||||
ctModeConflict,
|
||||
ctMeaninglessColorChange,
|
||||
ctMeaninglessToolChange,
|
||||
ctRedundant
|
||||
};
|
||||
|
||||
enum DrawMode
|
||||
{
|
||||
dmRegular,
|
||||
@ -69,111 +51,6 @@ enum LabelType
|
||||
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{ true };
|
||||
|
||||
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; }
|
||||
bool used_default_colors() const { return m_use_default_colors; }
|
||||
|
||||
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 DSForLayers : public Manager<double>
|
||||
{
|
||||
public:
|
||||
@ -194,17 +71,19 @@ public:
|
||||
|
||||
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
|
||||
|
||||
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);
|
||||
|
||||
void UseDefaultColors(bool def_colors_on);
|
||||
bool IsNewPrint(const std::string& print_obj_idxs);
|
||||
|
||||
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override;
|
||||
|
||||
void set_callback_on_ticks_changed(std::function<void()> cb) { m_cb_ticks_changed = cb; };
|
||||
// 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
|
||||
|
||||
@ -214,82 +93,107 @@ public:
|
||||
void delete_current_tick();
|
||||
// process adding of auto color change
|
||||
void auto_color_change();
|
||||
// jump to selected layer
|
||||
void jump_to_value();
|
||||
|
||||
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:
|
||||
|
||||
void add_code_as_tick(Type type, int selected_extruder = -1);
|
||||
void edit_tick(int tick = -1);
|
||||
void discard_all_thicks();
|
||||
void edit_extruder_sequence();
|
||||
void show_cog_icon_context_menu();
|
||||
|
||||
bool is_wipe_tower_layer(int tick) const;
|
||||
|
||||
std::string get_label(int tick, LabelType label_type = ltHeightWithLayer) const;
|
||||
|
||||
int get_tick_from_value(double value, bool force_lower_bound = false);
|
||||
std::string get_tooltip(int tick = -1);
|
||||
|
||||
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;
|
||||
|
||||
void process_ticks_changed() {
|
||||
if (m_cb_ticks_changed)
|
||||
m_cb_ticks_changed();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
bool check_ticks_changed_event(Type type);
|
||||
|
||||
void append_change_extruder_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME !
|
||||
void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME !
|
||||
|
||||
bool is_osx { false };
|
||||
bool m_allow_editing { true };
|
||||
bool m_is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower
|
||||
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 };
|
||||
|
||||
int m_only_extruder { -1 };
|
||||
|
||||
std::string m_print_obj_idxs;
|
||||
TickCodeManager m_ticks;
|
||||
Slic3r::GUI::ImGuiWrapper* m_imgui { nullptr };
|
||||
|
||||
std::vector<double> m_layers_times;
|
||||
std::vector<double> m_layers_values;
|
||||
std::vector<std::string> m_extruder_colors;
|
||||
|
||||
TickCodeInfo m_ticks;
|
||||
bool is_wipe_tower_layer(int tick) const;
|
||||
|
||||
ExtrudersSequence m_extruders_sequence;
|
||||
std::string get_label(int tick, LabelType label_type) const;
|
||||
|
||||
std::function<void()> m_cb_ticks_changed{ nullptr };
|
||||
std::string get_tooltip(int tick = -1);
|
||||
|
||||
bool m_can_change_color{ true };
|
||||
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 update_draw_scroll_line_cb();
|
||||
void add_code_as_tick(Type type, int selected_extruder = -1);
|
||||
void edit_tick(int tick = -1);
|
||||
void discard_all_thicks();
|
||||
|
||||
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_color_picker { false };
|
||||
bool m_close { false };
|
||||
|
||||
std::string m_print_obj_idxs;
|
||||
std::string m_selectable_color;
|
||||
|
||||
void render_add_tick_menu();
|
||||
void render_multi_extruders_menu();
|
||||
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;
|
||||
|
||||
} // Slic3r
|
||||
|
||||
|
||||
|
||||
#endif // slic3r_GUI_DoubleSliderForLayers_hpp_
|
||||
|
@ -21,8 +21,10 @@
|
||||
#include "libslic3r/PresetBundle.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>
|
||||
@ -34,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"
|
||||
@ -258,11 +261,6 @@ Preview::~Preview()
|
||||
|
||||
if (m_canvas_widget != nullptr)
|
||||
delete m_canvas_widget;
|
||||
|
||||
if (m_layers_slider)
|
||||
delete m_layers_slider;
|
||||
if (m_moves_slider)
|
||||
delete m_moves_slider;
|
||||
}
|
||||
|
||||
void Preview::set_as_dirty()
|
||||
@ -357,33 +355,163 @@ void Preview::on_size(wxSizeEvent& evt)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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 = new DoubleSlider::DSForLayers(0, 0, 0, 100, wxGetApp().is_editor());
|
||||
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->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } );
|
||||
|
||||
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();
|
||||
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_keep_current_preview_type = false;
|
||||
reload_print();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_empty_auto_color_change([]() -> void {
|
||||
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange);
|
||||
});
|
||||
|
||||
m_layers_slider->set_callback_on_get_extruder_colors([]() -> std::vector<std::string> {
|
||||
return wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
||||
});
|
||||
|
||||
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 = new DoubleSlider::DSForGcode(0, 0, 0, 100);
|
||||
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 { Preview::on_moves_slider_scroll_changed(); });
|
||||
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);
|
||||
@ -514,7 +642,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
// 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(get_print_obj_idxs()))
|
||||
m_layers_slider->is_new_print(get_print_obj_idxs()))
|
||||
{
|
||||
const Print& print = wxGetApp().plater()->fff_print();
|
||||
|
||||
@ -805,8 +933,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
||||
// the view type may have been changed by the call m_canvas->load_gcode_preview()
|
||||
gcode_view_type = m_canvas->get_gcode_view_type();
|
||||
zs = m_canvas->get_gcode_layers_zs();
|
||||
if (!zs.empty())
|
||||
m_moves_slider->Show();
|
||||
m_loaded = true;
|
||||
}
|
||||
else if (is_pregcode_preview) {
|
||||
@ -815,12 +941,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_moves_slider->Hide();
|
||||
zs = m_canvas->get_gcode_layers_zs();
|
||||
}
|
||||
else {
|
||||
m_moves_slider->Hide();
|
||||
}
|
||||
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() ?
|
||||
|
@ -20,6 +20,11 @@ class wxComboBox;
|
||||
class wxComboCtrl;
|
||||
class wxCheckBox;
|
||||
|
||||
namespace DoubleSlider {
|
||||
class DSForGcode;
|
||||
class DSForLayers;
|
||||
};
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
@ -27,11 +32,6 @@ class Print;
|
||||
class BackgroundSlicingProcess;
|
||||
class Model;
|
||||
|
||||
namespace DoubleSlider {
|
||||
class DSForGcode;
|
||||
class DSForLayers;
|
||||
};
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
@ -96,8 +96,8 @@ class Preview : public wxPanel
|
||||
|
||||
bool m_loaded { false };
|
||||
|
||||
DoubleSlider::DSForLayers* m_layers_slider{ nullptr };
|
||||
DoubleSlider::DSForGcode* 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
|
||||
|
@ -5,11 +5,8 @@
|
||||
|
||||
#include "ImGuiDoubleSlider.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace DoubleSlider {
|
||||
|
||||
using namespace GUI;
|
||||
|
||||
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);
|
||||
@ -255,6 +252,11 @@ float ImGuiControl::GetPositionInRect(int pos, const ImRect& rect) const
|
||||
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)
|
||||
@ -548,9 +550,5 @@ bool ImGuiControl::render()
|
||||
return result;
|
||||
}
|
||||
|
||||
//} // DoubleSlider
|
||||
|
||||
} // GUI
|
||||
|
||||
} // Slic3r
|
||||
} // DoubleSlider
|
||||
|
||||
|
@ -23,7 +23,6 @@ std::string to_string_with_precision(const T a_value, const int n = 2)
|
||||
return std::move(out).str();
|
||||
}
|
||||
|
||||
namespace Slic3r {
|
||||
namespace DoubleSlider {
|
||||
|
||||
enum SelectedSlider {
|
||||
@ -80,6 +79,7 @@ public:
|
||||
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; }
|
||||
|
||||
@ -192,7 +192,7 @@ public:
|
||||
int minPos,
|
||||
int maxPos,
|
||||
const std::string& name,
|
||||
bool is_horizontal)
|
||||
bool is_horizontal)
|
||||
{
|
||||
Init (lowerPos, higherPos, minPos, maxPos, name, is_horizontal);
|
||||
}
|
||||
@ -257,10 +257,10 @@ protected:
|
||||
int m_em{ 10 };
|
||||
float m_scale{ 1.f };
|
||||
|
||||
std::string get_label(int pos) const {
|
||||
virtual std::string get_label(int pos) const {
|
||||
if (m_values.empty())
|
||||
return std::to_string(pos);
|
||||
if (pos >= m_values.size())
|
||||
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]));
|
||||
}
|
||||
@ -276,10 +276,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
} // DoubleSlider
|
||||
|
||||
|
||||
#endif // slic3r_ImGUI_DoubleSlider_hpp_
|
||||
|
@ -617,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
|
||||
|
@ -148,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 };
|
||||
|
@ -1552,234 +1552,6 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text)
|
||||
}
|
||||
}
|
||||
|
||||
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(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(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(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
|
@ -153,9 +153,6 @@ namespace ImGuiPSWrap
|
||||
ColorRGBA from_ImU32(const ImU32& color);
|
||||
ColorRGBA from_ImVec4(const ImVec4& color);
|
||||
}
|
||||
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);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
704
src/slic3r/GUI/TickCodesManager.cpp
Normal file
704
src/slic3r/GUI/TickCodesManager.cpp
Normal 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
|
||||
|
||||
|
221
src/slic3r/GUI/TickCodesManager.hpp
Normal file
221
src/slic3r/GUI/TickCodesManager.hpp
Normal 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_
|
Loading…
x
Reference in New Issue
Block a user