mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-26 18:21:18 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_labels
This commit is contained in:
commit
5c4b481e35
30 changed files with 846 additions and 270 deletions
|
|
@ -66,6 +66,10 @@ bool Version::is_current_slic3r_supported() const
|
|||
return this->is_slic3r_supported(Slic3r::SEMVER);
|
||||
}
|
||||
|
||||
bool Version::is_current_slic3r_downgrade() const
|
||||
{
|
||||
return Slic3r::SEMVER < min_slic3r_version;
|
||||
}
|
||||
#if 0
|
||||
//TODO: This test should be moved to a unit test, once we have C++ unit tests in place.
|
||||
static int version_test()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct Version
|
|||
|
||||
bool is_slic3r_supported(const Semver &slicer_version) const;
|
||||
bool is_current_slic3r_supported() const;
|
||||
bool is_current_slic3r_downgrade() const;
|
||||
};
|
||||
|
||||
// Index of vendor specific config bundle versions and Slic3r compatibilities.
|
||||
|
|
|
|||
|
|
@ -2001,6 +2001,10 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA);
|
||||
p->add_page(p->page_msla);
|
||||
|
||||
// Pages for 3rd party vendors
|
||||
p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors
|
||||
p->add_page(p->page_vendors = new PageVendors(this));
|
||||
|
||||
p->any_sla_selected = p->check_sla_selected();
|
||||
p->any_fff_selected = p->check_fff_selected();
|
||||
|
||||
|
|
@ -2022,10 +2026,6 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
|||
p->add_page(p->page_diams = new PageDiameters(this));
|
||||
p->add_page(p->page_temps = new PageTemperatures(this));
|
||||
|
||||
// Pages for 3rd party vendors
|
||||
p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors
|
||||
p->add_page(p->page_vendors = new PageVendors(this));
|
||||
|
||||
p->load_pages();
|
||||
p->index->go_to(size_t{0});
|
||||
|
||||
|
|
|
|||
|
|
@ -603,21 +603,24 @@ std::string Control::get_color_for_color_change_tick(std::set<TickCode>::const_i
|
|||
return "";
|
||||
}
|
||||
|
||||
void Control::draw_colored_band(wxDC& dc)
|
||||
wxRect Control::get_colored_band_rect()
|
||||
{
|
||||
if (!m_is_enabled_tick_manipulation)
|
||||
return;
|
||||
|
||||
int height, width;
|
||||
get_size(&width, &height);
|
||||
|
||||
const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width;
|
||||
|
||||
wxRect main_band = is_horizontal() ?
|
||||
wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y),
|
||||
width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) :
|
||||
wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN,
|
||||
lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1);
|
||||
return is_horizontal() ?
|
||||
wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y),
|
||||
width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) :
|
||||
wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN,
|
||||
lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1);
|
||||
}
|
||||
|
||||
void Control::draw_colored_band(wxDC& dc)
|
||||
{
|
||||
if (!m_is_enabled_tick_manipulation)
|
||||
return;
|
||||
|
||||
auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc)
|
||||
{
|
||||
|
|
@ -626,6 +629,8 @@ void Control::draw_colored_band(wxDC& dc)
|
|||
dc.DrawRectangle(band_rc);
|
||||
};
|
||||
|
||||
wxRect main_band = get_colored_band_rect();
|
||||
|
||||
// don't color a band for MultiExtruder mode
|
||||
if (m_ticks.empty() || m_mode == t_mode::MultiExtruder)
|
||||
{
|
||||
|
|
@ -875,87 +880,111 @@ void Control::correct_higher_value()
|
|||
m_lower_value = m_higher_value;
|
||||
}
|
||||
|
||||
wxString Control::get_tooltip(IconFocus icon_focus)
|
||||
wxString Control::get_tooltip(FocusItem focused_item, int tick/*=-1*/)
|
||||
{
|
||||
wxString tooltip(wxEmptyString);
|
||||
if (m_is_one_layer_icon_focesed)
|
||||
tooltip = _(L("One layer mode"));
|
||||
if (focused_item == fiNone)
|
||||
return "";
|
||||
if (focused_item == fiOneLayerIcon)
|
||||
return _(L("One layer mode"));
|
||||
if (focused_item == fiRevertIcon)
|
||||
return _(L("Discard all custom changes"));
|
||||
if (focused_item == fiCogIcon)
|
||||
return _(L("Set extruder sequence for whole print"));
|
||||
if (focused_item == fiColorBand)
|
||||
return m_mode != t_mode::SingleExtruder ? "" :
|
||||
_(L("For edit current color use right mouse button click on colored band"));
|
||||
|
||||
if (icon_focus == ifRevert)
|
||||
tooltip = _(L("Discard all custom changes"));
|
||||
if (icon_focus == ifCog)
|
||||
tooltip = _(L("Set extruder sequence for whole print"));
|
||||
else if (m_is_action_icon_focesed)
|
||||
wxString tooltip;
|
||||
const auto tick_code_it = m_ticks.ticks.find(TickCode{tick});
|
||||
|
||||
if (tick_code_it == m_ticks.ticks.end() && focused_item == fiActionIcon) // tick doesn't exist
|
||||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
const auto tick_code_it = m_ticks.ticks.find(TickCode{tick});
|
||||
// Show mode as a first string of tooltop
|
||||
tooltip = " " + _(L("Slider(print) mode")) + ": ";
|
||||
tooltip += (m_mode == t_mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
m_mode == t_mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
CustomGCode::MultiExtruderMode );
|
||||
tooltip += "\n\n";
|
||||
|
||||
/* Note: just on OSX!!!
|
||||
* Right click event causes a little scrolling.
|
||||
* Right click event causes a little scrolling.
|
||||
* So, as a workaround we use Ctrl+LeftMouseClick instead of RightMouseClick
|
||||
* Show this information in tooltip
|
||||
* */
|
||||
|
||||
if (tick_code_it == m_ticks.ticks.end()) // tick doesn't exist
|
||||
{
|
||||
// Show mode as a first string of tooltop
|
||||
tooltip = " " + _(L("Slider(print) mode")) + ": ";
|
||||
tooltip += (m_mode == t_mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
m_mode == t_mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
CustomGCode::MultiExtruderMode );
|
||||
tooltip += "\n\n";
|
||||
|
||||
// Show list of actions with new tick
|
||||
tooltip += ( m_mode == t_mode::MultiAsSingle ?
|
||||
_(L("For add change extruder use left mouse button click")) :
|
||||
_(L("For add color change use left mouse button click")) ) + " " +
|
||||
_(L("OR pres \"+\" key")) + "\n" + (
|
||||
is_osx ?
|
||||
_(L("For add another code use Ctrl + Left mouse button click")) :
|
||||
_(L("For add another code use right mouse button click")) );
|
||||
}
|
||||
else // tick exists
|
||||
{
|
||||
// Show custom Gcode as a first string of tooltop
|
||||
tooltip = " ";
|
||||
tooltip += tick_code_it->gcode == ColorChangeCode ? (
|
||||
m_mode == t_mode::SingleExtruder ?
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) %
|
||||
tick_code_it->gcode % tick_code_it->extruder).str()) ) :
|
||||
tick_code_it->gcode == PausePrintCode ?
|
||||
from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
tick_code_it->gcode == ToolChangeCode ?
|
||||
from_u8((boost::format(_utf8(L("Extruder(tool) is changed to Extruder \"%1%\""))) % tick_code_it->extruder ).str()) :
|
||||
from_u8((boost::format(_utf8(L("\"%1%\""))) % tick_code_it->gcode ).str()) ;
|
||||
|
||||
// If tick is marked as a conflict (exclamation icon),
|
||||
// we should to explain why
|
||||
ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]);
|
||||
if (conflict != ctNone)
|
||||
tooltip += "\n\n" + _(L("Note")) + "! ";
|
||||
if (conflict == ctModeConflict)
|
||||
tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n"
|
||||
"Any its editing will cause a changes of DoubleSlider data."));
|
||||
else if (conflict == ctMeaninglessColorChange)
|
||||
tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n"
|
||||
"This code wouldn't be processed during GCode generation."));
|
||||
else if (conflict == ctMeaninglessToolChange)
|
||||
tooltip += _(L("There is a extruder change to the same extruder.\n"
|
||||
"This code wouldn't be processed during GCode generation."));
|
||||
else if (conflict == ctRedundant)
|
||||
tooltip += _(L("There is a color change for extruder that has not been used before.\n"
|
||||
"Check your choice to avoid redundant color changes."));
|
||||
|
||||
// Show list of actions with existing tick
|
||||
tooltip += "\n\n" + _(L("For Delete tick use left mouse button click OR pres \"-\" key")) + "\n" + (
|
||||
is_osx ?
|
||||
_(L("For Edit tick use Ctrl + Left mouse button click")) :
|
||||
_(L("For Edit tick use right mouse button click")) );
|
||||
}
|
||||
// Show list of actions with new tick
|
||||
tooltip += ( m_mode == t_mode::MultiAsSingle ?
|
||||
_(L("For add change extruder use left mouse button click")) :
|
||||
m_mode == t_mode::SingleExtruder ?
|
||||
_(L("For add color change use left mouse button click "
|
||||
"if you want to use colors from default color list, "
|
||||
"or Shift + left mouse button click if you want to select a color")) :
|
||||
_(L("For add color change use left mouse button click")) ) + " " +
|
||||
_(L("OR pres \"+\" key")) + "\n" + (
|
||||
is_osx ?
|
||||
_(L("For add another code use Ctrl + left mouse button click")) :
|
||||
_(L("For add another code use right mouse button click")) );
|
||||
}
|
||||
|
||||
if (tick_code_it != m_ticks.ticks.end()) // tick exists
|
||||
{
|
||||
// Show custom Gcode as a first string of tooltop
|
||||
tooltip = " ";
|
||||
tooltip += tick_code_it->gcode == ColorChangeCode ? (
|
||||
m_mode == t_mode::SingleExtruder ?
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) %
|
||||
tick_code_it->gcode % tick_code_it->extruder).str()) ) :
|
||||
tick_code_it->gcode == PausePrintCode ?
|
||||
from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) :
|
||||
tick_code_it->gcode == ToolChangeCode ?
|
||||
from_u8((boost::format(_utf8(L("Extruder(tool) is changed to Extruder \"%1%\""))) % tick_code_it->extruder ).str()) :
|
||||
from_u8((boost::format(_utf8(L("\"%1%\""))) % tick_code_it->gcode ).str()) ;
|
||||
|
||||
// If tick is marked as a conflict (exclamation icon),
|
||||
// we should to explain why
|
||||
ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]);
|
||||
if (conflict != ctNone)
|
||||
tooltip += "\n\n" + _(L("Note")) + "! ";
|
||||
if (conflict == ctModeConflict)
|
||||
tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n"
|
||||
"Any its editing will cause a changes of DoubleSlider data."));
|
||||
else if (conflict == ctMeaninglessColorChange)
|
||||
tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n"
|
||||
"This code wouldn't be processed during GCode generation."));
|
||||
else if (conflict == ctMeaninglessToolChange)
|
||||
tooltip += _(L("There is a extruder change to the same extruder.\n"
|
||||
"This code wouldn't be processed during GCode generation."));
|
||||
else if (conflict == ctRedundant)
|
||||
tooltip += _(L("There is a color change for extruder that has not been used before.\n"
|
||||
"Check your choice to avoid redundant color changes."));
|
||||
|
||||
// Show list of actions with existing tick
|
||||
if (focused_item == fiActionIcon)
|
||||
tooltip += "\n\n" + _(L("For Delete tick use left mouse button click OR pres \"-\" key")) + "\n" + (
|
||||
is_osx ?
|
||||
_(L("For Edit tick use Ctrl + Left mouse button click")) :
|
||||
_(L("For Edit tick use right mouse button click")) );
|
||||
}
|
||||
return tooltip;
|
||||
|
||||
}
|
||||
|
||||
int Control::get_edited_tick_for_position(const wxPoint pos, const std::string& gcode /*= ColorChangeCode*/)
|
||||
{
|
||||
if (m_ticks.empty())
|
||||
return -1;
|
||||
|
||||
int tick = get_value_from_position(pos);
|
||||
auto it = std::lower_bound(m_ticks.ticks.begin(), m_ticks.ticks.end(), TickCode{ tick });
|
||||
|
||||
while (it != m_ticks.ticks.begin()) {
|
||||
--it;
|
||||
if (it->gcode == gcode)
|
||||
return it->tick;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Control::OnMotion(wxMouseEvent& event)
|
||||
|
|
@ -966,14 +995,29 @@ void Control::OnMotion(wxMouseEvent& event)
|
|||
const wxPoint pos = event.GetLogicalPosition(dc);
|
||||
|
||||
m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon);
|
||||
IconFocus icon_focus = ifNone;
|
||||
|
||||
FocusItem focused_item = fiNone;
|
||||
int tick = -1;
|
||||
|
||||
if (!m_is_left_down && !m_is_one_layer) {
|
||||
m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
|
||||
if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon))
|
||||
icon_focus = ifRevert;
|
||||
if (m_is_one_layer_icon_focesed)
|
||||
focused_item = fiOneLayerIcon;
|
||||
else if (m_is_action_icon_focesed) {
|
||||
focused_item = fiActionIcon;
|
||||
tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
}
|
||||
else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon))
|
||||
focused_item = fiRevertIcon;
|
||||
else if (is_point_in_rect(pos, m_rect_cog_icon))
|
||||
icon_focus = ifCog;
|
||||
focused_item = fiCogIcon;
|
||||
else if (m_mode == t_mode::SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) &&
|
||||
get_edited_tick_for_position(pos) >= 0 )
|
||||
focused_item = fiColorBand;
|
||||
else {
|
||||
focused_item = fiTick;
|
||||
tick = get_tick_near_point(pos);
|
||||
}
|
||||
}
|
||||
else if (m_is_left_down || m_is_right_down) {
|
||||
if (m_selection == ssLower) {
|
||||
|
|
@ -994,7 +1038,7 @@ void Control::OnMotion(wxMouseEvent& event)
|
|||
event.Skip();
|
||||
|
||||
// Set tooltips with information for each icon
|
||||
this->SetToolTip(get_tooltip(icon_focus));
|
||||
this->SetToolTip(get_tooltip(focused_item, tick));
|
||||
|
||||
if (action)
|
||||
{
|
||||
|
|
@ -1227,7 +1271,7 @@ void Control::OnRightDown(wxMouseEvent& event)
|
|||
const wxClientDC dc(this);
|
||||
|
||||
wxPoint pos = event.GetLogicalPosition(dc);
|
||||
if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation)
|
||||
if (m_is_enabled_tick_manipulation && is_point_in_rect(pos, m_rect_tick_action))
|
||||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
if (m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick
|
||||
|
|
@ -1239,6 +1283,13 @@ void Control::OnRightDown(wxMouseEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_is_enabled_tick_manipulation && m_mode == t_mode::SingleExtruder &&
|
||||
is_point_in_rect(pos, get_colored_band_rect()))
|
||||
{
|
||||
m_force_color_edit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
detect_selected_slider(event.GetLogicalPosition(dc));
|
||||
if (!m_selection)
|
||||
return;
|
||||
|
|
@ -1409,6 +1460,17 @@ void Control::OnRightUp(wxMouseEvent& event)
|
|||
|
||||
m_show_edit_menu = false;
|
||||
}
|
||||
else if (m_force_color_edit)
|
||||
{
|
||||
const wxClientDC dc(this);
|
||||
wxPoint pos = event.GetLogicalPosition(dc);
|
||||
|
||||
int edited_tick = get_edited_tick_for_position(pos);
|
||||
if (edited_tick >= 0)
|
||||
edit_tick(edited_tick);
|
||||
|
||||
m_force_color_edit = false;
|
||||
}
|
||||
|
||||
Refresh();
|
||||
Update();
|
||||
|
|
@ -1572,9 +1634,10 @@ void Control::delete_current_tick()
|
|||
post_ticks_changed_event(code);
|
||||
}
|
||||
|
||||
void Control::edit_tick()
|
||||
void Control::edit_tick(int tick/* = -1*/)
|
||||
{
|
||||
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
if (tick < 0)
|
||||
tick = m_selection == ssLower ? m_lower_value : m_higher_value;
|
||||
const std::set<TickCode>::iterator it = m_ticks.ticks.find(TickCode{ tick });
|
||||
|
||||
if (it == m_ticks.ticks.end() ||
|
||||
|
|
@ -1896,9 +1959,11 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod
|
|||
if (it == ticks.begin())
|
||||
return tick.extruder == std::max<int>(only_extruder, 1) ? ctMeaninglessToolChange : ctNone;
|
||||
|
||||
--it;
|
||||
if (it->gcode == ToolChangeCode && tick.extruder == it->extruder)
|
||||
return ctMeaninglessToolChange;
|
||||
while (it != ticks.begin()) {
|
||||
--it;
|
||||
if (it->gcode == ToolChangeCode)
|
||||
return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone;
|
||||
}
|
||||
}
|
||||
|
||||
return ctNone;
|
||||
|
|
|
|||
|
|
@ -33,10 +33,14 @@ enum SelectedSlider {
|
|||
ssHigher
|
||||
};
|
||||
|
||||
enum IconFocus {
|
||||
ifNone,
|
||||
ifRevert,
|
||||
ifCog
|
||||
enum FocusItem {
|
||||
fiNone,
|
||||
fiRevertIcon,
|
||||
fiOneLayerIcon,
|
||||
fiCogIcon,
|
||||
fiColorBand,
|
||||
fiActionIcon,
|
||||
fiTick
|
||||
};
|
||||
|
||||
enum ConflictType
|
||||
|
|
@ -221,7 +225,7 @@ public:
|
|||
void add_current_tick(bool call_from_keyboard = false);
|
||||
// delete current tick, when press "-"
|
||||
void delete_current_tick();
|
||||
void edit_tick();
|
||||
void edit_tick(int tick = -1);
|
||||
void edit_extruder_sequence();
|
||||
|
||||
ExtrudersSequence m_extruders_sequence;
|
||||
|
|
@ -259,14 +263,17 @@ private:
|
|||
wxString get_label(const SelectedSlider& selection) const;
|
||||
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
|
||||
int get_value_from_position(const wxCoord x, const wxCoord y);
|
||||
int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); }
|
||||
wxCoord get_position_from_value(const int value);
|
||||
wxSize get_size();
|
||||
void get_size(int *w, int *h);
|
||||
double get_double_value(const SelectedSlider& selection);
|
||||
wxString get_tooltip(IconFocus icon_focus);
|
||||
wxString get_tooltip(FocusItem focused_item, int tick = -1);
|
||||
int get_edited_tick_for_position(wxPoint pos, const std::string& gcode = ColorChangeCode);
|
||||
|
||||
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
|
||||
wxRect get_colored_band_rect();
|
||||
|
||||
// Get active extruders for tick.
|
||||
// Means one current extruder for not existing tick OR
|
||||
|
|
@ -312,6 +319,7 @@ private:
|
|||
bool m_force_mode_apply = true;
|
||||
bool m_force_add_tick = false;
|
||||
bool m_force_delete_tick = false;
|
||||
bool m_force_color_edit = false;
|
||||
t_mode m_mode = t_mode::SingleExtruder;
|
||||
int m_only_extruder = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -690,7 +690,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
|||
case ObjectOutside : text = L("An object outside the print area was detected"); break;
|
||||
case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break;
|
||||
case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break;
|
||||
case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break;
|
||||
case SomethingNotShown : text = L("Some objects are not visible"); break;
|
||||
case ObjectClashed: {
|
||||
text = L("An object outside the print area was detected\n"
|
||||
"Resolve the current problem to continue slicing");
|
||||
|
|
@ -1384,6 +1384,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event<int>);
|
|||
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event<bool>);
|
||||
|
|
@ -2849,8 +2850,127 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
}
|
||||
}
|
||||
|
||||
class TranslationProcessor
|
||||
{
|
||||
using UpAction = std::function<void(void)>;
|
||||
using DownAction = std::function<void(const Vec3d&, bool, bool)>;
|
||||
|
||||
UpAction m_up_action{ nullptr };
|
||||
DownAction m_down_action{ nullptr };
|
||||
|
||||
bool m_running{ false };
|
||||
Vec3d m_direction{ Vec3d::UnitX() };
|
||||
|
||||
public:
|
||||
TranslationProcessor(UpAction up_action, DownAction down_action)
|
||||
: m_up_action(up_action), m_down_action(down_action)
|
||||
{
|
||||
}
|
||||
|
||||
void process(wxKeyEvent& evt)
|
||||
{
|
||||
const int keyCode = evt.GetKeyCode();
|
||||
wxEventType type = evt.GetEventType();
|
||||
if (type == wxEVT_KEY_UP) {
|
||||
switch (keyCode)
|
||||
{
|
||||
case WXK_NUMPAD_LEFT: case WXK_LEFT:
|
||||
case WXK_NUMPAD_RIGHT: case WXK_RIGHT:
|
||||
case WXK_NUMPAD_UP: case WXK_UP:
|
||||
case WXK_NUMPAD_DOWN: case WXK_DOWN:
|
||||
{
|
||||
m_running = false;
|
||||
m_up_action();
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
else if (type == wxEVT_KEY_DOWN) {
|
||||
bool apply = false;
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case WXK_SHIFT:
|
||||
{
|
||||
if (m_running)
|
||||
apply = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_LEFT:
|
||||
case WXK_LEFT:
|
||||
{
|
||||
m_direction = -Vec3d::UnitX();
|
||||
apply = true;
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_RIGHT:
|
||||
case WXK_RIGHT:
|
||||
{
|
||||
m_direction = Vec3d::UnitX();
|
||||
apply = true;
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_UP:
|
||||
case WXK_UP:
|
||||
{
|
||||
m_direction = Vec3d::UnitY();
|
||||
apply = true;
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_DOWN:
|
||||
case WXK_DOWN:
|
||||
{
|
||||
m_direction = -Vec3d::UnitY();
|
||||
apply = true;
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
if (apply) {
|
||||
m_running = true;
|
||||
m_down_action(m_direction, evt.ShiftDown(), evt.CmdDown());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
{
|
||||
static TranslationProcessor translationProcessor(
|
||||
[this]() {
|
||||
do_move(L("Gizmo-Move"));
|
||||
m_gizmos.update_data();
|
||||
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
// Let the plater know that the dragging finished, so a delayed refresh
|
||||
// of the scene with the background processing data should be performed.
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||
// updates camera target constraints
|
||||
refresh_camera_scene_box();
|
||||
m_dirty = true;
|
||||
},
|
||||
[this](const Vec3d& direction, bool slow, bool camera_space) {
|
||||
m_selection.start_dragging();
|
||||
double multiplier = slow ? 1.0 : 10.0;
|
||||
|
||||
Vec3d displacement;
|
||||
if (camera_space)
|
||||
{
|
||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> inv_view_3x3 = m_camera.get_view_matrix().inverse().matrix().block(0, 0, 3, 3);
|
||||
displacement = multiplier * (inv_view_3x3 * direction);
|
||||
displacement(2) = 0.0;
|
||||
}
|
||||
else
|
||||
displacement = multiplier * direction;
|
||||
|
||||
m_selection.translate(displacement);
|
||||
m_dirty = true;
|
||||
}
|
||||
);
|
||||
|
||||
const int keyCode = evt.GetKeyCode();
|
||||
|
||||
auto imgui = wxGetApp().imgui();
|
||||
|
|
@ -2869,6 +2989,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
}
|
||||
else if (keyCode == WXK_SHIFT)
|
||||
{
|
||||
translationProcessor.process(evt);
|
||||
|
||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||
{
|
||||
_update_selection_from_hover();
|
||||
|
|
@ -2892,26 +3014,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
else if (keyCode == WXK_CONTROL)
|
||||
m_dirty = true;
|
||||
else if (m_gizmos.is_enabled() && !m_selection.is_empty()) {
|
||||
translationProcessor.process(evt);
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case WXK_NUMPAD_LEFT: case WXK_LEFT:
|
||||
case WXK_NUMPAD_RIGHT: case WXK_RIGHT:
|
||||
case WXK_NUMPAD_UP: case WXK_UP:
|
||||
case WXK_NUMPAD_DOWN: case WXK_DOWN:
|
||||
{
|
||||
do_move(L("Gizmo-Move"));
|
||||
m_gizmos.update_data();
|
||||
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
// Let the plater know that the dragging finished, so a delayed refresh
|
||||
// of the scene with the background processing data should be performed.
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||
// updates camera target constraints
|
||||
refresh_camera_scene_box();
|
||||
m_dirty = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP:
|
||||
case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN:
|
||||
{
|
||||
|
|
@ -2936,6 +3042,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers();
|
||||
if (keyCode == WXK_SHIFT)
|
||||
{
|
||||
translationProcessor.process(evt);
|
||||
|
||||
if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports))
|
||||
{
|
||||
m_mouse.ignore_left_up = false;
|
||||
|
|
@ -2954,12 +3062,6 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
m_dirty = true;
|
||||
else if (m_gizmos.is_enabled() && !m_selection.is_empty())
|
||||
{
|
||||
auto do_move = [this](const Vec3d& displacement) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.translate(displacement);
|
||||
m_dirty = true;
|
||||
// wxGetApp().obj_manipul()->set_dirty();
|
||||
};
|
||||
auto do_rotate = [this](double angle_z_rad) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
|
||||
|
|
@ -2967,12 +3069,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
// wxGetApp().obj_manipul()->set_dirty();
|
||||
};
|
||||
|
||||
translationProcessor.process(evt);
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case WXK_NUMPAD_LEFT: case WXK_LEFT: { do_move(-Vec3d::UnitX()); break; }
|
||||
case WXK_NUMPAD_RIGHT: case WXK_RIGHT: { do_move(Vec3d::UnitX()); break; }
|
||||
case WXK_NUMPAD_UP: case WXK_UP: { do_move(Vec3d::UnitY()); break; }
|
||||
case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-Vec3d::UnitY()); break; }
|
||||
case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { do_rotate(0.25 * M_PI); break; }
|
||||
case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; }
|
||||
default: { break; }
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event<int>); // data: +1 => increase, -1 => decrease
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent);
|
||||
|
|
|
|||
|
|
@ -278,34 +278,23 @@ bool GUI_App::on_init_inner()
|
|||
RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
|
||||
#endif
|
||||
|
||||
// Preset updating & Configwizard are done after the above initializations,
|
||||
// and after MainFrame is created & shown.
|
||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||
// to popup a modal dialog on start without screwing combo boxes.
|
||||
// This is ugly but I honestly found no better way to do it.
|
||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||
// Preset updating & Configwizard are done after the above initializations,
|
||||
// and after MainFrame is created & shown.
|
||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||
// to popup a modal dialog on start without screwing combo boxes.
|
||||
// This is ugly but I honestly found no better way to do it.
|
||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||
|
||||
static bool once = true;
|
||||
if (once) {
|
||||
once = false;
|
||||
check_updates(false);
|
||||
|
||||
PresetUpdater::UpdateResult updater_result;
|
||||
try {
|
||||
updater_result = preset_updater->config_update(app_config->orig_version());
|
||||
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
|
||||
mainframe->Close();
|
||||
} else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) {
|
||||
app_conf_exists = true;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, from_u8(ex.what()));
|
||||
}
|
||||
|
||||
CallAfter([this] {
|
||||
config_wizard_startup();
|
||||
preset_updater->slic3r_update_notify();
|
||||
preset_updater->sync(preset_bundle);
|
||||
});
|
||||
|
||||
CallAfter([this] {
|
||||
config_wizard_startup();
|
||||
preset_updater->slic3r_update_notify();
|
||||
preset_updater->sync(preset_bundle);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -810,7 +799,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration &Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots +
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -841,6 +830,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
case ConfigMenuWizard:
|
||||
run_wizard(ConfigWizard::RR_USER);
|
||||
break;
|
||||
case ConfigMenuUpdate:
|
||||
check_updates(true);
|
||||
break;
|
||||
case ConfigMenuTakeSnapshot:
|
||||
// Take a configuration snapshot.
|
||||
if (check_unsaved_changes()) {
|
||||
|
|
@ -1230,6 +1222,30 @@ bool GUI_App::config_wizard_startup()
|
|||
return false;
|
||||
}
|
||||
|
||||
void GUI_App::check_updates(const bool verbose)
|
||||
{
|
||||
|
||||
PresetUpdater::UpdateResult updater_result;
|
||||
try {
|
||||
updater_result = preset_updater->config_update(app_config->orig_version());
|
||||
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
|
||||
mainframe->Close();
|
||||
}
|
||||
else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) {
|
||||
app_conf_exists = true;
|
||||
}
|
||||
else if(verbose && updater_result == PresetUpdater::R_NOOP)
|
||||
{
|
||||
MsgNoUpdates dlg;
|
||||
dlg.ShowModal();
|
||||
}
|
||||
}
|
||||
catch (const std::exception & ex) {
|
||||
show_error(nullptr, from_u8(ex.what()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// static method accepting a wxWindow object as first parameter
|
||||
// void warning_catcher{
|
||||
// my($self, $message_dialog) = @_;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ private:
|
|||
bool select_language();
|
||||
|
||||
bool config_wizard_startup();
|
||||
void check_updates(const bool verbose);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
void associate_3mf_files();
|
||||
|
|
|
|||
|
|
@ -358,12 +358,14 @@ bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* mode
|
|||
m_backend_mesh_transformed = po->get_mesh_to_print();
|
||||
m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse());
|
||||
m_mesh = &m_backend_mesh_transformed;
|
||||
m_has_drilled_mesh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_mesh) {
|
||||
m_mesh = &m_model_object->volumes.front()->mesh();
|
||||
m_backend_mesh_transformed.clear();
|
||||
m_has_drilled_mesh = false;
|
||||
}
|
||||
|
||||
m_model_object_id = m_model_object->id();
|
||||
|
|
|
|||
|
|
@ -227,10 +227,13 @@ public:
|
|||
m_clipping_plane_distance = m_clipping_plane_distance_stash;
|
||||
}
|
||||
|
||||
bool has_drilled_mesh() const { return m_has_drilled_mesh; }
|
||||
|
||||
private:
|
||||
const TriangleMesh* m_old_mesh;
|
||||
TriangleMesh m_backend_mesh_transformed;
|
||||
float m_clipping_plane_distance_stash = 0.f;
|
||||
bool m_has_drilled_mesh = false;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
|
|||
{
|
||||
if (! m_c->m_mesh_raycaster)
|
||||
return false;
|
||||
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
// !!! is it really necessary?
|
||||
//m_c->update_from_backend(m_parent, m_c->m_model_object);
|
||||
|
|
@ -344,6 +345,19 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
|
|||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) {
|
||||
|
||||
// User is about to manipulate a hole. If the gizmo currently shows drilled mesh,
|
||||
// invalidate slaposDrillHoles so it returns to normal. To do this, hackishly
|
||||
// add a hole, force SLAPrint::apply call that will invalidate the step because
|
||||
// of it and then remove the hole again.
|
||||
if (m_c->has_drilled_mesh()) {
|
||||
m_c->m_model_object->sla_drain_holes.push_back(sla::DrainHole());
|
||||
m_selected.push_back(false);
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
|
||||
wxGetApp().CallAfter([this] { m_c->m_model_object->sla_drain_holes.pop_back(); m_selected.pop_back(); });
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit, normal);
|
||||
return true;
|
||||
|
|
@ -513,6 +527,8 @@ void GLGizmoHollow::delete_selected_points()
|
|||
}
|
||||
}
|
||||
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
|
||||
|
||||
select_point(NoPoints);
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +668,7 @@ void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit)
|
|||
|
||||
bool first_run = true; // This is a hack to redraw the button when all points are removed,
|
||||
// so it is not delayed until the background process finishes.
|
||||
|
||||
RENDER_AGAIN:
|
||||
const float approx_height = m_imgui->scaled(20.0f);
|
||||
y = std::min(y, bottom_limit - approx_height);
|
||||
|
|
@ -677,6 +694,8 @@ RENDER_AGAIN:
|
|||
|
||||
if (m_imgui->button(m_desc["preview"]))
|
||||
hollow_mesh();
|
||||
|
||||
bool config_changed = false;
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
|
@ -686,6 +705,7 @@ RENDER_AGAIN:
|
|||
if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) {
|
||||
m_c->m_model_object->config.opt<ConfigOptionBool>("hollowing_enable", true)->value = m_enable_hollowing;
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
config_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -761,8 +781,10 @@ RENDER_AGAIN:
|
|||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_min_thickness", true)->value = offset;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_quality", true)->value = quality;
|
||||
m_c->m_model_object->config.opt<ConfigOptionFloat>("hollowing_closing_distance", true)->value = closing_d;
|
||||
if (slider_released)
|
||||
if (slider_released) {
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
config_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_imgui->disabled_end();
|
||||
|
|
@ -887,6 +909,9 @@ RENDER_AGAIN:
|
|||
|
||||
if (force_refresh)
|
||||
m_parent.set_as_dirty();
|
||||
|
||||
if (config_changed)
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
|
||||
}
|
||||
|
||||
bool GLGizmoHollow::on_is_activable() const
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
}
|
||||
|
||||
// Now render the drain holes:
|
||||
/*if (! m_c->m_cavity_mesh) {
|
||||
if (! m_c->has_drilled_mesh()) {
|
||||
render_color[0] = 0.7f;
|
||||
render_color[1] = 0.7f;
|
||||
render_color[2] = 0.7f;
|
||||
|
|
@ -370,7 +370,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
glFrontFace(GL_CCW);
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (!picking)
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
|
|
@ -414,14 +414,14 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
|
|||
// In case the hollowed and drilled mesh is available, we can allow
|
||||
// placing points in holes, because they should never end up
|
||||
// on surface that's been drilled away.
|
||||
/*if (! m_c->m_cavity_mesh) {
|
||||
if (! m_c->has_drilled_mesh()) {
|
||||
for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) {
|
||||
if (hole.is_inside(hit)) {
|
||||
in_hole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
if (! in_hole) {
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit, normal);
|
||||
|
|
@ -1291,6 +1291,8 @@ bool GLGizmoSlaSupports::unsaved_changes() const
|
|||
|
||||
void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const
|
||||
{
|
||||
if (! m_c->m_model_object)
|
||||
return;
|
||||
Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ?
|
||||
m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward());
|
||||
|
||||
|
|
|
|||
|
|
@ -2067,6 +2067,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int> &evt)
|
||||
{ if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); });
|
||||
|
|
@ -5384,6 +5385,13 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::set_bed_shape() const
|
||||
{
|
||||
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
|
||||
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
|
||||
p->config->option<ConfigOptionString>("bed_custom_model")->value);
|
||||
}
|
||||
|
||||
void Plater::force_filament_colors_update()
|
||||
{
|
||||
bool update_scheduled = false;
|
||||
|
|
|
|||
|
|
@ -289,6 +289,8 @@ public:
|
|||
const Mouse3DController& get_mouse3d_controller() const;
|
||||
Mouse3DController& get_mouse3d_controller();
|
||||
|
||||
void set_bed_shape() const;
|
||||
|
||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||
class SuppressSnapshots
|
||||
{
|
||||
|
|
|
|||
|
|
@ -142,6 +142,71 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates) :
|
|||
|
||||
MsgUpdateConfig::~MsgUpdateConfig() {}
|
||||
|
||||
//MsgUpdateForced
|
||||
|
||||
MsgUpdateForced::MsgUpdateForced(const std::vector<Update>& updates) :
|
||||
MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("Configuration update is necessary to install")), wxID_NONE)
|
||||
{
|
||||
auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L(
|
||||
"%s will now start updates. Otherwise it won't be able to start.\n\n"
|
||||
"Note that a full configuration snapshot will be created first. It can then be restored at any time "
|
||||
"should there be a problem with the new version.\n\n"
|
||||
"Updated configuration bundles:"
|
||||
)), SLIC3R_APP_NAME));
|
||||
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
|
||||
|
||||
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
const auto lang_code = wxGetApp().current_language_code_safe().ToStdString();
|
||||
|
||||
auto* versions = new wxBoxSizer(wxVERTICAL);
|
||||
for (const auto& update : updates) {
|
||||
auto* flex = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
|
||||
auto* text_vendor = new wxStaticText(this, wxID_ANY, update.vendor);
|
||||
text_vendor->SetFont(boldfont);
|
||||
flex->Add(text_vendor);
|
||||
flex->Add(new wxStaticText(this, wxID_ANY, update.version.to_string()));
|
||||
|
||||
if (!update.comment.empty()) {
|
||||
flex->Add(new wxStaticText(this, wxID_ANY, _(L("Comment:"))), 0, wxALIGN_RIGHT);
|
||||
auto* update_comment = new wxStaticText(this, wxID_ANY, from_u8(update.comment));
|
||||
update_comment->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
|
||||
flex->Add(update_comment);
|
||||
}
|
||||
|
||||
versions->Add(flex);
|
||||
|
||||
if (!update.changelog_url.empty() && update.version.prerelease() == nullptr) {
|
||||
auto* line = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto changelog_url = (boost::format(update.changelog_url) % lang_code).str();
|
||||
line->AddSpacer(3 * VERT_SPACING);
|
||||
line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url));
|
||||
versions->Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2 * VERT_SPACING);
|
||||
|
||||
auto* btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME));
|
||||
btn_sizer->Add(btn_exit);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
auto* btn_ok = new wxButton(this, wxID_OK);
|
||||
btn_sizer->Add(btn_ok);
|
||||
btn_ok->SetFocus();
|
||||
|
||||
auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); };
|
||||
btn_exit->Bind(wxEVT_BUTTON, exiter);
|
||||
btn_ok->Bind(wxEVT_BUTTON, exiter);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgUpdateForced::~MsgUpdateForced() {}
|
||||
|
||||
// MsgDataIncompatible
|
||||
|
||||
|
|
@ -157,7 +222,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, w
|
|||
|
||||
"You may either exit %s and try again with a newer version, or you may re-run the initial configuration. "
|
||||
"Doing so will create a backup snapshot of the existing configuration before installing files compatible with this %s.")) + "\n",
|
||||
SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME));
|
||||
SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME, SLIC3R_APP_NAME));
|
||||
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
|
||||
content_sizer->Add(text);
|
||||
|
||||
|
|
@ -236,5 +301,28 @@ MsgDataLegacy::MsgDataLegacy() :
|
|||
MsgDataLegacy::~MsgDataLegacy() {}
|
||||
|
||||
|
||||
// MsgNoUpdate
|
||||
|
||||
MsgNoUpdates::MsgNoUpdates() :
|
||||
MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates aviable")))
|
||||
{
|
||||
|
||||
auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(
|
||||
_(L(
|
||||
"%s has no configuration updates aviable."
|
||||
)),
|
||||
SLIC3R_APP_NAME, ConfigWizard::name()
|
||||
));
|
||||
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgNoUpdates::~MsgNoUpdates() {}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,33 @@ public:
|
|||
~MsgUpdateConfig();
|
||||
};
|
||||
|
||||
// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action.
|
||||
class MsgUpdateForced : public MsgDialog
|
||||
{
|
||||
public:
|
||||
struct Update
|
||||
{
|
||||
std::string vendor;
|
||||
Semver version;
|
||||
std::string comment;
|
||||
std::string changelog_url;
|
||||
|
||||
Update(std::string vendor, Semver version, std::string comment, std::string changelog_url)
|
||||
: vendor(std::move(vendor))
|
||||
, version(std::move(version))
|
||||
, comment(std::move(comment))
|
||||
, changelog_url(std::move(changelog_url))
|
||||
{}
|
||||
};
|
||||
|
||||
MsgUpdateForced(const std::vector<Update>& updates);
|
||||
MsgUpdateForced(MsgUpdateForced&&) = delete;
|
||||
MsgUpdateForced(const MsgUpdateForced&) = delete;
|
||||
MsgUpdateForced& operator=(MsgUpdateForced&&) = delete;
|
||||
MsgUpdateForced& operator=(const MsgUpdateForced&) = delete;
|
||||
~MsgUpdateForced();
|
||||
};
|
||||
|
||||
// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action.
|
||||
class MsgDataIncompatible : public MsgDialog
|
||||
{
|
||||
|
|
@ -87,6 +114,17 @@ public:
|
|||
~MsgDataLegacy();
|
||||
};
|
||||
|
||||
// Informs about absence of bundles requiring update.
|
||||
class MsgNoUpdates : public MsgDialog
|
||||
{
|
||||
public:
|
||||
MsgNoUpdates();
|
||||
MsgNoUpdates(MsgNoUpdates&&) = delete;
|
||||
MsgNoUpdates(const MsgNoUpdates&) = delete;
|
||||
MsgNoUpdates& operator=(MsgNoUpdates&&) = delete;
|
||||
MsgNoUpdates& operator=(const MsgNoUpdates&) = delete;
|
||||
~MsgNoUpdates();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,13 +73,16 @@ struct Update
|
|||
std::string vendor;
|
||||
std::string changelog_url;
|
||||
|
||||
bool forced_update;
|
||||
|
||||
Update() {}
|
||||
Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url)
|
||||
Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url, bool forced = false)
|
||||
: source(std::move(source))
|
||||
, target(std::move(target))
|
||||
, version(version)
|
||||
, vendor(std::move(vendor))
|
||||
, changelog_url(std::move(changelog_url))
|
||||
, forced_update(forced)
|
||||
{}
|
||||
|
||||
void install() const
|
||||
|
|
@ -297,6 +300,12 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
|
|||
const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME;
|
||||
const std::string idx_path = (cache_path / (vendor.id + ".idx")).string();
|
||||
const std::string idx_path_temp = idx_path + "-update";
|
||||
//check if idx_url is leading to our site
|
||||
if (! boost::starts_with(idx_url, "http://files.prusa3d.com/wp-content/uploads/repository/"))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor \"" << vendor.name << "\" rejected: " << idx_url;
|
||||
continue;
|
||||
}
|
||||
if (!get_file(idx_url, idx_path_temp)) { continue; }
|
||||
if (cancel) { return; }
|
||||
|
||||
|
|
@ -418,11 +427,16 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ver_current_found && !ver_current->is_current_slic3r_supported()) {
|
||||
// "Reconfigure" situation.
|
||||
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
|
||||
updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name);
|
||||
continue;
|
||||
bool current_not_supported = false; //if slcr is incompatible but situation is not downgrade, we do forced updated and this bool is information to do it
|
||||
|
||||
if (ver_current_found && !ver_current->is_current_slic3r_supported()){
|
||||
if(ver_current->is_current_slic3r_downgrade()) {
|
||||
// "Reconfigure" situation.
|
||||
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
|
||||
updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name);
|
||||
continue;
|
||||
}
|
||||
current_not_supported = true;
|
||||
}
|
||||
|
||||
if (recommended->config_version < vp.config_version) {
|
||||
|
|
@ -462,7 +476,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
if (new_vp.config_version == recommended->config_version) {
|
||||
// The config bundle from the cache directory matches the recommended version of the index from the cache directory.
|
||||
// This is the newest known recommended config. Use it.
|
||||
new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url);
|
||||
new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported);
|
||||
// and install the config index from the cache into vendor's directory.
|
||||
bundle_path_idx_to_install = idx.path();
|
||||
found = true;
|
||||
|
|
@ -492,7 +506,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
}
|
||||
recommended = rsrc_idx.recommended();
|
||||
if (recommended != rsrc_idx.end() && recommended->config_version == rsrc_vp.config_version && recommended->config_version > vp.config_version) {
|
||||
new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url);
|
||||
new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported);
|
||||
bundle_path_idx_to_install = path_idx_in_rsrc;
|
||||
found = true;
|
||||
} else {
|
||||
|
|
@ -513,24 +527,27 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||
// Find a recommended config bundle version for the slic3r version last executed. This makes sure that a config bundle update will not be missed
|
||||
// when upgrading an application. On the other side, the user will be bugged every time he will switch between slic3r versions.
|
||||
const auto existing_recommended = existing_idx.recommended(old_slic3r_version);
|
||||
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
|
||||
/*if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
|
||||
// The user has already seen (and presumably rejected) this update
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
|
||||
continue;
|
||||
}
|
||||
}*/
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Cannot load the installed index at `%1%`: %2%") % bundle_path_idx % err.what();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the update is already present in a snapshot
|
||||
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
|
||||
if (recommended_snap != SnapshotDB::singleton().end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...")
|
||||
% vp.name
|
||||
% recommended->config_version.to_string()
|
||||
% recommended_snap->id;
|
||||
continue;
|
||||
if(!current_not_supported)
|
||||
{
|
||||
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
|
||||
if (recommended_snap != SnapshotDB::singleton().end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...")
|
||||
% vp.name
|
||||
% recommended->config_version.to_string()
|
||||
% recommended_snap->id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
updates.updates.emplace_back(std::move(new_update));
|
||||
|
|
@ -554,14 +571,17 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE);
|
||||
}
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size();
|
||||
|
||||
for (auto &incompat : updates.incompats) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << incompat;
|
||||
incompat.remove();
|
||||
}
|
||||
|
||||
|
||||
} else if (updates.updates.size() > 0) {
|
||||
|
||||
if (snapshot) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE);
|
||||
|
|
@ -688,6 +708,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
);
|
||||
} else if (min_slic3r != Semver::zero()) {
|
||||
restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string());
|
||||
BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen.";
|
||||
} else {
|
||||
restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string());
|
||||
}
|
||||
|
|
@ -704,16 +725,59 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
|
|||
// (snapshot is taken beforehand)
|
||||
p->perform_updates(std::move(updates));
|
||||
|
||||
if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) {
|
||||
if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) {
|
||||
return R_INCOMPAT_EXIT;
|
||||
}
|
||||
|
||||
return R_INCOMPAT_CONFIGURED;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
|
||||
return R_INCOMPAT_EXIT;
|
||||
}
|
||||
|
||||
} else if (updates.updates.size() > 0) {
|
||||
|
||||
bool incompatible_version = false;
|
||||
for (const auto& update : updates.updates) {
|
||||
incompatible_version = (update.forced_update ? true : incompatible_version);
|
||||
//td::cout << update.forced_update << std::endl;
|
||||
//BOOST_LOG_TRIVIAL(info) << boost::format("Update requires higher version.");
|
||||
}
|
||||
|
||||
//forced update
|
||||
if(incompatible_version)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. At least one requires higher version of Slicer.") % updates.updates.size();
|
||||
|
||||
std::vector<GUI::MsgUpdateForced::Update> updates_msg;
|
||||
for (const auto& update : updates.updates) {
|
||||
std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
|
||||
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
|
||||
}
|
||||
|
||||
GUI::MsgUpdateForced dlg(updates_msg);
|
||||
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_OK) {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
|
||||
|
||||
p->perform_updates(std::move(updates));
|
||||
|
||||
// Reload global configuration
|
||||
auto* app_config = GUI::wxGetApp().app_config;
|
||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
||||
GUI::wxGetApp().load_current_presets();
|
||||
GUI::wxGetApp().plater()->set_bed_shape();
|
||||
return R_UPDATE_INSTALLED;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
|
||||
return R_INCOMPAT_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
// regular update
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size();
|
||||
|
||||
std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue