mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 08:11:11 -06:00
Merge remote-tracking branch 'origin/master' into ys_sla_time_estimation
This commit is contained in:
commit
fb8c66f223
61 changed files with 5158 additions and 3475 deletions
|
@ -793,7 +793,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
|
|||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const
|
||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func) const
|
||||
{
|
||||
glsafe(glEnable(GL_BLEND));
|
||||
glsafe(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
@ -805,7 +805,7 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface)
|
|||
glsafe(glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function<bool(const GLVolume&)>());
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
|
||||
for (GLVolumeWithZ& volume : to_render)
|
||||
{
|
||||
volume.first->set_render_color();
|
||||
|
|
|
@ -456,7 +456,7 @@ public:
|
|||
|
||||
// Render the volumes by OpenGL.
|
||||
void render_VBOs(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||
void render_legacy(ERenderType type, bool disable_cullface) const;
|
||||
void render_legacy(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,30 +16,30 @@ namespace GUI {
|
|||
class ConfigWizard: public wxDialog
|
||||
{
|
||||
public:
|
||||
// Why is the Wizard run
|
||||
enum RunReason {
|
||||
RR_DATA_EMPTY, // No or empty datadir
|
||||
RR_DATA_LEGACY, // Pre-updating datadir
|
||||
RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation
|
||||
RR_USER, // User requested the Wizard from the menus
|
||||
};
|
||||
// Why is the Wizard run
|
||||
enum RunReason {
|
||||
RR_DATA_EMPTY, // No or empty datadir
|
||||
RR_DATA_LEGACY, // Pre-updating datadir
|
||||
RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation
|
||||
RR_USER, // User requested the Wizard from the menus
|
||||
};
|
||||
|
||||
ConfigWizard(wxWindow *parent, RunReason run_reason);
|
||||
ConfigWizard(ConfigWizard &&) = delete;
|
||||
ConfigWizard(const ConfigWizard &) = delete;
|
||||
ConfigWizard &operator=(ConfigWizard &&) = delete;
|
||||
ConfigWizard &operator=(const ConfigWizard &) = delete;
|
||||
~ConfigWizard();
|
||||
ConfigWizard(wxWindow *parent, RunReason run_reason);
|
||||
ConfigWizard(ConfigWizard &&) = delete;
|
||||
ConfigWizard(const ConfigWizard &) = delete;
|
||||
ConfigWizard &operator=(ConfigWizard &&) = delete;
|
||||
ConfigWizard &operator=(const ConfigWizard &) = delete;
|
||||
~ConfigWizard();
|
||||
|
||||
// Run the Wizard. Return whether it was completed.
|
||||
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
// Run the Wizard. Return whether it was completed.
|
||||
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
|
||||
static const wxString& name(const bool from_menu = false);
|
||||
static const wxString& name(const bool from_menu = false);
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
friend struct ConfigWizardPage;
|
||||
friend struct ConfigWizardPage;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include <wx/button.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
|
@ -26,211 +28,264 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
enum {
|
||||
WRAP_WIDTH = 500,
|
||||
MODEL_MIN_WRAP = 150,
|
||||
WRAP_WIDTH = 500,
|
||||
MODEL_MIN_WRAP = 150,
|
||||
|
||||
DIALOG_MARGIN = 15,
|
||||
INDEX_MARGIN = 40,
|
||||
BTN_SPACING = 10,
|
||||
INDENT_SPACING = 30,
|
||||
VERTICAL_SPACING = 10,
|
||||
DIALOG_MARGIN = 15,
|
||||
INDEX_MARGIN = 40,
|
||||
BTN_SPACING = 10,
|
||||
INDENT_SPACING = 30,
|
||||
VERTICAL_SPACING = 10,
|
||||
|
||||
MAX_COLS = 4,
|
||||
ROW_SPACING = 75,
|
||||
};
|
||||
|
||||
typedef std::function<bool(const VendorProfile::PrinterModel&)> ModelFilter;
|
||||
|
||||
struct PrinterPicker: wxPanel
|
||||
{
|
||||
struct Checkbox : wxCheckBox
|
||||
{
|
||||
Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
|
||||
wxCheckBox(parent, wxID_ANY, label),
|
||||
model(model),
|
||||
variant(variant)
|
||||
{}
|
||||
struct Checkbox : wxCheckBox
|
||||
{
|
||||
Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
|
||||
wxCheckBox(parent, wxID_ANY, label),
|
||||
model(model),
|
||||
variant(variant)
|
||||
{}
|
||||
|
||||
std::string model;
|
||||
std::string variant;
|
||||
};
|
||||
std::string model;
|
||||
std::string variant;
|
||||
};
|
||||
|
||||
const std::string vendor_id;
|
||||
std::vector<Checkbox*> cboxes;
|
||||
unsigned variants_checked;
|
||||
const std::string vendor_id;
|
||||
std::vector<Checkbox*> cboxes;
|
||||
std::vector<Checkbox*> cboxes_alt;
|
||||
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors);
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter);
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors);
|
||||
|
||||
void select_all(bool select);
|
||||
void select_one(size_t i, bool select);
|
||||
void on_checkbox(const Checkbox *cbox, bool checked);
|
||||
void select_all(bool select, bool alternates = false);
|
||||
void select_one(size_t i, bool select);
|
||||
void on_checkbox(const Checkbox *cbox, bool checked);
|
||||
|
||||
int get_width() const { return width; }
|
||||
private:
|
||||
int width;
|
||||
};
|
||||
|
||||
struct ConfigWizardPage: wxPanel
|
||||
{
|
||||
ConfigWizard *parent;
|
||||
const wxString shortname;
|
||||
wxBoxSizer *content;
|
||||
ConfigWizard *parent;
|
||||
const wxString shortname;
|
||||
wxBoxSizer *content;
|
||||
const unsigned indent;
|
||||
|
||||
ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname);
|
||||
ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname, unsigned indent = 0);
|
||||
virtual ~ConfigWizardPage();
|
||||
|
||||
virtual ~ConfigWizardPage();
|
||||
template<class T>
|
||||
void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
|
||||
{
|
||||
content->Add(thing, proportion, flag, border);
|
||||
}
|
||||
|
||||
ConfigWizardPage* page_prev() const { return p_prev; }
|
||||
ConfigWizardPage* page_next() const { return p_next; }
|
||||
ConfigWizardPage* chain(ConfigWizardPage *page);
|
||||
void append_text(wxString text);
|
||||
void append_spacer(int space);
|
||||
|
||||
template<class T>
|
||||
void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
|
||||
{
|
||||
content->Add(thing, proportion, flag, border);
|
||||
}
|
||||
ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
|
||||
|
||||
void append_text(wxString text);
|
||||
void append_spacer(int space);
|
||||
|
||||
ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
|
||||
|
||||
virtual bool Show(bool show = true);
|
||||
virtual bool Hide() { return Show(false); }
|
||||
virtual wxPanel* extra_buttons() { return nullptr; }
|
||||
virtual void on_page_set() {}
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config) {}
|
||||
|
||||
void enable_next(bool enable);
|
||||
private:
|
||||
ConfigWizardPage *p_prev;
|
||||
ConfigWizardPage *p_next;
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config) {}
|
||||
};
|
||||
|
||||
struct PageWelcome: ConfigWizardPage
|
||||
{
|
||||
PrinterPicker *printer_picker;
|
||||
wxPanel *others_buttons;
|
||||
wxCheckBox *cbox_reset;
|
||||
wxCheckBox *cbox_reset;
|
||||
|
||||
PageWelcome(ConfigWizard *parent, bool check_first_variant);
|
||||
PageWelcome(ConfigWizard *parent);
|
||||
|
||||
virtual wxPanel* extra_buttons() { return others_buttons; }
|
||||
virtual void on_page_set();
|
||||
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
||||
};
|
||||
|
||||
struct PagePrinters: ConfigWizardPage
|
||||
{
|
||||
enum Technology {
|
||||
// Bitflag equivalent of PrinterTechnology
|
||||
T_FFF = 0x1,
|
||||
T_SLA = 0x2,
|
||||
T_Any = ~0,
|
||||
};
|
||||
|
||||
std::vector<PrinterPicker *> printer_pickers;
|
||||
|
||||
PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology);
|
||||
|
||||
void select_all(bool select, bool alternates = false);
|
||||
int get_width() const;
|
||||
};
|
||||
|
||||
struct PageCustom: ConfigWizardPage
|
||||
{
|
||||
PageCustom(ConfigWizard *parent);
|
||||
|
||||
bool custom_wanted() const { return cb_custom->GetValue(); }
|
||||
std::string profile_name() const { return into_u8(tc_profile_name->GetValue()); }
|
||||
|
||||
private:
|
||||
static const char* default_profile_name;
|
||||
|
||||
wxCheckBox *cb_custom;
|
||||
wxTextCtrl *tc_profile_name;
|
||||
wxString profile_name_prev;
|
||||
|
||||
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
||||
void on_variant_checked();
|
||||
};
|
||||
|
||||
struct PageUpdate: ConfigWizardPage
|
||||
{
|
||||
bool version_check;
|
||||
bool preset_update;
|
||||
bool version_check;
|
||||
bool preset_update;
|
||||
|
||||
PageUpdate(ConfigWizard *parent);
|
||||
PageUpdate(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
struct PageVendors: ConfigWizardPage
|
||||
{
|
||||
std::vector<PrinterPicker*> pickers;
|
||||
std::vector<PrinterPicker*> pickers;
|
||||
|
||||
PageVendors(ConfigWizard *parent);
|
||||
PageVendors(ConfigWizard *parent);
|
||||
|
||||
virtual void on_page_set();
|
||||
|
||||
void on_vendor_pick(size_t i);
|
||||
void on_variant_checked();
|
||||
void on_vendor_pick(size_t i);
|
||||
};
|
||||
|
||||
struct PageFirmware: ConfigWizardPage
|
||||
{
|
||||
const ConfigOptionDef &gcode_opt;
|
||||
wxChoice *gcode_picker;
|
||||
const ConfigOptionDef &gcode_opt;
|
||||
wxChoice *gcode_picker;
|
||||
|
||||
PageFirmware(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
PageFirmware(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageBedShape: ConfigWizardPage
|
||||
{
|
||||
BedShapePanel *shape_panel;
|
||||
BedShapePanel *shape_panel;
|
||||
|
||||
PageBedShape(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
PageBedShape(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageDiameters: ConfigWizardPage
|
||||
{
|
||||
wxSpinCtrlDouble *spin_nozzle;
|
||||
wxSpinCtrlDouble *spin_filam;
|
||||
wxSpinCtrlDouble *spin_nozzle;
|
||||
wxSpinCtrlDouble *spin_filam;
|
||||
|
||||
PageDiameters(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
PageDiameters(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageTemperatures: ConfigWizardPage
|
||||
{
|
||||
wxSpinCtrlDouble *spin_extr;
|
||||
wxSpinCtrlDouble *spin_bed;
|
||||
wxSpinCtrlDouble *spin_extr;
|
||||
wxSpinCtrlDouble *spin_bed;
|
||||
|
||||
PageTemperatures(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
PageTemperatures(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
|
||||
class ConfigWizardIndex: public wxPanel
|
||||
{
|
||||
public:
|
||||
ConfigWizardIndex(wxWindow *parent);
|
||||
ConfigWizardIndex(wxWindow *parent);
|
||||
|
||||
void add_page(ConfigWizardPage *page);
|
||||
void add_label(wxString label, unsigned indent = 0);
|
||||
|
||||
size_t active_item() const { return item_active; }
|
||||
ConfigWizardPage* active_page() const;
|
||||
bool active_is_last() const { return item_active < items.size() && item_active == last_page; }
|
||||
|
||||
void go_prev();
|
||||
void go_next();
|
||||
void go_to(size_t i);
|
||||
void go_to(ConfigWizardPage *page);
|
||||
|
||||
void clear();
|
||||
|
||||
void load_items(ConfigWizardPage *firstpage);
|
||||
void set_active(ConfigWizardPage *page);
|
||||
private:
|
||||
const wxBitmap bg;
|
||||
const wxBitmap bullet_black;
|
||||
const wxBitmap bullet_blue;
|
||||
const wxBitmap bullet_white;
|
||||
int text_height;
|
||||
struct Item
|
||||
{
|
||||
wxString label;
|
||||
unsigned indent;
|
||||
ConfigWizardPage *page; // nullptr page => label-only item
|
||||
|
||||
std::vector<wxString> items;
|
||||
std::vector<wxString>::const_iterator item_active;
|
||||
bool operator==(ConfigWizardPage *page) const { return this->page == page; }
|
||||
};
|
||||
|
||||
void on_paint(wxPaintEvent &evt);
|
||||
int em;
|
||||
int em_h;
|
||||
|
||||
const wxBitmap bg;
|
||||
const wxBitmap bullet_black;
|
||||
const wxBitmap bullet_blue;
|
||||
const wxBitmap bullet_white;
|
||||
|
||||
std::vector<Item> items;
|
||||
size_t item_active;
|
||||
ssize_t item_hover;
|
||||
size_t last_page;
|
||||
|
||||
int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em) + em; }
|
||||
|
||||
void on_paint(wxPaintEvent &evt);
|
||||
void on_mouse_move(wxMouseEvent &evt);
|
||||
};
|
||||
|
||||
wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
|
||||
|
||||
struct ConfigWizard::priv
|
||||
{
|
||||
ConfigWizard *q;
|
||||
ConfigWizard::RunReason run_reason;
|
||||
AppConfig appconfig_vendors;
|
||||
std::unordered_map<std::string, VendorProfile> vendors;
|
||||
std::unordered_map<std::string, std::string> vendors_rsrc;
|
||||
std::unique_ptr<DynamicPrintConfig> custom_config;
|
||||
ConfigWizard *q;
|
||||
ConfigWizard::RunReason run_reason;
|
||||
AppConfig appconfig_vendors;
|
||||
std::unordered_map<std::string, VendorProfile> vendors;
|
||||
std::unordered_map<std::string, std::string> vendors_rsrc;
|
||||
std::unique_ptr<DynamicPrintConfig> custom_config;
|
||||
|
||||
wxScrolledWindow *hscroll = nullptr;
|
||||
wxBoxSizer *hscroll_sizer = nullptr;
|
||||
wxBoxSizer *btnsizer = nullptr;
|
||||
ConfigWizardPage *page_current = nullptr;
|
||||
ConfigWizardIndex *index = nullptr;
|
||||
wxButton *btn_prev = nullptr;
|
||||
wxButton *btn_next = nullptr;
|
||||
wxButton *btn_finish = nullptr;
|
||||
wxButton *btn_cancel = nullptr;
|
||||
wxScrolledWindow *hscroll = nullptr;
|
||||
wxBoxSizer *hscroll_sizer = nullptr;
|
||||
wxBoxSizer *btnsizer = nullptr;
|
||||
ConfigWizardPage *page_current = nullptr;
|
||||
ConfigWizardIndex *index = nullptr;
|
||||
wxButton *btn_prev = nullptr;
|
||||
wxButton *btn_next = nullptr;
|
||||
wxButton *btn_finish = nullptr;
|
||||
wxButton *btn_cancel = nullptr;
|
||||
|
||||
PageWelcome *page_welcome = nullptr;
|
||||
PageUpdate *page_update = nullptr;
|
||||
PageVendors *page_vendors = nullptr;
|
||||
PageFirmware *page_firmware = nullptr;
|
||||
PageBedShape *page_bed = nullptr;
|
||||
PageDiameters *page_diams = nullptr;
|
||||
PageTemperatures *page_temps = nullptr;
|
||||
PageWelcome *page_welcome = nullptr;
|
||||
PagePrinters *page_fff = nullptr;
|
||||
PagePrinters *page_msla = nullptr;
|
||||
PageCustom *page_custom = nullptr;
|
||||
PageUpdate *page_update = nullptr;
|
||||
PageVendors *page_vendors = nullptr; // XXX: ?
|
||||
|
||||
priv(ConfigWizard *q) : q(q) {}
|
||||
// Custom setup pages
|
||||
PageFirmware *page_firmware = nullptr;
|
||||
PageBedShape *page_bed = nullptr;
|
||||
PageDiameters *page_diams = nullptr;
|
||||
PageTemperatures *page_temps = nullptr;
|
||||
|
||||
void load_vendors();
|
||||
void add_page(ConfigWizardPage *page);
|
||||
void index_refresh();
|
||||
void set_page(ConfigWizardPage *page);
|
||||
void layout_fit();
|
||||
void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } }
|
||||
void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } }
|
||||
void enable_next(bool enable);
|
||||
priv(ConfigWizard *q) : q(q) {}
|
||||
|
||||
void on_other_vendors();
|
||||
void on_custom_setup();
|
||||
void load_pages(bool custom_setup);
|
||||
|
||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
bool check_first_variant() const;
|
||||
void load_vendors();
|
||||
void add_page(ConfigWizardPage *page);
|
||||
void enable_next(bool enable);
|
||||
|
||||
void on_custom_setup(bool custom_wanted);
|
||||
|
||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -3158,6 +3158,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
|||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||
float height = _get_total_overlay_height();
|
||||
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
|
||||
|
||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
|
@ -3440,42 +3441,28 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
|
|||
reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object);
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
|
||||
#else
|
||||
void GLCanvas3D::Gizmos::set_model_object_ptr(ModelObject* model_object)
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
|
||||
if (it != m_gizmos.end())
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, selection);
|
||||
#else
|
||||
reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_model_object_ptr(model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
|
||||
void GLCanvas3D::Gizmos::clicked_on_object(const Vec2d& mouse_position)
|
||||
|
||||
// Returns true if the gizmo used the event to do something, false otherwise.
|
||||
bool GLCanvas3D::Gizmos::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
|
||||
if (it != m_gizmos.end())
|
||||
reinterpret_cast<GLGizmoSlaSupports*>(it->second)->clicked_on_object(mouse_position);
|
||||
}
|
||||
return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->mouse_event(action, mouse_position, shift_down);
|
||||
|
||||
void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
|
||||
if (it != m_gizmos.end())
|
||||
reinterpret_cast<GLGizmoSlaSupports*>(it->second)->delete_current_grabber(delete_all);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const
|
||||
|
@ -3690,7 +3677,40 @@ GLCanvas3D::WarningTexture::WarningTexture()
|
|||
{
|
||||
}
|
||||
|
||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg, const GLCanvas3D& canvas)
|
||||
void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas)
|
||||
{
|
||||
auto it = std::find(m_warnings.begin(), m_warnings.end(), warning);
|
||||
|
||||
if (state) {
|
||||
if (it != m_warnings.end()) // this warning is already set to be shown
|
||||
return;
|
||||
|
||||
m_warnings.push_back(warning);
|
||||
std::sort(m_warnings.begin(), m_warnings.end());
|
||||
}
|
||||
else {
|
||||
if (it == m_warnings.end()) // deactivating something that is not active is an easy task
|
||||
return;
|
||||
|
||||
m_warnings.erase(it);
|
||||
if (m_warnings.empty()) { // nothing remains to be shown
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Look at the end of our vector and generate proper texture.
|
||||
std::string text;
|
||||
switch (m_warnings.back()) {
|
||||
case ObjectOutside : text = L("Detected object outside print volume"); break;
|
||||
case ToolpathOutside : text = L("Detected toolpath outside print volume"); break;
|
||||
case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break;
|
||||
}
|
||||
|
||||
_generate(text, canvas); // GUI::GLTexture::reset() is called at the beginning of generate(...)
|
||||
}
|
||||
|
||||
bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanvas3D& canvas)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
@ -3763,6 +3783,9 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg, const GLCanvas
|
|||
|
||||
void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
|
||||
{
|
||||
if (m_warnings.empty())
|
||||
return;
|
||||
|
||||
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
|
||||
{
|
||||
::glDisable(GL_DEPTH_TEST);
|
||||
|
@ -4087,7 +4110,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
|||
, m_apply_zoom_to_volumes_filter(false)
|
||||
, m_hover_volume_id(-1)
|
||||
, m_toolbar_action_running(false)
|
||||
, m_warning_texture_enabled(false)
|
||||
, m_legend_texture_enabled(false)
|
||||
, m_picking_enabled(false)
|
||||
, m_moving_enabled(false)
|
||||
|
@ -4097,6 +4119,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
|||
, m_moving(false)
|
||||
, m_color_by("volume")
|
||||
, m_reload_delayed(false)
|
||||
, m_render_sla_auxiliaries(true)
|
||||
#if !ENABLE_IMGUI
|
||||
, m_external_gizmo_widgets_parent(nullptr)
|
||||
#endif // not ENABLE_IMGUI
|
||||
|
@ -4244,8 +4267,7 @@ void GLCanvas3D::reset_volumes()
|
|||
m_dirty = true;
|
||||
}
|
||||
|
||||
enable_warning_texture(false);
|
||||
_reset_warning_texture();
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, false);
|
||||
}
|
||||
|
||||
int GLCanvas3D::check_volumes_outside_state() const
|
||||
|
@ -4255,6 +4277,34 @@ int GLCanvas3D::check_volumes_outside_state() const
|
|||
return (int)state;
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible)
|
||||
{
|
||||
for (GLVolume* vol : m_volumes.volumes) {
|
||||
if (vol->composite_id.volume_id < 0)
|
||||
vol->is_active = visible;
|
||||
}
|
||||
|
||||
m_render_sla_auxiliaries = visible;
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
||||
{
|
||||
for (GLVolume* vol : m_volumes.volumes) {
|
||||
if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo)
|
||||
&& (instance_idx == -1 || vol->composite_id.instance_id == instance_idx))
|
||||
vol->is_active = visible;
|
||||
}
|
||||
if (visible && !mo)
|
||||
toggle_sla_auxiliaries_visibility(true);
|
||||
|
||||
if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1))
|
||||
_set_warning_texture(WarningTexture::SomethingNotShown, true);
|
||||
|
||||
if (!mo && visible)
|
||||
_set_warning_texture(WarningTexture::SomethingNotShown, false);
|
||||
}
|
||||
|
||||
|
||||
void GLCanvas3D::set_config(const DynamicPrintConfig* config)
|
||||
{
|
||||
m_config = config;
|
||||
|
@ -4365,11 +4415,6 @@ void GLCanvas3D::enable_layers_editing(bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_warning_texture(bool enable)
|
||||
{
|
||||
m_warning_texture_enabled = enable;
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_legend_texture(bool enable)
|
||||
{
|
||||
m_legend_texture_enabled = enable;
|
||||
|
@ -4609,9 +4654,9 @@ void GLCanvas3D::render()
|
|||
// this position is used later into on_mouse() to drag the objects
|
||||
m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<int>());
|
||||
|
||||
_render_current_gizmo();
|
||||
_render_selection_sidebar_hints();
|
||||
|
||||
_render_current_gizmo();
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
_render_camera_target();
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
|
@ -4988,22 +5033,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
if (!contained)
|
||||
{
|
||||
enable_warning_texture(true);
|
||||
_generate_warning_texture(L("Detected object outside print volume"));
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, true);
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside));
|
||||
}
|
||||
else
|
||||
{
|
||||
enable_warning_texture(false);
|
||||
m_volumes.reset_outside_state();
|
||||
_reset_warning_texture();
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, false);
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
enable_warning_texture(false);
|
||||
_reset_warning_texture();
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, false);
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
||||
}
|
||||
|
||||
|
@ -5119,6 +5161,7 @@ void GLCanvas3D::bind_event_handlers()
|
|||
m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
|
||||
m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
|
||||
m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
|
||||
m_canvas->Bind(wxEVT_KEY_UP, &GLCanvas3D::on_key_up, this);
|
||||
m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
|
||||
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
|
||||
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
|
||||
|
@ -5144,6 +5187,7 @@ void GLCanvas3D::unbind_event_handlers()
|
|||
m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
|
||||
m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
|
||||
m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
|
||||
m_canvas->Unbind(wxEVT_KEY_UP, &GLCanvas3D::on_key_up, this);
|
||||
m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
|
||||
m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
|
||||
m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
|
||||
|
@ -5187,7 +5231,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
switch (keyCode) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
case WXK_CONTROL_A: post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break;
|
||||
case WXK_CONTROL_A:
|
||||
if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::SelectAll)) // Sla gizmo selects all support points
|
||||
m_dirty = true;
|
||||
else
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
|
||||
break;
|
||||
#ifdef __APPLE__
|
||||
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
|
||||
#else /* __APPLE__ */
|
||||
|
@ -5208,7 +5257,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
#else /* __APPLE__ */
|
||||
case WXK_DELETE:
|
||||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break;
|
||||
if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::Delete))
|
||||
m_dirty = true;
|
||||
else
|
||||
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
|
||||
break;
|
||||
|
||||
case '0': { select_view("iso"); break; }
|
||||
case '1': { select_view("top"); break; }
|
||||
case '2': { select_view("bottom"); break; }
|
||||
|
@ -5245,6 +5299,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_key_up(wxKeyEvent& evt)
|
||||
{
|
||||
// see include/wx/defs.h enum wxKeyCode
|
||||
int keyCode = evt.GetKeyCode();
|
||||
|
||||
// shift has been just released - SLA gizmo might want to close rectangular selection.
|
||||
if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT && m_gizmos.mouse_event(SLAGizmoEventType::ShiftUp))
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
{
|
||||
// Ignore the wheel events if the middle button is pressed.
|
||||
|
@ -5332,14 +5396,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
// Set focus in order to remove it from sidebar fields
|
||||
if (m_canvas != nullptr) {
|
||||
// Only set focus, if the top level window of this canvas is active.
|
||||
auto p = dynamic_cast<wxWindow*>(evt.GetEventObject());
|
||||
auto p = dynamic_cast<wxWindow*>(evt.GetEventObject());
|
||||
while (p->GetParent())
|
||||
p = p->GetParent();
|
||||
auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (top_level_wnd && top_level_wnd->IsActive())
|
||||
{
|
||||
m_canvas->SetFocus();
|
||||
}
|
||||
|
||||
// forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
|
||||
// the context menu is shown, ensuring it to disappear if the mouse is outside any volume and to
|
||||
// change the volume hover state if any is under the mouse
|
||||
m_mouse.position = pos.cast<double>();
|
||||
render();
|
||||
}
|
||||
}
|
||||
m_mouse.set_start_position_2D_as_invalid();
|
||||
//#endif
|
||||
}
|
||||
|
@ -5385,22 +5456,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
#if !ENABLE_IMGUI
|
||||
else if ((m_gizmos.get_current_type() == Gizmos::SlaSupports) && gizmo_reset_rect_contains(*this, pos(0), pos(1)))
|
||||
{
|
||||
if (evt.LeftDown())
|
||||
{
|
||||
m_gizmos.delete_current_grabber(true);
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
#endif // not ENABLE_IMGUI
|
||||
else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse)
|
||||
{
|
||||
m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection);
|
||||
_update_gizmos_data();
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown() && m_gizmos.mouse_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown()))
|
||||
{
|
||||
// the gizmo got the event and took some action, there is no need to do anything more
|
||||
}
|
||||
else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse())
|
||||
{
|
||||
_update_gizmos_data();
|
||||
|
@ -5416,9 +5481,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
m_dirty = true;
|
||||
}
|
||||
else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.RightDown()) {
|
||||
if (m_gizmos.get_current_type() == Gizmos::SlaSupports)
|
||||
m_gizmos.delete_current_grabber();
|
||||
else if ((selected_object_idx != -1) && evt.RightDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::RightDown))
|
||||
{
|
||||
// event was taken care of by the SlaSupports gizmo
|
||||
}
|
||||
else if (view_toolbar_contains_mouse != -1)
|
||||
{
|
||||
|
@ -5474,7 +5539,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
|
||||
// propagate event through callback
|
||||
|
||||
if (m_hover_volume_id != -1)
|
||||
{
|
||||
if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1))
|
||||
|
@ -5493,9 +5557,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
else if (evt.RightDown())
|
||||
{
|
||||
m_mouse.position = pos.cast<double>();
|
||||
// forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
|
||||
// the context menu is already shown, ensuring it to disappear if the mouse is outside any volume
|
||||
m_mouse.position = Vec2d((double)pos(0), (double)pos(1));
|
||||
// the context menu is already shown
|
||||
render();
|
||||
if (m_hover_volume_id != -1)
|
||||
{
|
||||
|
@ -5509,14 +5573,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
|
||||
_update_gizmos_data();
|
||||
wxGetApp().obj_manipul()->update_settings_value(m_selection);
|
||||
// forces a frame render to update the view before the context menu is shown
|
||||
render();
|
||||
|
||||
// // forces a frame render to update the view before the context menu is shown
|
||||
// render();
|
||||
|
||||
Vec2d logical_pos = pos.cast<double>();
|
||||
#if ENABLE_RETINA_GL
|
||||
const float factor = m_retina_helper->get_scale_factor();
|
||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||
#endif
|
||||
#endif // ENABLE_RETINA_GL
|
||||
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
|
||||
}
|
||||
}
|
||||
|
@ -5524,7 +5588,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
|
||||
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown)
|
||||
&& (m_mouse.drag.move_volume_idx != -1) && m_gizmos.get_current_type() != Gizmos::SlaSupports /* don't allow dragging objects with the Sla gizmo on */)
|
||||
{
|
||||
#if ENABLE_MOVE_MIN_THRESHOLD
|
||||
if (!m_mouse.drag.move_requires_threshold)
|
||||
|
@ -5584,6 +5649,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown() && m_gizmos.mouse_event(SLAGizmoEventType::Dragging, Vec2d(pos(0), pos(1)), evt.ShiftDown()))
|
||||
{
|
||||
// the gizmo got the event and took some action, no need to do anything more here
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
|
||||
{
|
||||
m_mouse.dragging = true;
|
||||
|
@ -5639,6 +5709,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
_stop_timer();
|
||||
m_layers_editing.accept_changes(*this);
|
||||
}
|
||||
else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports && !m_gizmos.is_dragging()
|
||||
&& !m_mouse.dragging && m_gizmos.mouse_event(SLAGizmoEventType::LeftUp, Vec2d(pos(0), pos(1)), evt.ShiftDown()))
|
||||
{
|
||||
// the gizmo got the event and took some action, no need to do anything more
|
||||
}
|
||||
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
|
||||
{
|
||||
m_regenerate_volumes = false;
|
||||
|
@ -5648,16 +5723,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
// of the scene with the background processing data should be performed.
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||
}
|
||||
else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_hover_volume_id != -1)
|
||||
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging()
|
||||
&& !is_layers_editing_enabled() && (m_gizmos.get_current_type() != Gizmos::SlaSupports || !m_gizmos.mouse_event(SLAGizmoEventType::LeftUp, Vec2d(pos(0), pos(1)), evt.ShiftDown())))
|
||||
{
|
||||
int id = m_selection.get_object_idx();
|
||||
// SLA gizmo cannot be deselected by clicking in canvas area to avoid inadvertent unselection and losing manual changes
|
||||
// that's why the mouse_event function was called so that the gizmo can refuse the deselection in manual editing mode
|
||||
|
||||
if ((id != -1) && (m_model != nullptr)) {
|
||||
m_gizmos.clicked_on_object(Vec2d(pos(0), pos(1)));
|
||||
}
|
||||
}
|
||||
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
|
||||
{
|
||||
// deselect and propagate event through callback
|
||||
if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event)
|
||||
{
|
||||
|
@ -5690,10 +5761,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
do_rotate();
|
||||
break;
|
||||
}
|
||||
case Gizmos::SlaSupports:
|
||||
// End of mouse dragging, update the SLAPrint/SLAPrintObjects with the new support points.
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -6143,7 +6210,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "add";
|
||||
item.tooltip = GUI::L_str("Add...") + " [" + GUI::shortkey_ctrl_prefix() + "I]";
|
||||
item.sprite_id = 0;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_ADD;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6151,7 +6217,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "delete";
|
||||
item.tooltip = GUI::L_str("Delete") + " [Del]";
|
||||
item.sprite_id = 1;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_DELETE;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6159,7 +6224,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "deleteall";
|
||||
item.tooltip = GUI::L_str("Delete all") + " [" + GUI::shortkey_ctrl_prefix() + "Del]";
|
||||
item.sprite_id = 2;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_DELETE_ALL;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6167,7 +6231,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "arrange";
|
||||
item.tooltip = GUI::L_str("Arrange [A]");
|
||||
item.sprite_id = 3;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_ARRANGE;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6178,7 +6241,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "more";
|
||||
item.tooltip = GUI::L_str("Add instance [+]");
|
||||
item.sprite_id = 4;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_MORE;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6186,7 +6248,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "fewer";
|
||||
item.tooltip = GUI::L_str("Remove instance [-]");
|
||||
item.sprite_id = 5;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_FEWER;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6197,7 +6258,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "splitobjects";
|
||||
item.tooltip = GUI::L_str("Split to objects");
|
||||
item.sprite_id = 6;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_SPLIT_OBJECTS;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6205,7 +6265,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.name = "splitvolumes";
|
||||
item.tooltip = GUI::L_str("Split to parts");
|
||||
item.sprite_id = 8;
|
||||
item.is_toggable = false;
|
||||
item.action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES;
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
@ -6586,7 +6645,9 @@ void GLCanvas3D::_render_objects() const
|
|||
m_layers_editing.render_volumes(*this, this->m_volumes);
|
||||
} else {
|
||||
// do not cull backfaces to show broken geometry, if any
|
||||
m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled);
|
||||
m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) {
|
||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
||||
});
|
||||
}
|
||||
m_volumes.render_VBOs(GLVolumeCollection::Transparent, false);
|
||||
m_shader.stop_using();
|
||||
|
@ -6602,7 +6663,9 @@ void GLCanvas3D::_render_objects() const
|
|||
}
|
||||
|
||||
// do not cull backfaces to show broken geometry, if any
|
||||
m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled);
|
||||
m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) {
|
||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
||||
});
|
||||
m_volumes.render_legacy(GLVolumeCollection::Transparent, false);
|
||||
|
||||
if (m_use_clipping_planes)
|
||||
|
@ -6636,9 +6699,6 @@ void GLCanvas3D::_render_selection_center() const
|
|||
|
||||
void GLCanvas3D::_render_warning_texture() const
|
||||
{
|
||||
if (!m_warning_texture_enabled)
|
||||
return;
|
||||
|
||||
m_warning_texture.render(*this);
|
||||
}
|
||||
|
||||
|
@ -6683,7 +6743,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
|
|||
::glColor4fv(vol->render_color);
|
||||
}
|
||||
|
||||
if (!fake_colors || !vol->disabled)
|
||||
if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries))
|
||||
vol->render();
|
||||
|
||||
++volume_id;
|
||||
|
@ -6834,20 +6894,20 @@ void GLCanvas3D::_render_sla_slices() const
|
|||
{
|
||||
// calculate model bottom cap
|
||||
if (bottom_obj_triangles.empty() && (it_min_z->second.model_slices_idx < model_slices.size()))
|
||||
bottom_obj_triangles = triangulate_expolygons_3df(model_slices[it_min_z->second.model_slices_idx], min_z, true);
|
||||
bottom_obj_triangles = triangulate_expolygons_3d(model_slices[it_min_z->second.model_slices_idx], min_z, true);
|
||||
// calculate support bottom cap
|
||||
if (bottom_sup_triangles.empty() && (it_min_z->second.support_slices_idx < support_slices.size()))
|
||||
bottom_sup_triangles = triangulate_expolygons_3df(support_slices[it_min_z->second.support_slices_idx], min_z, true);
|
||||
bottom_sup_triangles = triangulate_expolygons_3d(support_slices[it_min_z->second.support_slices_idx], min_z, true);
|
||||
}
|
||||
|
||||
if (it_max_z != index.end())
|
||||
{
|
||||
// calculate model top cap
|
||||
if (top_obj_triangles.empty() && (it_max_z->second.model_slices_idx < model_slices.size()))
|
||||
top_obj_triangles = triangulate_expolygons_3df(model_slices[it_max_z->second.model_slices_idx], max_z, false);
|
||||
top_obj_triangles = triangulate_expolygons_3d(model_slices[it_max_z->second.model_slices_idx], max_z, false);
|
||||
// calculate support top cap
|
||||
if (top_sup_triangles.empty() && (it_max_z->second.support_slices_idx < support_slices.size()))
|
||||
top_sup_triangles = triangulate_expolygons_3df(support_slices[it_max_z->second.support_slices_idx], max_z, false);
|
||||
top_sup_triangles = triangulate_expolygons_3d(support_slices[it_max_z->second.support_slices_idx], max_z, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6960,11 +7020,7 @@ void GLCanvas3D::_update_gizmos_data()
|
|||
m_gizmos.set_rotation(Vec3d::Zero());
|
||||
ModelObject* model_object = m_model->objects[m_selection.get_object_idx()];
|
||||
m_gizmos.set_flattening_data(model_object);
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_gizmos.set_sla_support_data(model_object, m_selection);
|
||||
#else
|
||||
m_gizmos.set_model_object_ptr(model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
else if (m_selection.is_single_volume() || m_selection.is_single_modifier())
|
||||
{
|
||||
|
@ -6972,22 +7028,14 @@ void GLCanvas3D::_update_gizmos_data()
|
|||
m_gizmos.set_scale(volume->get_volume_scaling_factor());
|
||||
m_gizmos.set_rotation(Vec3d::Zero());
|
||||
m_gizmos.set_flattening_data(nullptr);
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_gizmos.set_sla_support_data(nullptr, m_selection);
|
||||
#else
|
||||
m_gizmos.set_model_object_ptr(nullptr);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gizmos.set_scale(Vec3d::Ones());
|
||||
m_gizmos.set_rotation(Vec3d::Zero());
|
||||
m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr);
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_gizmos.set_sla_support_data(nullptr, m_selection);
|
||||
#else
|
||||
m_gizmos.set_model_object_ptr(nullptr);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8185,17 +8233,7 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state()
|
|||
void GLCanvas3D::_show_warning_texture_if_needed()
|
||||
{
|
||||
_set_current();
|
||||
|
||||
if (_is_any_volume_outside())
|
||||
{
|
||||
enable_warning_texture(true);
|
||||
_generate_warning_texture(L("Detected toolpath outside print volume"));
|
||||
}
|
||||
else
|
||||
{
|
||||
enable_warning_texture(false);
|
||||
_reset_warning_texture();
|
||||
}
|
||||
_set_warning_texture(WarningTexture::ToolpathOutside, _is_any_volume_outside());
|
||||
}
|
||||
|
||||
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
|
||||
|
@ -8228,14 +8266,9 @@ void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data,
|
|||
m_legend_texture.generate(preview_data, tool_colors, *this, m_dynamic_background_enabled && _is_any_volume_outside());
|
||||
}
|
||||
|
||||
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
|
||||
void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state)
|
||||
{
|
||||
m_warning_texture.generate(msg, *this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_reset_warning_texture()
|
||||
{
|
||||
m_warning_texture.reset();
|
||||
m_warning_texture.activate(warning, state, *this);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_any_volume_outside() const
|
||||
|
|
|
@ -132,6 +132,18 @@ wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event<bool>);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
|
||||
// this describes events being passed from GLCanvas3D to SlaSupport gizmo
|
||||
enum class SLAGizmoEventType {
|
||||
LeftDown = 1,
|
||||
LeftUp,
|
||||
RightDown,
|
||||
Dragging,
|
||||
Delete,
|
||||
SelectAll,
|
||||
ShiftUp
|
||||
};
|
||||
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
struct GCodePreviewVolumeIndex
|
||||
|
@ -788,12 +800,8 @@ private:
|
|||
|
||||
void set_flattening_data(const ModelObject* model_object);
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
|
||||
#else
|
||||
void set_model_object_ptr(ModelObject* model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void clicked_on_object(const Vec2d& mouse_position);
|
||||
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false);
|
||||
void delete_current_grabber(bool delete_all = false);
|
||||
|
||||
void render_current_gizmo(const Selection& selection) const;
|
||||
|
@ -835,18 +843,32 @@ private:
|
|||
|
||||
class WarningTexture : public GUI::GLTexture
|
||||
{
|
||||
public:
|
||||
WarningTexture();
|
||||
|
||||
enum Warning {
|
||||
ObjectOutside,
|
||||
ToolpathOutside,
|
||||
SomethingNotShown
|
||||
};
|
||||
|
||||
// Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously,
|
||||
// only the last one is shown (decided by the order in the enum above).
|
||||
void activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas);
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
|
||||
private:
|
||||
static const unsigned char Background_Color[3];
|
||||
static const unsigned char Opacity;
|
||||
|
||||
int m_original_width;
|
||||
int m_original_height;
|
||||
|
||||
public:
|
||||
WarningTexture();
|
||||
// Information about which warnings are currently active.
|
||||
std::vector<Warning> m_warnings;
|
||||
|
||||
bool generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
// Generates the texture with given text.
|
||||
bool _generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||
};
|
||||
|
||||
class LegendTexture : public GUI::GLTexture
|
||||
|
@ -923,6 +945,7 @@ private:
|
|||
bool m_multisample_allowed;
|
||||
bool m_regenerate_volumes;
|
||||
bool m_moving;
|
||||
bool m_render_sla_auxiliaries;
|
||||
|
||||
std::string m_color_by;
|
||||
|
||||
|
@ -954,6 +977,9 @@ public:
|
|||
void reset_volumes();
|
||||
int check_volumes_outside_state() const;
|
||||
|
||||
void toggle_sla_auxiliaries_visibility(bool visible);
|
||||
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
|
||||
void set_config(const DynamicPrintConfig* config);
|
||||
void set_process(BackgroundSlicingProcess* process);
|
||||
void set_model(Model* model);
|
||||
|
@ -991,7 +1017,6 @@ public:
|
|||
bool is_reload_delayed() const;
|
||||
|
||||
void enable_layers_editing(bool enable);
|
||||
void enable_warning_texture(bool enable);
|
||||
void enable_legend_texture(bool enable);
|
||||
void enable_picking(bool enable);
|
||||
void enable_moving(bool enable);
|
||||
|
@ -1050,6 +1075,7 @@ public:
|
|||
void on_size(wxSizeEvent& evt);
|
||||
void on_idle(wxIdleEvent& evt);
|
||||
void on_char(wxKeyEvent& evt);
|
||||
void on_key_up(wxKeyEvent& evt);
|
||||
void on_mouse_wheel(wxMouseEvent& evt);
|
||||
void on_timer(wxTimerEvent& evt);
|
||||
void on_mouse(wxMouseEvent& evt);
|
||||
|
@ -1176,8 +1202,7 @@ private:
|
|||
void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
|
||||
|
||||
// generates a warning texture containing the given message
|
||||
void _generate_warning_texture(const std::string& msg);
|
||||
void _reset_warning_texture();
|
||||
void _set_warning_texture(WarningTexture::Warning warning, bool state);
|
||||
|
||||
bool _is_any_volume_outside() const;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/SLA/SLASupportTree.hpp"
|
||||
#include "libslic3r/SLA/SLACommon.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -1741,28 +1741,20 @@ Vec3d GLGizmoFlatten::get_flattening_normal() const
|
|||
}
|
||||
|
||||
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
|
||||
#else
|
||||
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero())
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
// using GLU_FILL does not work when the instance's transformation
|
||||
// contains mirroring (normals are reverted)
|
||||
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
GLGizmoSlaSupports::~GLGizmoSlaSupports()
|
||||
{
|
||||
if (m_quadric != nullptr)
|
||||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
bool GLGizmoSlaSupports::on_init()
|
||||
{
|
||||
|
@ -1782,7 +1774,6 @@ bool GLGizmoSlaSupports::on_init()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
m_starting_center = Vec3d::Zero();
|
||||
|
@ -1791,208 +1782,162 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G
|
|||
if (selection.is_empty())
|
||||
m_old_instance_id = -1;
|
||||
|
||||
if ((model_object != nullptr) && selection.is_from_single_instance())
|
||||
m_active_instance = selection.get_instance_idx();
|
||||
|
||||
if (model_object && selection.is_from_single_instance())
|
||||
{
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
|
||||
// If there are no points, let's ask the backend if it calculated some.
|
||||
if (model_object->sla_support_points.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) {
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == model_object->id()) {
|
||||
const Eigen::MatrixXd& points = po->get_support_points();
|
||||
for (unsigned int i=0; i<points.rows();++i)
|
||||
model_object->sla_support_points.push_back(Vec3f(po->trafo().inverse().cast<float>() * Vec3f(points(i,0), points(i,1), points(i,2))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_editing_mode_cache.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints))
|
||||
get_data_from_backend();
|
||||
|
||||
if (m_model_object != m_old_model_object)
|
||||
m_editing_mode = false;
|
||||
if (m_state == On) {
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object)
|
||||
{
|
||||
if (model_object != nullptr) {
|
||||
m_starting_center = Vec3d::Zero();
|
||||
m_model_object = model_object;
|
||||
|
||||
int selected_instance = m_parent.get_selection().get_instance_idx();
|
||||
assert(selected_instance < (int)model_object->instances.size());
|
||||
|
||||
m_instance_matrix = model_object->instances[selected_instance]->get_matrix();
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
::glEnable(GL_BLEND);
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
// the dragged_offset is a vector measuring where was the object moved
|
||||
// with the gizmo being on. This is reset in set_model_object_ptr and
|
||||
// does not work correctly when there are multiple copies.
|
||||
|
||||
if (m_starting_center == Vec3d::Zero())
|
||||
m_starting_center = selection.get_bounding_box().center();
|
||||
Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
|
||||
for (auto& g : m_grabbers) {
|
||||
g.color[0] = 1.f;
|
||||
g.color[1] = 0.f;
|
||||
g.color[2] = 0.f;
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_grabbers(selection, false);
|
||||
#else
|
||||
//::glTranslatef((GLfloat)dragged_offset(0), (GLfloat)dragged_offset(1), (GLfloat)dragged_offset(2));
|
||||
render_grabbers(false);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_points(selection, false);
|
||||
render_selection_rectangle();
|
||||
|
||||
#if !ENABLE_IMGUI
|
||||
render_tooltip_texture();
|
||||
#endif // not ENABLE_IMGUI
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::render_selection_rectangle() const
|
||||
{
|
||||
if (!m_selection_rectangle_active)
|
||||
return;
|
||||
|
||||
::glLineWidth(1.5f);
|
||||
float render_color[3] = {1.f, 0.f, 0.f};
|
||||
::glColor3fv(render_color);
|
||||
|
||||
::glPushAttrib(GL_TRANSFORM_BIT); // remember current MatrixMode
|
||||
|
||||
::glMatrixMode(GL_MODELVIEW); // cache modelview matrix and set to identity
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
|
||||
::glMatrixMode(GL_PROJECTION); // cache projection matrix and set to identity
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
|
||||
::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f); // set projection matrix so that world coords = window coords
|
||||
|
||||
// render the selection rectangle (window coordinates):
|
||||
::glPushAttrib(GL_ENABLE_BIT);
|
||||
::glLineStipple(4, 0xAAAA);
|
||||
::glEnable(GL_LINE_STIPPLE);
|
||||
|
||||
::glBegin(GL_LINE_LOOP);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
|
||||
::glEnd();
|
||||
::glPopAttrib();
|
||||
|
||||
::glPopMatrix(); // restore former projection matrix
|
||||
::glMatrixMode(GL_MODELVIEW);
|
||||
::glPopMatrix(); // restore former modelview matrix
|
||||
::glPopAttrib(); // restore former MatrixMode
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
for (unsigned int i=0; i<m_grabbers.size(); ++i) {
|
||||
m_grabbers[i].color[0] = 1.0f;
|
||||
m_grabbers[i].color[1] = 1.0f;
|
||||
m_grabbers[i].color[2] = picking_color_component(i);
|
||||
}
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_grabbers(selection, true);
|
||||
#else
|
||||
render_grabbers(true);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
render_points(selection, true);
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, bool picking) const
|
||||
void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, bool picking) const
|
||||
{
|
||||
if (m_quadric == nullptr)
|
||||
if (m_quadric == nullptr || !selection.is_from_single_instance())
|
||||
return;
|
||||
|
||||
if (!selection.is_from_single_instance())
|
||||
return;
|
||||
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_shift = v->get_sla_shift_z();
|
||||
|
||||
::glPushMatrix();
|
||||
::glTranslated(0.0, 0.0, z_shift);
|
||||
|
||||
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
|
||||
::glMultMatrixd(m.data());
|
||||
|
||||
if (!picking)
|
||||
::glEnable(GL_LIGHTING);
|
||||
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_shift = vol->get_sla_shift_z();
|
||||
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
||||
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
|
||||
|
||||
::glPushMatrix();
|
||||
::glTranslated(0.0, 0.0, z_shift);
|
||||
::glMultMatrixd(instance_matrix.data());
|
||||
|
||||
float render_color[3];
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
||||
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
|
||||
{
|
||||
// first precalculate the grabber position in world coordinates, so that the grabber
|
||||
// is not scaled with the object (as it would be if rendered with current gl matrix).
|
||||
Eigen::Matrix<GLfloat, 4, 4> glmatrix;
|
||||
glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data());
|
||||
Eigen::Matrix<float, 4, 1> grabber_pos;
|
||||
for (int j=0; j<3; ++j)
|
||||
grabber_pos(j) = m_grabbers[i].center(j);
|
||||
grabber_pos[3] = 1.f;
|
||||
Eigen::Matrix<float, 4, 1> grabber_world_position = glmatrix * grabber_pos;
|
||||
const sla::SupportPoint& support_point = m_editing_mode_cache[i].first;
|
||||
const bool& point_selected = m_editing_mode_cache[i].second;
|
||||
|
||||
if (!picking && (m_hover_id == i))
|
||||
{
|
||||
render_color[0] = 1.0f - m_grabbers[i].color[0];
|
||||
render_color[1] = 1.0f - m_grabbers[i].color[1];
|
||||
render_color[2] = 1.0f - m_grabbers[i].color[2];
|
||||
// First decide about the color of the point.
|
||||
if (picking) {
|
||||
render_color[0] = 1.0f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = picking_color_component(i);
|
||||
}
|
||||
else {
|
||||
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
|
||||
render_color[0] = 0.f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = 1.0f;
|
||||
}
|
||||
else { // neigher hover nor picking
|
||||
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island;
|
||||
if (m_editing_mode) {
|
||||
render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f);
|
||||
render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f);
|
||||
render_color[2] = point_selected ? 0.3f : (supports_new_island ? 1.0f : 0.7f);
|
||||
}
|
||||
else
|
||||
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
|
||||
}
|
||||
}
|
||||
else
|
||||
::memcpy((void*)render_color, (const void*)m_grabbers[i].color, 3 * sizeof(float));
|
||||
|
||||
::glColor3fv(render_color);
|
||||
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
|
||||
::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
|
||||
|
||||
// Now render the sphere. Inverse matrix of the instance scaling is applied so that the
|
||||
// sphere does not scale with the object.
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift);
|
||||
const float diameter = 0.8f;
|
||||
::gluSphere(m_quadric, diameter/2.f, 64, 36);
|
||||
::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2));
|
||||
::glMultMatrixd(instance_scaling_matrix_inverse.data());
|
||||
::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius * RenderPointScale, 64, 36);
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
{
|
||||
// Reset emissive component to zero (the default value)
|
||||
float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f };
|
||||
::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
|
||||
}
|
||||
|
||||
if (!picking)
|
||||
::glDisable(GL_LIGHTING);
|
||||
|
||||
::glPopMatrix();
|
||||
}
|
||||
#else
|
||||
void GLGizmoSlaSupports::render_grabbers(bool picking) const
|
||||
{
|
||||
if (m_parent.get_selection().is_empty())
|
||||
return;
|
||||
|
||||
float z_shift = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
|
||||
::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)z_shift);
|
||||
|
||||
int selected_instance = m_parent.get_selection().get_instance_idx();
|
||||
assert(selected_instance < (int)m_model_object->instances.size());
|
||||
|
||||
float render_color_inactive[3] = { 0.5f, 0.5f, 0.5f };
|
||||
|
||||
for (const ModelInstance* inst : m_model_object->instances) {
|
||||
bool active = inst == m_model_object->instances[selected_instance];
|
||||
if (picking && ! active)
|
||||
continue;
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
||||
{
|
||||
if (!m_grabbers[i].enabled)
|
||||
continue;
|
||||
|
||||
float render_color[3];
|
||||
if (! picking && active && m_hover_id == i) {
|
||||
render_color[0] = 1.0f - m_grabbers[i].color[0];
|
||||
render_color[1] = 1.0f - m_grabbers[i].color[1];
|
||||
render_color[2] = 1.0f - m_grabbers[i].color[2];
|
||||
}
|
||||
else
|
||||
::memcpy((void*)render_color, active ? (const void*)m_grabbers[i].color : (const void*)render_color_inactive, 3 * sizeof(float));
|
||||
if (!picking)
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]);
|
||||
::glPushMatrix();
|
||||
Vec3d center = inst->get_matrix() * m_grabbers[i].center;
|
||||
::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2));
|
||||
GLUquadricObj *quadric;
|
||||
quadric = ::gluNewQuadric();
|
||||
::gluQuadricDrawStyle(quadric, GLU_FILL );
|
||||
::gluSphere( quadric , 0.4, 64 , 32 );
|
||||
::gluDeleteQuadric(quadric);
|
||||
::glPopMatrix();
|
||||
if (!picking)
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
}
|
||||
|
||||
::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)-z_shift);
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
return (m_state == On) && (m_model_object != nullptr) && (m_model_object != m_old_model_object) && !m_model_object->instances.empty();
|
||||
#else
|
||||
return m_state == On && m_model_object && !m_model_object->instances.empty() && !m_instance_matrix.isApprox(m_source_data.matrix);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
|
||||
// return false;
|
||||
|
@ -2027,27 +1972,14 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
|
||||
m_AABB.init(m_V, m_F);
|
||||
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_source_data.matrix = m_instance_matrix;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
// we'll now reload Grabbers (selection might have changed):
|
||||
m_grabbers.clear();
|
||||
|
||||
for (const Vec3f& point : m_model_object->sla_support_points) {
|
||||
m_grabbers.push_back(Grabber());
|
||||
m_grabbers.back().center = point.cast<double>();
|
||||
}
|
||||
// we'll now reload support points (selection might have changed):
|
||||
editing_mode_reload_cache();
|
||||
}
|
||||
|
||||
Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
if (m_V.size() == 0)
|
||||
#else
|
||||
if (m_V.size() == 0 || is_mesh_update_necessary())
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
update_mesh();
|
||||
|
||||
Eigen::Matrix<GLint, 4, 1, Eigen::DontAlign> viewport;
|
||||
|
@ -2064,20 +1996,15 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
|||
|
||||
igl::Hit hit;
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
const GLCanvas3D::Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
#else
|
||||
double z_offset = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
point1(2) -= z_offset;
|
||||
point2(2) -= z_offset;
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
|
||||
#else
|
||||
Transform3d inv = m_instance_matrix.inverse();
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
point1 = inv * point1;
|
||||
point2 = inv * point2;
|
||||
|
||||
|
@ -2089,68 +2016,174 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
|||
return bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
|
||||
// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo
|
||||
// concludes that the event was not intended for it, it should return false.
|
||||
bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
int instance_id = m_parent.get_selection().get_instance_idx();
|
||||
if (m_old_instance_id != instance_id)
|
||||
{
|
||||
bool something_selected = (m_old_instance_id != -1);
|
||||
m_old_instance_id = instance_id;
|
||||
if (something_selected)
|
||||
return;
|
||||
if (!m_editing_mode)
|
||||
return false;
|
||||
|
||||
// left down - show the selection rectangle:
|
||||
if (action == SLAGizmoEventType::LeftDown && shift_down) {
|
||||
if (m_hover_id == -1) {
|
||||
m_selection_rectangle_active = true;
|
||||
m_selection_rectangle_start_corner = mouse_position;
|
||||
m_selection_rectangle_end_corner = mouse_position;
|
||||
m_canvas_width = m_parent.get_canvas_size().get_width();
|
||||
m_canvas_height = m_parent.get_canvas_size().get_height();
|
||||
}
|
||||
else
|
||||
select_point(m_hover_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
if (instance_id == -1)
|
||||
return;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new grabber in that case
|
||||
// dragging the selection rectangle:
|
||||
if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) {
|
||||
m_selection_rectangle_end_corner = mouse_position;
|
||||
return true;
|
||||
}
|
||||
catch (...) { return; }
|
||||
|
||||
m_grabbers.push_back(Grabber());
|
||||
m_grabbers.back().center = new_pos.cast<double>();
|
||||
m_model_object->sla_support_points.push_back(new_pos);
|
||||
// mouse up without selection rectangle - place point on the mesh:
|
||||
if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) {
|
||||
if (m_ignore_up_event) {
|
||||
m_ignore_up_event = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
int instance_id = m_parent.get_selection().get_instance_idx();
|
||||
if (m_old_instance_id != instance_id)
|
||||
{
|
||||
bool something_selected = (m_old_instance_id != -1);
|
||||
m_old_instance_id = instance_id;
|
||||
if (something_selected)
|
||||
return false;
|
||||
}
|
||||
if (instance_id == -1)
|
||||
return false;
|
||||
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
// Regardless of whether the user clicked the object or not, we will unselect all points:
|
||||
select_point(NoPoints);
|
||||
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case
|
||||
m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), true));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
catch (...) { // not clicked on object
|
||||
return true; // prevents deselection of the gizmo by GLCanvas3D
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// left up with selection rectangle - select points inside the rectangle:
|
||||
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp)
|
||||
&& m_selection_rectangle_active) {
|
||||
if (action == SLAGizmoEventType::ShiftUp)
|
||||
m_ignore_up_event = true;
|
||||
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
|
||||
GLint viewport[4];
|
||||
::glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
GLdouble modelview_matrix[16];
|
||||
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
|
||||
GLdouble projection_matrix[16];
|
||||
::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
|
||||
|
||||
const GLCanvas3D::Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
|
||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())});
|
||||
|
||||
const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true);
|
||||
// we'll recover current look direction from the modelview matrix (in world coords)...
|
||||
Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]);
|
||||
// ...and transform it to model coords.
|
||||
direction_to_camera = instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera.eval();
|
||||
|
||||
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
|
||||
for (std::pair<sla::SupportPoint, bool>& point_and_selection : m_editing_mode_cache) {
|
||||
const sla::SupportPoint& support_point = point_and_selection.first;
|
||||
Vec3f pos = instance_matrix.cast<float>() * support_point.pos;
|
||||
pos(2) += z_offset;
|
||||
GLdouble out_x, out_y, out_z;
|
||||
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z);
|
||||
out_y = m_canvas_height - out_y;
|
||||
|
||||
if (rectangle.contains(Point(out_x, out_y))) {
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos, direction_to_camera, hits))
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (hits.size() > 1 || hits.front().t > 0.001f)
|
||||
is_obscured = true;
|
||||
|
||||
if (!is_obscured)
|
||||
point_and_selection.second = true;
|
||||
}
|
||||
}
|
||||
m_selection_rectangle_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::Delete) {
|
||||
// delete key pressed
|
||||
delete_selected_points();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::RightDown) {
|
||||
if (m_hover_id != -1) {
|
||||
select_point(NoPoints);
|
||||
select_point(m_hover_id);
|
||||
delete_selected_points();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::SelectAll) {
|
||||
select_point(AllPoints);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::delete_current_grabber(bool delete_all)
|
||||
void GLGizmoSlaSupports::delete_selected_points()
|
||||
{
|
||||
if (delete_all) {
|
||||
m_grabbers.clear();
|
||||
m_model_object->sla_support_points.clear();
|
||||
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
}
|
||||
else
|
||||
if (m_hover_id != -1) {
|
||||
m_grabbers.erase(m_grabbers.begin() + m_hover_id);
|
||||
m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id);
|
||||
m_hover_id = -1;
|
||||
if (!m_editing_mode)
|
||||
return;
|
||||
|
||||
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
|
||||
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands)) {
|
||||
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
}
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (m_hover_id != -1 && data.mouse_pos) {
|
||||
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].first.is_new_island || !m_lock_unique_islands)) {
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
|
||||
}
|
||||
catch (...) { return; }
|
||||
m_grabbers[m_hover_id].center = new_pos.cast<double>();
|
||||
m_model_object->sla_support_points[m_hover_id] = new_pos;
|
||||
m_editing_mode_cache[m_hover_id].first.pos = new_pos;
|
||||
m_editing_mode_cache[m_hover_id].first.is_new_island = false;
|
||||
m_unsaved_changes = true;
|
||||
// Do not update immediately, wait until the mouse is released.
|
||||
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
@ -2196,37 +2229,165 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas
|
|||
RENDER_AGAIN:
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
m_imgui->set_next_window_bg_alpha(0.5f);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
ImGui::PushItemWidth(100.0f);
|
||||
m_imgui->text(_(L("Left mouse click - add point")));
|
||||
m_imgui->text(_(L("Right mouse click - remove point")));
|
||||
m_imgui->text(" ");
|
||||
|
||||
bool generate = m_imgui->button(_(L("Generate points automatically")));
|
||||
bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")"));
|
||||
bool force_refresh = false;
|
||||
bool remove_selected = false;
|
||||
bool old_editing_state = m_editing_mode;
|
||||
|
||||
if (m_editing_mode) {
|
||||
m_imgui->text(_(L("Left mouse click - add point")));
|
||||
m_imgui->text(_(L("Right mouse click - remove point")));
|
||||
m_imgui->text(_(L("Shift + Left (+ drag) - select point(s)")));
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
std::vector<wxString> options = {"0.2", "0.4", "0.6", "0.8", "1.0"};
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(1) << m_new_point_head_diameter;
|
||||
wxString str = ss.str();
|
||||
|
||||
bool old_combo_state = m_combo_box_open;
|
||||
m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str);
|
||||
force_refresh |= (old_combo_state != m_combo_box_open);
|
||||
|
||||
float current_number = atof(str);
|
||||
if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change)
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
if (point_and_selection.second) {
|
||||
point_and_selection.first.head_front_radius = current_number / 2.f;
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
|
||||
if (std::abs(current_number - m_new_point_head_diameter) > 0.001) {
|
||||
force_refresh = true;
|
||||
m_new_point_head_diameter = current_number;
|
||||
}
|
||||
|
||||
bool changed = m_lock_unique_islands;
|
||||
m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands);
|
||||
force_refresh |= changed != m_lock_unique_islands;
|
||||
|
||||
remove_selected = m_imgui->button(_(L("Remove selected points")));
|
||||
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
bool apply_changes = m_imgui->button(_(L("Apply changes")));
|
||||
if (apply_changes) {
|
||||
editing_mode_apply_changes();
|
||||
force_refresh = true;
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
bool discard_changes = m_imgui->button(_(L("Discard changes")));
|
||||
if (discard_changes) {
|
||||
editing_mode_discard_changes();
|
||||
force_refresh = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* ImGui::PushItemWidth(50.0f);
|
||||
m_imgui->text(_(L("Minimal points distance: ")));
|
||||
ImGui::SameLine();
|
||||
bool value_changed = ImGui::InputDouble("mm", &m_minimal_point_distance, 0.0f, 0.0f, "%.2f");
|
||||
m_imgui->text(_(L("Support points density: ")));
|
||||
ImGui::SameLine();
|
||||
value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/
|
||||
|
||||
bool generate = m_imgui->button(_(L("Auto-generate points")));
|
||||
|
||||
if (generate) {
|
||||
#if SLAGIZMO_IMGUI_MODAL
|
||||
ImGui::OpenPopup(_(L("Warning")));
|
||||
m_show_modal = true;
|
||||
force_refresh = true;
|
||||
#else
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L(
|
||||
"Autogeneration will erase all manually edited points.\n\n"
|
||||
"Are you sure you want to do it?\n"
|
||||
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
|
||||
if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) {
|
||||
m_model_object->sla_support_points.clear();
|
||||
m_editing_mode_cache.clear();
|
||||
wxGetApp().plater()->reslice();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if SLAGIZMO_IMGUI_MODAL
|
||||
if (m_show_modal) {
|
||||
if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/))
|
||||
{
|
||||
m_imgui->text(_(L("Autogeneration will erase all manually edited points.")));
|
||||
m_imgui->text("");
|
||||
m_imgui->text(_(L("Are you sure you want to do it?")));
|
||||
|
||||
if (m_imgui->button(_(L("Continue"))))
|
||||
{
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show_modal = false;
|
||||
|
||||
m_model_object->sla_support_points.clear();
|
||||
m_editing_mode_cache.clear();
|
||||
wxGetApp().plater()->reslice();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (m_imgui->button(_(L("Cancel")))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show_modal = false;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (!m_show_modal)
|
||||
force_refresh = true;
|
||||
}
|
||||
#endif
|
||||
m_imgui->text("");
|
||||
m_imgui->text("");
|
||||
bool editing_clicked = m_imgui->button(_(L("Manual editing")));
|
||||
if (editing_clicked) {
|
||||
editing_mode_reload_cache();
|
||||
m_editing_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_imgui->end();
|
||||
|
||||
if (remove_all_clicked) {
|
||||
delete_current_grabber(true);
|
||||
if (m_editing_mode != old_editing_state) { // user just toggled between editing/non-editing mode
|
||||
m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode);
|
||||
force_refresh = true;
|
||||
}
|
||||
|
||||
|
||||
if (remove_selected) {
|
||||
force_refresh = false;
|
||||
m_parent.reload_scene(true);
|
||||
delete_selected_points();
|
||||
if (first_run) {
|
||||
first_run = false;
|
||||
goto RENDER_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_all_clicked || generate) {
|
||||
if (force_refresh)
|
||||
m_parent.reload_scene(true);
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_IMGUI
|
||||
|
||||
bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
||||
&& selection.is_from_single_instance();
|
||||
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
|
||||
|| !selection.is_from_single_instance())
|
||||
return false;
|
||||
|
||||
// Check that none of the selected volumes is outside.
|
||||
const GLCanvas3D::Selection::IndicesList& list = selection.get_volume_idxs();
|
||||
for (const auto& idx : list)
|
||||
if (selection.get_volume(idx)->is_outside)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLGizmoSlaSupports::on_is_selectable() const
|
||||
|
@ -2239,6 +2400,105 @@ std::string GLGizmoSlaSupports::on_get_name() const
|
|||
return L("SLA Support Points [L]");
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_set_state()
|
||||
{
|
||||
if (m_state == On) {
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
if (m_model_object)
|
||||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
}
|
||||
if (m_state == Off) {
|
||||
if (m_old_state != Off && m_model_object) { // the gizmo was just turned Off
|
||||
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
}
|
||||
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_editing_mode = false; // so it is not active next time the gizmo opens
|
||||
|
||||
#if SLAGIZMO_IMGUI_MODAL
|
||||
if (m_show_modal) {
|
||||
m_show_modal = false;
|
||||
on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
m_old_state = m_state;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (m_hover_id != -1) {
|
||||
select_point(NoPoints);
|
||||
select_point(m_hover_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::select_point(int i)
|
||||
{
|
||||
if (i == AllPoints || i == NoPoints) {
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
point_and_selection.second = ( i == AllPoints ? true : false);
|
||||
}
|
||||
else
|
||||
m_editing_mode_cache[i].second = true;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_discard_changes()
|
||||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_apply_changes()
|
||||
{
|
||||
// If there are no changes, don't touch the front-end. The data in the cache could have been
|
||||
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
|
||||
if (m_unsaved_changes) {
|
||||
m_model_object->sla_support_points.clear();
|
||||
for (const std::pair<sla::SupportPoint, bool>& point_and_selection : m_editing_mode_cache)
|
||||
m_model_object->sla_support_points.push_back(point_and_selection.first);
|
||||
}
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_reload_cache()
|
||||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::get_data_from_backend()
|
||||
{
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == m_model_object->id()) {
|
||||
const std::vector<sla::SupportPoint>& points = po->get_support_points();
|
||||
auto mat = po->trafo().inverse().cast<float>();
|
||||
for (unsigned int i=0; i<points.size();++i)
|
||||
m_editing_mode_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_unsaved_changes = false;
|
||||
|
||||
// We don't copy the data into ModelObject, as this would stop the background processing.
|
||||
}
|
||||
|
||||
|
||||
// GLGizmoCut
|
||||
|
|
|
@ -437,31 +437,26 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define SLAGIZMO_IMGUI_MODAL 0
|
||||
class GLGizmoSlaSupports : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
ModelObject* m_old_model_object = nullptr;
|
||||
int m_active_instance = -1;
|
||||
int m_old_instance_id = -1;
|
||||
#else
|
||||
Transform3d m_instance_matrix;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
Vec3f unproject_on_mesh(const Vec2d& mouse_pos);
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
GLUquadricObj* m_quadric;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
const float RenderPointScale = 1.f;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
Eigen::MatrixXf m_V; // vertices
|
||||
Eigen::MatrixXi m_F; // facets indices
|
||||
igl::AABB<Eigen::MatrixXf,3> m_AABB;
|
||||
|
||||
struct SourceDataSummary {
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
BoundingBoxf3 bounding_box;
|
||||
Transform3d matrix;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
Vec3d mesh_first_point;
|
||||
};
|
||||
|
||||
|
@ -472,14 +467,10 @@ private:
|
|||
|
||||
public:
|
||||
explicit GLGizmoSlaSupports(GLCanvas3D& parent);
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
virtual ~GLGizmoSlaSupports();
|
||||
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
|
||||
#else
|
||||
void set_model_object_ptr(ModelObject* model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void clicked_on_object(const Vec2d& mouse_position);
|
||||
void delete_current_grabber(bool delete_all);
|
||||
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
|
||||
void delete_selected_points();
|
||||
|
||||
private:
|
||||
bool on_init();
|
||||
|
@ -487,11 +478,8 @@ private:
|
|||
virtual void on_render(const GLCanvas3D::Selection& selection) const;
|
||||
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void render_grabbers(const GLCanvas3D::Selection& selection, bool picking = false) const;
|
||||
#else
|
||||
void render_grabbers(bool picking = false) const;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void render_selection_rectangle() const;
|
||||
void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const;
|
||||
bool is_mesh_update_necessary() const;
|
||||
void update_mesh();
|
||||
|
||||
|
@ -501,12 +489,41 @@ private:
|
|||
mutable GLTexture m_reset_texture;
|
||||
#endif // not ENABLE_IMGUI
|
||||
|
||||
bool m_lock_unique_islands = false;
|
||||
bool m_editing_mode = false;
|
||||
float m_new_point_head_diameter = 0.4f;
|
||||
double m_minimal_point_distance = 20.;
|
||||
double m_density = 100.;
|
||||
std::vector<std::pair<sla::SupportPoint, bool>> m_editing_mode_cache; // a support point and whether it is currently selected
|
||||
|
||||
bool m_selection_rectangle_active = false;
|
||||
Vec2d m_selection_rectangle_start_corner;
|
||||
Vec2d m_selection_rectangle_end_corner;
|
||||
bool m_ignore_up_event = false;
|
||||
bool m_combo_box_open = false;
|
||||
bool m_unsaved_changes = false;
|
||||
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
|
||||
#if SLAGIZMO_IMGUI_MODAL
|
||||
bool m_show_modal = false;
|
||||
#endif
|
||||
int m_canvas_width;
|
||||
int m_canvas_height;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
// editing mode selection, etc:
|
||||
enum {
|
||||
AllPoints = -2,
|
||||
NoPoints,
|
||||
};
|
||||
void select_point(int i);
|
||||
void editing_mode_apply_changes();
|
||||
void editing_mode_discard_changes();
|
||||
void editing_mode_reload_cache();
|
||||
void get_data_from_backend();
|
||||
|
||||
protected:
|
||||
void on_set_state() override {
|
||||
if (m_state == On && is_mesh_update_necessary()) {
|
||||
update_mesh();
|
||||
}
|
||||
}
|
||||
void on_set_state() override;
|
||||
void on_start_dragging(const GLCanvas3D::Selection& selection) override;
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override;
|
||||
|
|
|
@ -355,21 +355,6 @@ boost::filesystem::path into_path(const wxString &str)
|
|||
return boost::filesystem::path(str.wx_str());
|
||||
}
|
||||
|
||||
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
|
||||
{
|
||||
const auto idx = wxDisplay::GetFromWindow(window);
|
||||
if (idx == wxNOT_FOUND) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wxDisplay display(idx);
|
||||
const auto disp_size = display.GetClientArea();
|
||||
width = disp_size.GetWidth();
|
||||
height = disp_size.GetHeight();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
AboutDialog dlg;
|
||||
|
|
|
@ -71,9 +71,6 @@ wxString from_path(const boost::filesystem::path &path);
|
|||
// boost path from wxString
|
||||
boost::filesystem::path into_path(const wxString &str);
|
||||
|
||||
// Returns the dimensions of the screen on which the main frame is displayed
|
||||
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
||||
|
||||
// Display an About dialog
|
||||
extern void about();
|
||||
// Ask the destop to open the datadir using the default file explorer.
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/imagpng.h>
|
||||
|
@ -125,6 +127,10 @@ bool GUI_App::OnInit()
|
|||
app_config->save();
|
||||
|
||||
preset_updater = new PresetUpdater();
|
||||
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) {
|
||||
app_config->set("version_online", into_u8(evt.GetString()));
|
||||
app_config->save();
|
||||
});
|
||||
|
||||
load_language();
|
||||
|
||||
|
@ -181,7 +187,6 @@ bool GUI_App::OnInit()
|
|||
mainframe->Close();
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, ex.what());
|
||||
mainframe->Close();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -351,21 +356,10 @@ void GUI_App::persist_window_geometry(wxTopLevelWindow *window)
|
|||
});
|
||||
|
||||
window_pos_restore(window, name);
|
||||
#ifdef _WIN32
|
||||
// On windows, the wxEVT_SHOW is not received if the window is created maximized
|
||||
// cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI
|
||||
// so we sanitize the position right away
|
||||
window_pos_sanitize(window);
|
||||
#else
|
||||
// On other platforms on the other hand it's needed to wait before the window is actually on screen
|
||||
// and some initial round of events is complete otherwise position / display index is not reported correctly.
|
||||
window->Bind(wxEVT_SHOW, [=](wxShowEvent &event) {
|
||||
CallAfter([=]() {
|
||||
window_pos_sanitize(window);
|
||||
});
|
||||
event.Skip();
|
||||
|
||||
on_window_geometry(window, [=]() {
|
||||
window_pos_sanitize(window);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
||||
|
@ -418,6 +412,7 @@ bool GUI_App::select_language( wxArrayString & names,
|
|||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified();
|
||||
m_imgui->set_language(m_wxLocale->GetCanonicalName().ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -446,6 +441,7 @@ bool GUI_App::load_language()
|
|||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified();
|
||||
m_imgui->set_language(m_wxLocale->GetCanonicalName().ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +569,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode")));
|
||||
mode_menu->Check(config_id_base + ConfigMenuModeSimple + get_mode(), true);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comSimple); }, config_id_base + ConfigMenuModeSimple);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comAdvanced); }, config_id_base + ConfigMenuModeAdvanced);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comExpert); }, config_id_base + ConfigMenuModeExpert);
|
||||
|
||||
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), _(L("Slic3r View Mode")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application &Language")));
|
||||
|
@ -692,6 +691,23 @@ void GUI_App::load_current_presets()
|
|||
}
|
||||
}
|
||||
|
||||
bool GUI_App::OnExceptionInMainLoop()
|
||||
{
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::exception &ex) {
|
||||
const std::string error = (boost::format("Uncaught exception: %1%") % ex.what()).str();
|
||||
BOOST_LOG_TRIVIAL(error) << error;
|
||||
show_error(nullptr, from_u8(error));
|
||||
} catch (...) {
|
||||
const char *error = "Uncaught exception: Unknown error";
|
||||
BOOST_LOG_TRIVIAL(error) << error;
|
||||
show_error(nullptr, from_u8(error));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets override to get an event on open files.
|
||||
void GUI_App::MacOpenFiles(const wxArrayString &fileNames)
|
||||
|
|
|
@ -137,6 +137,8 @@ public:
|
|||
bool checked_tab(Tab* tab);
|
||||
void load_current_presets();
|
||||
|
||||
virtual bool OnExceptionInMainLoop();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets override to get an event on open files.
|
||||
void MacOpenFiles(const wxArrayString &fileNames) override;
|
||||
|
|
|
@ -26,6 +26,28 @@ wxTopLevelWindow* find_toplevel_parent(wxWindow *window)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// On windows, the wxEVT_SHOW is not received if the window is created maximized
|
||||
// cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI
|
||||
// OTOH the geometry is available very soon, so we can call the callback right away
|
||||
callback();
|
||||
#elif defined __linux__
|
||||
tlw->Bind(wxEVT_SHOW, [=](wxShowEvent &evt) {
|
||||
// On Linux, the geometry is only available after wxEVT_SHOW + CallAfter
|
||||
// cf. https://groups.google.com/forum/?pli=1#!topic/wx-users/fERSXdpVwAI
|
||||
tlw->CallAfter([=]() { callback(); });
|
||||
evt.Skip();
|
||||
});
|
||||
#elif defined __APPLE__
|
||||
tlw->Bind(wxEVT_SHOW, [=](wxShowEvent &evt) {
|
||||
callback();
|
||||
evt.Skip();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
|
@ -24,6 +25,8 @@ namespace GUI {
|
|||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
||||
|
||||
void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback);
|
||||
|
||||
|
||||
class EventGuard
|
||||
{
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace GUI {
|
|||
|
||||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_font_texture(0)
|
||||
: m_glyph_ranges(nullptr)
|
||||
, m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
, m_mouse_buttons(0)
|
||||
, m_disabled(false)
|
||||
|
@ -49,6 +50,35 @@ bool ImGuiWrapper::init()
|
|||
return true;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_language(const std::string &language)
|
||||
{
|
||||
const ImWchar *ranges = nullptr;
|
||||
size_t idx = language.find('_');
|
||||
std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);
|
||||
static const ImWchar ranges_latin2[] =
|
||||
{
|
||||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||
0x0100, 0x017F, // Latin Extended-A
|
||||
0,
|
||||
};
|
||||
if (lang == "cs" || lang == "pl") {
|
||||
ranges = ranges_latin2;
|
||||
} else if (lang == "ru" || lang == "uk") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic();
|
||||
} else if (lang == "jp") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese();
|
||||
} else if (lang == "kr") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean();
|
||||
} else if (lang == "zh") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
|
||||
}
|
||||
|
||||
if (ranges != m_glyph_ranges) {
|
||||
m_glyph_ranges = ranges;
|
||||
init_default_font(m_style_scaling);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_display_size(float w, float h)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
@ -125,6 +155,12 @@ bool ImGuiWrapper::button(const wxString &label)
|
|||
return ImGui::Button(label_utf8.c_str());
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return ImGui::RadioButton(label_utf8.c_str(), active);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str());
|
||||
|
@ -161,6 +197,28 @@ void ImGuiWrapper::text(const wxString &label)
|
|||
ImGui::Text(label_utf8.c_str(), NULL);
|
||||
}
|
||||
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<wxString>& options, wxString& selection)
|
||||
{
|
||||
std::string selection_u8 = into_u8(selection);
|
||||
|
||||
// this is to force the label to the left of the widget:
|
||||
text(label);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::BeginCombo("", selection_u8.c_str())) {
|
||||
for (const wxString& option : options) {
|
||||
std::string option_u8 = into_u8(option);
|
||||
bool is_selected = (selection_u8.empty()) ? false : (option_u8 == selection_u8);
|
||||
if (ImGui::Selectable(option_u8.c_str(), is_selected))
|
||||
selection = option_u8;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
{
|
||||
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
|
||||
|
@ -210,7 +268,7 @@ void ImGuiWrapper::init_default_font(float scaling)
|
|||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->Clear();
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling);
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling, nullptr, m_glyph_ranges);
|
||||
if (font == nullptr) {
|
||||
font = io.Fonts->AddFontDefault();
|
||||
if (font == nullptr) {
|
||||
|
|
|
@ -20,6 +20,7 @@ class ImGuiWrapper
|
|||
typedef std::map<std::string, ImFont*> FontsMap;
|
||||
|
||||
FontsMap m_fonts;
|
||||
const ImWchar *m_glyph_ranges;
|
||||
unsigned m_font_texture;
|
||||
float m_style_scaling;
|
||||
unsigned m_mouse_buttons;
|
||||
|
@ -32,6 +33,7 @@ public:
|
|||
bool init();
|
||||
void read_glsl_version();
|
||||
|
||||
void set_language(const std::string &language);
|
||||
void set_display_size(float w, float h);
|
||||
void set_style_scaling(float scaling);
|
||||
bool update_mouse_data(wxMouseEvent &evt);
|
||||
|
@ -47,10 +49,12 @@ public:
|
|||
void end();
|
||||
|
||||
bool button(const wxString &label);
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
|
||||
bool checkbox(const wxString &label, bool &value);
|
||||
void text(const wxString &label);
|
||||
bool combo(const wxString& label, const std::vector<wxString>& options, wxString& current_selection);
|
||||
|
||||
void disabled_begin(bool disabled);
|
||||
void disabled_end();
|
||||
|
@ -59,6 +63,7 @@ public:
|
|||
bool want_keyboard() const;
|
||||
bool want_text_input() const;
|
||||
bool want_any_input() const;
|
||||
|
||||
private:
|
||||
void init_default_font(float scaling);
|
||||
void create_device_objects();
|
||||
|
|
|
@ -105,6 +105,12 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
event.Skip();
|
||||
});
|
||||
|
||||
Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& event) {
|
||||
if (m_plater != nullptr && event.GetActive())
|
||||
m_plater->on_activate();
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
wxGetApp().persist_window_geometry(this);
|
||||
|
||||
update_ui_from_settings(); // FIXME (?)
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/statbox.h>
|
||||
|
@ -1008,6 +1007,7 @@ struct Plater::priv
|
|||
std::atomic<bool> arranging;
|
||||
std::atomic<bool> rotoptimizing;
|
||||
bool delayed_scene_refresh;
|
||||
std::string delayed_error_message;
|
||||
|
||||
wxTimer background_process_timer;
|
||||
|
||||
|
@ -1993,6 +1993,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
|||
this->background_process_timer.Stop();
|
||||
// Update the "out of print bed" state of ModelInstances.
|
||||
this->update_print_volume_state();
|
||||
// The delayed error message is no more valid.
|
||||
this->delayed_error_message.clear();
|
||||
// Apply new config to the possibly running background task.
|
||||
bool was_running = this->background_process.running();
|
||||
Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
|
||||
|
@ -2031,8 +2033,18 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
|||
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
|
||||
} else {
|
||||
// The print is not valid.
|
||||
// The error returned from the Print needs to be translated into the local language.
|
||||
GUI::show_error(this->q, _(err));
|
||||
// Only show the error message immediately, if the top level parent of this window is active.
|
||||
auto p = dynamic_cast<wxWindow*>(this->q);
|
||||
while (p->GetParent())
|
||||
p = p->GetParent();
|
||||
auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (top_level_wnd && top_level_wnd->IsActive()) {
|
||||
// The error returned from the Print needs to be translated into the local language.
|
||||
GUI::show_error(this->q, _(err));
|
||||
} else {
|
||||
// Show the error message once the main window gets activated.
|
||||
this->delayed_error_message = _(err);
|
||||
}
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||
}
|
||||
}
|
||||
|
@ -2277,6 +2289,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) {
|
||||
// Update SLA gizmo (reload_scene calls update_gizmos_data)
|
||||
q->canvas3D()->reload_scene(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::on_slicing_completed(wxCommandEvent &)
|
||||
|
@ -3141,6 +3157,26 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::on_activate()
|
||||
{
|
||||
#ifdef __linux__
|
||||
wxWindow *focus_window = wxWindow::FindFocus();
|
||||
// Activating the main frame, and no window has keyboard focus.
|
||||
// Set the keyboard focus to the visible Canvas3D.
|
||||
if (this->p->view3D->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
|
||||
this->p->view3D->get_wxglcanvas()->SetFocus();
|
||||
|
||||
else if (this->p->preview->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
|
||||
this->p->preview->get_wxglcanvas()->SetFocus();
|
||||
#endif
|
||||
|
||||
if (! this->p->delayed_error_message.empty()) {
|
||||
std::string msg = std::move(this->p->delayed_error_message);
|
||||
this->p->delayed_error_message.clear();
|
||||
GUI::show_error(this, msg);
|
||||
}
|
||||
}
|
||||
|
||||
const wxString& Plater::get_project_filename() const
|
||||
{
|
||||
return p->project_filename;
|
||||
|
|
|
@ -151,6 +151,8 @@ public:
|
|||
|
||||
void on_extruders_change(int extruders_count);
|
||||
void on_config_change(const DynamicPrintConfig &config);
|
||||
// On activating the parent window.
|
||||
void on_activate();
|
||||
|
||||
void update_object_menu();
|
||||
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
#include <Windows.h>
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
@ -83,6 +85,16 @@ VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool
|
|||
return VendorProfile::from_ini(tree, path, load_all);
|
||||
}
|
||||
|
||||
static const std::unordered_map<std::string, std::string> pre_family_model_map {{
|
||||
{ "MK3", "MK3" },
|
||||
{ "MK3MMU2", "MK3" },
|
||||
{ "MK2.5", "MK2.5" },
|
||||
{ "MK2.5MMU2", "MK2.5" },
|
||||
{ "MK2S", "MK2" },
|
||||
{ "MK2SMM", "MK2" },
|
||||
{ "SL1", "SL1" },
|
||||
}};
|
||||
|
||||
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
|
||||
{
|
||||
static const std::string printer_model_key = "printer_model:";
|
||||
|
@ -128,11 +140,21 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
VendorProfile::PrinterModel model;
|
||||
model.id = section.first.substr(printer_model_key.size());
|
||||
model.name = section.second.get<std::string>("name", model.id);
|
||||
auto technology_field = section.second.get<std::string>("technology", "FFF");
|
||||
|
||||
const char *technology_fallback = boost::algorithm::starts_with(model.id, "SL") ? "SLA" : "FFF";
|
||||
|
||||
auto technology_field = section.second.get<std::string>("technology", technology_fallback);
|
||||
if (! ConfigOptionEnum<PrinterTechnology>::from_string(technology_field, model.technology)) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field;
|
||||
model.technology = ptFFF;
|
||||
}
|
||||
|
||||
model.family = section.second.get<std::string>("family", std::string());
|
||||
if (model.family.empty() && res.name == "Prusa Research") {
|
||||
// If no family is specified, it can be inferred for known printers
|
||||
const auto from_pre_map = pre_family_model_map.find(model.id);
|
||||
if (from_pre_map != pre_family_model_map.end()) { model.family = from_pre_map->second; }
|
||||
}
|
||||
#if 0
|
||||
// Remove SLA printers from the initial alpha.
|
||||
if (model.technology == ptSLA)
|
||||
|
@ -157,6 +179,20 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
|
|||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> VendorProfile::families() const
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
unsigned num_familiies = 0;
|
||||
|
||||
for (auto &model : models) {
|
||||
if (std::find(res.begin(), res.end(), model.family) == res.end()) {
|
||||
res.push_back(model.family);
|
||||
num_familiies++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Suffix to be added to a modified preset name in the combo box.
|
||||
static std::string g_suffix_modified = " (modified)";
|
||||
|
@ -422,9 +458,8 @@ const std::vector<std::string>& Preset::sla_print_options()
|
|||
"support_critical_angle",
|
||||
"support_max_bridge_length",
|
||||
"support_object_elevation",
|
||||
"support_density_at_horizontal",
|
||||
"support_density_at_45",
|
||||
"support_minimal_z",
|
||||
"support_points_density_relative",
|
||||
"support_points_minimal_distance",
|
||||
"pad_enable",
|
||||
"pad_wall_thickness",
|
||||
"pad_wall_height",
|
||||
|
|
|
@ -54,13 +54,16 @@ public:
|
|||
std::string id;
|
||||
std::string name;
|
||||
PrinterTechnology technology;
|
||||
std::string family;
|
||||
std::vector<PrinterVariant> variants;
|
||||
|
||||
PrinterVariant* variant(const std::string &name) {
|
||||
for (auto &v : this->variants)
|
||||
if (v.name == name)
|
||||
return &v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); }
|
||||
};
|
||||
std::vector<PrinterModel> models;
|
||||
|
@ -72,6 +75,7 @@ public:
|
|||
static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
|
||||
|
||||
size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }
|
||||
std::vector<std::string> families() const;
|
||||
|
||||
bool operator< (const VendorProfile &rhs) const { return this->id < rhs.id; }
|
||||
bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
@ -24,10 +25,12 @@ namespace fs = boost::filesystem;
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static const char *CONFIG_KEY_PATH = "printhost_path";
|
||||
static const char *CONFIG_KEY_PRINT = "printhost_print";
|
||||
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||
, txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring()))
|
||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||
, box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
@ -44,11 +47,30 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
|||
|
||||
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
|
||||
|
||||
txt_filename->SetFocus();
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
box_print->SetValue(app_config->get("recent", CONFIG_KEY_PRINT) == "1");
|
||||
|
||||
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
|
||||
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
|
||||
recent_path += '/';
|
||||
}
|
||||
const auto recent_path_len = recent_path.Length();
|
||||
recent_path += path.filename().wstring();
|
||||
wxString stem(path.stem().wstring());
|
||||
txt_filename->SetSelection(0, stem.Length());
|
||||
const auto stem_len = stem.Length();
|
||||
|
||||
txt_filename->SetValue(recent_path);
|
||||
txt_filename->SetFocus();
|
||||
|
||||
Fit();
|
||||
|
||||
Bind(wxEVT_SHOW, [=](const wxShowEvent &) {
|
||||
// Another similar case where the function only works with EVT_SHOW + CallAfter,
|
||||
// this time on Mac.
|
||||
CallAfter([=]() {
|
||||
txt_filename->SetSelection(recent_path_len, recent_path_len + stem_len);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fs::path PrintHostSendDialog::filename() const
|
||||
|
@ -61,6 +83,24 @@ bool PrintHostSendDialog::start_print() const
|
|||
return box_print->GetValue();
|
||||
}
|
||||
|
||||
void PrintHostSendDialog::EndModal(int ret)
|
||||
{
|
||||
if (ret == wxID_OK) {
|
||||
// Persist path and print settings
|
||||
wxString path = txt_filename->GetValue();
|
||||
int last_slash = path.Find('/', true);
|
||||
if (last_slash != wxNOT_FOUND) {
|
||||
path = path.SubString(0, last_slash);
|
||||
wxGetApp().app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
|
||||
}
|
||||
|
||||
bool print = box_print->GetValue();
|
||||
GUI::get_app_config()->set("recent", CONFIG_KEY_PRINT, print ? "1" : "0");
|
||||
}
|
||||
|
||||
MsgDialog::EndModal(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
boost::filesystem::path filename() const;
|
||||
bool start_print() const;
|
||||
|
||||
virtual void EndModal(int ret) override;
|
||||
private:
|
||||
wxTextCtrl *txt_filename;
|
||||
wxCheckBox *box_print;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "3DScene.hpp"
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/platinfo.h>
|
||||
|
||||
|
|
|
@ -3210,9 +3210,8 @@ void TabSLAPrint::build()
|
|||
optgroup->append_single_option_line("support_max_bridge_length");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Automatic generation")));
|
||||
optgroup->append_single_option_line("support_density_at_horizontal");
|
||||
optgroup->append_single_option_line("support_density_at_45");
|
||||
optgroup->append_single_option_line("support_minimal_z");
|
||||
optgroup->append_single_option_line("support_points_density_relative");
|
||||
optgroup->append_single_option_line("support_points_minimal_distance");
|
||||
|
||||
page = add_options_page(_(L("Pad")), "brick.png");
|
||||
optgroup = page->new_optgroup(_(L("Pad")));
|
||||
|
|
|
@ -244,7 +244,7 @@ void Http::priv::http_perform()
|
|||
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
||||
#endif
|
||||
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 4);
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 5);
|
||||
|
||||
if (headerlist != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
@ -90,9 +89,25 @@ struct Updates
|
|||
std::vector<Update> updates;
|
||||
};
|
||||
|
||||
static Semver get_slic3r_version()
|
||||
{
|
||||
auto res = Semver::parse(SLIC3R_VERSION);
|
||||
|
||||
if (! res) {
|
||||
const char *error = "Could not parse Slic3r version string: " SLIC3R_VERSION;
|
||||
BOOST_LOG_TRIVIAL(error) << error;
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
return *res;
|
||||
}
|
||||
|
||||
wxDEFINE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
|
||||
|
||||
|
||||
struct PresetUpdater::priv
|
||||
{
|
||||
const Semver ver_slic3r;
|
||||
std::vector<Index> index_db;
|
||||
|
||||
bool enabled_version_check;
|
||||
|
@ -122,12 +137,13 @@ struct PresetUpdater::priv
|
|||
static void copy_file(const fs::path &from, const fs::path &to);
|
||||
};
|
||||
|
||||
PresetUpdater::priv::priv() :
|
||||
had_config_update(false),
|
||||
cache_path(fs::path(Slic3r::data_dir()) / "cache"),
|
||||
rsrc_path(fs::path(resources_dir()) / "profiles"),
|
||||
vendor_path(fs::path(Slic3r::data_dir()) / "vendor"),
|
||||
cancel(false)
|
||||
PresetUpdater::priv::priv()
|
||||
: ver_slic3r(get_slic3r_version())
|
||||
, had_config_update(false)
|
||||
, cache_path(fs::path(Slic3r::data_dir()) / "cache")
|
||||
, rsrc_path(fs::path(resources_dir()) / "profiles")
|
||||
, vendor_path(fs::path(Slic3r::data_dir()) / "vendor")
|
||||
, cancel(false)
|
||||
{
|
||||
set_download_prefs(GUI::wxGetApp().app_config);
|
||||
check_install_indices();
|
||||
|
@ -209,11 +225,10 @@ void PresetUpdater::priv::sync_version() const
|
|||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
boost::trim(body);
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body;
|
||||
// wxCommandEvent* evt = new wxCommandEvent(version_online_event);
|
||||
// evt->SetString(body);
|
||||
// GUI::get_app()->QueueEvent(evt);
|
||||
GUI::wxGetApp().app_config->set("version_online", body);
|
||||
GUI::wxGetApp().app_config->save();
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
|
||||
evt->SetString(GUI::from_u8(body));
|
||||
GUI::wxGetApp().QueueEvent(evt);
|
||||
})
|
||||
.perform_sync();
|
||||
}
|
||||
|
@ -260,7 +275,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
|
|||
continue;
|
||||
}
|
||||
if (new_index.version() < index.version()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.") % idx_path_temp % vendor.name;
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.") % idx_path_temp % vendor.name;
|
||||
continue;
|
||||
}
|
||||
Slic3r::rename_file(idx_path_temp, idx_path);
|
||||
|
@ -275,6 +290,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
|
|||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto recommended = recommended_it->config_version;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%")
|
||||
|
@ -341,7 +357,8 @@ Updates PresetUpdater::priv::get_config_updates() const
|
|||
if (ver_current == idx.end()) {
|
||||
auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
|
||||
BOOST_LOG_TRIVIAL(error) << message;
|
||||
throw std::runtime_error(message);
|
||||
GUI::show_error(nullptr, GUI::from_u8(message));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Getting a recommended version from the latest index, wich may have been downloaded
|
||||
|
@ -528,18 +545,14 @@ void PresetUpdater::slic3r_update_notify()
|
|||
}
|
||||
|
||||
auto* app_config = GUI::wxGetApp().app_config;
|
||||
const auto ver_slic3r = Semver::parse(SLIC3R_VERSION);
|
||||
const auto ver_online_str = app_config->get("version_online");
|
||||
const auto ver_online = Semver::parse(ver_online_str);
|
||||
const auto ver_online_seen = Semver::parse(app_config->get("version_online_seen"));
|
||||
if (! ver_slic3r) {
|
||||
throw std::runtime_error("Could not parse Slic3r version string: " SLIC3R_VERSION);
|
||||
}
|
||||
|
||||
if (ver_online) {
|
||||
// Only display the notification if the version available online is newer AND if we haven't seen it before
|
||||
if (*ver_online > *ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) {
|
||||
GUI::MsgUpdateSlic3r notification(*ver_slic3r, *ver_online);
|
||||
if (*ver_online > p->ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) {
|
||||
GUI::MsgUpdateSlic3r notification(p->ver_slic3r, *ver_online);
|
||||
notification.ShowModal();
|
||||
if (notification.disable_version_check()) {
|
||||
app_config->set("version_check", "0");
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
|
@ -37,6 +39,8 @@ private:
|
|||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue