From 10bfa82107f576f43b11d02c8ac55345cf959709 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 14 Dec 2018 09:57:48 +0100 Subject: [PATCH 01/57] Fix msvc build --- src/libslic3r/SLAPrint.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 328dc5483..9d35a4a34 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -423,6 +423,7 @@ void swapXY(ExPolygon& expoly) { void SLAPrint::process() { using namespace sla; + using ExPolygon = Slic3r::ExPolygon; // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered From 48a94ebae14cf99726bb83369eb7886db0d70340 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Dec 2018 09:52:54 +0100 Subject: [PATCH 02/57] Fixed scroll the Object List to selected item on OSX & GTK + fixed #1395 (maximum PresetComboBox's width limit on Ubuntu) --- src/slic3r/GUI/GUI_ObjectList.cpp | 8 +++----- src/slic3r/GUI/GUI_ObjectList.hpp | 2 -- src/slic3r/GUI/Plater.cpp | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 575444c4b..a90dff621 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1459,13 +1459,11 @@ void ObjectList::update_selections() select_items(sels); -#ifdef __WXMSW__ if (GetSelection()) { - const int sel_item_row = GetRowByItem(GetSelection()); - ScrollLines(sel_item_row - m_selected_row); - m_selected_row = sel_item_row; + const wxRect& top_rc = GetItemRect(GetTopItem()); + const wxRect& sel_rc = GetItemRect(GetSelection()); + ScrollLines(int((sel_rc.y - top_rc.y) / top_rc.GetHeight()) - 0.5*GetCountPerPage()); } -#endif //__WXMSW__ } void ObjectList::update_selections_on_canvas() diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 7631782df..3664e6fda 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -108,8 +108,6 @@ class ObjectList : public wxDataViewCtrl bool m_parts_changed = false; bool m_part_settings_changed = false; - int m_selected_row = 0; - public: ObjectList(wxWindow* parent); ~ObjectList(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b1526fdb4..381a9e864 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -213,7 +213,7 @@ void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const w } PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : - wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY), + wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,-1), 0, nullptr, wxCB_READONLY), preset_type(preset_type), last_selected(wxNOT_FOUND) { From 2be23c8b149e4c4713707861f0528da06b9def59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 14 Dec 2018 13:45:58 +0100 Subject: [PATCH 03/57] Eliminate build warning with TBB_USE_CAPTURED_EXCEPTION also make small adjustment on wxWidgets detection on Unix. --- src/CMakeLists.txt | 13 ++++++++++++- src/libnest2d/CMakeLists.txt | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d714eb91a..31c801379 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,7 +46,18 @@ endif() add_subdirectory(libslic3r) if (SLIC3R_GUI) - message(STATUS "WXWIN environment set to: $ENV{WXWIN}") + if(WIN32) + message(STATUS "WXWIN environment set to: $ENV{WXWIN}") + elseif(UNIX) + message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}") + set(wxWidgets_USE_UNICODE ON) + if(SLIC3R_STATIC) + set(wxWidgets_USE_STATIC ON) + else() + set(wxWidgets_USE_STATIC OFF) + endif() + endif() + find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl) include(${wxWidgets_USE_FILE}) endif() diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 163143bcb..1faf542dd 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -68,7 +68,7 @@ if(TBB_FOUND) target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE) endif() # The Intel TBB library will use the std::exception_ptr feature of C++11. - target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=1) + target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0) target_link_libraries(libnest2d INTERFACE tbb) else() From dae01af2aca8a872a0393c9461c98691e3de9332 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 14 Dec 2018 16:58:55 +0100 Subject: [PATCH 04/57] Logging during sla processing. --- src/libslic3r/SLAPrint.cpp | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0c61b591b..96b304ac2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -420,6 +420,12 @@ void swapXY(ExPolygon& expoly) { } +template +void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) { + BOOST_LOG_TRIVIAL(info) << st << "% " << msg; + p.set_status(st, msg, std::forward(args)...); +} + void SLAPrint::process() { using namespace sla; @@ -524,9 +530,11 @@ void SLAPrint::process() // scaling for the sub operations double d = *stthis / (objcount * 100.0); - ctl.statuscb = [this, init, d](unsigned st, const std::string& msg){ - set_status(int(init + st*d), msg); + ctl.statuscb = [this, init, d](unsigned st, const std::string& msg) + { + report_status(*this, int(init + st*d), msg); }; + ctl.stopcondition = [this](){ return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; @@ -536,9 +544,9 @@ void SLAPrint::process() // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; - set_status(-1, L("Visualizing supports")); + report_status(*this, -1, L("Visualizing supports")); po.m_supportdata->support_tree_ptr->merged_mesh(); - set_status(-1, L("Visualizing supports"), rc); + report_status(*this, -1, L("Visualizing supports"), rc); } catch(sla::SLASupportsStoppedException&) { // no need to rethrow // throw_if_canceled(); @@ -581,7 +589,7 @@ void SLAPrint::process() po.throw_if_canceled(); auto rc = SlicingStatus::RELOAD_SCENE; - set_status(-1, L("Visualizing supports"), rc); + report_status(*this, -1, L("Visualizing supports"), rc); }; // Slicing the support geometries similarly to the model slicing procedure. @@ -740,8 +748,12 @@ void SLAPrint::process() auto lvlcnt = unsigned(m_printer_input.size()); printer.layers(lvlcnt); + // slot is the portion of 100% that is realted to rasterization unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; + // ist: initial state; pst: previous state unsigned ist = max_objstatus, pst = ist; + // coefficient to map the rasterization state (0-99) to the allocated + // portion (slot) of the process state double sd = (100 - ist) / 100.0; SpinMutex slck; @@ -778,11 +790,11 @@ void SLAPrint::process() // Finish the layer for later saving it. printer.finish_layer(level_id); - // Status indication + // Status indication guarded with the spinlock auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size()); { std::lock_guard lck(slck); if( st > pst) { - set_status(int(st), PRINT_STEP_LABELS[slapsRasterize]); + report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]); pst = st; } } @@ -832,9 +844,14 @@ void SLAPrint::process() unsigned st = min_objstatus; unsigned incr = 0; + BOOST_LOG_TRIVIAL(info) << "Start slicing process."; + // TODO: this loop could run in parallel but should not exhaust all the CPU // power available for(SLAPrintObject * po : m_objects) { + + BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name; + for(size_t s = 0; s < objectsteps.size(); ++s) { auto currentstep = objectsteps[s]; @@ -846,8 +863,7 @@ void SLAPrint::process() st += unsigned(incr * ostepd); if(po->m_stepmask[currentstep] && po->set_started(currentstep)) { - - set_status(int(st), OBJ_STEP_LABELS[currentstep]); + report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]); pobj_program[currentstep](*po); po->set_done(currentstep); } @@ -872,7 +888,7 @@ void SLAPrint::process() if(m_stepmask[currentstep] && set_started(currentstep)) { - set_status(int(st), PRINT_STEP_LABELS[currentstep]); + report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]); print_program[currentstep](); set_done(currentstep); } @@ -881,7 +897,7 @@ void SLAPrint::process() } // If everything vent well - set_status(100, L("Slicing done")); + report_status(*this, 100, L("Slicing done")); } bool SLAPrint::invalidate_state_by_config_options(const std::vector &opt_keys) From 907e51053553e393e95768b1a7bc2f9e7c5135c7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Dec 2018 16:07:28 +0100 Subject: [PATCH 05/57] Changed PrusaBitmapTextRenderer's inheritance to wxDataViewRenderer instead of wxDataViewCustomRenderer : to fix name editing under OSX --- src/slic3r/GUI/GUI_ObjectList.cpp | 4 +++- src/slic3r/GUI/wxExtensions.cpp | 34 +++++++++++++++++++++++++++---- src/slic3r/GUI/wxExtensions.hpp | 14 +++++++------ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a90dff621..78310443d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1654,7 +1654,9 @@ void ObjectList::change_part_type() void ObjectList::last_volume_is_deleted(const int obj_idx) { - if (obj_idx < 0 || (*m_objects).empty() || (*m_objects)[obj_idx]->volumes.empty()) + if (obj_idx < 0 || m_objects->empty() || + obj_idx <= m_objects->size() || + (*m_objects)[obj_idx]->volumes.empty()) return; auto volume = (*m_objects)[obj_idx]->volumes[0]; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 2e5a37040..710710913 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1228,6 +1228,14 @@ void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const i ItemChanged(item); } +PrusaBitmapTextRenderer::PrusaBitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, + int align /*= wxDVR_DEFAULT_ALIGNMENT*/): +wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) +{ + SetMode(mode); + SetAlignment(align); +} + //----------------------------------------------------------------------------- // PrusaDataViewBitmapText //----------------------------------------------------------------------------- @@ -1251,6 +1259,13 @@ bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const return false; } +#if wxUSE_ACCESSIBILITY +wxString PrusaBitmapTextRenderer::GetAccessibleDescription() const +{ + return m_value.GetText(); +} +#endif // wxUSE_ACCESSIBILITY + bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) { int xoffset = 0; @@ -1291,12 +1306,12 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab PrusaDataViewBitmapText data; data << value; - m_bmp_from_editing_item = data.GetBitmap(); + m_was_unusable_symbol = false; wxPoint position = labelRect.GetPosition(); - if (m_bmp_from_editing_item.IsOk()) { - const int bmp_width = m_bmp_from_editing_item.GetWidth(); + if (data.GetBitmap().IsOk()) { + const int bmp_width = data.GetBitmap().GetWidth(); position.x += bmp_width; labelRect.SetWidth(labelRect.GetWidth() - bmp_width); } @@ -1304,6 +1319,7 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), position, labelRect.GetSize(), wxTE_PROCESS_ENTER); text_editor->SetInsertionPointEnd(); + text_editor->SelectAll(); return text_editor; } @@ -1323,7 +1339,17 @@ bool PrusaBitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& } } - value << PrusaDataViewBitmapText(text_editor->GetValue(), m_bmp_from_editing_item); + // The icon can't be edited so get its old value and reuse it. + wxVariant valueOld; + GetView()->GetModel()->GetValue(valueOld, m_item, 0); + + PrusaDataViewBitmapText bmpText; + bmpText << valueOld; + + // But replace the text with the value entered by user. + bmpText.SetText(text_editor->GetValue()); + + value << bmpText; return true; } diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 3fbf9a083..e3fd1cbf6 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -519,15 +519,18 @@ public: // PrusaBitmapTextRenderer // ---------------------------------------------------------------------------- -class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer +class PrusaBitmapTextRenderer : public wxDataViewRenderer//CustomRenderer { public: - PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, - int align = wxDVR_DEFAULT_ALIGNMENT): - wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {} + PrusaBitmapTextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, + int align = wxDVR_DEFAULT_ALIGNMENT);//: +// wxDataViewRenderer/*CustomRenderer*/(wxT("PrusaDataViewBitmapText"), mode, align) {} bool SetValue(const wxVariant &value); bool GetValue(wxVariant &value) const; +#if wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const override; +#endif // wxUSE_ACCESSIBILITY virtual bool Render(wxRect cell, wxDC *dc, int state); virtual wxSize GetSize() const; @@ -542,8 +545,7 @@ public: private: PrusaDataViewBitmapText m_value; - wxBitmap m_bmp_from_editing_item; - bool m_was_unusable_symbol; + bool m_was_unusable_symbol {false}; }; From 880c1ef2b4ea05cef2d5db28531efe80db06fc57 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Dec 2018 17:09:44 +0100 Subject: [PATCH 06/57] Next try to fix scroll the Object List to selected item (OSX & GTK) --- src/slic3r/GUI/GUI_ObjectList.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 78310443d..a7f1ca608 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1460,9 +1460,12 @@ void ObjectList::update_selections() select_items(sels); if (GetSelection()) { - const wxRect& top_rc = GetItemRect(GetTopItem()); const wxRect& sel_rc = GetItemRect(GetSelection()); - ScrollLines(int((sel_rc.y - top_rc.y) / top_rc.GetHeight()) - 0.5*GetCountPerPage()); + if (!sel_rc.IsEmpty()) { + const int rc_h = sel_rc.height; + const int displ = GetMainWindow()->GetClientRect().GetHeight()/(2*rc_h)+1; + ScrollLines(int(sel_rc.y / rc_h - displ)); + } } } From 506cbcb4a7027834bd07038c8e186cda197915bd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Dec 2018 17:17:51 +0100 Subject: [PATCH 07/57] Fix of G-code invalidation on "wipe_into_object" "wipe_into_infill" changes. WIP: Fix of "levitating objects cannot be sliced". --- src/libslic3r/Layer.cpp | 10 ++++++++++ src/libslic3r/Layer.hpp | 3 ++- src/libslic3r/PrintObject.cpp | 24 +++++++++++++++--------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 7878bffab..fa5d29692 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -17,6 +17,16 @@ Layer::~Layer() m_regions.clear(); } +// Test whether whether there are any slices assigned to this layer. +bool Layer::empty() const +{ + for (const LayerRegion *layerm : m_regions) + if (layerm != nullptr && ! layerm->slices.empty()) + // Non empty layer. + return false; + return true; +} + LayerRegion* Layer::add_region(PrintRegion* print_region) { m_regions.emplace_back(new LayerRegion(this, print_region)); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 8dbe850cc..78897a2db 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -114,7 +114,8 @@ public: LayerRegion* get_region(int idx) { return m_regions[idx]; } LayerRegion* add_region(PrintRegion* print_region); const LayerRegionPtrs& regions() const { return m_regions; } - + // Test whether whether there are any slices assigned to this layer. + bool empty() const; void make_slices(); void merge_slices(); template bool any_internal_region_slice_contains(const T &item) const { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4b7a8906f..772d930aa 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -384,6 +384,12 @@ void PrintObject::generate_support_material() m_print->set_status(85, "Generating support material"); this->_generate_support_material(); m_print->throw_if_canceled(); + } else { + // Printing without supports. Empty layer means some objects or object parts are levitating, + // therefore they cannot be printed without supports. + for (const Layer *layer : m_layers) + if (layer->empty()) + throw std::runtime_error("Levitating objects cannot be printed without supports."); } this->set_done(posSupportMaterial); } @@ -522,11 +528,13 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_step(psGCodeExport); + } else if ( + opt_key == "wipe_into_infill" + || opt_key == "wipe_into_objects") { + invalidated |= m_print->invalidate_step(psWipeTower); + invalidated |= m_print->invalidate_step(psGCodeExport); } else { // for legacy, if we can't handle this option let's invalidate all steps this->invalidate_all_steps(); @@ -1463,10 +1471,8 @@ void PrintObject::_slice() BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; while (! m_layers.empty()) { const Layer *layer = m_layers.back(); - for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) - if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty()) - // Non empty layer. - goto end; + if (! layer->empty()) + goto end; delete layer; m_layers.pop_back(); if (! m_layers.empty()) From d6471e7b071129a49f936a85697d5e8c3759730f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Dec 2018 17:19:17 +0100 Subject: [PATCH 08/57] Fix of SLADisplayOrientation updates --- src/libslic3r/PrintConfig.cpp | 6 ++++-- src/libslic3r/PrintConfig.hpp | 4 ++-- src/slic3r/GUI/Field.cpp | 2 ++ src/slic3r/GUI/GUI.cpp | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ab26f5d54..8a564b6b5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2394,8 +2394,10 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Display orientation"); def->cli = "display-orientation=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("Landscape"); - def->enum_values.push_back("Portrait"); + def->enum_values.push_back("landscape"); + def->enum_values.push_back("portrait"); + def->enum_labels.push_back(L("Landscape")); + def->enum_labels.push_back(L("Portrait")); def->default_value = new ConfigOptionEnum(sladoPortrait); def = this->add("printer_correction", coFloats); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f639cad76..1949a8ac8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -155,8 +155,8 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::ge template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static const t_config_enum_values keys_map = { - { "Landscape", sladoLandscape}, - { "Portrait", sladoPortrait} + { "landscape", sladoLandscape}, + { "portrait", sladoPortrait} }; return keys_map; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index ebc7f3665..d4126d933 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -638,6 +638,8 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("host_type") == 0) m_value = static_cast(ret_enum); + else if (m_opt_id.compare("display_orientation") == 0) + m_value = static_cast(ret_enum); } return m_value; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index bc7ea9899..fb6676aed 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -195,6 +195,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if (opt_key.compare("host_type") == 0) config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if (opt_key.compare("display_orientation") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); } break; case coPoints:{ From 77d37f108c3ba68afaebda9f54933f2b4185aaab Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Dec 2018 19:29:58 +0100 Subject: [PATCH 09/57] Iterative, not recursive, version of the Douglas-Peucker-Ramer algorithm based on the work by @fuchstraumer https://github.com/slic3r/Slic3r/pull/3825 https://gist.github.com/fuchstraumer/9421573fc281b946e5f561758961212a which was based on http://anis-moussa.blogspot.com/2014/03/ramer-douglas-peucker-algorithm-for.html --- src/libslic3r/Line.cpp | 23 +++++------ src/libslic3r/Line.hpp | 6 ++- src/libslic3r/MultiPoint.cpp | 78 +++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 35cfa2b76..02f1cb7c2 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -34,23 +34,22 @@ bool Line::intersection_infinite(const Line &other, Point* point) const return true; } -/* distance to the closest point of line */ -double Line::distance_to(const Point &point) const +// Distance to the closest point of line. +double Line::distance_to_squared(const Point &point, const Point &a, const Point &b) { - const Line &line = *this; - const Vec2d v = (line.b - line.a).cast(); - const Vec2d va = (point - line.a).cast(); + const Vec2d v = (b - a).cast(); + const Vec2d va = (point - a).cast(); const double l2 = v.squaredNorm(); // avoid a sqrt if (l2 == 0.0) - // line.a == line.b case - return va.norm(); - // Consider the line extending the segment, parameterized as line.a + t (line.b - line.a). + // a == b case + return va.squaredNorm(); + // Consider the line extending the segment, parameterized as a + t (b - a). // We find projection of this point onto the line. - // It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2 + // It falls where t = [(this-a) . (b-a)] / |b-a|^2 const double t = va.dot(v) / l2; - if (t < 0.0) return va.norm(); // beyond the 'a' end of the segment - else if (t > 1.0) return (point - line.b).cast().norm(); // beyond the 'b' end of the segment - return (t * v - va).norm(); + if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment + else if (t > 1.0) return (point - b).cast().squaredNorm(); // beyond the 'b' end of the segment + return (t * v - va).squaredNorm(); } double Line::perp_distance_to(const Point &point) const diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 36e02247c..559ca946a 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -31,7 +31,8 @@ public: Point midpoint() const { return (this->a + this->b) / 2; } bool intersection_infinite(const Line &other, Point* point) const; bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } - double distance_to(const Point &point) const; + double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); } + double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); } double perp_distance_to(const Point &point) const; bool parallel_to(double angle) const; bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); } @@ -43,6 +44,9 @@ public: bool intersection(const Line& line, Point* intersection) const; double ccw(const Point& point) const { return point.ccw(*this); } + static double distance_to_squared(const Point &point, const Point &a, const Point &b); + static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); } + Point a; Point b; }; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 93c63c4cf..795ee38c2 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -162,45 +162,51 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const return found; } -//FIXME This is very inefficient in term of memory use. -// The recursive algorithm shall run in place, not allocating temporary data in each recursion. -Points -MultiPoint::_douglas_peucker(const Points &points, const double tolerance) +std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) { - assert(points.size() >= 2); - Points results; - double dmax = 0; - size_t index = 0; - Line full(points.front(), points.back()); - for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { - // we use shortest distance, not perpendicular distance - double d = full.distance_to(*it); - if (d > dmax) { - index = it - points.begin(); - dmax = d; + std::vector result_pts; + if (! pts.empty()) { + const Point *anchor = &pts.front(); + size_t anchor_idx = 0; + const Point *floater = &pts.back(); + size_t floater_idx = pts.size() - 1; + result_pts.reserve(pts.size()); + result_pts.emplace_back(*anchor); + if (anchor_idx != floater_idx) { + assert(pts.size() > 1); + std::vector dpStack; + dpStack.reserve(pts.size()); + dpStack.emplace_back(floater_idx); + for (;;) { + double max_distSq = 0.0; + size_t furthest_idx = anchor_idx; + // find point furthest from line seg created by (anchor, floater) and note it + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { + double dist = Line::distance_to_squared(pts[i], *anchor, *floater); + if (dist > max_distSq) { + max_distSq = dist; + furthest_idx = i; + } + } + // remove point if less than tolerance + if (max_distSq <= tolerance) { + result_pts.emplace_back(*floater); + anchor_idx = floater_idx; + anchor = floater; + assert(dpStack.back() == floater_idx); + dpStack.pop_back(); + if (dpStack.empty()) + break; + floater_idx = dpStack.back(); + } else { + floater_idx = furthest_idx; + dpStack.emplace_back(floater_idx); + } + floater = &pts[floater_idx]; + } } } - if (dmax >= tolerance) { - Points dp0; - dp0.reserve(index + 1); - dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1); - // Recursive call. - Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); - results.reserve(results.size() + dp1.size() - 1); - results.insert(results.end(), dp1.begin(), dp1.end() - 1); - - dp0.clear(); - dp0.reserve(points.size() - index); - dp0.insert(dp0.end(), points.begin() + index, points.end()); - // Recursive call. - dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); - results.reserve(results.size() + dp1.size()); - results.insert(results.end(), dp1.begin(), dp1.end()); - } else { - results.push_back(points.front()); - results.push_back(points.back()); - } - return results; + return result_pts; } // Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825 From 37806c020a1e4bcc54d9349ba009c7d4acd0d73b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Dec 2018 19:40:02 +0100 Subject: [PATCH 10/57] Temporarily disabled the following check: "Levitating objects cannot be printed without support" until the unit tests are adjusted. --- src/libslic3r/PrintObject.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 772d930aa..fdf950ee5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -385,11 +385,13 @@ void PrintObject::generate_support_material() this->_generate_support_material(); m_print->throw_if_canceled(); } else { +#if 0 // Printing without supports. Empty layer means some objects or object parts are levitating, // therefore they cannot be printed without supports. for (const Layer *layer : m_layers) if (layer->empty()) throw std::runtime_error("Levitating objects cannot be printed without supports."); +#endif } this->set_done(posSupportMaterial); } From 6da83c797690afbb4c455e2a90e54fe3d6cc9ed3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Dec 2018 20:09:10 +0100 Subject: [PATCH 11/57] Make the increase of extruder motor current during MM filament exchange sequences configurable. --- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 24 +++++++++++++----------- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 5 ++++- src/libslic3r/Print.cpp | 4 +++- src/libslic3r/PrintConfig.cpp | 9 +++++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/slic3r/GUI/Preset.cpp | 3 ++- src/slic3r/GUI/Tab.cpp | 1 + 7 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 5c24cd71c..db38a7462 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -505,8 +505,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( .speed_override(100); writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position - .travel(cleaning_box.ld, 7200) - .set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. + .travel(cleaning_box.ld, 7200); + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { unsigned int tool = tools[idx_tool]; @@ -533,8 +534,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // in the output gcode - we should not remember emitting them (we will output them twice in the worst case) // Reset the extruder current to a normal value. - writer.set_extruder_trimpot(550) - .feedrate(6000) + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); + writer.feedrate(6000) .flush_planner_queue() .reset_extruder() .append("; CP PRIMING END\n" @@ -607,7 +609,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); // Increase the extruder driver current to allow fast ramming. - writer.set_extruder_trimpot(750); + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. if (tool != (unsigned int)-1){ // This is not the last change. @@ -635,8 +638,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo } } - writer.set_extruder_trimpot(550) // Reset the extruder current to a normal value. - .feedrate(6000) + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value. + writer.feedrate(6000) .flush_planner_queue() .reset_extruder() .append("; CP TOOLCHANGE END\n" @@ -916,12 +920,10 @@ void WipeTowerPrusaMM::toolchange_Load( .resume_preview(); // Reset the extruder current to the normal value. - writer.set_extruder_trimpot(550); + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); } - - - // Wipe the newly loaded filament until the end of the assigned wipe area. void WipeTowerPrusaMM::toolchange_Wipe( PrusaMultiMaterial::Writer &writer, diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 06625d189..c0aeee3ae 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -44,7 +44,8 @@ public: // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction, - float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging, + float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, + float bridging, bool set_extruder_trimpot, const std::vector>& wiping_matrix, unsigned int initial_tool) : m_wipe_tower_pos(x, y), m_wipe_tower_width(width), @@ -57,6 +58,7 @@ public: m_parking_pos_retraction(parking_pos_retraction), m_extra_loading_move(extra_loading_move), m_bridging(bridging), + m_set_extruder_trimpot(set_extruder_trimpot), m_current_tool(initial_tool), wipe_volumes(wiping_matrix) {} @@ -212,6 +214,7 @@ private: float m_parking_pos_retraction = 0.f; float m_extra_loading_move = 0.f; float m_bridging = 0.f; + bool m_set_extruder_trimpot = false; bool m_adhesion = true; float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 77cf9c0f6..e3382573c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -213,6 +213,7 @@ bool Print::invalidate_state_by_config_options(const std::vectormode = comExpert; def->default_value = new ConfigOptionEnum(gcfRepRap); + def = this->add("high_current_on_filament_swap", coBool); + def->label = L("High extruder current on filament swap"); + def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" + " sequence to allow for rapid ramming feed rates and to overcome resistance when loading" + " a filament with an ugly shaped tip."); + def->cli = "high-current-on-filament-swap!"; + def->mode = comExpert; + def->default_value = new ConfigOptionBool(0); + def = this->add("infill_acceleration", coFloat); def->label = L("Infill"); def->tooltip = L("This is the acceleration your printer will use for infill. Set zero to disable " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1949a8ac8..487e35bf2 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -627,6 +627,7 @@ public: ConfigOptionBool variable_layer_height; ConfigOptionFloat cooling_tube_retraction; ConfigOptionFloat cooling_tube_length; + ConfigOptionBool high_current_on_filament_swap; ConfigOptionFloat parking_pos_retraction; ConfigOptionBool remaining_times; ConfigOptionBool silent_mode; @@ -695,6 +696,7 @@ protected: OPT_PTR(variable_layer_height); OPT_PTR(cooling_tube_retraction); OPT_PTR(cooling_tube_length); + OPT_PTR(high_current_on_filament_swap); OPT_PTR(parking_pos_retraction); OPT_PTR(remaining_times); OPT_PTR(silent_mode); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 639f70cf7..90372ecd4 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -366,7 +366,8 @@ const std::vector& Preset::printer_options() "host_type", "print_host", "printhost_apikey", "printhost_cafile", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", - "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits", + "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height", + "default_print_profile", "inherits", "remaining_times", "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6372f7321..30d83d845 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2041,6 +2041,7 @@ void TabPrinter::build_extruder_pages() optgroup->append_single_option_line("cooling_tube_length"); optgroup->append_single_option_line("parking_pos_retraction"); optgroup->append_single_option_line("extra_loading_move"); + optgroup->append_single_option_line("high_current_on_filament_swap"); m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page); m_has_single_extruder_MM_page = true; } From 18a6205738ce8a4a817533d70311aab30b0387f0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 08:37:50 +0100 Subject: [PATCH 12/57] Fixed build on OSX & Linux --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/wxExtensions.cpp | 22 ++++++++++++---------- src/slic3r/GUI/wxExtensions.hpp | 19 +++++++++++++------ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index cef735db0..e0c35d55c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -36,6 +36,8 @@ #define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0) // Constrains the camera target into the scene bounding box #define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0) +// Use wxDataViewRender instead of wxDataViewCustomRenderer +#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 710710913..98e758bc8 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1228,14 +1228,6 @@ void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const i ItemChanged(item); } -PrusaBitmapTextRenderer::PrusaBitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, - int align /*= wxDVR_DEFAULT_ALIGNMENT*/): -wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) -{ - SetMode(mode); - SetAlignment(align); -} - //----------------------------------------------------------------------------- // PrusaDataViewBitmapText //----------------------------------------------------------------------------- @@ -1248,6 +1240,16 @@ IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText) // PrusaIconTextRenderer // --------------------------------------------------------- +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +PrusaBitmapTextRenderer::PrusaBitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, + int align /*= wxDVR_DEFAULT_ALIGNMENT*/): +wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) +{ + SetMode(mode); + SetAlignment(align); +} +#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; @@ -1259,12 +1261,12 @@ bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const return false; } -#if wxUSE_ACCESSIBILITY +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY wxString PrusaBitmapTextRenderer::GetAccessibleDescription() const { return m_value.GetText(); } -#endif // wxUSE_ACCESSIBILITY +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index e3fd1cbf6..637f1bcff 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -518,19 +518,26 @@ public: // ---------------------------------------------------------------------------- // PrusaBitmapTextRenderer // ---------------------------------------------------------------------------- - -class PrusaBitmapTextRenderer : public wxDataViewRenderer//CustomRenderer +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +class PrusaBitmapTextRenderer : public wxDataViewRenderer +#else +class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING { public: PrusaBitmapTextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, - int align = wxDVR_DEFAULT_ALIGNMENT);//: -// wxDataViewRenderer/*CustomRenderer*/(wxT("PrusaDataViewBitmapText"), mode, align) {} + int align = wxDVR_DEFAULT_ALIGNMENT +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ); +#else + ) : wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {} +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING bool SetValue(const wxVariant &value); bool GetValue(wxVariant &value) const; -#if wxUSE_ACCESSIBILITY +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY virtual wxString GetAccessibleDescription() const override; -#endif // wxUSE_ACCESSIBILITY +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING virtual bool Render(wxRect cell, wxDC *dc, int state); virtual wxSize GetSize() const; From 45e611c9fc979a7edbd3ab77e18b17c1246b21a5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 09:57:24 +0100 Subject: [PATCH 13/57] Fixed M203 processing by the time estimator for Smoothieware. Fixes "Print time estimate incorrect for Smoothie flavor with M203 #1259" --- src/libslic3r/GCodeTimeEstimator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 4bfd5d63f..5c8cc2659 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -1223,7 +1223,8 @@ namespace Slic3r { return; // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate - float factor = (dialect == gcfMarlin) ? 1.0f : MMMIN_TO_MMSEC; + // http://smoothieware.org/supported-g-codes + float factor = (dialect == gcfMarlin || dialect == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; if (line.has_x()) set_axis_max_feedrate(X, line.x() * factor); From d0edc3640062316d8a137a41387e00395b2ef1ae Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 10:30:20 +0100 Subject: [PATCH 14/57] Implemented M220 B / M220 R for backup / restore of the speed override at the firmware. The M220 B / M220 R are only applied for MM prints without any flex or soluble (PVA, BVOH) material, as for these materials the MMU slows down the print using the M220 code. fixes "LCD Speed Modifier Lost on Tool Change #421" --- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 41 ++++++++++++++++++------ src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 6 ++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index db38a7462..b0da3a210 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -315,6 +315,20 @@ public: return *this; }; + // Let the firmware back up the active speed override value. + Writer& speed_override_backup() + { + m_gcode += "M220 B\n"; + return *this; + }; + + // Let the firmware restore the active speed override value. + Writer& speed_override_restore() + { + m_gcode += "M220 R\n"; + return *this; + }; + // Set digital trimpot motor Writer& set_extruder_trimpot(int current) { @@ -501,8 +515,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( .set_initial_tool(m_current_tool) .append(";--------------------\n" "; CP PRIMING START\n") - .append(";--------------------\n") - .speed_override(100); + .append(";--------------------\n"); + if (m_retain_speed_override) + writer.speed_override_backup(); + writer.speed_override(100); writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position .travel(cleaning_box.ld, 7200); @@ -536,6 +552,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // Reset the extruder current to a normal value. if (m_set_extruder_trimpot) writer.set_extruder_trimpot(550); + if (m_retain_speed_override) + writer.speed_override_restore(); writer.feedrate(6000) .flush_planner_queue() .reset_extruder() @@ -602,8 +620,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo "; CP TOOLCHANGE START\n") .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based .comment_material(m_filpar[m_current_tool].material) - .append(";--------------------\n") - .speed_override(100); + .append(";--------------------\n"); + if (m_retain_speed_override) + writer.speed_override_backup(); + writer.speed_override(100); xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed); writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); @@ -640,6 +660,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if (m_set_extruder_trimpot) writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value. + if (m_retain_speed_override) + writer.speed_override_restore(); writer.feedrate(6000) .flush_planner_queue() .reset_extruder() @@ -885,14 +907,15 @@ void WipeTowerPrusaMM::toolchange_Change( case FLEX: speed_override = 35; break; default: speed_override = 100; } - writer.set_tool(new_tool) - .speed_override(speed_override) - .flush_planner_queue(); + writer.set_tool(new_tool); + if (m_retain_speed_override) + assert(speed_override == 100); + else + writer.speed_override(speed_override); + writer.flush_planner_queue(); m_current_tool = new_tool; } - - void WipeTowerPrusaMM::toolchange_Load( PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index c0aeee3ae..70c9526e6 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -75,6 +75,11 @@ public: m_filpar.push_back(FilamentParameters()); m_filpar[idx].material = material; + if (material == FLEX || material == SCAFF || material == PVA) { + // MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials. + // Therefore it does not make sense to use the new M220 B and M220 R (backup / restore). + m_retain_speed_override = false; + } m_filpar[idx].temperature = temp; m_filpar[idx].first_layer_temperature = first_layer_temp; m_filpar[idx].loading_speed = loading_speed; @@ -215,6 +220,7 @@ private: float m_extra_loading_move = 0.f; float m_bridging = 0.f; bool m_set_extruder_trimpot = false; + bool m_retain_speed_override = true; bool m_adhesion = true; float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill. From 083c626770fcfb5a050328641c020ac0bff09919 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 10:55:14 +0100 Subject: [PATCH 15/57] Added background texture to toolbars --- resources/icons/toolbar_background.png | Bin 0 -> 1544 bytes resources/icons/view_toolbar.png | Bin 18226 -> 21977 bytes src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 92 +++- src/slic3r/GUI/GLCanvas3D.hpp | 8 + src/slic3r/GUI/GLToolbar.cpp | 683 +++++++++++++++++++++++-- src/slic3r/GUI/GLToolbar.hpp | 159 +++++- src/slic3r/GUI/GUI_Preview.cpp | 8 + src/slic3r/GUI/GUI_Preview.hpp | 14 + src/slic3r/GUI/Plater.cpp | 59 +++ 10 files changed, 976 insertions(+), 49 deletions(-) create mode 100644 resources/icons/toolbar_background.png diff --git a/resources/icons/toolbar_background.png b/resources/icons/toolbar_background.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5ea013be7a6b804fb8390bec7d386e39e42d16 GIT binary patch literal 1544 zcma)5c{~(&6#vaIGt4kn>!`tC9b1l^Q(SI$%a^FMlM&#PE&cROm-~Q9?=Y78Kect!I&wKBW?@K*nXCaEjAOQfP6ic!r zKQg{T2+sGd#@-zOK*wU8&qg?UM=6AchfrxjehLvWp?(T}(KIRm(VS(LNBx+BNb;nc z8H=IJ^fI{FVH4H!VG}_w=%94lXo%Airub|f-D$8gr;PZ(>fmgIRb)=8c&BM1MiMjN>QU~KnrI~B&V5@S zL`l~hhN-+GUbU^)M@VT%4hT}84-tQ!c*?EJ(WWa!>zW1~+H)RPpDoI<4oJ^$A{(Et zd(jll)z8yu@5Fcl;>Fj_u5qqmQ8BZtEl8n7B>e?E&BQL1w2?l))l#tFB|SJ@$NhO#96RhGU|LBC zur*+{#4~dlzc6Lh)G!4V<~UpSBXX)@V0!Ni{?7GOm{S@xPs4+fMeLKvF3ky7%YQH> z)GzzsNkMp5saGF#A*mOJEtQRXBaS{j<85L1*+H$ZEV%;bQd!P*$DE`^?|B4q-2#D1FChkyd`r{>H{%h_UlwT zszO^LVO$lWtZz>>b`Zk1+ft47Jdu89lnO&Jwmu4n4eQWNBk$kDw|COEG=q8S;laP} zO3#gx8{6Z#8%gf!pJdFWinpb@ZpI!tH8R!XH0{id>XUC=34u)zLrNNZJU(i!OU0u0 zOo##|*frPwJ;9gpH&{H_qdC6X;Xc+DWN`4+il3Eb^BLh#OE)?I=$Bss$yGag6KWq7lBsy z5`M=xrVTf5;f;v;S|n6MB4%a~^9j8}I!O@i?MV>~p3bo!tHnC%<5|Qz?N%;)nDfHINII>JQXrmitg}A)C*;Uaaju)`sMtQF? zYv$9DHPXX7HK6JCn##0l^Gb=zEqSe&EF#?quU; z-IZfp(MPEM)1Pw|$guR5fiA0`^dD0or=b69#kUXmS2bb-xcsK*RtYo;N%eV zJE7W<=yo>TYe_Z>hi(x%Ym_!umFNJtI?z%-!FOMFfx!HY0@DO}-~|;ar06Dxbxw{8 zFA1Rhoubno4}qM^6VWhFhKIj%fLKcn2?Fr1ifredEhicB&00C=xR2r^hfNiw4|ey{ zN~4VNklTs&gwfISdpZZ+2U;eqz1a?2G3sIA?BMO?&F!fe`^$657y=?-`rx~BO}MdJ z_^Uj}Ri)8XnVL2sHsSP&q8e7Ui~4FKY(IUO^epTgZ@W%rOH?eyDK?*_-NqL)pqSZ_ ItNBv=8$V5yi2wiq literal 0 HcmV?d00001 diff --git a/resources/icons/view_toolbar.png b/resources/icons/view_toolbar.png index 26202a2a4236d2ca6f08d56b9d49db6267512f0f..dd1f5aca49d65d43bcbda9028c341ecc0998fde1 100644 GIT binary patch literal 21977 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 literal 18226 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

b*64gd8qw#8#Cdn^kIY9Igy zAPMfYC#_N={6Ff;|G#N?a`c;5taId=VqWTs`{hjahe(|_6vAGF|_ z`$g3fqD2vMaVAe=Dp=>!P8f-x>jbGR!|I4dX3le5O*DCzFgnkB_$*- zcPQBoNYLa7RLi;|mSj!g!g%$n~}T zDLQQ`^zEFR4P~rqf#BEt#fPa8Uu{?HeU5FRtW8_paYq+fx3xWok-xKh zeI(=Br|u_3m8z;(6h^7MdKswPg7qHUajMFaZKG>Dqf?!PDDiSUn`}bB59W{DGc@h* zuhj*bA3Qla;6mwFM$-wiu_93wC68NK@1`w0mET`>+EHR`EpH1QlrZS)Vo~kUj|+5& zOf$VxM2azTlz`lX)((ZEaXH*9q#$Y1dS2{Uldq^yCtrvSg=gGgs6g zuhEm^e{qdI+QF6SC=`gx%117~m+c9E>)P95@3C|dT4f0M>q_vj+ngu=o_}bjT}r<^ z4zzvib=Yrw_~ZDT>MiXuk+bs`!u65`AzRF8t4CL8M&_8XbwTpV9Y^PxRl=&~gWG*u znv(uj2mjoI;iX?N23=bAJ3e8F z8pyNFxIPgw(6j7ZZH1uH6>qFq4iCNH>5Hc3+wiCV0)xarrd46U4RJaH!XAD0fg=Gr z=W5B?hjQvTXdZ%}amer`h#Swpw0SGk8%fGXiZaP{`%v|L5i(*Ktk}F(Xbf-Mo_T?It4U6k@#c@c4f<95xgBMg!dQ_U%iPglk9FFjR5}teHBZEP|GX}|Jjt}tWECu-!yBs; ztCVQInZ)>JYJjzBihlPj0b!UApT{;uF=$sVb_p0CbxCu-8t&2tGY&b6+Ho`}&nkaw z@lj+dF-%z%Y0|&*hd0eyz7Ux!WN}rCm;7<@s+~@L_L!!_^|-hI2159T*$N4ITg}Zf?5z7LYH2_x$laQQfpVEm&@ja)>jTZ!Jm8^e}0C+`U)fpzP>5e;&xj8`p729?;+qJRTgDm1~?% z{Sy$9DV6m5oN+}`+1)dd%cl<>504K8UW8L|EuNhdEeh(@h5m13%Fx@q%NUB19onT@z)PuUW~VovZ|`{n z?`4o$<0pWhX71J)!JI>u#$7w7ByK937Oz%BPsjN0D`CF>4V$R}VGK^F8@N1mlTZbZ zkrQND#;fdDgGif2Y{H z7l3ZMHf8K?_EtyGM9+C0rH57J6h0H5dkdB`aK@s4cX#ooSz-okF|HHX_8y1}S(TBy zP0%@cC)N-GZYIxcTn+7>ZI<%Jjr4zw56LrOzo33=jrna172T%3b&DycgcoQjpM~UZ z=7Kfy>ZIV!Pi(*`ZEhHpOsg)1m;mPn<^?BFEB&W%XGJyQ^W;bJxs)xeeYV0Q49UQo zaCpn|xvf`POLTEV0`oEAq}zQ*os9b&I7pS z{Yb-Lp6F*+kDOS-*TX#Z-e@McIk~Cn24yo%*geJP>!owMMRMuJy(Y)uEF(nv4uxKL zbI*tgEy2~rJ!-s~@?PWNKDv%{GD2YRf^aat4qL-X2B-wt+unJsUuXiIJ8s^HU2eZH zV^MQ&-S%uq=46)X@=r$mv2xii5(JkERu3X^ zl++J&zX&DSFIpV;3cnRPdKvf8fQ(Pko>)NPV8h#4e!uHVX`=O~YGU!d|JooG0D7KX zr$QX%kTh&ls*RL}4IO``G0qwI^sazOaVU~0=K;l5fmp0+!z+T{83M84lRAon!J5oB z4u=L*#4K91rj-)!^&IGNxg-a+;##X!ahUFi0o^{9)Iv>o-)lKee(f zSi$d~XyQlXH`{n>vM<-I?c^<@YOuYx$UV%G;Vkn?)r&<*SDy9HcW}p~!f)D@kLMK6 zXJmeje)*LdUQ-fgAK3hu7k6_Wb=Cm*#a4as{eWU+uc$u7Q6+Qvt13Mx~4&~vJOhK^aDZ9;06^aS2V z_#ib|X({FWo}$shKPpdtYe)T1i<&UAUI%rH2-Y4&-ic_lfFXsClQN#8iHq+EDvne#8Ftb zxOMEag=H3e8Sf?v^C&HCC&tJ?_$n3JC{1zoT@70uepA&O`w%h+Xm&NVxF#6{s_zm0 z;Oa<}_CL6uXtOv=L!(~N+q!`j6#9vyX2ptnIwXBE%6L(qPScnP^44?cS=`ag36%80 z*6XbPG7XX*p&0pPWj0QY*z2xt8f z=g^eD<`Iey2scwFCS(@IyHl&~d~r7wVBoUVBrUd1IwK}ojO9R>aTi}BGDbSJx+Bur zan{P3UrF)Vwis~o^HSeqW)ouQvj9iOJDl|t`Fy*UnrgP}l&GQWzWY%Tiog4MEh9@7 z#%*^EL}Qh^CXc8CcG4*%C)aRkQ}q!0|3hSHn)TyekX{uqwlObjs2-RH%(q4J&pH%9vCY z7q`sLDNwRpvO-)Q$WM?&@Um)UW%4>kgS%Nw86Y83Ey~t>Vx7_DF3{E9mWThx*B8t< zg?Q(+3CE?X)=JY?C~tdMouq;tAMK`;8_#8ie$!<3P;V__8dpKOxIVEoGBJ5&&^hr* zQ8iWlBec+e0xwvTQP{RdfpLO!uQvJ>n_AjPK5wCBR+|uGc<)nlyQjLR zdi_jM3xXu=`oHHK0+2Wb+h#xnGS|7WsrXvA8o1w#whhYnSxSO4VStU|nFJiC$$V|a zZ!B;YBdg$iMR2=}?5$>=dFpr5*saqiMgRcm;NMz+lg~(RUrZ&k3%EBy*{%yuJb#fq)yU(!rZcONO*bcW9g{9r?Nh&apQ#MXUBSt_DAr7Y z*zP!u2OS^?_-CKPZPn-YZa53^X=3zmU;JWP78UbDh8wlS9^< z##zeN>-{V$2J5pvd9F)^y;aFu!X@0Jsnf43!4_GohfA>t-{Ry(<8t2Sv-LPza%gov z3;&4ybSq?aa5I#y$;J+{a(wYv8XJQkUK&i_60Y%7NP{i7?aM=B%u^kawkw}e(`itd z4yuy%ziaxACV|k!n^m=4NL-?J!`ey*rlxUo(=e^lJ5lS+t6zfUI$oENB9`|6Cg+st zIr?Mgswwli&%z+&e0MYW6Xw4Ljth8RmiT;do!ra!3nu=?t(azqNQz33DBfD zqlcy~l$XGi%B!g}Hvylzg+>nMKNd+`HJ+Zk(>_H^Wv)i*UC#-$6a%MKW+o4(&d*ed z7Y~Z>kyu;rSI-G|A#t7yXNB;xj#F9aDt6p$Zyldw`{UGE@w^Ra_4xC-?EP;#o7s@6 z8Wv1c^TH{b%Xh!);G-DUf2qwP*7Tyc919hEj*t07!DFN{cWttyXua;h0yUK{gn$#4|lSwpPE?t)bK^56i({l+X??4p;u%e_>WP8{Y@IVxtIt;u==4PBUz`s};oLnX*?7-Ey_cpU5$sVA2FXTQ{>_VIU;oAOZ3LqTW-|I;jI>H-6yplHB#Uak6nFu!K0dkJf5G^sBQr&UR=zNcVc~Qnj|s>YuyN`*v`26L4t1jM6{XXjIQ?_jl7lCXs~sUd8t822 z@G9x{g9Y+OiD#B0hfmIn{GmG`+@lvvebsd9-y#egRm6=s4+is9dRL2mc_04s9~9~| z!p&Eb%)JZlooQYMr{}ydZ|{%ZTGl*`CmZI2S`EJae-sf7Ww#4X3i7}T!;w|Nrtw7; zdbJq&3}+jqIp}=p>}oupG=2AZE0w>0KR=Tnh#^n4HoM)9a_$^`8(=pmM z2LTqnfDV9RP_6n-_Um&P0`|}Ru0_Gh2rC;OY~-GqtJ7`?FrS^%aVl{vgBDW;yG<%0kgxyfGLzb zRkiP;Z1yVYH+u4%!ngfDmtbbEV??6+I|?u(N*J+F8b4I68hJfPWa(4CY%3RT@nAD5<)liA9b$ z>`%olOT(g-52Oq~bCF)>-n6%+UtEo-_C#(nRpJ4s+-f*B3@AJb(m(iao-KMEgjjc5m)_9y9n+B_fw612Z2)dGJdEEzAp7?`BS*pMkiodiNyj>=WtpcD zi$bZFyVQzCrxCxy&<^^8)7I=1PQhXvEc~{bxA0oQE4+G4Cg<8`v|)Pej=RL?{MtWX z=t&h&AC%VK2GI=t3AM&g!5MK zX~KpnezmP&O31cOIt!^YC%rrPOBeDpG-_QVtuSXcB~9c9gYI#7%q)(0*^O(=_Vv3b zCISq_>ba*ah>+D6q@KNH1UN}OcIcoc)*$Mu^hNfFkZZ+p0JqLx?SAd>7U|C@?b^W?TjH#~l90Fh_fl39|n{L*_KUWZu z?oc`cv~sbyJwK&ZYFM}+9!TgBpSjd4mw9sA%qw-cV$Wb8x?i{8==zTdEq>)GjDI?H z=W1%N)kTx;vDcl}IynWot4NyBgxozl8pL~-4Zmj$8m6@zt0tx2*4S zGc4oXS?J9q;Vowjh%fE#2r^AOkSY`Cyaf*1`g}JABe7%Gg_Acvg)l8%M+1R(JXP)cp5ob&asKBG!I!J0%76AByj<0 zQ|d=84H)Z|c(_OJP|(;Ahs;*c;-=hZEt_wM$AWd}+fxV<*hMc~ihY;nG{6Mkr8#x} z<{79D?-EwTXXlFF$YCebljPJXE%m~8Rkl|1t-gvIhx}BYPFs51TVY+HE~Y%ZU*hdU zcB=U+p<{kq)t%2b`oOvS)kQwdfDqZ?T@-&@fLm z5V;xfV0O!0Rr%Pr8O0&m=O%uXV(TRxB(vdw4>6tZ0LI@ITWORl_O2-bk7lMk)4oYL zwUpn+2UKl!Loq0r>Sk$eZ8d_`YiYYd91$shxUNc#6OOd$i)e&sq=8LkGoVlmk0GY z+&SrCCs7b@mt5Oy1TjgFM*l2Lp!2zoa#bCD&WIB>sf5YI4Xa{T{5ScI?v$ymln8sq z%AWcCoH;1>w>d>c^$`1*(>0mg8y)4EvpFg>Vu3_tT}-hmF%Fr&k7bzqFZKQYG2lkf z|5&HvW_0{jWttWFH-{u-#;*Sb7DNvg-O_vb%tG#ty2Ay*n|@+Y)o<4A*v(holA=;2oR%VV1NRew|Zg?G6Ar8M3X z{IyNv@D~)a-)uvxdK2`Tz0oHg1sbL;yItQ%FWPo%0)s+>g?u&(c0xA?p0|w7tloOV zy!M9DHqG~Z1urkZHYBZU^|(P3KsjV*1o6J-e>O9M)PdDb%YXv=+d%yrE_8C~CU7$n zeN1W=LCn#si(w;HMj}ap$9uyUFpd3BTa_b!y6^-~Du(x)xsBHL(V%HhTPYC7r9+}c z)61l7&4m0gPQ}=hsl?a%N(wa#;5WS=B`%2E6lR$hXiTaS)sX3ZcO?q{!9#e5=ROqb z+obt|#bYHjp*Gd^^WMxt!R7AobpwY_Toa@YX1^TEE2>dj$46J^u{NB$h@~QOQNPKE z-^k1Lm@V$MbY+}|2;+Jaf1q&OmA+^UZ0tqJ4I zEk~%6b00;k%2ck5`rozm_#?o zxr^D`(&3E+X#?{2T?y)6e@Dt^u0?n^sX0wU8gwyqK@%f!6MbKCi}X{8QLFfniz*h6Uwi*88Kwl7$^6R?PXfv@tu+u53Q@t-=Jko5+Vlo? zA0wu?W_A+KG|p<}-dJ9v;mI*x&4!DLns0lPC9~@D_9JwyM<02`k-(HaAhNRBU@$F) zB@#B~84UZ(<6=FY>MpjTp}=jlI?jCpDXk@y;%L!K{$-!}WzNI;Uq{TBfBDh3AriE1yal*!9K6y%i;Y31g}?rl>e<|< z6T3xbpS~QL-=`FZ{iR+1J3x zz{qaiRF0;3^7=MWHn@}K=-bI&!q5B&uot36%)T-G@y GGywpU_whdf diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index e0c35d55c..d604896ad 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -38,6 +38,8 @@ #define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0) // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) +// Adds background texture to toolbars +#define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 352ea2f49..8b1d636e5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3434,7 +3434,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) , m_in_render(false) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + , m_toolbar(GLToolbar::Normal) +#else , m_toolbar(*this) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER , m_view_toolbar(nullptr) #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -4675,7 +4679,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); +#else int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1; #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -4699,7 +4707,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { m_toolbar_action_running = true; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); +#else m_toolbar.do_action((unsigned int)toolbar_contains_mouse); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined)) { @@ -4778,7 +4790,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (toolbar_contains_mouse != -1) { m_toolbar_action_running = true; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); +#else m_toolbar.do_action((unsigned int)toolbar_contains_mouse); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_mouse.left_down = false; } else @@ -5061,7 +5077,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // updates toolbar overlay if (tooltip.empty()) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + tooltip = m_toolbar.update_hover_state(m_mouse.position, *this); +#else tooltip = m_toolbar.update_hover_state(m_mouse.position); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // updates view toolbar overlay if (tooltip.empty() && (m_view_toolbar != nullptr)) @@ -5429,7 +5449,24 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.is_enabled()) return true; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + ItemsIconsTexture::Metadata icons_data; + icons_data.filename = "toolbar.png"; + icons_data.icon_size = 36; + icons_data.icon_border_size = 1; + icons_data.icon_gap_size = 1; + + BackgroundTexture::Metadata background_data; + background_data.filename = "toolbar_background.png"; + background_data.left = 16; + background_data.top = 16; + background_data.right = 16; + background_data.bottom = 16; + + if (!m_toolbar.init(icons_data, background_data)) +#else if (!m_toolbar.init("toolbar.png", 36, 1, 1)) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // unable to init the toolbar texture, disable it m_toolbar.set_enabled(false); @@ -5438,6 +5475,10 @@ bool GLCanvas3D::_init_toolbar() // m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_toolbar.set_layout_orientation(GLToolbar::Layout::Top); + m_toolbar.set_border(5.0f); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_separator_size(5); m_toolbar.set_gap_size(2); @@ -5524,9 +5565,6 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) - return false; - enable_toolbar_item("add", true); return true; @@ -6063,7 +6101,11 @@ void GLCanvas3D::_render_toolbar() const #if !ENABLE_REMOVE_TABS_FROM_PLATER _resize_toolbar(); #endif // !ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_toolbar.render(*this); +#else m_toolbar.render(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -7721,25 +7763,54 @@ void GLCanvas3D::_resize_toolbar() const float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + switch (m_toolbar.get_layout_type()) { default: case GLToolbar::Layout::Horizontal: { // centers the toolbar on the top edge of the 3d scene - unsigned int toolbar_width = m_toolbar.get_width(); - float top = (0.5f * (float)cnv_size.get_height() - 2.0f) * inv_zoom; - float left = -0.5f * (float)toolbar_width * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float top, left; + if (orientation == GLToolbar::Layout::Top) + { + top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + left = -0.5f * m_toolbar.get_width() * inv_zoom; + } + else + { + top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; + left = -0.5f * m_toolbar.get_width() * inv_zoom; + } +#else + float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + float left = -0.5f * m_toolbar.get_width() * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } case GLToolbar::Layout::Vertical: { // centers the toolbar on the right edge of the 3d scene - unsigned int toolbar_width = m_toolbar.get_width(); - unsigned int toolbar_height = m_toolbar.get_height(); - float top = 0.5f * (float)toolbar_height * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - toolbar_width - 2.0f) * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float top, left; + if (orientation == GLToolbar::Layout::Left) + { + top = 0.5f * m_toolbar.get_height() * inv_zoom; + left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; + } + else + { + top = 0.5f * m_toolbar.get_height() * inv_zoom; + left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; + } +#else + float top = 0.5f * m_toolbar.get_height() * inv_zoom; + float left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } @@ -7748,6 +7819,7 @@ void GLCanvas3D::_resize_toolbar() const #if ENABLE_REMOVE_TABS_FROM_PLATER if (m_view_toolbar != nullptr) { + // places the toolbar on the bottom-left corner of the 3d scene float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; m_view_toolbar->set_position(top, left); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 12a83eebd..ebc2929d9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -769,7 +769,11 @@ private: mutable Gizmos m_gizmos; mutable GLToolbar m_toolbar; #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLToolbar* m_view_toolbar; +#else GLRadioToolbar* m_view_toolbar; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; @@ -824,7 +828,11 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } +#else void set_view_toolbar(GLRadioToolbar* toolbar) { m_view_toolbar = toolbar; } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER bool init(bool useVBOs, bool use_legacy_opengl); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 92b3e96af..a7f623b8d 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -75,6 +75,13 @@ bool GLToolbarItem::is_enabled() const return m_state != Disabled; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +bool GLToolbarItem::is_disabled() const +{ + return m_state == Disabled; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + bool GLToolbarItem::is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); @@ -124,24 +131,63 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned i return uvs; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +ItemsIconsTexture::Metadata::Metadata() + : filename("") + , icon_size(0) + , icon_border_size(0) + , icon_gap_size(0) +{ +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + +#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::ItemsIconsTexture() : items_icon_size(0) , items_icon_border_size(0) , items_icon_gap_size(0) { } +#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE + +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +BackgroundTexture::Metadata::Metadata() + : filename("") + , left(0) + , right(0) + , top(0) + , bottom(0) +{ +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::Layout() : type(Horizontal) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + , orientation(Center) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , top(0.0f) , left(0.0f) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + , border(0.0f) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , separator_size(0.0f) , gap_size(0.0f) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + , width(0.0f) + , height(0.0f) + , dirty(true) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +GLToolbar::GLToolbar(GLToolbar::EType type) + : m_type(type) +#else GLToolbar::GLToolbar(GLCanvas3D& parent) : m_parent(parent) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , m_enabled(false) { } @@ -154,6 +200,26 @@ GLToolbar::~GLToolbar() } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture) +{ + if (m_icons_texture.texture.get_id() != 0) + return true; + + std::string path = resources_dir() + "/icons/"; + bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false); + if (res) + m_icons_texture.metadata = icons_texture; + + if (!background_texture.filename.empty()) + res = m_background_texture.texture.load_from_file(path + background_texture.filename, false); + + if (res) + m_background_texture.metadata = background_texture; + + return res; +} +#else bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) { std::string path = resources_dir() + "/icons/"; @@ -167,31 +233,61 @@ bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int ite return res; } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -GLToolbar::Layout::Type GLToolbar::get_layout_type() const +GLToolbar::Layout::EType GLToolbar::get_layout_type() const { return m_layout.type; } -void GLToolbar::set_layout_type(GLToolbar::Layout::Type type) +void GLToolbar::set_layout_type(GLToolbar::Layout::EType type) { m_layout.type = type; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +GLToolbar::Layout::EOrientation GLToolbar::get_layout_orientation() const +{ + return m_layout.orientation; +} + +void GLToolbar::set_layout_orientation(GLToolbar::Layout::EOrientation orientation) +{ + m_layout.orientation = orientation; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void GLToolbar::set_position(float top, float left) { m_layout.top = top; m_layout.left = left; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::set_border(float border) +{ + m_layout.border = border; + m_layout.dirty = true; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void GLToolbar::set_separator_size(float size) { m_layout.separator_size = size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::set_gap_size(float size) { m_layout.gap_size = size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } bool GLToolbar::is_enabled() const @@ -211,6 +307,9 @@ bool GLToolbar::add_item(const GLToolbarItem::Data& data) return false; m_items.push_back(item); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } @@ -222,11 +321,20 @@ bool GLToolbar::add_separator() return false; m_items.push_back(item); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } float GLToolbar::get_width() const { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + if (m_layout.dirty) + calc_layout(); + + return m_layout.width; +#else switch (m_layout.type) { default: @@ -239,10 +347,17 @@ float GLToolbar::get_width() const return get_width_vertical(); } } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height() const { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + if (m_layout.dirty) + calc_layout(); + + return m_layout.height; +#else switch (m_layout.type) { default: @@ -255,6 +370,7 @@ float GLToolbar::get_height() const return get_height_vertical(); } } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::enable_item(const std::string& name) @@ -281,6 +397,23 @@ void GLToolbar::disable_item(const std::string& name) } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::select_item(const std::string& name) +{ + if (is_item_disabled(name)) + return; + + for (GLToolbarItem* item : m_items) + { + if (!item->is_disabled()) + { + bool hover = item->is_hovered(); + item->set_state((item->get_name() == name) ? (hover ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed) : (hover ? GLToolbarItem::Hover : GLToolbarItem::Normal)); + } + } +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + bool GLToolbar::is_item_pressed(const std::string& name) const { for (GLToolbarItem* item : m_items) @@ -292,10 +425,31 @@ bool GLToolbar::is_item_pressed(const std::string& name) const return false; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +bool GLToolbar::is_item_disabled(const std::string& name) const +{ + for (GLToolbarItem* item : m_items) + { + if (item->get_name() == name) + return item->is_disabled(); + } + + return false; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) +#else std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) #else void GLToolbar::update_hover_state(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -310,24 +464,30 @@ void GLToolbar::update_hover_state(const Vec2d& mouse_pos) { default: #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } + case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } +#else case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos); } case Layout::Vertical: { return update_hover_state_vertical(mouse_pos); } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #else - case Layout::Horizontal: - { - update_hover_state_horizontal(mouse_pos); - break; - } - case Layout::Vertical: - { - update_hover_state_vertical(mouse_pos); - break; - } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } + case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } +#else + case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos); break; } + case Layout::Vertical: { update_hover_state_vertical(mouse_pos); break; } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const +#else int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled) return -1; @@ -335,18 +495,21 @@ int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const switch (m_layout.type) { default: - case Layout::Horizontal: - { - return contains_mouse_horizontal(mouse_pos); - } - case Layout::Vertical: - { - return contains_mouse_vertical(mouse_pos); - } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } + case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } +#else + case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos); } + case Layout::Vertical: { return contains_mouse_vertical(mouse_pos); } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) +#else void GLToolbar::do_action(unsigned int item_id) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (item_id < (unsigned int)m_items.size()) { @@ -361,26 +524,51 @@ void GLToolbar::do_action(unsigned int item_id) else if (state == GLToolbarItem::HoverPressed) item->set_state(GLToolbarItem::Hover); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.render(); + item->do_action(parent.get_wxglcanvas()); +#else m_parent.render(); item->do_action(m_parent.get_wxglcanvas()); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else { - item->set_state(GLToolbarItem::HoverPressed); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + if (m_type == Radio) + select_item(item->get_name()); + else +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + item->set_state(GLToolbarItem::HoverPressed); + +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.render(); + item->do_action(parent.get_wxglcanvas()); + if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) +#else m_parent.render(); item->do_action(m_parent.get_wxglcanvas()); if (item->get_state() != GLToolbarItem::Disabled) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // the item may get disabled during the action, if not, set it back to hover state item->set_state(GLToolbarItem::Hover); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.render(); +#else m_parent.render(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } } } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::render(const GLCanvas3D& parent) const +#else void GLToolbar::render() const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled || m_items.empty()) return; @@ -393,21 +581,42 @@ void GLToolbar::render() const switch (m_layout.type) { default: - case Layout::Horizontal: - { - render_horizontal(); - break; - } - case Layout::Vertical: - { - render_vertical(); - break; - } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + case Layout::Horizontal: { render_horizontal(parent); break; } + case Layout::Vertical: { render_vertical(parent); break; } +#else + case Layout::Horizontal: { render_horizontal(); break; } + case Layout::Vertical: { render_vertical(); break; } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } ::glPopMatrix(); } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::calc_layout() const +{ + switch (m_layout.type) + { + default: + case Layout::Horizontal: + { + m_layout.width = get_width_horizontal(); + m_layout.height = get_height_horizontal(); + break; + } + case Layout::Vertical: + { + m_layout.width = get_width_vertical(); + m_layout.height = get_height_vertical(); + break; + } + } + + m_layout.dirty = false; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float GLToolbar::get_width_horizontal() const { return get_main_size(); @@ -415,12 +624,20 @@ float GLToolbar::get_width_horizontal() const float GLToolbar::get_width_vertical() const { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size; +#else return m_icons_texture.items_icon_size; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_horizontal() const { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size; +#else return m_icons_texture.items_icon_size; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_vertical() const @@ -430,13 +647,21 @@ float GLToolbar::get_height_vertical() const float GLToolbar::get_main_size() const { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float size = 2.0f * m_layout.border; +#else float size = 0.0f; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) { if (m_items[i]->is_separator()) size += m_layout.separator_size; else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + size += (float)m_icons_texture.metadata.icon_size; +#else size += (float)m_icons_texture.items_icon_size; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } if (m_items.size() > 1) @@ -446,26 +671,54 @@ float GLToolbar::get_main_size() const } #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) +#else std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) #else void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Size cnv_size = parent.get_canvas_size(); +#else Size cnv_size = m_parent.get_canvas_size(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float left = m_layout.left + scaled_border; + float top = m_layout.top - scaled_border; +#else float left = m_layout.left; float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -486,7 +739,14 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -495,15 +755,29 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); - +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + break; } case GLToolbarItem::Pressed: { if (inside) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); - +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + break; } case GLToolbarItem::HoverPressed: @@ -511,8 +785,15 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); - +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + break; } default: @@ -535,26 +816,54 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) } #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) +#else std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) #else void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Size cnv_size = parent.get_canvas_size(); +#else Size cnv_size = m_parent.get_canvas_size(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float left = m_layout.left + scaled_border; + float top = m_layout.top - scaled_border; +#else float left = m_layout.left; float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -575,7 +884,14 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -584,14 +900,28 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } case GLToolbarItem::Pressed: { if (inside) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -600,7 +930,14 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + { +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + parent.set_as_dirty(); + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -622,23 +959,47 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) #endif // ENABLE_REMOVE_TABS_FROM_PLATER } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const +#else int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Size cnv_size = parent.get_canvas_size(); +#else Size cnv_size = m_parent.get_canvas_size(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float left = m_layout.left + scaled_border; + float top = m_layout.top - scaled_border; +#else float left = m_layout.left; float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -663,23 +1024,47 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const return -1; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const +#else int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Size cnv_size = parent.get_canvas_size(); +#else Size cnv_size = m_parent.get_canvas_size(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float left = m_layout.left + scaled_border; + float top = m_layout.top - scaled_border; +#else float left = m_layout.left; float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -704,7 +1089,11 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const return -1; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::render_horizontal(const GLCanvas3D& parent) const +#else void GLToolbar::render_horizontal() const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -712,18 +1101,124 @@ void GLToolbar::render_horizontal() const if ((tex_id == 0) || (tex_size <= 0)) return; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; + float scaled_width = get_width() * inv_zoom; + float scaled_height = get_height() * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; + float right = left + scaled_width; + float bottom = top - scaled_height; + + // renders background + unsigned int bg_tex_id = m_background_texture.texture.get_id(); + float bg_tex_width = (float)m_background_texture.texture.get_width(); + float bg_tex_height = (float)m_background_texture.texture.get_height(); + if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) + { + float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; + float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; + + float bg_uv_left = 0.0f; + float bg_uv_right = 1.0f; + float bg_uv_top = 1.0f; + float bg_uv_bottom = 0.0f; + + float bg_left = left; + float bg_right = right; + float bg_top = top; + float bg_bottom = bottom; + float bg_width = right - left; + float bg_height = top - bottom; + float bg_min_size = std::min(bg_width, bg_height); + + float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; + float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; + float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; + float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; + + float bg_i_left = bg_left + scaled_border; + float bg_i_right = bg_right - scaled_border; + float bg_i_top = bg_top - scaled_border; + float bg_i_bottom = bg_bottom + scaled_border; + + switch (m_layout.orientation) + { + case Layout::Top: + { + bg_uv_top = bg_uv_i_top; + bg_i_top = bg_top; + break; + } + case Layout::Bottom: + { + bg_uv_bottom = bg_uv_i_bottom; + bg_i_bottom = bg_bottom; + break; + } + case Layout::Center: + { + break; + } + }; + + if ((m_layout.border > 0) && (bg_uv_top != bg_uv_i_top)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); + } + + if ((m_layout.border > 0) && (bg_uv_left != bg_uv_i_left)) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); + + if ((m_layout.border > 0) && (bg_uv_right != bg_uv_i_right)) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); + + if ((m_layout.border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); + } + } + + left += scaled_border; + top -= scaled_border; +#else + float left = m_layout.left; + float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -732,13 +1227,21 @@ void GLToolbar::render_horizontal() const left += separator_stride; else { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); +#else item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE left += icon_stride; } } } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void GLToolbar::render_vertical(const GLCanvas3D& parent) const +#else void GLToolbar::render_vertical() const +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -746,18 +1249,124 @@ void GLToolbar::render_vertical() const if ((tex_id == 0) || (tex_size <= 0)) return; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float zoom = parent.get_camera_zoom(); +#else float zoom = m_parent.get_camera_zoom(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; +#else float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = m_layout.border * inv_zoom; + float scaled_width = get_width() * inv_zoom; + float scaled_height = get_height() * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; + float right = left + scaled_width; + float bottom = top - scaled_height; + + // renders background + unsigned int bg_tex_id = m_background_texture.texture.get_id(); + float bg_tex_width = (float)m_background_texture.texture.get_width(); + float bg_tex_height = (float)m_background_texture.texture.get_height(); + if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) + { + float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; + float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; + + float bg_uv_left = 0.0f; + float bg_uv_right = 1.0f; + float bg_uv_top = 1.0f; + float bg_uv_bottom = 0.0f; + + float bg_left = left; + float bg_right = right; + float bg_top = top; + float bg_bottom = bottom; + float bg_width = right - left; + float bg_height = top - bottom; + float bg_min_size = std::min(bg_width, bg_height); + + float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; + float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; + float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; + float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; + + float bg_i_left = bg_left + scaled_border; + float bg_i_right = bg_right - scaled_border; + float bg_i_top = bg_top - scaled_border; + float bg_i_bottom = bg_bottom + scaled_border; + + switch (m_layout.orientation) + { + case Layout::Left: + { + bg_uv_left = bg_uv_i_left; + bg_i_left = bg_left; + break; + } + case Layout::Right: + { + bg_uv_right = bg_uv_i_right; + bg_i_right = bg_right; + break; + } + case Layout::Center: + { + break; + } + }; + + if ((m_layout.border > 0) && (bg_uv_top != bg_uv_i_top)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); + } + + if ((m_layout.border > 0) && (bg_uv_left != bg_uv_i_left)) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); + + if ((m_layout.border > 0) && (bg_uv_right != bg_uv_i_right)) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); + + if ((m_layout.border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); + } + } + + left += scaled_border; + top -= scaled_border; +#else + float left = m_layout.left; + float top = m_layout.top; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -766,12 +1375,17 @@ void GLToolbar::render_vertical() const top -= separator_stride; else { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); +#else item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE top -= icon_stride; } } } +#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLRadioToolbarItem::Data::Data() : name("") , tooltip("") @@ -1075,6 +1689,7 @@ void GLRadioToolbar::render(const GLCanvas3D& parent) const ::glPopMatrix(); } +#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 7eab518f7..4e15edd17 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -77,6 +77,9 @@ public: void do_action(wxEvtHandler *target); bool is_enabled() const; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + bool is_disabled() const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_hovered() const; bool is_pressed() const; @@ -94,7 +97,25 @@ private: // from left to right struct ItemsIconsTexture { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + struct Metadata + { + // path of the file containing the icons' texture + std::string filename; + // size of the square icons, in pixels + unsigned int icon_size; + // size of the border, in pixels + unsigned int icon_border_size; + // distance between two adjacent icons (to avoid filtering artifacts), in pixels + unsigned int icon_gap_size; + + Metadata(); + }; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLTexture texture; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Metadata metadata; +#else // size of the square icons, in pixels unsigned int items_icon_size; // distance from the border, in pixels @@ -103,49 +124,129 @@ struct ItemsIconsTexture unsigned int items_icon_gap_size; ItemsIconsTexture(); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE }; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +struct BackgroundTexture +{ + struct Metadata + { + // path of the file containing the background texture + std::string filename; + // size of the left edge, in pixels + unsigned int left; + // size of the right edge, in pixels + unsigned int right; + // size of the top edge, in pixels + unsigned int top; + // size of the bottom edge, in pixels + unsigned int bottom; + + Metadata(); + }; + + GLTexture texture; + Metadata metadata; +}; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + class GLToolbar { public: +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + enum EType : unsigned char + { + Normal, + Radio, + Num_Types + }; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + struct Layout { - enum Type : unsigned char + enum EType : unsigned char { Horizontal, Vertical, Num_Types }; - Type type; + enum EOrientation : unsigned int + { + Top, + Bottom, + Left, + Right, + Center, + Num_Locations + }; + + EType type; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + EOrientation orientation; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top; float left; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float border; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_size; float gap_size; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float width; + float height; + bool dirty; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Layout(); }; private: typedef std::vector ItemsList; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + EType m_type; +#else GLCanvas3D& m_parent; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool m_enabled; ItemsIconsTexture m_icons_texture; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + BackgroundTexture m_background_texture; + mutable Layout m_layout; +#else Layout m_layout; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsList m_items; public: +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + explicit GLToolbar(EType type); +#else explicit GLToolbar(GLCanvas3D& parent); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ~GLToolbar(); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture); +#else bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); - - Layout::Type get_layout_type() const; - void set_layout_type(Layout::Type type); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + + Layout::EType get_layout_type() const; + void set_layout_type(Layout::EType type); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + Layout::EOrientation get_layout_orientation() const; + void set_layout_orientation(Layout::EOrientation orientation); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_position(float top, float left); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void set_border(float border); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_separator_size(float size); void set_gap_size(float size); @@ -160,42 +261,89 @@ public: void enable_item(const std::string& name); void disable_item(const std::string& name); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void select_item(const std::string& name); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_item_pressed(const std::string& name) const; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + bool is_item_disabled(const std::string& name) const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); +#else std::string update_hover_state(const Vec2d& mouse_pos); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); #else void update_hover_state(const Vec2d& mouse_pos); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + // returns the id of the item under the given mouse position or -1 if none + int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + + void do_action(unsigned int item_id, GLCanvas3D& parent); +#else // returns the id of the item under the given mouse position or -1 if none int contains_mouse(const Vec2d& mouse_pos) const; void do_action(unsigned int item_id); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void render(const GLCanvas3D& parent) const; +#else void render() const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE private: +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void calc_layout() const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float get_width_horizontal() const; float get_width_vertical() const; float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); + std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); +#else std::string update_hover_state_horizontal(const Vec2d& mouse_pos); std::string update_hover_state_vertical(const Vec2d& mouse_pos); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#else +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); + void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); #else void update_hover_state_horizontal(const Vec2d& mouse_pos); void update_hover_state_vertical(const Vec2d& mouse_pos); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + + void render_horizontal(const GLCanvas3D& parent) const; + void render_vertical(const GLCanvas3D& parent) const; +#else int contains_mouse_horizontal(const Vec2d& mouse_pos) const; int contains_mouse_vertical(const Vec2d& mouse_pos) const; void render_horizontal() const; void render_vertical() const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE }; +#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE class GLRadioToolbarItem { public: @@ -274,6 +422,7 @@ public: void render(const GLCanvas3D& parent) const; }; +#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 724815076..4e14aefe8 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -92,7 +92,11 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void View3D::set_view_toolbar(GLToolbar* toolbar) +#else void View3D::set_view_toolbar(GLRadioToolbar* toolbar) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); @@ -365,7 +369,11 @@ Preview::~Preview() } #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +void Preview::set_view_toolbar(GLToolbar* toolbar) +#else void Preview::set_view_toolbar(GLRadioToolbar* toolbar) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 2aebdccd5..23e6a682f 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -28,9 +28,15 @@ class Model; namespace GUI { class GLCanvas3D; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +#if ENABLE_REMOVE_TABS_FROM_PLATER +class GLToolbar; +#endif // ENABLE_REMOVE_TABS_FROM_PLATER +#else #if ENABLE_REMOVE_TABS_FROM_PLATER class GLRadioToolbar; #endif // ENABLE_REMOVE_TABS_FROM_PLATER +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER class View3D : public wxPanel @@ -53,7 +59,11 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void set_view_toolbar(GLToolbar* toolbar); +#else void set_view_toolbar(GLRadioToolbar* toolbar); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_as_dirty(); void set_bed_shape(const Pointfs& shape); @@ -122,7 +132,11 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } #if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + void set_view_toolbar(GLToolbar* toolbar); +#else void set_view_toolbar(GLRadioToolbar* toolbar); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_number_extruders(unsigned int number_extruders); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 381a9e864..8db804c12 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -913,7 +913,11 @@ struct Plater::priv Sidebar *sidebar; #if ENABLE_REMOVE_TABS_FROM_PLATER View3D* view3D; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLToolbar view_toolbar; +#else GLRadioToolbar view_toolbar; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #else #if !ENABLE_IMGUI wxPanel *panel3d; @@ -1068,6 +1072,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // !ENABLE_REMOVE_TABS_FROM_PLATER , delayed_scene_refresh(false) , project_filename(wxEmptyString) +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + , view_toolbar(GLToolbar::Radio) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { arranging.store(false); rotoptimizing.store(false); @@ -1286,7 +1293,9 @@ void Plater::priv::select_view_3D(const std::string& name) else if (name == "Preview") set_current_panel(preview); +#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE view_toolbar.set_selection(name); +#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE } #else void Plater::priv::select_view(const std::string& direction) @@ -2646,9 +2655,58 @@ bool Plater::priv::complit_init_part_menu() #if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::init_view_toolbar() { +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + ItemsIconsTexture::Metadata icons_data; + icons_data.filename = "view_toolbar.png"; + icons_data.icon_size = 64; + icons_data.icon_border_size = 0; + icons_data.icon_gap_size = 0; + + BackgroundTexture::Metadata background_data; + background_data.filename = "toolbar_background.png"; + background_data.left = 16; + background_data.top = 16; + background_data.right = 16; + background_data.bottom = 16; + + if (!view_toolbar.init(icons_data, background_data)) +#else if (!view_toolbar.init("view_toolbar.png", 64, 0, 0)) +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); + view_toolbar.set_border(5.0f); + view_toolbar.set_gap_size(1.0f); + + GLToolbarItem::Data item; + + item.name = "3D"; + item.tooltip = GUI::L_str("3D editor view"); + item.sprite_id = 0; + item.action_event = EVT_GLVIEWTOOLBAR_3D; + item.is_toggable = false; + if (!view_toolbar.add_item(item)) + return; + + item.name = "Preview"; + item.tooltip = GUI::L_str("Preview"); + item.sprite_id = 1; + item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; + item.is_toggable = false; + if (!view_toolbar.add_item(item)) + return; + + view_toolbar.enable_item("3D"); + view_toolbar.enable_item("Preview"); + + view_toolbar.select_item("3D"); + view_toolbar.set_enabled(true); + + view3D->set_view_toolbar(&view_toolbar); + preview->set_view_toolbar(&view_toolbar); +#else GLRadioToolbarItem::Data item; item.name = "3D"; @@ -2669,6 +2727,7 @@ void Plater::priv::init_view_toolbar() preview->set_view_toolbar(&view_toolbar); view_toolbar.set_selection("3D"); +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } #endif // ENABLE_REMOVE_TABS_FROM_PLATER From 0a6e4cb39ac4e432863405f07e63eb1506cda92c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 11:11:49 +0100 Subject: [PATCH 16/57] Scalable toolbar icons --- src/slic3r/GUI/GLToolbar.cpp | 45 +++++++++++++++++++++--------------- src/slic3r/GUI/GLToolbar.hpp | 2 ++ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index a7f623b8d..43da81809 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -173,6 +173,7 @@ GLToolbar::Layout::Layout() #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , separator_size(0.0f) , gap_size(0.0f) + , icons_scale(1.0f) #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , width(0.0f) , height(0.0f) @@ -290,6 +291,14 @@ void GLToolbar::set_gap_size(float size) #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } +void GLToolbar::set_icons_scale(float scale) +{ + m_layout.icons_scale = scale; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_layout.dirty = true; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE +} + bool GLToolbar::is_enabled() const { return m_enabled; @@ -625,18 +634,18 @@ float GLToolbar::get_width_horizontal() const float GLToolbar::get_width_vertical() const { #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size; + return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; #else - return m_icons_texture.items_icon_size; + return m_icons_texture.items_icon_size * m_layout.icons_scale; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_horizontal() const { #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size; + return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; #else - return m_icons_texture.items_icon_size; + return m_icons_texture.items_icon_size * m_layout.icons_scale; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } @@ -658,9 +667,9 @@ float GLToolbar::get_main_size() const size += m_layout.separator_size; else #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - size += (float)m_icons_texture.metadata.icon_size; + size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale; #else - size += (float)m_icons_texture.items_icon_size; + size += (float)m_icons_texture.items_icon_size * m_layout.icons_scale; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } @@ -699,9 +708,9 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; @@ -844,9 +853,9 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; @@ -980,9 +989,9 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; @@ -1045,9 +1054,9 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; @@ -1109,9 +1118,9 @@ void GLToolbar::render_horizontal() const float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; @@ -1257,9 +1266,9 @@ void GLToolbar::render_vertical() const float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; #else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; + float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 4e15edd17..430d4a844 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -193,6 +193,7 @@ public: #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_size; float gap_size; + float icons_scale; #if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float width; @@ -249,6 +250,7 @@ public: #endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_separator_size(float size); void set_gap_size(float size); + void set_icons_scale(float scale); bool is_enabled() const; void set_enabled(bool enable); From 7e8d9c154d06397a859622316372b73c3baee357 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 12:11:51 +0100 Subject: [PATCH 17/57] WIP: Fix of PrusaControl import. Now the transformation matrices need to be restored as well. --- src/libslic3r/Format/PRUS.cpp | 2 +- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 1 - src/slic3r/GUI/GLCanvas3D.hpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index f79721be7..6c0b055d6 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -329,7 +329,7 @@ bool load_prus(const char *path, Model *model) if (! mz_zip_reader_file_stat(&archive, i, &stat)) continue; std::vector buffer; - buffer.assign((size_t)stat.m_uncomp_size + 1, 0); + buffer.assign((size_t)stat.m_uncomp_size, 0); res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == MZ_FALSE) std::runtime_error(std::string("Error while extracting a file from ") + path); diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index b0da3a210..f87969505 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -487,7 +487,6 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam return INVALID; } - // Returns gcode to prime the nozzles at the front edge of the print bed. WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // print_z of the first layer. diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ebc2929d9..eecc3261b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -155,7 +155,7 @@ class GLCanvas3D // float distance; #if !ENABLE_CONSTRAINED_CAMERA_TARGET Vec3d target; -#endif !// ENABLE_CONSTRAINED_CAMERA_TARGET +#endif // ENABLE_CONSTRAINED_CAMERA_TARGET private: #if ENABLE_CONSTRAINED_CAMERA_TARGET From 825f3641e2b2e0039370a82682e44b69665ed9b0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 12:46:36 +0100 Subject: [PATCH 18/57] Created GetRowByItem() to fix the Scrolling of the Object List to selected item under all platforms + temporary suppressed object/part mane editing under OSX --- src/slic3r/GUI/GUI_ObjectList.cpp | 9 +++----- src/slic3r/GUI/GUI_ObjectList.hpp | 2 ++ src/slic3r/GUI/wxExtensions.cpp | 38 +++++++++++++++++++++++++++++++ src/slic3r/GUI/wxExtensions.hpp | 20 +++++++++++++--- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a7f1ca608..cba42470a 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1460,12 +1460,9 @@ void ObjectList::update_selections() select_items(sels); if (GetSelection()) { - const wxRect& sel_rc = GetItemRect(GetSelection()); - if (!sel_rc.IsEmpty()) { - const int rc_h = sel_rc.height; - const int displ = GetMainWindow()->GetClientRect().GetHeight()/(2*rc_h)+1; - ScrollLines(int(sel_rc.y / rc_h - displ)); - } + const int sel_item_row = m_objects_model->GetRowByItem(GetSelection()); + ScrollLines(sel_item_row - m_selected_row); + m_selected_row = sel_item_row; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 3664e6fda..7631782df 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -108,6 +108,8 @@ class ObjectList : public wxDataViewCtrl bool m_parts_changed = false; bool m_part_settings_changed = false; + int m_selected_row = 0; + public: ObjectList(wxWindow* parent); ~ObjectList(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 98e758bc8..2daba5df4 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -953,6 +953,44 @@ void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type = itUndef; } +int PrusaObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const +{ + if (m_objects.empty()) + return -1; + + int row_num = 0; + + for (int i = 0; i < m_objects.size(); i++) + { + row_num++; + if (item == wxDataViewItem(m_objects[i])) + return row_num; + + for (int j = 0; j < m_objects[i]->GetChildCount(); j++) + { + row_num++; + PrusaObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j); + if (item == wxDataViewItem(cur_node)) + return row_num; + + if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1) + row_num++; + if (cur_node->m_type == itInstanceRoot) + { + row_num++; + for (int t = 0; t < cur_node->GetChildCount(); t++) + { + row_num++; + if (item == wxDataViewItem(cur_node->GetNthChild(t))) + return row_num; + } + } + } + } + + return -1; +} + wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const { PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 637f1bcff..e8fba1ea2 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -463,6 +463,7 @@ public: int GetVolumeIdByItem(const wxDataViewItem& item) const; int GetInstanceIdByItem(const wxDataViewItem& item) const; void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); + int GetRowByItem(const wxDataViewItem& item) const; bool IsEmpty() { return m_objects.empty(); } // helper method for wxLog @@ -525,8 +526,14 @@ class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING { public: - PrusaBitmapTextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, - int align = wxDVR_DEFAULT_ALIGNMENT + PrusaBitmapTextRenderer(wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + + ,int align = wxDVR_DEFAULT_ALIGNMENT #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING ); #else @@ -542,7 +549,14 @@ public: virtual bool Render(wxRect cell, wxDC *dc, int state); virtual wxSize GetSize() const; - bool HasEditorCtrl() const override { return true; } + bool HasEditorCtrl() const override + { +#ifdef __WXOSX__ + return false; +#else + return true; +#endif + } wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override; From 57e80f896c8408098c8566827ef7c0d56d7da4fb Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 12:49:47 +0100 Subject: [PATCH 19/57] Small refactoring into gizmos overlay in preparation for background texture --- src/slic3r/GUI/GLCanvas3D.cpp | 41 +++++++++++++++++------------------ src/slic3r/GUI/GLCanvas3D.hpp | 4 ++-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8b1d636e5..acfc642a3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2501,9 +2501,9 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING -const float GLCanvas3D::Gizmos::OverlayTexturesScale = 1.0f; -const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; -const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; +const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; +const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; +const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) @@ -2606,19 +2606,18 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height); + float top_y = 0.5f * (cnv_h - height) + OverlayBorder; for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; - float half_tex_size = 0.5f * tex_size; + float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; // we currently use circular icons for gizmo, so we check the radius if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) { - bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size; + bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size); it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); if (inside) name = it->second->get_name(); @@ -2636,17 +2635,17 @@ 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); + float top_y = 0.5f * (cnv_h - height) + OverlayBorder; for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; - float half_tex_size = 0.5f * tex_size; + float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; // we currently use circular icons for gizmo, so we check the radius - if (it->second->is_activable(selection) && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)) + bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size); + if (it->second->is_activable(selection) && inside) { if ((it->second->get_state() == GLGizmoBase::On)) { @@ -2734,17 +2733,17 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height); + float top_y = 0.5f * (cnv_h - height) + OverlayBorder; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; + float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; float half_tex_size = 0.5f * tex_size; // we currently use circular icons for gizmo, so we check the radius - if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) + if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size)) return true; top_y += (tex_size + OverlayGapY); @@ -3020,19 +3019,19 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = _get_total_overlay_height(); - float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom; - float top_y = 0.5f * height * inv_zoom; + float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom; + float top_y = (0.5f * height - OverlayBorder) * inv_zoom; float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom; + float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom; GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); #if ENABLE_IMGUI if (it->second->get_state() == GLGizmoBase::On) - it->second->render_input_window(2.0f * OverlayOffsetX + tex_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); + it->second->render_input_window(2.0f * OverlayBorder + tex_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); #endif // ENABLE_IMGUI top_y -= (tex_size + scaled_gap_y); } @@ -3047,14 +3046,14 @@ void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& sele float GLCanvas3D::Gizmos::_get_total_overlay_height() const { - float height = 0.0f; + float height = 2.0f * OverlayBorder; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - if (it->first == SlaSupports && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + if ((it->second == nullptr) || !it->second->is_selectable()) continue; - height += (float)it->second->get_textures_size() * OverlayTexturesScale + OverlayGapY; + height += (float)it->second->get_textures_size() * OverlayIconsScale + OverlayGapY; } return height - OverlayGapY; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index eecc3261b..8683ef636 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -607,8 +607,8 @@ public: private: class Gizmos { - static const float OverlayTexturesScale; - static const float OverlayOffsetX; + static const float OverlayIconsScale; + static const float OverlayBorder; static const float OverlayGapY; public: From 40f74fe6eb625e18e82392c5ebca648f9fe3170d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 13:20:57 +0100 Subject: [PATCH 20/57] Added background texture to gizmos overlay --- src/slic3r/GUI/GLCanvas3D.cpp | 144 ++++++++++++++++++++++++++++++---- src/slic3r/GUI/GLCanvas3D.hpp | 6 ++ 2 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index acfc642a3..6a8043cf6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2584,6 +2584,23 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + m_background_texture.metadata.filename = "toolbar_background.png"; + m_background_texture.metadata.left = 16; + m_background_texture.metadata.top = 16; + m_background_texture.metadata.right = 16; + m_background_texture.metadata.bottom = 16; + + if (!m_background_texture.metadata.filename.empty()) + { + if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false)) + { + _reset(); + return false; + } + } +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + return true; } @@ -2612,17 +2629,16 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; + float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; - // we currently use circular icons for gizmo, so we check the radius if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) { - bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size); + bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); if (inside) name = it->second->get_name(); } - top_y += (tex_size + OverlayGapY); + top_y += (icon_size + OverlayGapY); } return name; @@ -2641,10 +2657,9 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; + float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; - // we currently use circular icons for gizmo, so we check the radius - bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size); + bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); if (it->second->is_activable(selection) && inside) { if ((it->second->get_state() == GLGizmoBase::On)) @@ -2661,7 +2676,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec else it->second->set_state(GLGizmoBase::Off); - top_y += (tex_size + OverlayGapY); + top_y += (icon_size + OverlayGapY); } GizmosMap::iterator it = m_gizmos.find(m_current); @@ -2739,14 +2754,12 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale; - float half_tex_size = 0.5f * tex_size; + float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; - // we currently use circular icons for gizmo, so we check the radius - if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + tex_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + tex_size)) + if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size)) return true; - top_y += (tex_size + OverlayGapY); + top_y += (icon_size + OverlayGapY); } return false; @@ -3019,21 +3032,102 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = _get_total_overlay_height(); +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float scaled_border = OverlayBorder * inv_zoom; + + float top_x = (-0.5f * cnv_w) * inv_zoom; + float top_y = (0.5f * height) * inv_zoom; + + float left = top_x; + float top = top_y; + float right = left + _get_total_overlay_width() * inv_zoom; + float bottom = top - height * inv_zoom; + + // renders background + unsigned int bg_tex_id = m_background_texture.texture.get_id(); + float bg_tex_width = (float)m_background_texture.texture.get_width(); + float bg_tex_height = (float)m_background_texture.texture.get_height(); + if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0)) + { + float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f; + float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f; + + float bg_uv_left = 0.0f; + float bg_uv_right = 1.0f; + float bg_uv_top = 1.0f; + float bg_uv_bottom = 0.0f; + + float bg_left = left; + float bg_right = right; + float bg_top = top; + float bg_bottom = bottom; + float bg_width = right - left; + float bg_height = top - bottom; + float bg_min_size = std::min(bg_width, bg_height); + + float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width; + float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width; + float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height; + float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height; + + float bg_i_left = bg_left + scaled_border; + float bg_i_right = bg_right - scaled_border; + float bg_i_top = bg_top - scaled_border; + float bg_i_bottom = bg_bottom + scaled_border; + + bg_uv_left = bg_uv_i_left; + bg_i_left = bg_left; + + if ((OverlayBorder > 0) && (bg_uv_top != bg_uv_i_top)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); + } + + if ((OverlayBorder > 0) && (bg_uv_left != bg_uv_i_left)) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); + + if ((OverlayBorder > 0) && (bg_uv_right != bg_uv_i_right)) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); + + if ((OverlayBorder > 0) && (bg_uv_bottom != bg_uv_i_bottom)) + { + if (bg_uv_left != bg_uv_i_left) + GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); + + GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } }); + + if (bg_uv_right != bg_uv_i_right) + GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } }); + } + } + + top_x += OverlayBorder * inv_zoom; + top_y -= OverlayBorder * inv_zoom; +#else float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom; float top_y = (0.5f * height - OverlayBorder) * inv_zoom; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float tex_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom; - GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); + float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom; + GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y); #if ENABLE_IMGUI if (it->second->get_state() == GLGizmoBase::On) - it->second->render_input_window(2.0f * OverlayBorder + tex_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); + it->second->render_input_window(2.0f * OverlayBorder + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); #endif // ENABLE_IMGUI - top_y -= (tex_size + scaled_gap_y); + top_y -= (icon_size + scaled_gap_y); } } @@ -3059,6 +3153,22 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height - OverlayGapY; } +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE +float GLCanvas3D::Gizmos::_get_total_overlay_width() const +{ + float max_icon_width = 0.0f; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second == nullptr) || !it->second->is_selectable()) + continue; + + max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * OverlayIconsScale); + } + + return max_icon_width + 2.0f * OverlayBorder; +} +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const { GizmosMap::const_iterator it = m_gizmos.find(m_current); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8683ef636..fd732ddf1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -628,6 +628,9 @@ private: bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + BackgroundTexture m_background_texture; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE EType m_current; public: @@ -696,6 +699,9 @@ private: void _render_current_gizmo(const Selection& selection) const; float _get_total_overlay_height() const; +#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE + float _get_total_overlay_width() const; +#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLGizmoBase* _get_current() const; }; From 985cd17265be09ca40144d275ec070633c3e8f33 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 14:09:35 +0100 Subject: [PATCH 21/57] Axes rendering --- src/slic3r/GUI/GLCanvas3D.cpp | 96 ++++++++++++++++++++++------------- src/slic3r/GUI/GLCanvas3D.hpp | 19 +++++-- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6a8043cf6..eecefd381 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -617,42 +617,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) return true; } +const double GLCanvas3D::Axes::Radius = 0.5; +const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius; +const double GLCanvas3D::Axes::ArrowLength = 5.0; + GLCanvas3D::Axes::Axes() : origin(Vec3d::Zero()) - , length(0.0f) + , length(Vec3d::Zero()) { + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); } -void GLCanvas3D::Axes::render(bool depth_test) const +GLCanvas3D::Axes::~Axes() { - if (depth_test) - ::glEnable(GL_DEPTH_TEST); - else - ::glDisable(GL_DEPTH_TEST); + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} - ::glLineWidth(2.0f); - ::glBegin(GL_LINES); - // draw line for x axis +void GLCanvas3D::Axes::render() const +{ + if (m_quadric == nullptr) + return; + + ::glEnable(GL_DEPTH_TEST); + ::glEnable(GL_LIGHTING); + + // x axis ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); - ::glEnd(); - // draw line for Z axis - // (re-enable depth test so that axis is correctly shown when objects are behind it) - if (!depth_test) - ::glEnable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(90.0, 0.0, 1.0, 0.0); + render_axis(length(0)); + ::glPopMatrix(); - ::glBegin(GL_LINES); + // y axis + ::glColor3f(0.0f, 1.0f, 0.0f); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(-90.0, 1.0, 0.0, 0.0); + render_axis(length(1)); + ::glPopMatrix(); + + // z axis ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); - ::glEnd(); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + render_axis(length(2)); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); } +void GLCanvas3D::Axes::render_axis(double length) const +{ + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, Radius, 32, 1); + ::glTranslated(0.0, 0.0, length); + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); +} GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -3781,7 +3810,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); + set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); if (new_shape) zoom_to_bed(); @@ -3789,9 +3818,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_dirty = true; } -void GLCanvas3D::set_axes_length(float length) +void GLCanvas3D::set_bed_axes_length(double length) { - m_axes.length = length; + m_axes.length = length * Vec3d::Ones(); } void GLCanvas3D::set_color_by(const std::string& value) @@ -4051,21 +4080,16 @@ void GLCanvas3D::render() _render_background(); if (is_custom_bed) // untextured bed needs to be rendered before objects - { _render_bed(theta); - // disable depth testing so that axes are not covered by ground - _render_axes(false); - } _render_objects(); _render_sla_slices(); _render_selection(); + _render_axes(); + if (!is_custom_bed) // textured bed needs to be rendered after objects - { - _render_axes(true); _render_bed(theta); - } // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods @@ -6019,9 +6043,9 @@ void GLCanvas3D::_render_bed(float theta) const m_bed.render(theta); } -void GLCanvas3D::_render_axes(bool depth_test) const +void GLCanvas3D::_render_axes() const { - m_axes.render(depth_test); + m_axes.render(); } void GLCanvas3D::_render_objects() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fd732ddf1..e07c60ed2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -20,6 +20,8 @@ class wxTimerEvent; class wxPaintEvent; class wxGLCanvas; +class GLUquadric; +typedef class GLUquadric GLUquadricObj; namespace Slic3r { @@ -231,12 +233,20 @@ class GLCanvas3D struct Axes { + static const double Radius; + static const double ArrowBaseRadius; + static const double ArrowLength; Vec3d origin; - float length; + Vec3d length; + GLUquadricObj* m_quadric; Axes(); + ~Axes(); - void render(bool depth_test) const; + void render() const; + + private: + void render_axis(double length) const; }; class Shader @@ -870,8 +880,7 @@ public: // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); - - void set_axes_length(float length); + void set_bed_axes_length(double length); void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { @@ -1011,7 +1020,7 @@ private: void _picking_pass() const; void _render_background() const; void _render_bed(float theta) const; - void _render_axes(bool depth_test) const; + void _render_axes() const; void _render_objects() const; void _render_selection() const; void _render_warning_texture() const; From cdc654540b245ed8938cb0b7f03e7eed486925a0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 14:40:54 +0100 Subject: [PATCH 22/57] Added accelerator table on Windows to let numpad work with CTRL key as required by window menu shortcuts --- src/slic3r/GUI/MainFrame.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a74d5f72a..aa5634fec 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -326,7 +326,7 @@ void MainFrame::init_menubar() size_t tab_offset = 0; if (m_plater) { #if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Plater Tab\tCtrl+1"), L("Show the plater"), + append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"), [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); #else append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"), @@ -338,22 +338,35 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); } #if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); - append_menu_item(windowMenu, wxID_ANY, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); - append_menu_item(windowMenu, wxID_ANY, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); if (m_plater) { windowMenu->AppendSeparator(); - wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_ANY, L("3D\tCtrl+5"), L("Show the 3D editing view"), - [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); - wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), - [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); + wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"), + [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); + wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), + [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId()); } + +#if _WIN32 + // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad + wxAcceleratorEntry entries[6]; + entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1); + entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2); + entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3); + entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4); + entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5); + entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6); + wxAcceleratorTable accel(6, entries); + SetAcceleratorTable(accel); +#endif // _WIN32 #else append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); From 11cf10774b69e223e350b734481eb76dddd9377c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 15:28:17 +0100 Subject: [PATCH 23/57] Fixed import from PrusaControl files --- src/libslic3r/Format/PRUS.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 6c0b055d6..80aae75cf 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -96,7 +96,6 @@ static void extract_model_from_archive( const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); const char *zero_tag = ""; const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); - float trafo[3][4] = { 0 }; Vec3d instance_rotation = Vec3d::Zero(); Vec3d instance_scaling_factor = Vec3d::Ones(); Vec3d instance_offset = Vec3d::Zero(); @@ -124,19 +123,7 @@ static void extract_model_from_archive( "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]); instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); - Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; - mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * - Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * - Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); - mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); - mat_trafo = mat_rot * mat_scale; - for (size_t r = 0; r < 3; ++ r) { - for (size_t c = 0; c < 3; ++ c) - trafo[r][c] += mat_trafo(r, c); - } instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); - // CHECK_ME -> Is the following correct ? - trafo[2][3] = position[2] / (float)instance_scaling_factor(2); trafo_set = true; } const char *group_tag = ""; @@ -189,8 +176,6 @@ static void extract_model_from_archive( // All the faces have been read. stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); if (std::abs(stl.stats.min(2)) < EPSILON) stl.stats.min(2) = 0.; // Add a mesh to a model. @@ -274,8 +259,6 @@ static void extract_model_from_archive( memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); // Add a mesh to a model. if (mesh.facets_count() > 0) mesh_valid = true; From f7a6ee9e29766773d2c85416c4f1f2925df84761 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 15:45:20 +0100 Subject: [PATCH 24/57] Fixed volume shown in info panel for scaled objects --- src/slic3r/GUI/Plater.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8db804c12..aeda2e8c2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -732,8 +732,7 @@ void Sidebar::show_info_sizer() p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); auto& stats = model_object->volumes.front()->mesh.stl.stats; - auto sf = model_instance->get_scaling_factor(); - p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2))); + p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2))); p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast(model_object->facets_count()), stats.number_of_parts)); int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + From b613334b818011f332e6bc7a5b2f9f472c34f893 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 17 Dec 2018 15:58:15 +0100 Subject: [PATCH 25/57] Considering multiple neighboring triangles for support point normals --- src/eigen/unsupported/Eigen/SparseExtra | 53 + .../SparseExtra/BlockOfDynamicSparseMatrix.h | 122 ++ .../Eigen/src/SparseExtra/BlockSparseMatrix.h | 1079 +++++++++++++++++ .../src/SparseExtra/DynamicSparseMatrix.h | 392 ++++++ .../Eigen/src/SparseExtra/MarketIO.h | 275 +++++ .../src/SparseExtra/MatrixMarketIterator.h | 247 ++++ .../Eigen/src/SparseExtra/RandomSetter.h | 327 +++++ src/libslic3r/SLA/SLASupportTree.cpp | 6 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 127 +- 9 files changed, 2605 insertions(+), 23 deletions(-) create mode 100644 src/eigen/unsupported/Eigen/SparseExtra create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h diff --git a/src/eigen/unsupported/Eigen/SparseExtra b/src/eigen/unsupported/Eigen/SparseExtra new file mode 100644 index 000000000..819cffa27 --- /dev/null +++ b/src/eigen/unsupported/Eigen/SparseExtra @@ -0,0 +1,53 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_EXTRA_MODULE_H +#define EIGEN_SPARSE_EXTRA_MODULE_H + +#include "../../Eigen/Sparse" + +#include "../../Eigen/src/Core/util/DisableStupidWarnings.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef EIGEN_GOOGLEHASH_SUPPORT + #include +#endif + +/** + * \defgroup SparseExtra_Module SparseExtra module + * + * This module contains some experimental features extending the sparse module. + * + * \code + * #include + * \endcode + */ + + +#include "src/SparseExtra/DynamicSparseMatrix.h" +#include "src/SparseExtra/BlockOfDynamicSparseMatrix.h" +#include "src/SparseExtra/RandomSetter.h" + +#include "src/SparseExtra/MarketIO.h" + +#if !defined(_WIN32) +#include +#include "src/SparseExtra/MatrixMarketIterator.h" +#endif + +#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h" + +#endif // EIGEN_SPARSE_EXTRA_MODULE_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h new file mode 100644 index 000000000..e9ec746e3 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h @@ -0,0 +1,122 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H +#define EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H + +namespace Eigen { + +#if 0 + +// NOTE Have to be reimplemented as a specialization of BlockImpl< DynamicSparseMatrix<_Scalar, _Options, _Index>, ... > +// See SparseBlock.h for an example + + +/*************************************************************************** +* specialisation for DynamicSparseMatrix +***************************************************************************/ + +template +class SparseInnerVectorSet, Size> + : public SparseMatrixBase, Size> > +{ + typedef DynamicSparseMatrix<_Scalar, _Options, _Index> MatrixType; + public: + + enum { IsRowMajor = internal::traits::IsRowMajor }; + + EIGEN_SPARSE_PUBLIC_INTERFACE(SparseInnerVectorSet) + class InnerIterator: public MatrixType::InnerIterator + { + public: + inline InnerIterator(const SparseInnerVectorSet& xpr, Index outer) + : MatrixType::InnerIterator(xpr.m_matrix, xpr.m_outerStart + outer), m_outer(outer) + {} + inline Index row() const { return IsRowMajor ? m_outer : this->index(); } + inline Index col() const { return IsRowMajor ? this->index() : m_outer; } + protected: + Index m_outer; + }; + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outerStart, Index outerSize) + : m_matrix(matrix), m_outerStart(outerStart), m_outerSize(outerSize) + { + eigen_assert( (outerStart>=0) && ((outerStart+outerSize)<=matrix.outerSize()) ); + } + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outer) + : m_matrix(matrix), m_outerStart(outer), m_outerSize(Size) + { + eigen_assert(Size!=Dynamic); + eigen_assert( (outer>=0) && (outer + inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) + { + if (IsRowMajor != ((OtherDerived::Flags&RowMajorBit)==RowMajorBit)) + { + // need to transpose => perform a block evaluation followed by a big swap + DynamicSparseMatrix aux(other); + *this = aux.markAsRValue(); + } + else + { + // evaluate/copy vector per vector + for (Index j=0; j aux(other.innerVector(j)); + m_matrix.const_cast_derived()._data()[m_outerStart+j].swap(aux._data()); + } + } + return *this; + } + + inline SparseInnerVectorSet& operator=(const SparseInnerVectorSet& other) + { + return operator=(other); + } + + Index nonZeros() const + { + Index count = 0; + for (Index j=0; j0); + return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1); + } + +// template +// inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) +// { +// return *this; +// } + + EIGEN_STRONG_INLINE Index rows() const { return IsRowMajor ? m_outerSize.value() : m_matrix.rows(); } + EIGEN_STRONG_INLINE Index cols() const { return IsRowMajor ? m_matrix.cols() : m_outerSize.value(); } + + protected: + + const typename MatrixType::Nested m_matrix; + Index m_outerStart; + const internal::variable_if_dynamic m_outerSize; + +}; + +#endif + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h new file mode 100644 index 000000000..0e8350a7d --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h @@ -0,0 +1,1079 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2013 Desire Nuentsa +// Copyright (C) 2013 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSEBLOCKMATRIX_H +#define EIGEN_SPARSEBLOCKMATRIX_H + +namespace Eigen { +/** \ingroup SparseCore_Module + * + * \class BlockSparseMatrix + * + * \brief A versatile sparse matrix representation where each element is a block + * + * This class provides routines to manipulate block sparse matrices stored in a + * BSR-like representation. There are two main types : + * + * 1. All blocks have the same number of rows and columns, called block size + * in the following. In this case, if this block size is known at compile time, + * it can be given as a template parameter like + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * \endcode + * Here, bmat is a b_rows x b_cols block sparse matrix + * where each coefficient is a 3x3 dense matrix. + * If the block size is fixed but will be given at runtime, + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * bmat.setBlockSize(block_size); + * \endcode + * + * 2. The second case is for variable-block sparse matrices. + * Here each block has its own dimensions. The only restriction is that all the blocks + * in a row (resp. a column) should have the same number of rows (resp. of columns). + * It is thus required in this case to describe the layout of the matrix by calling + * setBlockLayout(rowBlocks, colBlocks). + * + * In any of the previous case, the matrix can be filled by calling setFromTriplets(). + * A regular sparse matrix can be converted to a block sparse matrix and vice versa. + * It is obviously required to describe the block layout beforehand by calling either + * setBlockSize() for fixed-size blocks or setBlockLayout for variable-size blocks. + * + * \tparam _Scalar The Scalar type + * \tparam _BlockAtCompileTime The block layout option. It takes the following values + * Dynamic : block size known at runtime + * a numeric number : fixed-size block known at compile time + */ +template class BlockSparseMatrix; + +template class BlockSparseMatrixView; + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _Index Index; + typedef Sparse StorageKind; // FIXME Where is it used ?? + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + BlockSize = _BlockAtCompileTime, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = InnerRandomAccessPattern + }; +}; +template +struct traits > +{ + typedef Ref > Scalar; + typedef Ref > RealScalar; + +}; + +// Function object to sort a triplet list +template +struct TripletComp +{ + typedef typename Iterator::value_type Triplet; + bool operator()(const Triplet& a, const Triplet& b) + { if(IsColMajor) + return ((a.col() == b.col() && a.row() < b.row()) || (a.col() < b.col())); + else + return ((a.row() == b.row() && a.col() < b.col()) || (a.row() < b.row())); + } +}; +} // end namespace internal + + +/* Proxy to view the block sparse matrix as a regular sparse matrix */ +template +class BlockSparseMatrixView : public SparseMatrixBase +{ + public: + typedef Ref Scalar; + typedef Ref RealScalar; + typedef typename BlockSparseMatrixT::Index Index; + typedef BlockSparseMatrixT Nested; + enum { + Flags = BlockSparseMatrixT::Options, + Options = BlockSparseMatrixT::Options, + RowsAtCompileTime = BlockSparseMatrixT::RowsAtCompileTime, + ColsAtCompileTime = BlockSparseMatrixT::ColsAtCompileTime, + MaxColsAtCompileTime = BlockSparseMatrixT::MaxColsAtCompileTime, + MaxRowsAtCompileTime = BlockSparseMatrixT::MaxRowsAtCompileTime + }; + public: + BlockSparseMatrixView(const BlockSparseMatrixT& spblockmat) + : m_spblockmat(spblockmat) + {} + + Index outerSize() const + { + return (Flags&RowMajorBit) == 1 ? this->rows() : this->cols(); + } + Index cols() const + { + return m_spblockmat.blockCols(); + } + Index rows() const + { + return m_spblockmat.blockRows(); + } + Scalar coeff(Index row, Index col) + { + return m_spblockmat.coeff(row, col); + } + Scalar coeffRef(Index row, Index col) + { + return m_spblockmat.coeffRef(row, col); + } + // Wrapper to iterate over all blocks + class InnerIterator : public BlockSparseMatrixT::BlockInnerIterator + { + public: + InnerIterator(const BlockSparseMatrixView& mat, Index outer) + : BlockSparseMatrixT::BlockInnerIterator(mat.m_spblockmat, outer) + {} + + }; + + protected: + const BlockSparseMatrixT& m_spblockmat; +}; + +// Proxy to view a regular vector as a block vector +template +class BlockVectorView +{ + public: + enum { + BlockSize = BlockSparseMatrixT::BlockSize, + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref >Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorView(const BlockSparseMatrixT& spblockmat, const VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index cols() const + { + return m_vec.cols(); + } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeff(Index bi) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeff(Index bi, Index j) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + protected: + const BlockSparseMatrixT& m_spblockmat; + const VectorType& m_vec; +}; + +template class BlockVectorReturn; + + +// Proxy to view a regular vector as a block vector +template +class BlockVectorReturn +{ + public: + enum { + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref > Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorReturn(const BlockSparseMatrixT& spblockmat, VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeffRef(Index bi) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeffRef(Index bi, Index j) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + + protected: + const BlockSparseMatrixT& m_spblockmat; + VectorType& m_vec; +}; + +// Block version of the sparse dense product +template +class BlockSparseTimeDenseProduct; + +namespace internal { + +template +struct traits > +{ + typedef Dense StorageKind; + typedef MatrixXpr XprKind; + typedef typename BlockSparseMatrixT::Scalar Scalar; + typedef typename BlockSparseMatrixT::Index Index; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = 0, + CoeffReadCost = internal::traits::CoeffReadCost + }; +}; +} // end namespace internal + +template +class BlockSparseTimeDenseProduct + : public ProductBase, Lhs, Rhs> +{ + public: + EIGEN_PRODUCT_PUBLIC_INTERFACE(BlockSparseTimeDenseProduct) + + BlockSparseTimeDenseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + {} + + template void scaleAndAddTo(Dest& dest, const typename Rhs::Scalar& alpha) const + { + BlockVectorReturn tmpDest(m_lhs, dest); + internal::sparse_time_dense_product( BlockSparseMatrixView(m_lhs), BlockVectorView(m_lhs, m_rhs), tmpDest, alpha); + } + + private: + BlockSparseTimeDenseProduct& operator=(const BlockSparseTimeDenseProduct&); +}; + +template +class BlockSparseMatrix : public SparseMatrixBase > +{ + public: + typedef _Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef _StorageIndex StorageIndex; + typedef typename internal::ref_selector >::type Nested; + + enum { + Options = _Options, + Flags = Options, + BlockSize=_BlockAtCompileTime, + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + IsVectorAtCompileTime = 0, + IsColMajor = Flags&RowMajorBit ? 0 : 1 + }; + typedef Matrix BlockScalar; + typedef Matrix BlockRealScalar; + typedef typename internal::conditional<_BlockAtCompileTime==Dynamic, Scalar, BlockScalar>::type BlockScalarReturnType; + typedef BlockSparseMatrix PlainObject; + public: + // Default constructor + BlockSparseMatrix() + : m_innerBSize(0),m_outerBSize(0),m_innerOffset(0),m_outerOffset(0), + m_nonzerosblocks(0),m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + + /** + * \brief Construct and resize + * + */ + BlockSparseMatrix(Index brow, Index bcol) + : m_innerBSize(IsColMajor ? brow : bcol), + m_outerBSize(IsColMajor ? bcol : brow), + m_innerOffset(0),m_outerOffset(0),m_nonzerosblocks(0), + m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + /** + * \brief Copy-constructor + */ + BlockSparseMatrix(const BlockSparseMatrix& other) + : m_innerBSize(other.m_innerBSize),m_outerBSize(other.m_outerBSize), + m_nonzerosblocks(other.m_nonzerosblocks),m_nonzeros(other.m_nonzeros), + m_blockPtr(0),m_blockSize(other.m_blockSize) + { + // should we allow copying between variable-size blocks and fixed-size blocks ?? + eigen_assert(m_blockSize == BlockSize && " CAN NOT COPY BETWEEN FIXED-SIZE AND VARIABLE-SIZE BLOCKS"); + + std::copy(other.m_innerOffset, other.m_innerOffset+m_innerBSize+1, m_innerOffset); + std::copy(other.m_outerOffset, other.m_outerOffset+m_outerBSize+1, m_outerOffset); + std::copy(other.m_values, other.m_values+m_nonzeros, m_values); + + if(m_blockSize != Dynamic) + std::copy(other.m_blockPtr, other.m_blockPtr+m_nonzerosblocks, m_blockPtr); + + std::copy(other.m_indices, other.m_indices+m_nonzerosblocks, m_indices); + std::copy(other.m_outerIndex, other.m_outerIndex+m_outerBSize, m_outerIndex); + } + + friend void swap(BlockSparseMatrix& first, BlockSparseMatrix& second) + { + std::swap(first.m_innerBSize, second.m_innerBSize); + std::swap(first.m_outerBSize, second.m_outerBSize); + std::swap(first.m_innerOffset, second.m_innerOffset); + std::swap(first.m_outerOffset, second.m_outerOffset); + std::swap(first.m_nonzerosblocks, second.m_nonzerosblocks); + std::swap(first.m_nonzeros, second.m_nonzeros); + std::swap(first.m_values, second.m_values); + std::swap(first.m_blockPtr, second.m_blockPtr); + std::swap(first.m_indices, second.m_indices); + std::swap(first.m_outerIndex, second.m_outerIndex); + std::swap(first.m_BlockSize, second.m_blockSize); + } + + BlockSparseMatrix& operator=(BlockSparseMatrix other) + { + //Copy-and-swap paradigm ... avoid leaked data if thrown + swap(*this, other); + return *this; + } + + // Destructor + ~BlockSparseMatrix() + { + delete[] m_outerIndex; + delete[] m_innerOffset; + delete[] m_outerOffset; + delete[] m_indices; + delete[] m_blockPtr; + delete[] m_values; + } + + + /** + * \brief Constructor from a sparse matrix + * + */ + template + inline BlockSparseMatrix(const MatrixType& spmat) : m_blockSize(BlockSize) + { + EIGEN_STATIC_ASSERT((m_blockSize != Dynamic), THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE); + + *this = spmat; + } + + /** + * \brief Assignment from a sparse matrix with the same storage order + * + * Convert from a sparse matrix to block sparse matrix. + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + */ + template + inline BlockSparseMatrix& operator=(const MatrixType& spmat) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) + && "Trying to assign to a zero-size matrix, call resize() first"); + eigen_assert(((MatrixType::Options&RowMajorBit) != IsColMajor) && "Wrong storage order"); + typedef SparseMatrix MatrixPatternType; + MatrixPatternType blockPattern(blockRows(), blockCols()); + m_nonzeros = 0; + + // First, compute the number of nonzero blocks and their locations + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Browse each outer block and compute the structure + std::vector nzblocksFlag(m_innerBSize,false); // Record the existing blocks + blockPattern.startVec(bj); + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + if(!nzblocksFlag[bi]) + { + // Save the index of this nonzero block + nzblocksFlag[bi] = true; + blockPattern.insertBackByOuterInnerUnordered(bj, bi) = true; + // Compute the total number of nonzeros (including explicit zeros in blocks) + m_nonzeros += blockOuterSize(bj) * blockInnerSize(bi); + } + } + } // end current outer block + } + blockPattern.finalize(); + + // Allocate the internal arrays + setBlockStructure(blockPattern); + + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Now copy the values + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + // Browse the outer block column by column (for column-major matrices) + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex idx = 0; // Position of this block in the column block + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + // Go to the inner block where this element belongs to + while(bi > m_indices[m_outerIndex[bj]+idx]) ++idx; // Not expensive for ordered blocks + StorageIndex idxVal;// Get the right position in the array of values for this element + if(m_blockSize == Dynamic) + { + // Offset from all blocks before ... + idxVal = m_blockPtr[m_outerIndex[bj]+idx]; + // ... and offset inside the block + idxVal += (j - blockOuterIndex(bj)) * blockOuterSize(bj) + it_spmat.index() - m_innerOffset[bi]; + } + else + { + // All blocks before + idxVal = (m_outerIndex[bj] + idx) * m_blockSize * m_blockSize; + // inside the block + idxVal += (j - blockOuterIndex(bj)) * m_blockSize + (it_spmat.index()%m_blockSize); + } + // Insert the value + m_values[idxVal] = it_spmat.value(); + } // end of this column + } // end of this block + } // end of this outer block + + return *this; + } + + /** + * \brief Set the nonzero block pattern of the matrix + * + * Given a sparse matrix describing the nonzero block pattern, + * this function prepares the internal pointers for values. + * After calling this function, any *nonzero* block (bi, bj) can be set + * with a simple call to coeffRef(bi,bj). + * + * + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + * + * \param blockPattern Sparse matrix of boolean elements describing the block structure + * + * \sa setBlockLayout() \sa setBlockSize() + */ + template + void setBlockStructure(const MatrixType& blockPattern) + { + resize(blockPattern.rows(), blockPattern.cols()); + reserve(blockPattern.nonZeros()); + + // Browse the block pattern and set up the various pointers + m_outerIndex[0] = 0; + if(m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + //Browse each outer block + + //First, copy and save the indices of nonzero blocks + //FIXME : find a way to avoid this ... + std::vector nzBlockIdx; + typename MatrixType::InnerIterator it(blockPattern, bj); + for(; it; ++it) + { + nzBlockIdx.push_back(it.index()); + } + std::sort(nzBlockIdx.begin(), nzBlockIdx.end()); + + // Now, fill block indices and (eventually) pointers to blocks + for(StorageIndex idx = 0; idx < nzBlockIdx.size(); ++idx) + { + StorageIndex offset = m_outerIndex[bj]+idx; // offset in m_indices + m_indices[offset] = nzBlockIdx[idx]; + if(m_blockSize == Dynamic) + m_blockPtr[offset] = m_blockPtr[offset-1] + blockInnerSize(nzBlockIdx[idx]) * blockOuterSize(bj); + // There is no blockPtr for fixed-size blocks... not needed !??? + } + // Save the pointer to the next outer block + m_outerIndex[bj+1] = m_outerIndex[bj] + nzBlockIdx.size(); + } + } + + /** + * \brief Set the number of rows and columns blocks + */ + inline void resize(Index brow, Index bcol) + { + m_innerBSize = IsColMajor ? brow : bcol; + m_outerBSize = IsColMajor ? bcol : brow; + } + + /** + * \brief set the block size at runtime for fixed-size block layout + * + * Call this only for fixed-size blocks + */ + inline void setBlockSize(Index blockSize) + { + m_blockSize = blockSize; + } + + /** + * \brief Set the row and column block layouts, + * + * This function set the size of each row and column block. + * So this function should be used only for blocks with variable size. + * \param rowBlocks : Number of rows per row block + * \param colBlocks : Number of columns per column block + * \sa resize(), setBlockSize() + */ + inline void setBlockLayout(const VectorXi& rowBlocks, const VectorXi& colBlocks) + { + const VectorXi& innerBlocks = IsColMajor ? rowBlocks : colBlocks; + const VectorXi& outerBlocks = IsColMajor ? colBlocks : rowBlocks; + eigen_assert(m_innerBSize == innerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + eigen_assert(m_outerBSize == outerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + m_outerBSize = outerBlocks.size(); + // starting index of blocks... cumulative sums + m_innerOffset = new StorageIndex[m_innerBSize+1]; + m_outerOffset = new StorageIndex[m_outerBSize+1]; + m_innerOffset[0] = 0; + m_outerOffset[0] = 0; + std::partial_sum(&innerBlocks[0], &innerBlocks[m_innerBSize-1]+1, &m_innerOffset[1]); + std::partial_sum(&outerBlocks[0], &outerBlocks[m_outerBSize-1]+1, &m_outerOffset[1]); + + // Compute the total number of nonzeros + m_nonzeros = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + for(StorageIndex bi = 0; bi < m_innerBSize; ++bi) + m_nonzeros += outerBlocks[bj] * innerBlocks[bi]; + + } + + /** + * \brief Allocate the internal array of pointers to blocks and their inner indices + * + * \note For fixed-size blocks, call setBlockSize() to set the block. + * And For variable-size blocks, call setBlockLayout() before using this function + * + * \param nonzerosblocks Number of nonzero blocks. The total number of nonzeros is + * is computed in setBlockLayout() for variable-size blocks + * \sa setBlockSize() + */ + inline void reserve(const Index nonzerosblocks) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) && + "TRYING TO RESERVE ZERO-SIZE MATRICES, CALL resize() first"); + + //FIXME Should free if already allocated + m_outerIndex = new StorageIndex[m_outerBSize+1]; + + m_nonzerosblocks = nonzerosblocks; + if(m_blockSize != Dynamic) + { + m_nonzeros = nonzerosblocks * (m_blockSize * m_blockSize); + m_blockPtr = 0; + } + else + { + // m_nonzeros is already computed in setBlockLayout() + m_blockPtr = new StorageIndex[m_nonzerosblocks+1]; + } + m_indices = new StorageIndex[m_nonzerosblocks+1]; + m_values = new Scalar[m_nonzeros]; + } + + + /** + * \brief Fill values in a matrix from a triplet list. + * + * Each triplet item has a block stored in an Eigen dense matrix. + * The InputIterator class should provide the functions row(), col() and value() + * + * \note For fixed-size blocks, call setBlockSize() before this function. + * + * FIXME Do not accept duplicates + */ + template + void setFromTriplets(const InputIterator& begin, const InputIterator& end) + { + eigen_assert((m_innerBSize!=0 && m_outerBSize !=0) && "ZERO BLOCKS, PLEASE CALL resize() before"); + + /* First, sort the triplet list + * FIXME This can be unnecessarily expensive since only the inner indices have to be sorted + * The best approach is like in SparseMatrix::setFromTriplets() + */ + internal::TripletComp tripletcomp; + std::sort(begin, end, tripletcomp); + + /* Count the number of rows and column blocks, + * and the number of nonzero blocks per outer dimension + */ + VectorXi rowBlocks(m_innerBSize); // Size of each block row + VectorXi colBlocks(m_outerBSize); // Size of each block column + rowBlocks.setZero(); colBlocks.setZero(); + VectorXi nzblock_outer(m_outerBSize); // Number of nz blocks per outer vector + VectorXi nz_outer(m_outerBSize); // Number of nz per outer vector...for variable-size blocks + nzblock_outer.setZero(); + nz_outer.setZero(); + for(InputIterator it(begin); it !=end; ++it) + { + eigen_assert(it->row() >= 0 && it->row() < this->blockRows() && it->col() >= 0 && it->col() < this->blockCols()); + eigen_assert((it->value().rows() == it->value().cols() && (it->value().rows() == m_blockSize)) + || (m_blockSize == Dynamic)); + + if(m_blockSize == Dynamic) + { + eigen_assert((rowBlocks[it->row()] == 0 || rowBlocks[it->row()] == it->value().rows()) && + "NON CORRESPONDING SIZES FOR ROW BLOCKS"); + eigen_assert((colBlocks[it->col()] == 0 || colBlocks[it->col()] == it->value().cols()) && + "NON CORRESPONDING SIZES FOR COLUMN BLOCKS"); + rowBlocks[it->row()] =it->value().rows(); + colBlocks[it->col()] = it->value().cols(); + } + nz_outer(IsColMajor ? it->col() : it->row()) += it->value().rows() * it->value().cols(); + nzblock_outer(IsColMajor ? it->col() : it->row())++; + } + // Allocate member arrays + if(m_blockSize == Dynamic) setBlockLayout(rowBlocks, colBlocks); + StorageIndex nzblocks = nzblock_outer.sum(); + reserve(nzblocks); + + // Temporary markers + VectorXi block_id(m_outerBSize); // To be used as a block marker during insertion + + // Setup outer index pointers and markers + m_outerIndex[0] = 0; + if (m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + m_outerIndex[bj+1] = m_outerIndex[bj] + nzblock_outer(bj); + block_id(bj) = m_outerIndex[bj]; + if(m_blockSize==Dynamic) + { + m_blockPtr[m_outerIndex[bj+1]] = m_blockPtr[m_outerIndex[bj]] + nz_outer(bj); + } + } + + // Fill the matrix + for(InputIterator it(begin); it!=end; ++it) + { + StorageIndex outer = IsColMajor ? it->col() : it->row(); + StorageIndex inner = IsColMajor ? it->row() : it->col(); + m_indices[block_id(outer)] = inner; + StorageIndex block_size = it->value().rows()*it->value().cols(); + StorageIndex nz_marker = blockPtr(block_id[outer]); + memcpy(&(m_values[nz_marker]), it->value().data(), block_size * sizeof(Scalar)); + if(m_blockSize == Dynamic) + { + m_blockPtr[block_id(outer)+1] = m_blockPtr[block_id(outer)] + block_size; + } + block_id(outer)++; + } + + // An alternative when the outer indices are sorted...no need to use an array of markers +// for(Index bcol = 0; bcol < m_outerBSize; ++bcol) +// { +// Index id = 0, id_nz = 0, id_nzblock = 0; +// for(InputIterator it(begin); it!=end; ++it) +// { +// while (idvalue().rows()*it->value().cols(); +// m_blockPtr[id_nzblock+1] = m_blockPtr[id_nzblock] + block_size; +// id_nzblock++; +// memcpy(&(m_values[id_nz]),it->value().data(), block_size*sizeof(Scalar)); +// id_nz += block_size; +// } +// while(id < m_outerBSize-1) // Empty columns at the end +// { +// id++; +// m_outerIndex[id+1]=m_outerIndex[id]; +// } +// } + } + + + /** + * \returns the number of rows + */ + inline Index rows() const + { +// return blockRows(); + return (IsColMajor ? innerSize() : outerSize()); + } + + /** + * \returns the number of cols + */ + inline Index cols() const + { +// return blockCols(); + return (IsColMajor ? outerSize() : innerSize()); + } + + inline Index innerSize() const + { + if(m_blockSize == Dynamic) return m_innerOffset[m_innerBSize]; + else return (m_innerBSize * m_blockSize) ; + } + + inline Index outerSize() const + { + if(m_blockSize == Dynamic) return m_outerOffset[m_outerBSize]; + else return (m_outerBSize * m_blockSize) ; + } + /** \returns the number of rows grouped by blocks */ + inline Index blockRows() const + { + return (IsColMajor ? m_innerBSize : m_outerBSize); + } + /** \returns the number of columns grouped by blocks */ + inline Index blockCols() const + { + return (IsColMajor ? m_outerBSize : m_innerBSize); + } + + inline Index outerBlocks() const { return m_outerBSize; } + inline Index innerBlocks() const { return m_innerBSize; } + + /** \returns the block index where outer belongs to */ + inline Index outerToBlock(Index outer) const + { + eigen_assert(outer < outerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (outer / m_blockSize); // Integer division + + StorageIndex b_outer = 0; + while(m_outerOffset[b_outer] <= outer) ++b_outer; + return b_outer - 1; + } + /** \returns the block index where inner belongs to */ + inline Index innerToBlock(Index inner) const + { + eigen_assert(inner < innerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (inner / m_blockSize); // Integer division + + StorageIndex b_inner = 0; + while(m_innerOffset[b_inner] <= inner) ++b_inner; + return b_inner - 1; + } + + /** + *\returns a reference to the (i,j) block as an Eigen Dense Matrix + */ + Ref coeffRef(Index brow, Index bcol) + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK nzblocksFlagCOLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) + offset++; + if(m_indices[offset] == inner) + { + return Map(&(m_values[blockPtr(offset)]), rsize, csize); + } + else + { + //FIXME the block does not exist, Insert it !!!!!!!!! + eigen_assert("DYNAMIC INSERTION IS NOT YET SUPPORTED"); + } + } + + /** + * \returns the value of the (i,j) block as an Eigen Dense Matrix + */ + Map coeff(Index brow, Index bcol) const + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK COLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) offset++; + if(m_indices[offset] == inner) + { + return Map (&(m_values[blockPtr(offset)]), rsize, csize); + } + else +// return BlockScalar::Zero(rsize, csize); + eigen_assert("NOT YET SUPPORTED"); + } + + // Block Matrix times vector product + template + BlockSparseTimeDenseProduct operator*(const VecType& lhs) const + { + return BlockSparseTimeDenseProduct(*this, lhs); + } + + /** \returns the number of nonzero blocks */ + inline Index nonZerosBlocks() const { return m_nonzerosblocks; } + /** \returns the total number of nonzero elements, including eventual explicit zeros in blocks */ + inline Index nonZeros() const { return m_nonzeros; } + + inline BlockScalarReturnType *valuePtr() {return static_cast(m_values);} +// inline Scalar *valuePtr(){ return m_values; } + inline StorageIndex *innerIndexPtr() {return m_indices; } + inline const StorageIndex *innerIndexPtr() const {return m_indices; } + inline StorageIndex *outerIndexPtr() {return m_outerIndex; } + inline const StorageIndex* outerIndexPtr() const {return m_outerIndex; } + + /** \brief for compatibility purposes with the SparseMatrix class */ + inline bool isCompressed() const {return true;} + /** + * \returns the starting index of the bi row block + */ + inline Index blockRowsIndex(Index bi) const + { + return IsColMajor ? blockInnerIndex(bi) : blockOuterIndex(bi); + } + + /** + * \returns the starting index of the bj col block + */ + inline Index blockColsIndex(Index bj) const + { + return IsColMajor ? blockOuterIndex(bj) : blockInnerIndex(bj); + } + + inline Index blockOuterIndex(Index bj) const + { + return (m_blockSize == Dynamic) ? m_outerOffset[bj] : (bj * m_blockSize); + } + inline Index blockInnerIndex(Index bi) const + { + return (m_blockSize == Dynamic) ? m_innerOffset[bi] : (bi * m_blockSize); + } + + // Not needed ??? + inline Index blockInnerSize(Index bi) const + { + return (m_blockSize == Dynamic) ? (m_innerOffset[bi+1] - m_innerOffset[bi]) : m_blockSize; + } + inline Index blockOuterSize(Index bj) const + { + return (m_blockSize == Dynamic) ? (m_outerOffset[bj+1]- m_outerOffset[bj]) : m_blockSize; + } + + /** + * \brief Browse the matrix by outer index + */ + class InnerIterator; // Browse column by column + + /** + * \brief Browse the matrix by block outer index + */ + class BlockInnerIterator; // Browse block by block + + friend std::ostream & operator << (std::ostream & s, const BlockSparseMatrix& m) + { + for (StorageIndex j = 0; j < m.outerBlocks(); ++j) + { + BlockInnerIterator itb(m, j); + for(; itb; ++itb) + { + s << "("< in the array of values + */ + Index blockPtr(Index id) const + { + if(m_blockSize == Dynamic) return m_blockPtr[id]; + else return id * m_blockSize * m_blockSize; + //return blockDynIdx(id, typename internal::conditional<(BlockSize==Dynamic), internal::true_type, internal::false_type>::type()); + } + + + protected: +// inline Index blockDynIdx(Index id, internal::true_type) const +// { +// return m_blockPtr[id]; +// } +// inline Index blockDynIdx(Index id, internal::false_type) const +// { +// return id * BlockSize * BlockSize; +// } + + // To be implemented + // Insert a block at a particular location... need to make a room for that + Map insert(Index brow, Index bcol); + + Index m_innerBSize; // Number of block rows + Index m_outerBSize; // Number of block columns + StorageIndex *m_innerOffset; // Starting index of each inner block (size m_innerBSize+1) + StorageIndex *m_outerOffset; // Starting index of each outer block (size m_outerBSize+1) + Index m_nonzerosblocks; // Total nonzeros blocks (lower than m_innerBSize x m_outerBSize) + Index m_nonzeros; // Total nonzeros elements + Scalar *m_values; //Values stored block column after block column (size m_nonzeros) + StorageIndex *m_blockPtr; // Pointer to the beginning of each block in m_values, size m_nonzeroblocks ... null for fixed-size blocks + StorageIndex *m_indices; //Inner block indices, size m_nonzerosblocks ... OK + StorageIndex *m_outerIndex; // Starting pointer of each block column in m_indices (size m_outerBSize)... OK + Index m_blockSize; // Size of a block for fixed-size blocks, otherwise -1 +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::BlockInnerIterator +{ + public: + + enum{ + Flags = _Options + }; + + BlockInnerIterator(const BlockSparseMatrix& mat, const Index outer) + : m_mat(mat),m_outer(outer), + m_id(mat.m_outerIndex[outer]), + m_end(mat.m_outerIndex[outer+1]) + { + } + + inline BlockInnerIterator& operator++() {m_id++; return *this; } + + inline const Map value() const + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + inline Map valueRef() + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + // Block inner index + inline Index index() const {return m_mat.m_indices[m_id]; } + inline Index outer() const { return m_outer; } + // block row index + inline Index row() const {return index(); } + // block column index + inline Index col() const {return outer(); } + // FIXME Number of rows in the current block + inline Index rows() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_innerOffset[index()+1] - m_mat.m_innerOffset[index()]) : m_mat.m_blockSize; } + // Number of columns in the current block ... + inline Index cols() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_outerOffset[m_outer+1]-m_mat.m_outerOffset[m_outer]) : m_mat.m_blockSize;} + inline operator bool() const { return (m_id < m_end); } + + protected: + const BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, StorageIndex>& m_mat; + const Index m_outer; + Index m_id; + Index m_end; +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::InnerIterator +{ + public: + InnerIterator(const BlockSparseMatrix& mat, Index outer) + : m_mat(mat),m_outerB(mat.outerToBlock(outer)),m_outer(outer), + itb(mat, mat.outerToBlock(outer)), + m_offset(outer - mat.blockOuterIndex(m_outerB)) + { + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + inline InnerIterator& operator++() + { + m_id++; + if (m_id >= m_end) + { + ++itb; + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + return *this; + } + inline const Scalar& value() const + { + return itb.value().coeff(m_id - m_start, m_offset); + } + inline Scalar& valueRef() + { + return itb.valueRef().coeff(m_id - m_start, m_offset); + } + inline Index index() const { return m_id; } + inline Index outer() const {return m_outer; } + inline Index col() const {return outer(); } + inline Index row() const { return index();} + inline operator bool() const + { + return itb; + } + protected: + const BlockSparseMatrix& m_mat; + const Index m_outer; + const Index m_outerB; + BlockInnerIterator itb; // Iterator through the blocks + const Index m_offset; // Position of this column in the block + Index m_start; // starting inner index of this block + Index m_id; // current inner index in the block + Index m_end; // starting inner index of the next block + +}; +} // end namespace Eigen + +#endif // EIGEN_SPARSEBLOCKMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h new file mode 100644 index 000000000..037a13f86 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h @@ -0,0 +1,392 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_DYNAMIC_SPARSEMATRIX_H +#define EIGEN_DYNAMIC_SPARSEMATRIX_H + +namespace Eigen { + +/** \deprecated use a SparseMatrix in an uncompressed mode + * + * \class DynamicSparseMatrix + * + * \brief A sparse matrix class designed for matrix assembly purpose + * + * \param _Scalar the scalar type, i.e. the type of the coefficients + * + * Unlike SparseMatrix, this class provides a much higher degree of flexibility. In particular, it allows + * random read/write accesses in log(rho*outer_size) where \c rho is the probability that a coefficient is + * nonzero and outer_size is the number of columns if the matrix is column-major and the number of rows + * otherwise. + * + * Internally, the data are stored as a std::vector of compressed vector. The performances of random writes might + * decrease as the number of nonzeros per inner-vector increase. In practice, we observed very good performance + * till about 100 nonzeros/vector, and the performance remains relatively good till 500 nonzeros/vectors. + * + * \see SparseMatrix + */ + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _StorageIndex StorageIndex; + typedef Sparse StorageKind; + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = OuterRandomAccessPattern + }; +}; +} + +template + class DynamicSparseMatrix + : public SparseMatrixBase > +{ + typedef SparseMatrixBase Base; + using Base::convert_index; + public: + EIGEN_SPARSE_PUBLIC_INTERFACE(DynamicSparseMatrix) + // FIXME: why are these operator already alvailable ??? + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, +=) + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, -=) + typedef MappedSparseMatrix Map; + using Base::IsRowMajor; + using Base::operator=; + enum { + Options = _Options + }; + + protected: + + typedef DynamicSparseMatrix TransposedSparseMatrix; + + Index m_innerSize; + std::vector > m_data; + + public: + + inline Index rows() const { return IsRowMajor ? outerSize() : m_innerSize; } + inline Index cols() const { return IsRowMajor ? m_innerSize : outerSize(); } + inline Index innerSize() const { return m_innerSize; } + inline Index outerSize() const { return convert_index(m_data.size()); } + inline Index innerNonZeros(Index j) const { return m_data[j].size(); } + + std::vector >& _data() { return m_data; } + const std::vector >& _data() const { return m_data; } + + /** \returns the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. + */ + inline Scalar coeff(Index row, Index col) const + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].at(inner); + } + + /** \returns a reference to the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. If the coefficient does not + * exist yet, then a sorted insertion into a sequential buffer is performed. + */ + inline Scalar& coeffRef(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].atWithInsertion(inner); + } + + class InnerIterator; + class ReverseInnerIterator; + + void setZero() + { + for (Index j=0; j0) + { + Index reserveSizePerVector = (std::max)(reserveSize/outerSize(),Index(4)); + for (Index j=0; j(m_data[outer].size()) - 1; + m_data[outer].resize(id+2,1); + + while ( (id >= startId) && (m_data[outer].index(id) > inner) ) + { + m_data[outer].index(id+1) = m_data[outer].index(id); + m_data[outer].value(id+1) = m_data[outer].value(id); + --id; + } + m_data[outer].index(id+1) = inner; + m_data[outer].value(id+1) = 0; + return m_data[outer].value(id+1); + } + + /** Does nothing: provided for compatibility with SparseMatrix */ + inline void finalize() {} + + /** Suppress all nonzeros which are smaller than \a reference under the tolerence \a epsilon */ + void prune(Scalar reference, RealScalar epsilon = NumTraits::dummy_precision()) + { + for (Index j=0; jinnerSize) + { + // remove all coefficients with innerCoord>=innerSize + // TODO + //std::cerr << "not implemented yet\n"; + exit(2); + } + if (m_data.size() != outerSize) + { + m_data.resize(outerSize); + } + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix() + : m_innerSize(0), m_data(0) + { + eigen_assert(innerSize()==0 && outerSize()==0); + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix(Index rows, Index cols) + : m_innerSize(0) + { + resize(rows, cols); + } + + /** The class DynamicSparseMatrix is deprectaed */ + template + EIGEN_DEPRECATED explicit inline DynamicSparseMatrix(const SparseMatrixBase& other) + : m_innerSize(0) + { + Base::operator=(other.derived()); + } + + inline DynamicSparseMatrix(const DynamicSparseMatrix& other) + : Base(), m_innerSize(0) + { + *this = other.derived(); + } + + inline void swap(DynamicSparseMatrix& other) + { + //EIGEN_DBG_SPARSE(std::cout << "SparseMatrix:: swap\n"); + std::swap(m_innerSize, other.m_innerSize); + //std::swap(m_outerSize, other.m_outerSize); + m_data.swap(other.m_data); + } + + inline DynamicSparseMatrix& operator=(const DynamicSparseMatrix& other) + { + if (other.isRValue()) + { + swap(other.const_cast_derived()); + } + else + { + resize(other.rows(), other.cols()); + m_data = other.m_data; + } + return *this; + } + + /** Destructor */ + inline ~DynamicSparseMatrix() {} + + public: + + /** \deprecated + * Set the matrix to zero and reserve the memory for \a reserveSize nonzero coefficients. */ + EIGEN_DEPRECATED void startFill(Index reserveSize = 1000) + { + setZero(); + reserve(reserveSize); + } + + /** \deprecated use insert() + * inserts a nonzero coefficient at given coordinates \a row, \a col and returns its reference assuming that: + * 1 - the coefficient does not exist yet + * 2 - this the coefficient with greater inner coordinate for the given outer coordinate. + * In other words, assuming \c *this is column-major, then there must not exists any nonzero coefficient of coordinates + * \c i \c x \a col such that \c i >= \a row. Otherwise the matrix is invalid. + * + * \see fillrand(), coeffRef() + */ + EIGEN_DEPRECATED Scalar& fill(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return insertBack(outer,inner); + } + + /** \deprecated use insert() + * Like fill() but with random inner coordinates. + * Compared to the generic coeffRef(), the unique limitation is that we assume + * the coefficient does not exist yet. + */ + EIGEN_DEPRECATED Scalar& fillrand(Index row, Index col) + { + return insert(row,col); + } + + /** \deprecated use finalize() + * Does nothing. Provided for compatibility with SparseMatrix. */ + EIGEN_DEPRECATED void endFill() {} + +# ifdef EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# include EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# endif + }; + +template +class DynamicSparseMatrix::InnerIterator : public SparseVector::InnerIterator +{ + typedef typename SparseVector::InnerIterator Base; + public: + InnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +template +class DynamicSparseMatrix::ReverseInnerIterator : public SparseVector::ReverseInnerIterator +{ + typedef typename SparseVector::ReverseInnerIterator Base; + public: + ReverseInnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +namespace internal { + +template +struct evaluator > + : evaluator_base > +{ + typedef _Scalar Scalar; + typedef DynamicSparseMatrix<_Scalar,_Options,_StorageIndex> SparseMatrixType; + typedef typename SparseMatrixType::InnerIterator InnerIterator; + typedef typename SparseMatrixType::ReverseInnerIterator ReverseInnerIterator; + + enum { + CoeffReadCost = NumTraits<_Scalar>::ReadCost, + Flags = SparseMatrixType::Flags + }; + + evaluator() : m_matrix(0) {} + evaluator(const SparseMatrixType &mat) : m_matrix(&mat) {} + + operator SparseMatrixType&() { return m_matrix->const_cast_derived(); } + operator const SparseMatrixType&() const { return *m_matrix; } + + Scalar coeff(Index row, Index col) const { return m_matrix->coeff(row,col); } + + Index nonZerosEstimate() const { return m_matrix->nonZeros(); } + + const SparseMatrixType *m_matrix; +}; + +} + +} // end namespace Eigen + +#endif // EIGEN_DYNAMIC_SPARSEMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h new file mode 100644 index 000000000..41e4af4a4 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h @@ -0,0 +1,275 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_MARKET_IO_H +#define EIGEN_SPARSE_MARKET_IO_H + +#include + +namespace Eigen { + +namespace internal +{ + template + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, Scalar& value) + { + line >> i >> j >> value; + i--; + j--; + if(i>=0 && j>=0 && i + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, std::complex& value) + { + Scalar valR, valI; + line >> i >> j >> valR >> valI; + i--; + j--; + if(i>=0 && j>=0 && i(valR, valI); + return true; + } + else + return false; + } + + template + inline void GetVectorElt (const std::string& line, RealScalar& val) + { + std::istringstream newline(line); + newline >> val; + } + + template + inline void GetVectorElt (const std::string& line, std::complex& val) + { + RealScalar valR, valI; + std::istringstream newline(line); + newline >> valR >> valI; + val = std::complex(valR, valI); + } + + template + inline void putMarketHeader(std::string& header,int sym) + { + header= "%%MatrixMarket matrix coordinate "; + if(internal::is_same >::value || internal::is_same >::value) + { + header += " complex"; + if(sym == Symmetric) header += " symmetric"; + else if (sym == SelfAdjoint) header += " Hermitian"; + else header += " general"; + } + else + { + header += " real"; + if(sym == Symmetric) header += " symmetric"; + else header += " general"; + } + } + + template + inline void PutMatrixElt(Scalar value, int row, int col, std::ofstream& out) + { + out << row << " "<< col << " " << value << "\n"; + } + template + inline void PutMatrixElt(std::complex value, int row, int col, std::ofstream& out) + { + out << row << " " << col << " " << value.real() << " " << value.imag() << "\n"; + } + + + template + inline void putVectorElt(Scalar value, std::ofstream& out) + { + out << value << "\n"; + } + template + inline void putVectorElt(std::complex value, std::ofstream& out) + { + out << value.real << " " << value.imag()<< "\n"; + } + +} // end namepsace internal + +inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isvector) +{ + sym = 0; + iscomplex = false; + isvector = false; + std::ifstream in(filename.c_str(),std::ios::in); + if(!in) + return false; + + std::string line; + // The matrix header is always the first line in the file + std::getline(in, line); eigen_assert(in.good()); + + std::stringstream fmtline(line); + std::string substr[5]; + fmtline>> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4]; + if(substr[2].compare("array") == 0) isvector = true; + if(substr[3].compare("complex") == 0) iscomplex = true; + if(substr[4].compare("symmetric") == 0) sym = Symmetric; + else if (substr[4].compare("Hermitian") == 0) sym = SelfAdjoint; + + return true; +} + +template +bool loadMarket(SparseMatrixType& mat, const std::string& filename) +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::Index Index; + std::ifstream input(filename.c_str(),std::ios::in); + if(!input) + return false; + + const int maxBuffersize = 2048; + char buffer[maxBuffersize]; + + bool readsizes = false; + + typedef Triplet T; + std::vector elements; + + Index M(-1), N(-1), NNZ(-1); + Index count = 0; + while(input.getline(buffer, maxBuffersize)) + { + // skip comments + //NOTE An appropriate test should be done on the header to get the symmetry + if(buffer[0]=='%') + continue; + + std::stringstream line(buffer); + + if(!readsizes) + { + line >> M >> N >> NNZ; + if(M > 0 && N > 0 && NNZ > 0) + { + readsizes = true; + //std::cout << "sizes: " << M << "," << N << "," << NNZ << "\n"; + mat.resize(M,N); + mat.reserve(NNZ); + } + } + else + { + Index i(-1), j(-1); + Scalar value; + if( internal::GetMarketLine(line, M, N, i, j, value) ) + { + ++ count; + elements.push_back(T(i,j,value)); + } + else + std::cerr << "Invalid read: " << i << "," << j << "\n"; + } + } + mat.setFromTriplets(elements.begin(), elements.end()); + if(count!=NNZ) + std::cerr << count << "!=" << NNZ << "\n"; + + input.close(); + return true; +} + +template +bool loadMarketVector(VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ifstream in(filename.c_str(), std::ios::in); + if(!in) + return false; + + std::string line; + int n(0), col(0); + do + { // Skip comments + std::getline(in, line); eigen_assert(in.good()); + } while (line[0] == '%'); + std::istringstream newline(line); + newline >> n >> col; + eigen_assert(n>0 && col>0); + vec.resize(n); + int i = 0; + Scalar value; + while ( std::getline(in, line) && (i < n) ){ + internal::GetVectorElt(line, value); + vec(i++) = value; + } + in.close(); + if (i!=n){ + std::cerr<< "Unable to read all elements from file " << filename << "\n"; + return false; + } + return true; +} + +template +bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0) +{ + typedef typename SparseMatrixType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + std::string header; + internal::putMarketHeader(header, sym); + out << header << std::endl; + out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n"; + int count = 0; + for(int j=0; j +bool saveMarketVector (const VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + if(internal::is_same >::value || internal::is_same >::value) + out << "%%MatrixMarket matrix array complex general\n"; + else + out << "%%MatrixMarket matrix array real general\n"; + out << vec.size() << " "<< 1 << "\n"; + for (int i=0; i < vec.size(); i++){ + internal::putVectorElt(vec(i), out); + } + out.close(); + return true; +} + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_MARKET_IO_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h new file mode 100644 index 000000000..02916ea6f --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h @@ -0,0 +1,247 @@ + +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_BROWSE_MATRICES_H +#define EIGEN_BROWSE_MATRICES_H + +namespace Eigen { + +enum { + SPD = 0x100, + NonSymmetric = 0x0 +}; + +/** + * @brief Iterator to browse matrices from a specified folder + * + * This is used to load all the matrices from a folder. + * The matrices should be in Matrix Market format + * It is assumed that the matrices are named as matname.mtx + * and matname_SPD.mtx if the matrix is Symmetric and positive definite (or Hermitian) + * The right hand side vectors are loaded as well, if they exist. + * They should be named as matname_b.mtx. + * Note that the right hand side for a SPD matrix is named as matname_SPD_b.mtx + * + * Sometimes a reference solution is available. In this case, it should be named as matname_x.mtx + * + * Sample code + * \code + * + * \endcode + * + * \tparam Scalar The scalar type + */ +template +class MatrixMarketIterator +{ + typedef typename NumTraits::Real RealScalar; + public: + typedef Matrix VectorType; + typedef SparseMatrix MatrixType; + + public: + MatrixMarketIterator(const std::string &folder) + : m_sym(0), m_isvalid(false), m_matIsLoaded(false), m_hasRhs(false), m_hasrefX(false), m_folder(folder) + { + m_folder_id = opendir(folder.c_str()); + if(m_folder_id) + Getnextvalidmatrix(); + } + + ~MatrixMarketIterator() + { + if (m_folder_id) closedir(m_folder_id); + } + + inline MatrixMarketIterator& operator++() + { + m_matIsLoaded = false; + m_hasrefX = false; + m_hasRhs = false; + Getnextvalidmatrix(); + return *this; + } + inline operator bool() const { return m_isvalid;} + + /** Return the sparse matrix corresponding to the current file */ + inline MatrixType& matrix() + { + // Read the matrix + if (m_matIsLoaded) return m_mat; + + std::string matrix_file = m_folder + "/" + m_matname + ".mtx"; + if ( !loadMarket(m_mat, matrix_file)) + { + std::cerr << "Warning loadMarket failed when loading \"" << matrix_file << "\"" << std::endl; + m_matIsLoaded = false; + return m_mat; + } + m_matIsLoaded = true; + + if (m_sym != NonSymmetric) + { + // Check whether we need to restore a full matrix: + RealScalar diag_norm = m_mat.diagonal().norm(); + RealScalar lower_norm = m_mat.template triangularView().norm(); + RealScalar upper_norm = m_mat.template triangularView().norm(); + if(lower_norm>diag_norm && upper_norm==diag_norm) + { + // only the lower part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + else if(upper_norm>diag_norm && lower_norm==diag_norm) + { + // only the upper part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + } + return m_mat; + } + + /** Return the right hand side corresponding to the current matrix. + * If the rhs file is not provided, a random rhs is generated + */ + inline VectorType& rhs() + { + // Get the right hand side + if (m_hasRhs) return m_rhs; + + std::string rhs_file; + rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx + m_hasRhs = Fileexists(rhs_file); + if (m_hasRhs) + { + m_rhs.resize(m_mat.cols()); + m_hasRhs = loadMarketVector(m_rhs, rhs_file); + } + if (!m_hasRhs) + { + // Generate a random right hand side + if (!m_matIsLoaded) this->matrix(); + m_refX.resize(m_mat.cols()); + m_refX.setRandom(); + m_rhs = m_mat * m_refX; + m_hasrefX = true; + m_hasRhs = true; + } + return m_rhs; + } + + /** Return a reference solution + * If it is not provided and if the right hand side is not available + * then refX is randomly generated such that A*refX = b + * where A and b are the matrix and the rhs. + * Note that when a rhs is provided, refX is not available + */ + inline VectorType& refX() + { + // Check if a reference solution is provided + if (m_hasrefX) return m_refX; + + std::string lhs_file; + lhs_file = m_folder + "/" + m_matname + "_x.mtx"; + m_hasrefX = Fileexists(lhs_file); + if (m_hasrefX) + { + m_refX.resize(m_mat.cols()); + m_hasrefX = loadMarketVector(m_refX, lhs_file); + } + else + m_refX.resize(0); + return m_refX; + } + + inline std::string& matname() { return m_matname; } + + inline int sym() { return m_sym; } + + bool hasRhs() {return m_hasRhs; } + bool hasrefX() {return m_hasrefX; } + bool isFolderValid() { return bool(m_folder_id); } + + protected: + + inline bool Fileexists(std::string file) + { + std::ifstream file_id(file.c_str()); + if (!file_id.good() ) + { + return false; + } + else + { + file_id.close(); + return true; + } + } + + void Getnextvalidmatrix( ) + { + m_isvalid = false; + // Here, we return with the next valid matrix in the folder + while ( (m_curs_id = readdir(m_folder_id)) != NULL) { + m_isvalid = false; + std::string curfile; + curfile = m_folder + "/" + m_curs_id->d_name; + // Discard if it is a folder + if (m_curs_id->d_type == DT_DIR) continue; //FIXME This may not be available on non BSD systems +// struct stat st_buf; +// stat (curfile.c_str(), &st_buf); +// if (S_ISDIR(st_buf.st_mode)) continue; + + // Determine from the header if it is a matrix or a right hand side + bool isvector,iscomplex=false; + if(!getMarketHeader(curfile,m_sym,iscomplex,isvector)) continue; + if(isvector) continue; + if (!iscomplex) + { + if(internal::is_same >::value || internal::is_same >::value) + continue; + } + if (iscomplex) + { + if(internal::is_same::value || internal::is_same::value) + continue; + } + + + // Get the matrix name + std::string filename = m_curs_id->d_name; + m_matname = filename.substr(0, filename.length()-4); + + // Find if the matrix is SPD + size_t found = m_matname.find("SPD"); + if( (found!=std::string::npos) && (m_sym != NonSymmetric) ) + m_sym = SPD; + + m_isvalid = true; + break; + } + } + int m_sym; // Symmetry of the matrix + MatrixType m_mat; // Current matrix + VectorType m_rhs; // Current vector + VectorType m_refX; // The reference solution, if exists + std::string m_matname; // Matrix Name + bool m_isvalid; + bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file + bool m_hasRhs; // The right hand side exists + bool m_hasrefX; // A reference solution is provided + std::string m_folder; + DIR * m_folder_id; + struct dirent *m_curs_id; + +}; + +} // end namespace Eigen + +#endif diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h new file mode 100644 index 000000000..ee97299af --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h @@ -0,0 +1,327 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_RANDOMSETTER_H +#define EIGEN_RANDOMSETTER_H + +namespace Eigen { + +/** Represents a std::map + * + * \see RandomSetter + */ +template struct StdMapTraits +{ + typedef int KeyType; + typedef std::map Type; + enum { + IsSorted = 1 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; + +#ifdef EIGEN_UNORDERED_MAP_SUPPORT +/** Represents a std::unordered_map + * + * To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file + * yourself making sure that unordered_map is defined in the std namespace. + * + * For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do: + * \code + * #include + * #define EIGEN_UNORDERED_MAP_SUPPORT + * namespace std { + * using std::tr1::unordered_map; + * } + * \endcode + * + * \see RandomSetter + */ +template struct StdUnorderedMapTraits +{ + typedef int KeyType; + typedef std::unordered_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif // EIGEN_UNORDERED_MAP_SUPPORT + +#ifdef _DENSE_HASH_MAP_H_ +/** Represents a google::dense_hash_map + * + * \see RandomSetter + */ +template struct GoogleDenseHashMapTraits +{ + typedef int KeyType; + typedef google::dense_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type& map, const KeyType& k) + { map.set_empty_key(k); } +}; +#endif + +#ifdef _SPARSE_HASH_MAP_H_ +/** Represents a google::sparse_hash_map + * + * \see RandomSetter + */ +template struct GoogleSparseHashMapTraits +{ + typedef int KeyType; + typedef google::sparse_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif + +/** \class RandomSetter + * + * \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access + * + * \tparam SparseMatrixType the type of the sparse matrix we are updating + * \tparam MapTraits a traits class representing the map implementation used for the temporary sparse storage. + * Its default value depends on the system. + * \tparam OuterPacketBits defines the number of rows (or columns) manage by a single map object + * as a power of two exponent. + * + * This class temporarily represents a sparse matrix object using a generic map implementation allowing for + * efficient random access. The conversion from the compressed representation to a hash_map object is performed + * in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy + * suggest the use of nested blocks as in this example: + * + * \code + * SparseMatrix m(rows,cols); + * { + * RandomSetter > w(m); + * // don't use m but w instead with read/write random access to the coefficients: + * for(;;) + * w(rand(),rand()) = rand; + * } + * // when w is deleted, the data are copied back to m + * // and m is ready to use. + * \endcode + * + * Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would + * involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter + * use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order. + * To reach optimal performance, this value should be adjusted according to the average number of nonzeros + * per rows/columns. + * + * The possible values for the template parameter MapTraits are: + * - \b StdMapTraits: corresponds to std::map. (does not perform very well) + * - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC) + * - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption) + * - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance) + * + * The default map implementation depends on the availability, and the preferred order is: + * GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits. + * + * For performance and memory consumption reasons it is highly recommended to use one of + * the Google's hash_map implementation. To enable the support for them, you have two options: + * - \#include yourself \b before Eigen/Sparse header + * - define EIGEN_GOOGLEHASH_SUPPORT + * In the later case the inclusion of is made for you. + * + * \see http://code.google.com/p/google-sparsehash/ + */ +template class MapTraits = +#if defined _DENSE_HASH_MAP_H_ + GoogleDenseHashMapTraits +#elif defined _HASH_MAP + GnuHashMapTraits +#else + StdMapTraits +#endif + ,int OuterPacketBits = 6> +class RandomSetter +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::StorageIndex StorageIndex; + + struct ScalarWrapper + { + ScalarWrapper() : value(0) {} + Scalar value; + }; + typedef typename MapTraits::KeyType KeyType; + typedef typename MapTraits::Type HashMapType; + static const int OuterPacketMask = (1 << OuterPacketBits) - 1; + enum { + SwapStorage = 1 - MapTraits::IsSorted, + TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0, + SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor + }; + + public: + + /** Constructs a random setter object from the sparse matrix \a target + * + * Note that the initial value of \a target are imported. If you want to re-set + * a sparse matrix from scratch, then you must set it to zero first using the + * setZero() function. + */ + inline RandomSetter(SparseMatrixType& target) + : mp_target(&target) + { + const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize(); + const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize(); + m_outerPackets = outerSize >> OuterPacketBits; + if (outerSize&OuterPacketMask) + m_outerPackets += 1; + m_hashmaps = new HashMapType[m_outerPackets]; + // compute number of bits needed to store inner indices + Index aux = innerSize - 1; + m_keyBitsOffset = 0; + while (aux) + { + ++m_keyBitsOffset; + aux = aux >> 1; + } + KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset)); + for (Index k=0; k::setInvalidKey(m_hashmaps[k],ik); + + // insert current coeffs + for (Index j=0; jouterSize(); ++j) + for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it) + (*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value(); + } + + /** Destructor updating back the sparse matrix target */ + ~RandomSetter() + { + KeyType keyBitsMask = (1<setZero(); + mp_target->makeCompressed(); + mp_target->reserve(nonZeros()); + Index prevOuter = -1; + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index inner = it->first & keyBitsMask; + if (prevOuter!=outer) + { + for (Index j=prevOuter+1;j<=outer;++j) + mp_target->startVec(j); + prevOuter = outer; + } + mp_target->insertBackByOuterInner(outer, inner) = it->second.value; + } + } + mp_target->finalize(); + } + else + { + VectorXi positions(mp_target->outerSize()); + positions.setZero(); + // pass 1 + for (Index k=0; kfirst & keyBitsMask; + ++positions[outer]; + } + } + // prefix sum + Index count = 0; + for (Index j=0; jouterSize(); ++j) + { + Index tmp = positions[j]; + mp_target->outerIndexPtr()[j] = count; + positions[j] = count; + count += tmp; + } + mp_target->makeCompressed(); + mp_target->outerIndexPtr()[mp_target->outerSize()] = count; + mp_target->resizeNonZeros(count); + // pass 2 + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index outer = it->first & keyBitsMask; + // sorted insertion + // Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients, + // moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a + // small fraction of them have to be sorted, whence the following simple procedure: + Index posStart = mp_target->outerIndexPtr()[outer]; + Index i = (positions[outer]++) - 1; + while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) ) + { + mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i]; + mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i]; + --i; + } + mp_target->innerIndexPtr()[i+1] = inner; + mp_target->valuePtr()[i+1] = it->second.value; + } + } + } + delete[] m_hashmaps; + } + + /** \returns a reference to the coefficient at given coordinates \a row, \a col */ + Scalar& operator() (Index row, Index col) + { + const Index outer = SetterRowMajor ? row : col; + const Index inner = SetterRowMajor ? col : row; + const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map + const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet + const KeyType key = internal::convert_index((outerMinor<(m_hashmaps[k].size()); + return nz; + } + + + protected: + + HashMapType* m_hashmaps; + SparseMatrixType* mp_target; + Index m_outerPackets; + unsigned char m_keyBitsOffset; +}; + +} // end namespace Eigen + +#endif // EIGEN_RANDOMSETTER_H diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 8615ab91c..37b0c0ffc 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -612,7 +612,9 @@ double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); -PointSet normals(const PointSet& points, const EigenMesh3D& mesh); +PointSet normals(const PointSet& points, const EigenMesh3D& mesh, + double eps = 0.05, // min distance from edges + std::function throw_on_cancel = [](){}); inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; @@ -1049,7 +1051,7 @@ bool SLASupportTree::generate(const PointSet &points, tifcl(); // calculate the normals to the triangles belonging to filtered points - auto nmls = sla::normals(filt_pts, mesh); + auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl); head_norm.resize(count, 3); head_pos.resize(count, 3); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 50d7775a2..5d40bb514 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -1,3 +1,4 @@ +#include #include "SLA/SLASupportTree.hpp" #include "SLA/SLABoilerPlate.hpp" #include "SLA/SLASpatIndex.hpp" @@ -9,15 +10,8 @@ #include "boost/geometry/index/rtree.hpp" #include - -//#if !defined(_MSC_VER) || defined(_WIN64) -#if 1 -#define IGL_COMPATIBLE -#endif - -#ifdef IGL_COMPATIBLE #include -#endif +#include #include "SLASpatIndex.hpp" #include "ClipperUtils.hpp" @@ -84,33 +78,124 @@ size_t SpatIndex::size() const return m_impl->m_store.size(); } -PointSet normals(const PointSet& points, const EigenMesh3D& mesh) { - if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {}; -#ifdef IGL_COMPATIBLE +bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, + double eps = 0.05) +{ + using Line3D = Eigen::ParametrizedLine; + + auto line = Line3D::Through(e1, e2); + double d = line.distance(p); + return std::abs(d) < eps; +} + +template double distance(const Vec& pp1, const Vec& pp2) { + auto p = pp2 - pp1; + return std::sqrt(p.transpose() * p); +} + +PointSet normals(const PointSet& points, const EigenMesh3D& emesh, + double eps, + std::function throw_on_cancel) { + if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0) + return {}; + Eigen::VectorXd dists; Eigen::VectorXi I; PointSet C; + // We need to remove duplicate vertices and have a true index triangle + // structure + EigenMesh3D mesh; + Eigen::VectorXi SVI, SVJ; + igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6, + mesh.V, SVI, SVJ, mesh.F); + igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); PointSet ret(I.rows(), 3); for(int i = 0; i < I.rows(); i++) { + throw_on_cancel(); auto idx = I(i); auto trindex = mesh.F.row(idx); - auto& p1 = mesh.V.row(trindex(0)); - auto& p2 = mesh.V.row(trindex(1)); - auto& p3 = mesh.V.row(trindex(2)); + const Vec3d& p1 = mesh.V.row(trindex(0)); + const Vec3d& p2 = mesh.V.row(trindex(1)); + const Vec3d& p3 = mesh.V.row(trindex(2)); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - ret.row(i) = U.cross(V).normalized(); + // We should check if the point lies on an edge of the hosting triangle. + // If it does than all the other triangles using the same two points + // have to be searched and the final normal should be some kind of + // aggregation of the participating triangle normals. We should also + // consider the cases where the support point lies right on a vertex + // of its triangle. The procedure is the same, get the neighbor + // triangles and calculate an average normal. + + const Vec3d& p = C.row(i); + + // mark the vertex indices of the edge. ia and ib marks and edge ic + // will mark a single vertex. + int ia = -1, ib = -1, ic = -1; + + if(std::abs(distance(p, p1)) < eps) { + ic = trindex(0); + } + else if(std::abs(distance(p, p2)) < eps) { + ic = trindex(1); + } + else if(std::abs(distance(p, p3)) < eps) { + ic = trindex(2); + } + else if(point_on_edge(p, p1, p2, eps)) { + ia = trindex(0); ib = trindex(1); + } + else if(point_on_edge(p, p2, p3, eps)) { + ia = trindex(1); ib = trindex(2); + } + else if(point_on_edge(p, p1, p3, eps)) { + ia = trindex(0); ib = trindex(2); + } + + std::vector neigh; + if(ic >= 0) { // The point is right on a vertex of the triangle + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) + neigh.emplace_back(ni); + } + } + else if(ia >= 0 && ib >= 0) { // the point is on and edge + // now get all the neigboring triangles + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && + (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) + neigh.emplace_back(ni); + } + } + + if(!neigh.empty()) { // there were neighbors to count with + Vec3d sumnorm(0, 0, 0); + for(const Vec3i& tri : neigh) { + const Vec3d& pt1 = mesh.V.row(tri(0)); + const Vec3d& pt2 = mesh.V.row(tri(1)); + const Vec3d& pt3 = mesh.V.row(tri(2)); + Eigen::Vector3d U = pt2 - pt1; + Eigen::Vector3d V = pt3 - pt1; + sumnorm += U.cross(V).normalized(); + } + sumnorm /= neigh.size(); + ret.row(i) = sumnorm; + } + else { // point lies safely within its triangle + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(i) = U.cross(V).normalized(); + } } return ret; -#else // TODO: do something on 32 bit windows - return {}; -#endif } double ray_mesh_intersect(const Vec3d& s, @@ -223,7 +308,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs) pp.emplace_back(p); } - ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true); + ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true); for(auto& expoly : merged) { auto lines = expoly.lines(); From 862217a6b3739fd66df277bc08f19cf4d4dbb45f Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 14 Dec 2018 15:27:34 +0100 Subject: [PATCH 26/57] OctoPrint basics working, niceties to-do --- src/libslic3r/Channel.hpp | 52 +++--- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 31 ++-- src/slic3r/GUI/GUI_App.cpp | 3 +- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 8 +- src/slic3r/GUI/MainFrame.hpp | 6 + src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 108 ++++++++++++- src/slic3r/GUI/PrintHostDialogs.hpp | 51 ++++-- src/slic3r/Utils/Duet.cpp | 166 ++++++++++---------- src/slic3r/Utils/Duet.hpp | 5 +- src/slic3r/Utils/Http.hpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 60 +++---- src/slic3r/Utils/OctoPrint.hpp | 5 +- src/slic3r/Utils/PrintHost.cpp | 142 ++++++++++++++--- src/slic3r/Utils/PrintHost.hpp | 15 +- 16 files changed, 450 insertions(+), 210 deletions(-) diff --git a/src/libslic3r/Channel.hpp b/src/libslic3r/Channel.hpp index 8d1a07d35..9cf025f2c 100644 --- a/src/libslic3r/Channel.hpp +++ b/src/libslic3r/Channel.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Channel_hpp_ #define slic3r_Channel_hpp_ +#include #include #include #include @@ -13,32 +14,26 @@ namespace Slic3r { template class Channel { -private: - using UniqueLock = std::unique_lock; - using Queue = std::deque; public: - class Guard + using UniqueLock = std::unique_lock; + + template class Unlocker { public: - Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {} - Guard(const Guard &other) = delete; - Guard(Guard &&other) = delete; - ~Guard() {} + Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {} + Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move + Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {} + Unlocker& operator=(const Unlocker &other) = delete; + Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); } - // Access trampolines - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } - typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); } - typename Queue::const_iterator end() const noexcept { return m_queue.end(); } - typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; } - - Guard& operator=(const Guard &other) = delete; - Guard& operator=(Guard &&other) = delete; + void operator()(Ptr*) { m_lock.unlock(); } private: - UniqueLock m_lock; - const Queue &m_queue; + mutable UniqueLock m_lock; // XXX: mutable: see above }; + using Queue = std::deque; + using LockedConstPtr = std::unique_ptr>; + using LockedPtr = std::unique_ptr>; Channel() {} ~Channel() {} @@ -56,7 +51,7 @@ public: { { UniqueLock lock(m_mutex); - m_queue.push_back(std::forward(item)); + m_queue.push_back(std::forward(item)); } if (! silent) { m_condition.notify_one(); } } @@ -82,19 +77,22 @@ public: } } - // Unlocked observers - // Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock! - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } + // Unlocked observers/hints + // Thread unsafe! Keep in mind you need to re-verify the result after locking! + size_t size_hint() const noexcept { return m_queue.size(); } - Guard read() const + LockedConstPtr lock_read() const { - return Guard(UniqueLock(m_mutex), m_queue); + return LockedConstPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); } + LockedPtr lock_rw() + { + return LockedPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); + } private: Queue m_queue; - std::mutex m_mutex; + mutable std::mutex m_mutex; std::condition_variable m_condition; }; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 891e5f0dc..d748919c9 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const return m_print->technology(); } -static bool isspace(int ch) -{ - return std::isspace(ch) != 0; -} - // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. void BackgroundSlicingProcess::process_fff() @@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff() m_print->set_status(95, "Running post-processing scripts"); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, "G-code file exported to " + export_path); + } else if (! m_upload_job.empty()) { + // A print host upload job has been scheduled + + // XXX: is fs::path::string() right? + + // Generate a unique temp path to which the gcode is copied + boost::filesystem::path source_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); + + if (copy_file(m_temp_output_path, source_path.string()) != 0) { + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + } + + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(source_path.string(), m_fff_print->config()); + m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); + + m_upload_job.upload_data.source_path = std::move(source_path); + m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); + + GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job)); } else { m_print->set_status(100, "Slicing complete"); } @@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) if (! m_export_path.empty()) return; - const boost::filesystem::path path = boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode"); - // Guard against entering the export step before changing the export path. tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); - m_export_path = path.string(); + m_export_path = std::string(); m_upload_job = std::move(upload_job); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f4047ae3e..e4db9b6e1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -73,7 +73,6 @@ GUI_App::GUI_App() : wxApp() #if ENABLE_IMGUI , m_imgui(new ImGuiWrapper()) - , m_printhost_queue(new PrintHostJobQueue()) #endif // ENABLE_IMGUI {} @@ -142,6 +141,8 @@ bool GUI_App::OnInit() update_mode(); SetTopWindow(mainframe); + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + CallAfter([this]() { // temporary workaround for the correct behavior of the Scrolled sidebar panel auto& panel = sidebar(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 875a92456..3c2b4a21f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -92,7 +92,7 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; #endif // ENABLE_IMGUI - std::unique_ptr m_printhost_queue; + std::unique_ptr m_printhost_job_queue; public: bool OnInit() override; @@ -164,7 +164,7 @@ public: ImGuiWrapper* imgui() { return m_imgui.get(); } #endif // ENABLE_IMGUI - PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); } + PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); } }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aa5634fec..4d4ee17ae 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -18,6 +18,7 @@ #include "ProgressStatusBar.hpp" #include "3DScene.hpp" #include "AppConfig.hpp" +#include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "I18N.hpp" @@ -30,7 +31,8 @@ namespace GUI { MainFrame::MainFrame(const bool no_plater, const bool loaded) : wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), m_no_plater(no_plater), - m_loaded(loaded) + m_loaded(loaded), + m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. #if _WIN32 @@ -375,6 +377,10 @@ void MainFrame::init_menubar() append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); #endif // ENABLE_REMOVE_TABS_FROM_PLATER + + windowMenu->AppendSeparator(); + append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"), + [this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png"); } // View menu diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 2559b5ed1..fab6aea90 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -21,7 +21,9 @@ class ProgressStatusBar; namespace GUI { + class Tab; +class PrintHostQueueDialog; enum QuickSlice { @@ -52,6 +54,8 @@ class MainFrame : public wxFrame wxMenuItem* m_menu_item_repeat { nullptr }; wxMenuItem* m_menu_item_reslice_now { nullptr }; + PrintHostQueueDialog *m_printhost_queue_dlg; + std::string get_base_name(const wxString full_name) const ; std::string get_dir_name(const wxString full_name) const ; @@ -93,6 +97,8 @@ public: void select_tab(size_t tab) const; void select_view(const std::string& direction); + PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } + Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aeda2e8c2..a8a75fc3f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3144,7 +3144,7 @@ void Plater::send_gcode() } default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); - Slic3r::PrintHostSendDialog dlg(default_output_file); + PrintHostSendDialog dlg(default_output_file); if (dlg.ShowModal() == wxID_OK) { upload_job.upload_data.upload_path = dlg.filename(); upload_job.upload_data.start_print = dlg.start_print(); diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index a5de7c3c6..8ac8615a8 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -1,20 +1,28 @@ #include "PrintHostDialogs.hpp" +#include + #include -#include #include #include #include #include #include +#include +#include +#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/I18N.hpp" +#include "GUI.hpp" +#include "MsgDialog.hpp" +#include "I18N.hpp" +#include "../Utils/PrintHost.hpp" namespace fs = boost::filesystem; namespace Slic3r { +namespace GUI { + 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) @@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() const bool PrintHostSendDialog::start_print() const { - return box_print->GetValue(); } + return box_print->GetValue(); } + + + +wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id) + : wxEvent(winid, eventType) + , job_id(job_id) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress) + : wxEvent(winid, eventType) + , job_id(job_id) + , progress(progress) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error) + : wxEvent(winid, eventType) + , job_id(job_id) + , error(std::move(error)) +{} + +wxEvent *PrintHostQueueDialog::Event::Clone() const +{ + return new Event(*this); +} + +PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) + : wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this) + , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) +{ + enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 }; + + SetMinSize(wxSize(HEIGHT, WIDTH)); + + auto *topsizer = new wxBoxSizer(wxVERTICAL); + + job_list = new wxDataViewListCtrl(this, wxID_ANY); + job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT); + job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); + + auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); + auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); + btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); + btnsizer->AddStretchSpacer(); + btnsizer->Add(btn_close); + + topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING); + topsizer->Add(btnsizer, 0, wxEXPAND); + SetSizer(topsizer); +} + +void PrintHostQueueDialog::append_job(const PrintHostJob &job) +{ + wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job"); + + wxVector fields; + fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1))); + fields.push_back(wxVariant(0)); + fields.push_back(wxVariant(_(L("Enqueued")))); + fields.push_back(wxVariant(job.printhost->get_host())); + fields.push_back(wxVariant(job.upload_data.upload_path.string())); + job_list->AppendItem(fields); +} + +void PrintHostQueueDialog::on_progress(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete"))); + + job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1); + job_list->SetValue(status, evt.job_id, 2); +} + +void PrintHostQueueDialog::on_error(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + // TODO +} + + +}} diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index d27fbe576..e38acee32 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -2,24 +2,27 @@ #define slic3r_PrintHostSendDialog_hpp_ #include - #include #include -#include #include -#include -#include -#include -#include -#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "MsgDialog.hpp" +#include "../Utils/PrintHost.hpp" +class wxTextCtrl; +class wxCheckBox; +class wxDataViewListCtrl; namespace Slic3r { +struct PrintHostJob; + +namespace GUI { + class PrintHostSendDialog : public GUI::MsgDialog { @@ -38,12 +41,38 @@ private: class PrintHostQueueDialog : public wxDialog { public: - PrintHostQueueDialog(); + class Event : public wxEvent + { + public: + size_t job_id; + int progress = 0; // in percent + wxString error; + Event(wxEventType eventType, int winid, size_t job_id); + Event(wxEventType eventType, int winid, size_t job_id, int progress); + Event(wxEventType eventType, int winid, size_t job_id, wxString error); + + virtual wxEvent *Clone() const; + }; + + + PrintHostQueueDialog(wxWindow *parent); + + void append_job(const PrintHostJob &job); private: + wxDataViewListCtrl *job_list; + // Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog + EventGuard on_progress_evt; + EventGuard on_error_evt; + + void on_progress(Event&); + void on_error(Event&); }; +wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); -} + +}} #endif diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 4eda7bd46..1772ae8ef 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -20,7 +20,6 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" namespace fs = boost::filesystem; @@ -55,89 +54,90 @@ wxString Duet::get_test_failed_msg (wxString &msg) const return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg); } -bool Duet::send_gcode(const std::string &filename) const -{ - enum { PROGRESS_RANGE = 1000 }; - - const auto errortitle = _(L("Error while uploading to the Duet")); - fs::path filepath(filename); - - PrintHostSendDialog send_dialog(filepath.filename()); - if (send_dialog.ShowModal() != wxID_OK) { return false; } - - const bool print = send_dialog.start_print(); - const auto upload_filepath = send_dialog.filename(); - const auto upload_filename = upload_filepath.filename(); - const auto upload_parent_path = upload_filepath.parent_path(); - - wxProgressDialog progress_dialog( - _(L("Duet upload")), - _(L("Sending G-code file to Duet...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); - - wxString connect_msg; - if (!connect(connect_msg)) { - auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); - GUI::show_error(&progress_dialog, std::move(errormsg)); - return false; - } - - bool res = true; - - auto upload_cmd = get_upload_url(upload_filepath.string()); - BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") - % filepath.string() - % upload_filename.string() - % upload_parent_path.string() - % print - % upload_cmd; - - auto http = Http::post(std::move(upload_cmd)); - http.set_post_body(filename) - .on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; - progress_dialog.Update(PROGRESS_RANGE); - - int err_code = get_err_code_from_body(body); - if (err_code != 0) { - auto msg = format_error(body, L("Unknown error occured"), 0); - GUI::show_error(&progress_dialog, std::move(msg)); - res = false; - } else if (print) { - wxString errormsg; - res = start_print(errormsg, upload_filepath.string()); - if (!res) { - GUI::show_error(&progress_dialog, std::move(errormsg)); - } - } - }) - .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); - GUI::show_error(&progress_dialog, std::move(errormsg)); - res = false; - }) - .on_progress([&](Http::Progress progress, bool &cancel) { - if (cancel) { - // Upload was canceled - res = false; - } else if (progress.ultotal > 0) { - int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; - cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing - } else { - cancel = !progress_dialog.Pulse(); - } - }) - .perform_sync(); - - disconnect(); - - return res; -} - -bool Duet::upload(PrintHostUpload upload_data) const +// bool Duet::send_gcode(const std::string &filename) const +// { +// enum { PROGRESS_RANGE = 1000 }; + +// const auto errortitle = _(L("Error while uploading to the Duet")); +// fs::path filepath(filename); + +// GUI::PrintHostSendDialog send_dialog(filepath.filename()); +// if (send_dialog.ShowModal() != wxID_OK) { return false; } + +// const bool print = send_dialog.start_print(); +// const auto upload_filepath = send_dialog.filename(); +// const auto upload_filename = upload_filepath.filename(); +// const auto upload_parent_path = upload_filepath.parent_path(); + +// wxProgressDialog progress_dialog( +// _(L("Duet upload")), +// _(L("Sending G-code file to Duet...")), +// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); +// progress_dialog.Pulse(); + +// wxString connect_msg; +// if (!connect(connect_msg)) { +// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// return false; +// } + +// bool res = true; + +// auto upload_cmd = get_upload_url(upload_filepath.string()); +// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") +// % filepath.string() +// % upload_filename.string() +// % upload_parent_path.string() +// % print +// % upload_cmd; + +// auto http = Http::post(std::move(upload_cmd)); +// http.set_post_body(filename) +// .on_complete([&](std::string body, unsigned status) { +// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; +// progress_dialog.Update(PROGRESS_RANGE); + +// int err_code = get_err_code_from_body(body); +// if (err_code != 0) { +// auto msg = format_error(body, L("Unknown error occured"), 0); +// GUI::show_error(&progress_dialog, std::move(msg)); +// res = false; +// } else if (print) { +// wxString errormsg; +// res = start_print(errormsg, upload_filepath.string()); +// if (!res) { +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// } +// } +// }) +// .on_error([&](std::string body, std::string error, unsigned status) { +// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; +// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// res = false; +// }) +// .on_progress([&](Http::Progress progress, bool &cancel) { +// if (cancel) { +// // Upload was canceled +// res = false; +// } else if (progress.ultotal > 0) { +// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; +// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing +// } else { +// cancel = !progress_dialog.Pulse(); +// } +// }) +// .perform_sync(); + +// disconnect(); + +// return res; +// } + +bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { + // XXX: TODO throw "unimplemented"; } diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index db21fd0a1..0608f85a5 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -22,11 +22,10 @@ public: bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; - // Send gcode file to duet, filename is expected to be in UTF-8 - bool send_gcode(const std::string &filename) const; - bool upload(PrintHostUpload upload_data) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; bool has_auto_discovery() const; bool can_test() const; + virtual std::string get_host() const { return host; } private: std::string host; std::string password; diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 44580b7ea..fd3f8830d 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -29,7 +29,7 @@ public: typedef std::shared_ptr Ptr; typedef std::function CompleteFn; - + // A HTTP request may fail at various stages of completeness (URL parsing, DNS lookup, TCP connection, ...). // If the HTTP request could not be made or failed before completion, the `error` arg contains a description // of the error and `http_status` is zero. diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index b2e2d4d45..67c58a972 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -4,9 +4,10 @@ #include #include +#include + #include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/I18N.hpp" -#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" @@ -59,32 +60,19 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } -bool OctoPrint::send_gcode(const std::string &filename) const +bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { - enum { PROGRESS_RANGE = 1000 }; - - const auto errortitle = _(L("Error while uploading to the OctoPrint server")); - fs::path filepath(filename); - - PrintHostSendDialog send_dialog(filepath.filename()); - if (send_dialog.ShowModal() != wxID_OK) { return false; } - - const bool print = send_dialog.start_print(); - const auto upload_filepath = send_dialog.filename(); - const auto upload_filename = upload_filepath.filename(); - const auto upload_parent_path = upload_filepath.parent_path(); - - wxProgressDialog progress_dialog( - _(L("OctoPrint upload")), - _(L("Sending G-code file to the OctoPrint server...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); wxString test_msg; - if (!test(test_msg)) { - auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - GUI::show_error(&progress_dialog, std::move(errormsg)); - return false; + if (! test(test_msg)) { + + // TODO: + + // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); + // GUI::show_error(&progress_dialog, std::move(errormsg)); + // return false; } bool res = true; @@ -92,36 +80,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const auto url = make_url("api/files/local"); BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % filepath.string() + % upload_data.source_path.string() % url % upload_filename.string() % upload_parent_path.string() - % print; + % upload_data.start_print; auto http = Http::post(std::move(url)); set_auth(http); - http.form_add("print", print ? "true" : "false") + http.form_add("print", upload_data.start_print ? "true" : "false") .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? - .form_add_file("file", filename, upload_filename.string()) + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) .on_complete([&](std::string body, unsigned status) { BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; - progress_dialog.Update(PROGRESS_RANGE); }) .on_error([&](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); - GUI::show_error(&progress_dialog, std::move(errormsg)); + error_fn(std::move(body), std::move(error), status); res = false; }) .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); if (cancel) { // Upload was canceled + BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; res = false; - } else if (progress.ultotal > 0) { - int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; - cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing - } else { - cancel = !progress_dialog.Pulse(); } }) .perform_sync(); @@ -129,11 +112,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const return res; } -bool OctoPrint::upload(PrintHostUpload upload_data) const -{ - throw "unimplemented"; -} - bool OctoPrint::has_auto_discovery() const { return true; diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 314e4cfae..9267b4c83 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -22,11 +22,10 @@ public: bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; - // Send gcode file to octoprint, filename is expected to be in UTF-8 - bool send_gcode(const std::string &filename) const; - bool upload(PrintHostUpload upload_data) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; bool has_auto_discovery() const; bool can_test() const; + virtual std::string get_host() const { return host; } private: std::string host; std::string apikey; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 570d72f68..48f504884 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -1,15 +1,21 @@ -#include "OctoPrint.hpp" -#include "Duet.hpp" +#include "PrintHost.hpp" #include #include #include +#include + +#include #include "libslic3r/PrintConfig.hpp" #include "libslic3r/Channel.hpp" +#include "OctoPrint.hpp" +#include "Duet.hpp" +#include "../GUI/PrintHostDialogs.hpp" +namespace fs = boost::filesystem; using boost::optional; - +using Slic3r::GUI::PrintHostQueueDialog; namespace Slic3r { @@ -30,30 +36,130 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) struct PrintHostJobQueue::priv { - std::vector jobs; - Channel channel; + // XXX: comment on how bg thread works + + PrintHostJobQueue *q; + + Channel channel_jobs; + Channel channel_cancels; + size_t job_id = 0; + int prev_progress = -1; std::thread bg_thread; - optional bg_job; + bool bg_exit = false; + + PrintHostQueueDialog *queue_dialog; + + priv(PrintHostJobQueue *q) : q(q) {} + + void start_bg_thread(); + void bg_thread_main(); + void progress_fn(Http::Progress progress, bool &cancel); + void error_fn(std::string body, std::string error, unsigned http_status); + void perform_job(PrintHostJob the_job); }; -PrintHostJobQueue::PrintHostJobQueue() - : p(new priv()) +PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog) + : p(new priv(this)) { - std::shared_ptr p2 = p; - p->bg_thread = std::thread([p2]() { - // Wait for commands on the channel: - auto cmd = p2->channel.pop(); - // TODO - }); + p->queue_dialog = queue_dialog; } PrintHostJobQueue::~PrintHostJobQueue() { - // TODO: stop the thread - // if (p && p->bg_thread.joinable()) { - // p->bg_thread.detach(); - // } + if (p && p->bg_thread.joinable()) { + p->bg_exit = true; + p->channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping + p->bg_thread.detach(); // Let the background thread go, it should exit on its own + } +} + +void PrintHostJobQueue::priv::start_bg_thread() +{ + if (bg_thread.joinable()) { return; } + + std::shared_ptr p2 = q->p; + bg_thread = std::thread([p2]() { + p2->bg_thread_main(); + }); +} + +void PrintHostJobQueue::priv::bg_thread_main() +{ + // bg thread entry point + + try { + // Pick up jobs from the job channel: + while (! bg_exit) { + auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs + if (! job.cancelled) { + perform_job(std::move(job)); + } + job_id++; + } + } catch (...) { + wxTheApp->OnUnhandledException(); + } +} + +void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) +{ + if (bg_exit) { + cancel = true; + return; + } + + if (channel_cancels.size_hint() > 0) { + // Lock both queues + auto cancels = channel_cancels.lock_rw(); + auto jobs = channel_jobs.lock_rw(); + + for (size_t cancel_id : *cancels) { + if (cancel_id == job_id) { + cancel = true; + } else if (cancel_id > job_id) { + jobs->at(cancel_id - job_id).cancelled = true; + } + } + + cancels->clear(); + } + + int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0; + if (gui_progress != prev_progress) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress); + wxQueueEvent(queue_dialog, evt); + prev_progress = gui_progress; + } +} + +void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status) +{ + // TODO +} + +void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) +{ + if (bg_exit || the_job.empty()) { return; } + + const fs::path gcode_path = the_job.upload_data.source_path; + + the_job.printhost->upload(std::move(the_job.upload_data), + [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, + [this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); } + ); + + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); + wxQueueEvent(queue_dialog, evt); + + fs::remove(gcode_path); // XXX: error handling +} + +void PrintHostJobQueue::enqueue(PrintHostJob job) +{ + p->start_bg_thread(); + p->queue_dialog->append_job(job); + p->channel_jobs.push(std::move(job)); } diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 53f7c43d3..52ef38058 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -7,6 +7,8 @@ #include +#include "Http.hpp" + namespace Slic3r { @@ -29,11 +31,10 @@ public: virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; - // Send gcode file to print host, filename is expected to be in UTF-8 - virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload() - virtual bool upload(PrintHostUpload upload_data) const = 0; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; + virtual std::string get_host() const = 0; static PrintHost* get_print_host(DynamicPrintConfig *config); }; @@ -43,6 +44,7 @@ struct PrintHostJob { PrintHostUpload upload_data; std::unique_ptr printhost; + bool cancelled = false; PrintHostJob() {} PrintHostJob(const PrintHostJob&) = delete; @@ -68,10 +70,12 @@ struct PrintHostJob }; +namespace GUI { class PrintHostQueueDialog; } + class PrintHostJobQueue { public: - PrintHostJobQueue(); + PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog); PrintHostJobQueue(const PrintHostJobQueue &) = delete; PrintHostJobQueue(PrintHostJobQueue &&other) = delete; ~PrintHostJobQueue(); @@ -79,6 +83,9 @@ public: PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete; PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete; + void enqueue(PrintHostJob job); + void cancel(size_t id); + private: struct priv; std::shared_ptr p; From 6411ab5b63f3ba88a5d59f8d792b4565cfe5b602 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 18:04:23 +0100 Subject: [PATCH 27/57] Fix of SPE-607 Part changed to Support enforcer is not sliced with one another part --- src/libslic3r/Print.cpp | 17 ++++++++++------- src/libslic3r/Print.hpp | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e3382573c..df50012b9 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1052,10 +1052,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co goto print_object_end; } else { this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders); - for (size_t i = 0; i < region_id; ++ i) - if (m_regions[i]->config().equals(this_region_config)) - // Regions were merged. Reset this print_object. - goto print_object_end; + for (size_t i = 0; i < region_id; ++i) { + const PrintRegion ®ion_other = *m_regions[i]; + if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + } this_region_config_set = true; } } @@ -1102,9 +1104,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Find an existing print region with the same config. int idx_empty_slot = -1; for (int i = 0; i < (int)m_regions.size(); ++ i) { - if (m_regions[i]->m_refcnt == 0) - idx_empty_slot = i; - else if (config.equals(m_regions[i]->config())) { + if (m_regions[i]->m_refcnt == 0) { + if (idx_empty_slot == -1) + idx_empty_slot = i; + } else if (config.equals(m_regions[i]->config())) { region_id = i; break; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1b79ef295..ad32f42ba 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -107,8 +107,8 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id) { if (region_id >= region_volumes.size()) - region_volumes.resize(region_id + 1); - region_volumes[region_id].push_back(volume_id); + region_volumes.assign(region_id + 1, std::vector()); + region_volumes[region_id].emplace_back(volume_id); } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id From 01edb23ffc6a526d59f011ebf1e29a4ddd85c185 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 15:44:30 +0100 Subject: [PATCH 28/57] Fixed the enabling of the "split to objects/parts" buttons --- src/slic3r/GUI/Plater.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a8a75fc3f..6544da789 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1033,6 +1033,7 @@ private: bool can_decrease_instances() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; + bool can_split() const; bool layers_height_allowed() const; bool can_delete_all() const; bool can_arrange() const; @@ -1652,8 +1653,8 @@ void Plater::priv::selection_changed() view3D->enable_toolbar_item("delete", can_delete_object()); view3D->enable_toolbar_item("more", can_increase_instances()); view3D->enable_toolbar_item("fewer", can_decrease_instances()); - view3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - view3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); + view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); + view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); view3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); @@ -1661,8 +1662,8 @@ void Plater::priv::selection_changed() this->canvas3D->enable_toolbar_item("delete", can_delete_object()); this->canvas3D->enable_toolbar_item("more", can_increase_instances()); this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances()); - this->canvas3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - this->canvas3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); + this->canvas3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); + this->canvas3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) this->canvas3D->render(); @@ -2454,8 +2455,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&) void Plater::priv::on_object_select(SimpleEvent& evt) { - selection_changed(); wxGetApp().obj_list()->update_selections(); + selection_changed(); } void Plater::priv::on_viewport_changed(SimpleEvent& evt) @@ -2605,9 +2606,9 @@ bool Plater::priv::complit_init_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); } return true; } @@ -2626,7 +2627,7 @@ bool Plater::priv::complit_init_sla_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); } return true; @@ -2645,7 +2646,7 @@ bool Plater::priv::complit_init_part_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); } return true; @@ -2763,6 +2764,13 @@ bool Plater::priv::can_split_to_volumes() const return sidebar->obj_list()->is_splittable(); } +bool Plater::priv::can_split() const +{ + if (printer_technology == ptSLA) + return false; + return sidebar->obj_list()->is_splittable(); +} + bool Plater::priv::layers_height_allowed() const { int obj_idx = get_selected_object_idx(); From 17164ee33312cd01f374df004b3bb5f23e184c55 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 18:07:31 +0100 Subject: [PATCH 29/57] Fixed #1225 (Added "Parameter validation" for "mm or %" values) + fixed correct updating of the "Contact Z distance" parameter --- src/slic3r/GUI/Field.cpp | 43 +++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d4126d933..fc428ac0d 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -163,9 +163,31 @@ void Field::get_value_by_opt_type(wxString& str) break; } case coString: case coStrings: - case coFloatOrPercent: - m_value = str.ToStdString(); - break; + case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') + { + double val; + if (!str.ToCDouble(&val)) + { + show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); + set_value(double_to_string(val), true); + } + else if (val > 1) + { + const int nVal = int(val); + wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %dmm?\n" + "Select YES if you want to change this value to %d%%, \n" + "or NO if you are sure that %dmm is a correct value.")), nVal, nVal, nVal, nVal); + auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO); + if (dialog->ShowModal() == wxID_YES) { + set_value(wxString::Format("%s%%", str), true); + str += "%%"; + } + } + } + + m_value = str.ToStdString(); + break; } default: break; } @@ -611,9 +633,7 @@ boost::any& Choice::get_value() if (m_opt_id == rp_option) return m_value = boost::any(ret_str); - if (m_opt.type != coEnum) - /*m_value = */get_value_by_opt_type(ret_str); - else + if (m_opt.type == coEnum) { int ret_enum = static_cast(window)->GetSelection(); if (m_opt_id.compare("external_fill_pattern") == 0) @@ -640,7 +660,16 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("display_orientation") == 0) m_value = static_cast(ret_enum); - } + } + else if (m_opt.gui_type == "f_enum_open") { + const int ret_enum = static_cast(window)->GetSelection(); + if (ret_enum < 0 || m_opt.enum_values.empty()) + get_value_by_opt_type(ret_str); + else + m_value = m_opt.enum_values[ret_enum]; + } + else + get_value_by_opt_type(ret_str); return m_value; } From 2c63af5dd9e21dd6465bffa0475929697809f6e1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 19:46:36 +0100 Subject: [PATCH 30/57] Fix of SPE-607 --- src/libslic3r/Print.cpp | 6 ++++-- src/libslic3r/Print.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index df50012b9..0a1bac4e4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1095,8 +1095,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co bool fresh = print_object.region_volumes.empty(); unsigned int volume_id = 0; for (const ModelVolume *volume : model_object.volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) - continue; + if (! volume->is_model_part() && ! volume->is_modifier()) { + ++ volume_id; + continue; + } int region_id = -1; if (&print_object == &print_object0) { // Get the config applied to this volume. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index ad32f42ba..e5060cb76 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -107,7 +107,7 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id) { if (region_id >= region_volumes.size()) - region_volumes.assign(region_id + 1, std::vector()); + region_volumes.resize(region_id + 1); region_volumes[region_id].emplace_back(volume_id); } // This is the *total* layer count (including support layers) From 0eca8f14cc68e596e597539d78a3ee8035a3edcd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 09:08:32 +0100 Subject: [PATCH 31/57] Fixed update of gcode preview view type when changing printer --- src/slic3r/GUI/GUI_Preview.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 4e14aefe8..71c92f2d1 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -385,17 +385,13 @@ void Preview::set_number_extruders(unsigned int number_extruders) if (m_number_extruders != number_extruders) { m_number_extruders = number_extruders; - int type = 0; // color by a feature type - if (number_extruders > 1) - { - int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); - int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type - m_choice_view_type->SetSelection(type); - if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) - m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; - m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; - } + m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; } } From a326ce06b1cdbe3abe122bdf022492fe10bc1dbe Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 09:34:41 +0100 Subject: [PATCH 32/57] Removed unneeded methods from ObjectManipulation --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 89 ----------------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 6 -- 2 files changed, 95 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d193a11a9..30e701907 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -25,18 +25,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { std::vector axes{ "_x", "_y", "_z" }; - if (opt_key == "scale_unit") { - const wxString& selection = boost::any_cast(value); - for (auto axis : axes) { - std::string key = "scale" + axis; - m_og->set_side_text(key, selection); - } - - m_is_percent_scale = selection == _("%"); - update_scale_values(); - return; - } - std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -318,59 +306,6 @@ void ObjectManipulation::reset_scale_value() m_og->set_value("scale_z", def_100); } -void ObjectManipulation::update_values() -{ - int selection = ol_selection(); - if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) { - m_og->set_value("position_x", def_0); - m_og->set_value("position_y", def_0); - m_og->set_value("position_z", def_0); - m_og->set_value("scale_x" , def_0); - m_og->set_value("scale_y" , def_0); - m_og->set_value("scale_z" , def_0); - m_og->set_value("rotation_x", def_0); - m_og->set_value("rotation_y", def_0); - m_og->set_value("rotation_z", def_0); - m_og->disable(); - return; - } - m_is_percent_scale = boost::any_cast(m_og->get_value("scale_unit")) == _("%"); - - update_position_values(); - update_scale_values(); - update_rotation_values(); - m_og->enable(); -} - -void ObjectManipulation::update_scale_values() -{ - int selection = ol_selection(); - ModelObjectPtrs& objects = wxGetApp().mainframe->m_plater->model().objects; - - auto instance = objects[selection]->instances.front(); - auto size = objects[selection]->instance_bounding_box(0).size(); - - if (m_is_percent_scale) { - m_og->set_value("scale_x", double_to_string(instance->get_scaling_factor(X) * 100, 2)); - m_og->set_value("scale_y", double_to_string(instance->get_scaling_factor(Y) * 100, 2)); - m_og->set_value("scale_z", double_to_string(instance->get_scaling_factor(Z) * 100, 2)); - } - else { - m_og->set_value("scale_x", double_to_string(size(0), 2)); - m_og->set_value("scale_y", double_to_string(size(1), 2)); - m_og->set_value("scale_z", double_to_string(size(2), 2)); - } -} - -void ObjectManipulation::update_position_values() -{ - auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front(); - - m_og->set_value("position_x", double_to_string(instance->get_offset(X), 2)); - m_og->set_value("position_y", double_to_string(instance->get_offset(Y), 2)); - m_og->set_value("position_z", double_to_string(instance->get_offset(Z), 2)); -} - void ObjectManipulation::update_position_value(const Vec3d& position) { m_og->set_value("position_x", double_to_string(position(0), 2)); @@ -397,29 +332,6 @@ void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) m_og->set_value("scale_z", double_to_string(scale(2), 2)); } -void ObjectManipulation::update_rotation_values() -{ - update_rotation_value(wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front()->get_rotation()); -} - -void ObjectManipulation::update_rotation_value(double angle, Axis axis) -{ - std::string axis_str; - switch (axis) { - case X: { - axis_str = "rotation_x"; - break; } - case Y: { - axis_str = "rotation_y"; - break; } - case Z: { - axis_str = "rotation_z"; - break; } - } - - m_og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); -} - void ObjectManipulation::update_rotation_value(const Vec3d& rotation) { m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); @@ -427,7 +339,6 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); } - void ObjectManipulation::change_position_value(const Vec3d& position) { Vec3d displacement(position - cache_position); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 3a8df4111..cbc22a2dd 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -37,17 +37,11 @@ public: void reset_rotation_value(); void reset_scale_value(); - void update_values(); // update position values displacements or "gizmos" - void update_position_values(); void update_position_value(const Vec3d& position); // update scale values after scale unit changing or "gizmos" - void update_scale_values(); void update_scale_value(const Vec3d& scaling_factor); - // update rotation values object selection changing - void update_rotation_values(); // update rotation value after "gizmos" - void update_rotation_value(double angle, Axis axis); void update_rotation_value(const Vec3d& rotation); void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } From 7cd612fc175655ea714c78065de07cfa4e74f889 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 09:57:19 +0100 Subject: [PATCH 33/57] Fix of SPE-694 Slicer do not generate infill after Contact Z distance settings change --- src/libslic3r/PrintObject.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fdf950ee5..efde5392d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -557,15 +557,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_step(posPrepareInfill); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_step(posInfill); } else if (step == posInfill) { invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial }); - invalidated |= m_print->invalidate_step(psWipeTower); + invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial }); + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSupportMaterial) invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); From 8854276965eb8757d659bc4a26c2fedb1a752de1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:10:14 +0100 Subject: [PATCH 34/57] Added size fields to sidebar matrix manipulators --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 142 +++++++++++++--------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 19 ++- 2 files changed, 93 insertions(+), 68 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 30e701907..9bc316deb 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -39,24 +39,43 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_rotation_value(new_value); else if (param == "scale") change_scale_value(new_value); + else if (param == "size") + change_size_value(new_value); }; m_og->m_fill_empty_value = [this](const std::string& opt_key) { - if (opt_key == "scale_unit") - return; - std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); + + double value = 0.0; + if (param == "position") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - m_og->set_value(opt_key, double_to_string(cache_position(axis))); - return; + value = cache_position(axis); + } + else if (param == "rotation") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_rotation(axis); + } + else if (param == "scale") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_scale(axis); + } + else if (param == "size") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_size(axis); } - m_og->set_value(opt_key, double_to_string(0.0)); + m_og->set_value(opt_key, double_to_string(value)); }; m_og->m_set_focus = [this](const std::string& opt_key) @@ -94,51 +113,28 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext) { Line line = { _(option_name), "" }; - if (option_name == "Scale") { - line.near_label_widget = [](wxWindow* parent) { - auto btn = new PrusaLockButton(parent, wxID_ANY); - btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) { - event.Skip(); - wxTheApp->CallAfter([btn]() { - wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked()); - }); - }); - return btn; - }; - } - ConfigOptionDef def; def.type = coFloat; def.default_value = new ConfigOptionFloat(0.0); def.width = 50; if (option_name == "Rotation") + { def.min = -360; + def.max = 360; + } const std::string lower_name = boost::algorithm::to_lower_copy(option_name); std::vector axes{ "x", "y", "z" }; for (auto axis : axes) { - if (axis == "z" && option_name != "Scale") + if (axis == "z") def.sidetext = sidetext; Option option = Option(def, lower_name + "_" + axis); option.opt.full_width = true; line.append_option(option); } - if (option_name == "Scale") - { - def.width = 45; - def.type = coStrings; - def.gui_type = "select_open"; - def.enum_labels.push_back(L("%")); - def.enum_labels.push_back(L("mm")); - def.default_value = new ConfigOptionStrings{ "mm" }; - - const Option option = Option(def, lower_name + "_unit"); - line.append_option(option); - } - return line; }; @@ -146,7 +142,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Settings table m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); - m_og->append_line(add_og_to_object_settings(L("Scale"), "mm")); + m_og->append_line(add_og_to_object_settings(L("Scale"), "%")); + m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); /* Unused parameter at this time def.label = L("Place on bed"); @@ -220,6 +217,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele update_position_value(volume->get_instance_offset()); update_rotation_value(volume->get_instance_rotation()); update_scale_value(volume->get_instance_scaling_factor()); + update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size()); #else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); @@ -250,6 +248,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele update_position_value(volume->get_volume_offset()); update_rotation_value(volume->get_volume_rotation()); update_scale_value(volume->get_volume_scaling_factor()); + update_size_value(volume->bounding_box.size()); #else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); @@ -261,13 +260,13 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { reset_settings_value(); move_label = _(L("Displacement")); + update_size_value(selection.get_bounding_box().size()); m_og->enable(); } else reset_settings_value(); m_move_Label->SetLabel(move_label); - m_og->get_field("scale_unit")->disable();// temporary decision } void ObjectManipulation::reset_settings_value() @@ -287,7 +286,7 @@ void ObjectManipulation::reset_position_value() m_og->set_value("position_y", def_0); m_og->set_value("position_z", def_0); - cache_position = { 0., 0., 0. }; + cache_position = Vec3d::Zero(); } void ObjectManipulation::reset_rotation_value() @@ -295,15 +294,27 @@ void ObjectManipulation::reset_rotation_value() m_og->set_value("rotation_x", def_0); m_og->set_value("rotation_y", def_0); m_og->set_value("rotation_z", def_0); + + cache_rotation = Vec3d::Zero(); } void ObjectManipulation::reset_scale_value() { - m_is_percent_scale = true; m_og->set_value("scale_unit", _("%")); m_og->set_value("scale_x", def_100); m_og->set_value("scale_y", def_100); m_og->set_value("scale_z", def_100); + + cache_scale = Vec3d(100.0, 100.0, 100.0); +} + +void ObjectManipulation::reset_size_value() +{ + m_og->set_value("size_x", def_0); + m_og->set_value("size_y", def_0); + m_og->set_value("size_z", def_0); + + cache_size = Vec3d::Zero(); } void ObjectManipulation::update_position_value(const Vec3d& position) @@ -317,19 +328,21 @@ void ObjectManipulation::update_position_value(const Vec3d& position) void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) { - // this is temporary - // to be able to update the values as size - // we need to store somewhere the original size - // or have it passed as parameter - if (!m_is_percent_scale) { - m_is_percent_scale = true; - m_og->set_value("scale_unit", _("%")); - } - auto scale = scaling_factor * 100.0; m_og->set_value("scale_x", double_to_string(scale(0), 2)); m_og->set_value("scale_y", double_to_string(scale(1), 2)); m_og->set_value("scale_z", double_to_string(scale(2), 2)); + + cache_scale = scale; +} + +void ObjectManipulation::update_size_value(const Vec3d& size) +{ + m_og->set_value("size_x", double_to_string(size(0), 2)); + m_og->set_value("size_y", double_to_string(size(1), 2)); + m_og->set_value("size_z", double_to_string(size(2), 2)); + + cache_size = size; } void ObjectManipulation::update_rotation_value(const Vec3d& rotation) @@ -337,6 +350,8 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2)); m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); + + cache_rotation = rotation; } void ObjectManipulation::change_position_value(const Vec3d& position) @@ -364,27 +379,40 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_scale_value(const Vec3d& scale) { - Vec3d scaling_factor; - if (m_is_percent_scale) - scaling_factor = scale*0.01; - else { - int selection = ol_selection(); - ModelObjectPtrs& objects = *wxGetApp().model_objects(); + Vec3d scaling_factor = scale; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + bool needs_uniform_scale = selection.is_single_full_object() && !selection.is_single_full_instance(); - auto size = objects[selection]->instance_bounding_box(0).size(); - for (size_t i = 0; i < 3; ++i) - scaling_factor(i) = scale(i) / size(i); + if (needs_uniform_scale) + { + double min = scaling_factor.minCoeff(); + double max = scaling_factor.maxCoeff(); + if (min != 100.0) + scaling_factor = Vec3d(min, min, min); + else if (max != 100.0) + scaling_factor = Vec3d(max, max, max); } + scaling_factor *= 0.01; + auto canvas = wxGetApp().plater()->canvas3D(); canvas->get_selection().start_dragging(); canvas->get_selection().scale(scaling_factor, false); canvas->do_scale(); } -void ObjectManipulation::print_cashe_value(const std::string& label, const Vec3d& v) +void ObjectManipulation::change_size_value(const Vec3d& size) { - std::cout << label << " => " << " X:" << v(0) << " Y:" << v(1) << " Z:" << v(2) << std::endl; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + + Vec3d ref_size = cache_size; + if (selection.is_single_full_instance()) + { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + ref_size = volume->bounding_box.size(); + } + + change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cbc22a2dd..cacceff9d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -14,11 +14,11 @@ namespace GUI { class ObjectManipulation : public OG_Settings { - bool m_is_percent_scale = false; // true -> percentage scale unit - // false -> uniform scale unit - bool m_is_uniform_scale = false; // It indicates if scale is uniform - Vec3d cache_position { 0., 0., 0. }; + Vec3d cache_rotation { 0., 0., 0. }; + Vec3d cache_scale { 100., 100., 100. }; + Vec3d cache_size { 0., 0., 0. }; + wxStaticText* m_move_Label = nullptr; public: @@ -36,25 +36,22 @@ public: void reset_position_value(); void reset_rotation_value(); void reset_scale_value(); + void reset_size_value(); // update position values displacements or "gizmos" void update_position_value(const Vec3d& position); // update scale values after scale unit changing or "gizmos" void update_scale_value(const Vec3d& scaling_factor); + // update size values after scale unit changing or "gizmos" + void update_size_value(const Vec3d& size); // update rotation value after "gizmos" void update_rotation_value(const Vec3d& rotation); - void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } - - // change values void change_position_value(const Vec3d& position); void change_rotation_value(const Vec3d& rotation); void change_scale_value(const Vec3d& scale); - - -private: - void print_cashe_value(const std::string& label, const Vec3d& value); + void change_size_value(const Vec3d& size); }; }} From 334f747fa99274e835d0d5b509ab0032b5854049 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:40:53 +0100 Subject: [PATCH 35/57] Sidebar matrix fields focus handling --- src/slic3r/GUI/GLCanvas3D.cpp | 12 ++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index eecefd381..22c399223 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3581,6 +3581,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_view_toolbar(nullptr) #endif // ENABLE_REMOVE_TABS_FROM_PLATER , m_use_clipping_planes(false) + , m_sidebar_field("") , m_config(nullptr) , m_process(nullptr) , m_model(nullptr) @@ -5566,6 +5567,17 @@ void GLCanvas3D::update_gizmos_on_off_state() m_gizmos.update_on_off_state(get_selection()); } +void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on) +{ + m_sidebar_field = focus_on ? opt_key : ""; + + if (!m_sidebar_field.empty()) + { + m_gizmos.reset_all_states(); + m_dirty = true; + } +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e07c60ed2..906412e6a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -794,6 +794,7 @@ private: ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; + std::string m_sidebar_field; mutable GLVolumeCollection m_volumes; Selection m_selection; @@ -995,7 +996,7 @@ public: void viewport_changed(); #endif // ENABLE_CONSTRAINED_CAMERA_TARGET - void handle_sidebar_focus_event(const std::string& opt_key) {} + void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on); private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 9bc316deb..0c07c1fe5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -41,6 +41,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_scale_value(new_value); else if (param == "size") change_size_value(new_value); + + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_fill_empty_value = [this](const std::string& opt_key) @@ -76,11 +78,12 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : } m_og->set_value(opt_key, double_to_string(value)); + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_set_focus = [this](const std::string& opt_key) { - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key); + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; ConfigOptionDef def; @@ -300,7 +303,6 @@ void ObjectManipulation::reset_rotation_value() void ObjectManipulation::reset_scale_value() { - m_og->set_value("scale_unit", _("%")); m_og->set_value("scale_x", def_100); m_og->set_value("scale_y", def_100); m_og->set_value("scale_z", def_100); From a394e55e0781986b22c09fafae50dd1f8ed1dcc1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:49:22 +0100 Subject: [PATCH 36/57] Added method ModelObject::full_raw_mesh() --- src/libslic3r/Model.cpp | 19 +++++++++++++++++++ src/libslic3r/Model.hpp | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 07f1b98c1..7ebcbd815 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -809,6 +809,25 @@ TriangleMesh ModelObject::raw_mesh() const return mesh; } +// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. +TriangleMesh ModelObject::full_raw_mesh() const +{ + TriangleMesh mesh; + for (const ModelVolume *v : this->volumes) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + mesh.merge(vol_mesh); + } +#else + { + mesh.merge(v->mesh); + } +#endif // ENABLE_MODELVOLUME_TRANSFORM + return mesh; +} + // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. BoundingBoxf3 ModelObject::raw_bounding_box() const diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b02862203..b396bd1db 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -218,6 +218,8 @@ public: // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. // Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter. TriangleMesh raw_mesh() const; + // Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. + TriangleMesh full_raw_mesh() const; // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. BoundingBoxf3 raw_bounding_box() const; From d453b6fb3f777320bab3fb949a3499cccd12a9dc Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 11:11:06 +0100 Subject: [PATCH 37/57] Sidebar matrix field behavior for single full instance selection --- src/slic3r/GUI/GLCanvas3D.cpp | 11 ++++-- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 47 ++++++++++++----------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 2 + 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 22c399223..f34341a0e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1549,9 +1549,14 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) if (is_single_full_instance()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); - (*m_volumes)[i]->set_instance_rotation(new_rotation); + if (local) + (*m_volumes)[i]->set_instance_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } } #else #if ENABLE_MODELVOLUME_TRANSFORM diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 0c07c1fe5..5c049144e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -144,8 +144,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Settings table m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); - m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); - m_og->append_line(add_og_to_object_settings(L("Scale"), "%")); + m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label); + m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label); m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); /* Unused parameter at this time @@ -192,9 +192,11 @@ int ObjectManipulation::ol_selection() void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) { - wxString move_label = _(L("Position")); + wxString move_label = _(L("Position:")); + wxString rotate_label = _(L("Rotation:")); + wxString scale_label = _(L("Scale factors:")); #if ENABLE_MODELVOLUME_TRANSFORM - if (selection.is_single_full_instance() || selection.is_single_full_object()) + if (selection.is_single_full_instance()) #else if (selection.is_single_full_object()) { @@ -228,19 +230,15 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele #endif // ENABLE_MODELVOLUME_TRANSFORM m_og->enable(); } - else if (selection.is_wipe_tower()) + else if (selection.is_single_full_object()) { - // the selection contains a single volume - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_volume_offset()); - update_rotation_value(volume->get_volume_rotation()); - update_scale_value(volume->get_volume_scaling_factor()); -#else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); -#endif // ENABLE_MODELVOLUME_TRANSFORM + const BoundingBoxf3& box = selection.get_bounding_box(); + update_position_value(box.center()); + reset_rotation_value(); + reset_scale_value(); + update_size_value(box.size()); + rotate_label = _(L("Rotate:")); + scale_label = _(L("Scale:")); m_og->enable(); } else if (selection.is_single_modifier() || selection.is_single_volume()) @@ -262,7 +260,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else if (wxGetApp().obj_list()->multiple_selection()) { reset_settings_value(); - move_label = _(L("Displacement")); + move_label = _(L("Translate:")); update_size_value(selection.get_bounding_box().size()); m_og->enable(); } @@ -270,6 +268,8 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele reset_settings_value(); m_move_Label->SetLabel(move_label); + m_rotate_Label->SetLabel(rotate_label); + m_scale_Label->SetLabel(scale_label); } void ObjectManipulation::reset_settings_value() @@ -358,11 +358,9 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_position_value(const Vec3d& position) { - Vec3d displacement(position - cache_position); - auto canvas = wxGetApp().plater()->canvas3D(); canvas->get_selection().start_dragging(); - canvas->get_selection().translate(displacement); + canvas->get_selection().translate(position - cache_position); canvas->do_move(); cache_position = position; @@ -370,12 +368,17 @@ void ObjectManipulation::change_position_value(const Vec3d& position) void ObjectManipulation::change_rotation_value(const Vec3d& rotation) { + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + const GLCanvas3D::Selection& selection = canvas->get_selection(); + Vec3d rad_rotation; for (size_t i = 0; i < 3; ++i) + { rad_rotation(i) = Geometry::deg2rad(rotation(i)); - auto canvas = wxGetApp().plater()->canvas3D(); + } + canvas->get_selection().start_dragging(); - canvas->get_selection().rotate(rad_rotation, false); + canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance()); canvas->do_rotate(); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cacceff9d..0ada37b96 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -20,6 +20,8 @@ class ObjectManipulation : public OG_Settings Vec3d cache_size { 0., 0., 0. }; wxStaticText* m_move_Label = nullptr; + wxStaticText* m_scale_Label = nullptr; + wxStaticText* m_rotate_Label = nullptr; public: ObjectManipulation(wxWindow* parent); From 771928d916064fc8ee6fd37a449b0186abcad02a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 11:31:41 +0100 Subject: [PATCH 38/57] Logging of memory allocations on Windows during the slicing process when the SLIC3R_LOGLEVEL >= info. --- src/libslic3r/CMakeLists.txt | 4 +++ src/libslic3r/GCode.cpp | 4 +-- src/libslic3r/Print.cpp | 11 +++--- src/libslic3r/PrintObject.cpp | 17 +++++----- src/libslic3r/Utils.hpp | 3 ++ src/libslic3r/utils.cpp | 63 +++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index df3ddb6de..7870bd1fb 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -194,6 +194,10 @@ target_link_libraries(libslic3r tbb ) +if(WIN32) + target_link_libraries(libslic3r Psapi.lib) +endif() + if(SLIC3R_PROFILE) target_link_libraries(slic3r Shiny) endif() diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7f56b5f99..894e623aa 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -423,7 +423,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ print->set_started(psGCodeExport); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); // Remove the old g-code if it exists. boost::nowide::remove(path); @@ -480,7 +480,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info(); print->set_done(psGCodeExport); // Write the profiler measurements to file diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0a1bac4e4..97eb03662 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -8,13 +8,14 @@ #include "SupportMaterial.hpp" #include "GCode.hpp" #include "GCode/WipeTowerPrusaMM.hpp" -#include -#include -#include +#include "Utils.hpp" #include "PrintExport.hpp" +#include +#include #include +#include //! macro used to mark string used at localization, //! return same string @@ -1475,7 +1476,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { - BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; + BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); this->set_status(70, "Infilling layers"); @@ -1507,7 +1508,7 @@ void Print::process() } this->set_done(psWipeTower); } - BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; + BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } // G-code export process, running at a background thread. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index efde5392d..877c77b0c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -5,6 +5,7 @@ #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" +#include "Utils.hpp" #include #include @@ -132,7 +133,7 @@ void PrintObject::make_perimeters() return; m_print->set_status(20, "Generating perimeters"); - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { @@ -253,7 +254,7 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); @@ -601,7 +602,7 @@ bool PrintObject::has_support_material() const // If a part of a region is of stBottom and stTop, the stBottom wins. void PrintObject::detect_surfaces_type() { - BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..." << log_memory_info(); // Interface shells: the intersecting parts are treated as self standing objects supporting each other. // Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers @@ -793,7 +794,7 @@ void PrintObject::detect_surfaces_type() void PrintObject::process_external_surfaces() { - BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -818,7 +819,7 @@ void PrintObject::discover_vertical_shells() { PROFILE_FUNC(); - BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; + BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info(); struct DiscoverVerticalShellsCacheEntry { @@ -1202,7 +1203,7 @@ void PrintObject::discover_vertical_shells() sparse infill */ void PrintObject::bridge_over_infill() { - BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; + BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -1387,7 +1388,7 @@ bool PrintObject::update_layer_height_profile() // this should be idempotent void PrintObject::_slice() { - BOOST_LOG_TRIVIAL(info) << "Slicing objects..."; + BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info(); this->typed_slices = false; @@ -1709,7 +1710,7 @@ void PrintObject::_make_perimeters() if (! this->set_started(posPerimeters)) return; - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 7f2c94f03..e34476d4c 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -12,6 +12,9 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +// Return string to be added to the boost::log output to inform about the current process memory allocation. +// The string is non-empty only if the loglevel >= info (3). +extern std::string log_memory_info(); extern void disable_multi_threading(); // Set a path with GUI resource files. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 7a51a6104..af6373361 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -8,6 +8,7 @@ #ifdef WIN32 #include +#include #else #include #endif @@ -365,4 +366,66 @@ std::string xml_escape(std::string text) return text; } +#ifdef WIN32 + +#ifndef PROCESS_MEMORY_COUNTERS_EX + // MingW32 doesn't have this struct in psapi.h + typedef struct _PROCESS_MEMORY_COUNTERS_EX { + DWORD cb; + DWORD PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivateUsage; + } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; +#endif /* PROCESS_MEMORY_COUNTERS_EX */ + +std::string format_memsize_MB(size_t n) +{ + std::string out; + size_t n2 = 0; + size_t scale = 1; + // Round to MB + n += 500000; + n /= 1000000; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; + } + char buf[8]; + sprintf(buf, "%d", n); + out = buf; + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + sprintf(buf, ",%03d", n); + out += buf; + } + return out + "MB"; +} + +std::string log_memory_info() +{ + std::string out; + if (logSeverity <= boost::log::trivial::info) { + HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId()); + if (hProcess != nullptr) { + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) + out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + " PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + " Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")"; + CloseHandle(hProcess); + } + } + return out; +} + +#endif + }; // namespace Slic3r From f54f96666330c39c21dca14c7cf80fc32d87b495 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 11:50:22 +0100 Subject: [PATCH 39/57] Sidebar matrix field behavior for single full object selection --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 5c049144e..4f63fb61f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -390,12 +390,20 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) if (needs_uniform_scale) { - double min = scaling_factor.minCoeff(); - double max = scaling_factor.maxCoeff(); - if (min != 100.0) - scaling_factor = Vec3d(min, min, min); - else if (max != 100.0) - scaling_factor = Vec3d(max, max, max); + Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs(); + double max_diff = abs_scale_diff(X); + Axis max_diff_axis = X; + if (max_diff < abs_scale_diff(Y)) + { + max_diff = abs_scale_diff(Y); + max_diff_axis = Y; + } + if (max_diff < abs_scale_diff(Z)) + { + max_diff = abs_scale_diff(Z); + max_diff_axis = Z; + } + scaling_factor = Vec3d(scale(max_diff_axis), scale(max_diff_axis), scale(max_diff_axis)); } scaling_factor *= 0.01; From b8c8dfbb2f5575e73a694aa0c7d85dfc4238b395 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 12:05:13 +0100 Subject: [PATCH 40/57] Fix of Linux/OSX build --- src/libslic3r/utils.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index af6373361..8081828c7 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -426,6 +426,11 @@ std::string log_memory_info() return out; } +#else +std::string log_memory_info() +{ + return std::string(); +} #endif }; // namespace Slic3r From 3f96f6df84b7db79ea4364a61f7a9d5e8255209f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 12:35:49 +0100 Subject: [PATCH 41/57] Rendering of selection center (disabled) --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 49 +++++++++++++++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 13 +++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d604896ad..0f0ae974b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -40,6 +40,8 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) // Adds background texture to toolbars #define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) +// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active +#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f34341a0e..776a31c47 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1162,8 +1162,21 @@ GLCanvas3D::Selection::Selection() , m_valid(false) , m_bounding_box_dirty(true) { +#if ENABLE_RENDER_SELECTION_CENTER + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +#endif // ENABLE_RENDER_SELECTION_CENTER } +#if ENABLE_RENDER_SELECTION_CENTER +GLCanvas3D::Selection::~Selection() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) { m_volumes = volumes; @@ -1994,7 +2007,7 @@ void GLCanvas3D::Selection::erase() void GLCanvas3D::Selection::render() const { - if (is_empty()) + if (!m_valid || is_empty()) return; // render cumulative bounding box of selected volumes @@ -2002,6 +2015,28 @@ void GLCanvas3D::Selection::render() const _render_synchronized_volumes(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::Selection::render_center() const +{ + if (!m_valid || is_empty() || (m_quadric == nullptr)) + return; + + const Vec3d& center = get_bounding_box().center(); + + ::glDisable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glColor3f(1.0f, 1.0f, 1.0f); + ::glPushMatrix(); + ::glTranslated(center(0), center(1), center(2)); + ::gluSphere(m_quadric, 0.75, 32, 32); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -4097,6 +4132,10 @@ void GLCanvas3D::render() if (!is_custom_bed) // textured bed needs to be rendered after objects _render_bed(theta); +#if ENABLE_RENDER_SELECTION_CENTER + _render_selection_center(); +#endif // ENABLE_RENDER_SELECTION_CENTER + // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods // this position is used later into on_mouse() to drag the objects @@ -6138,6 +6177,14 @@ void GLCanvas3D::_render_selection() const m_selection.render(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::_render_selection_center() const +{ + if (!m_gizmos.is_running()) + m_selection.render_center(); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::_render_warning_texture() const { if (!m_warning_texture_enabled) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 906412e6a..9fb514c86 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -491,8 +491,15 @@ public: mutable BoundingBoxf3 m_bounding_box; mutable bool m_bounding_box_dirty; +#if ENABLE_RENDER_SELECTION_CENTER + GLUquadricObj* m_quadric; +#endif // ENABLE_RENDER_SELECTION_CENTER + public: Selection(); +#if ENABLE_RENDER_SELECTION_CENTER + ~Selection(); +#endif // ENABLE_RENDER_SELECTION_CENTER void set_volumes(GLVolumePtrs* volumes); @@ -567,6 +574,9 @@ public: void erase(); void render() const; +#if ENABLE_RENDER_SELECTION_CENTER + void render_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER private: void _update_valid(); @@ -1024,6 +1034,9 @@ private: void _render_axes() const; void _render_objects() const; void _render_selection() const; +#if ENABLE_RENDER_SELECTION_CENTER + void _render_selection_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; From d6b8ed3e3e1f024fe27d7be5f0488bd38d8b20c3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 13:07:50 +0100 Subject: [PATCH 42/57] Sidebar matrix field behavior for single volume selection --- src/slic3r/GUI/GLCanvas3D.cpp | 33 +++++++++++++++++++++++---------- src/slic3r/GUI/GLCanvas3D.hpp | 1 + 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 776a31c47..cdadadb60 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1532,8 +1532,13 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement) #if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; - (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + if (_requires_local_axes()) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); + else + { + Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + } } else if (m_mode == Instance) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); @@ -1582,11 +1587,16 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); - Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - (*m_volumes)[i]->set_volume_rotation(new_rotation); - } + if (_requires_local_axes()) + (*m_volumes)[i]->set_volume_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); + Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + (*m_volumes)[i]->set_volume_rotation(new_rotation); + } + } #else (*m_volumes)[i]->set_volume_rotation(rotation); #endif // ENABLE_WORLD_ROTATIONS @@ -2570,6 +2580,11 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING +bool GLCanvas3D::Selection::_requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; @@ -6332,9 +6347,7 @@ void GLCanvas3D::_render_camera_target() const ::glColor3f(0.0f, 1.0f, 0.0f); ::glVertex3d(target(0), target(1) - half_length, target(2)); ::glVertex3d(target(0), target(1) + half_length, target(2)); - ::glEnd(); - - ::glBegin(GL_LINES); + // draw line for z axis ::glColor3f(0.0f, 0.0f, 1.0f); ::glVertex3d(target(0), target(1), target(2) - half_length); ::glVertex3d(target(0), target(1), target(2) + half_length); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9fb514c86..a5c97ee98 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -597,6 +597,7 @@ public: #if ENABLE_ENSURE_ON_BED_WHILE_SCALING void _ensure_on_bed(); #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING + bool _requires_local_axes() const; }; class ClippingPlane From 7077c1e4a13fb628f78b0b9b38e7c452c5ea4be8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 14:08:46 +0100 Subject: [PATCH 43/57] Sidebar matrix field behavior for multiple volume selection --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 +++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cdadadb60..32bbb32d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1478,6 +1478,14 @@ bool GLCanvas3D::Selection::is_from_single_object() const return (0 <= idx) && (idx < 1000); } +bool GLCanvas3D::Selection::requires_uniform_scale() const +{ + if (is_single_full_instance() || is_single_modifier() || is_single_volume()) + return false; + + return true; +} + int GLCanvas3D::Selection::get_object_idx() const { return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5c97ee98..08368a131 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -532,6 +532,7 @@ public: bool is_wipe_tower() const { return m_type == WipeTower; } bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } bool is_single_modifier() const { return m_type == SingleModifier; } + bool is_multiple_modifier() const { return m_type == MultipleModifier; } bool is_single_full_instance() const; bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } bool is_single_full_object() const { return m_type == SingleFullObject; } @@ -543,6 +544,7 @@ public: bool is_from_single_object() const; bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + bool requires_uniform_scale() const; // Returns the the object id if the selection is from a single object, otherwise is -1 int get_object_idx() const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 4f63fb61f..ddf699c2c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -261,6 +261,8 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { reset_settings_value(); move_label = _(L("Translate:")); + rotate_label = _(L("Rotate:")); + scale_label = _(L("Scale:")); update_size_value(selection.get_bounding_box().size()); m_og->enable(); } @@ -386,9 +388,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) { Vec3d scaling_factor = scale; const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - bool needs_uniform_scale = selection.is_single_full_object() && !selection.is_single_full_instance(); - - if (needs_uniform_scale) + if (selection.requires_uniform_scale()) { Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs(); double max_diff = abs_scale_diff(X); From bffcaeff4130079d31f9f8997962b1379b5b9bde Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 14:10:31 +0100 Subject: [PATCH 44/57] Time estimator: Added consumed memory tracing, replaced std::map with std::vector for lower memory consumption. --- src/libslic3r/GCode.cpp | 4 +++- src/libslic3r/GCodeTimeEstimator.cpp | 26 +++++++++++++++++++++----- src/libslic3r/GCodeTimeEstimator.hpp | 6 +++++- src/libslic3r/Utils.hpp | 2 ++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 894e623aa..ef143a3e8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1231,7 +1231,7 @@ void GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, - const LayerTools &layer_tools, + const LayerTools &layer_tools, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx) @@ -1644,6 +1644,8 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); _write(file, gcode); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " + + format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 5c8cc2659..0ab49a345 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -309,8 +309,9 @@ namespace Slic3r { gcode_line += "\n"; // add remaining time lines where needed + G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); _parser.parse_line(gcode_line, - [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) + [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) { if (line.cmd_is("G1")) { @@ -319,10 +320,10 @@ namespace Slic3r { if (!line.has_e()) return; - G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count); - if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size())) + if ((it_line_id != _g1_line_ids.end()) && (it_line_id->first == g1_lines_count) && (it_line_id->second < (unsigned int)_blocks.size())) { - const Block& block = _blocks[it->second]; + const Block& block = _blocks[it_line_id->second]; + ++ it_line_id; if (block.elapsed_time != -1.0f) { float block_remaining_time = _time - block.elapsed_time; @@ -667,6 +668,21 @@ namespace Slic3r { return _get_time_minutes(get_time()); } + // Return an estimate of the memory consumed by the time estimator. + size_t GCodeTimeEstimator::memory_used() const + { + size_t out = sizeof(*this); +#if WIN32 + #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) +#else + #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) +#endif + out += STDVEC_MEMSIZE(this->_blocks, Block); + out += STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); +#undef STDVEC_MEMSIZE + return out; + } + void GCodeTimeEstimator::_reset() { _curr.reset(); @@ -1072,7 +1088,7 @@ namespace Slic3r { // adds block to blocks list _blocks.emplace_back(block); - _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); + _g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); } void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index e9da584c3..ef91d5ff1 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -209,7 +209,8 @@ namespace Slic3r { typedef std::map MovesStatsMap; #endif // ENABLE_MOVE_STATS - typedef std::map G1LineIdToBlockIdMap; + typedef std::pair G1LineIdToBlockId; + typedef std::vector G1LineIdToBlockIdMap; private: EMode _mode; @@ -338,6 +339,9 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer) std::string get_time_minutes() const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + private: void _reset(); void _reset_time(); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index e34476d4c..f4f05ae66 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -12,6 +12,8 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +// Format memory allocated, separate thousands by comma. +extern std::string format_memsize_MB(size_t n); // Return string to be added to the boost::log output to inform about the current process memory allocation. // The string is non-empty only if the loglevel >= info (3). extern std::string log_memory_info(); From 66b5deccf5b90e24ecf5935f263b7f893dd5440e Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 18 Dec 2018 14:34:16 +0100 Subject: [PATCH 45/57] PrintHost: Basic SL1 support --- src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 + src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 197 ++++++++++++++++++--------------- src/slic3r/GUI/Tab.hpp | 2 + src/slic3r/Utils/OctoPrint.cpp | 188 +++++++++++++++++-------------- src/slic3r/Utils/OctoPrint.hpp | 45 +++++--- src/slic3r/Utils/PrintHost.cpp | 14 ++- 8 files changed, 255 insertions(+), 196 deletions(-) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 487e35bf2..08f42f39b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -36,7 +36,7 @@ enum GCodeFlavor { }; enum PrintHostType { - htOctoPrint, htDuet, + htOctoPrint, htDuet, htSL1, }; enum InfillPattern { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6544da789..51735fe67 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3244,6 +3244,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config) #endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); update_scheduled = true; + } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { + p->config->option>(opt_key)->value = htSL1; } } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 90372ecd4..bfda0a2f3 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -456,6 +456,7 @@ const std::vector& Preset::sla_printer_options() "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_orientation", "printer_correction", + "print_host", "printhost_apikey", "printhost_cafile", "printer_notes", "inherits" }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 30d83d845..f46ad2142 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1538,6 +1538,108 @@ bool Tab::current_preset_is_dirty() return m_presets->current_is_dirty(); } +void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) +{ + const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA; + + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + if (! sla) { + optgroup->append_single_option_line("host_type"); + } else { + m_config->option>("host_type", true)->value = htSL1; + } + + auto printhost_browse = [this, optgroup] (wxWindow* parent) { + + // TODO: SLA + + auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { + BonjourDialog dialog(parent); + if (dialog.show_and_lookup()) { + optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + } + }); + + return sizer; + }; + + auto print_host_test = [this](wxWindow* parent) { + auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), + wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (! host) { + const auto text = wxString::Format("%s", + _(L("Could not get a valid Printer Host reference"))); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _(L("Success!"))); + } else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + Line host_line = optgroup->create_single_option_line("print_host"); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + optgroup->append_line(host_line); + optgroup->append_single_option_line("printhost_apikey"); + + if (Http::ca_file_supported()) { + + Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); + + auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { + auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { + static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); + wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + optgroup->append_line(cafile_line); + + auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, + _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + + Line cafile_hint { "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = std::move(printhost_cafile_hint); + optgroup->append_line(cafile_hint); + + } +} + void TabPrinter::build() { m_presets = &m_preset_bundle->printers; @@ -1665,96 +1767,8 @@ void TabPrinter::build_fff() } #endif - optgroup = page->new_optgroup(_(L("Printer Host upload"))); - - optgroup->append_single_option_line("host_type"); - - auto printhost_browse = [this, optgroup] (wxWindow* parent) { - auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { - BonjourDialog dialog(parent); - if (dialog.show_and_lookup()) { - optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - } - }); - - return sizer; - }; - - auto print_host_test = [this](wxWindow* parent) { - auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), - wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - std::unique_ptr host(PrintHost::get_print_host(m_config)); - if (! host) { - const auto text = wxString::Format("%s", - _(L("Could not get a valid Printer Host reference"))); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _(L("Success!"))); - } else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - Line host_line = optgroup->create_single_option_line("print_host"); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - optgroup->append_line(host_line); - optgroup->append_single_option_line("printhost_apikey"); - - if (Http::ca_file_supported()) { - - Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); - - auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { - static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); - wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - optgroup->append_line(cafile_line); - - auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, - _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = std::move(printhost_cafile_hint); - optgroup->append_line(cafile_hint); - - } + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); optgroup = page->new_optgroup(_(L("Firmware"))); optgroup->append_single_option_line("gcode_flavor"); @@ -1897,6 +1911,9 @@ void TabPrinter::build_sla() } optgroup->append_line(line); + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); + page = add_options_page(_(L("Notes")), "note.png"); optgroup = page->new_optgroup(_(L("Notes")), 0); option = optgroup->get_option("printer_notes"); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 337fbf006..93db1383a 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -325,6 +325,8 @@ class TabPrinter : public Tab std::vector m_pages_fff; std::vector m_pages_sla; + + void build_printhost(ConfigOptionsGroup *optgroup); public: wxButton* m_serial_test_btn = nullptr; wxButton* m_print_host_test_btn = nullptr; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 67c58a972..cbb81c54f 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -17,141 +17,161 @@ namespace fs = boost::filesystem; namespace Slic3r { OctoPrint::OctoPrint(DynamicPrintConfig *config) : - host(config->opt_string("print_host")), - apikey(config->opt_string("printhost_apikey")), - cafile(config->opt_string("printhost_cafile")) + host(config->opt_string("print_host")), + apikey(config->opt_string("printhost_apikey")), + cafile(config->opt_string("printhost_cafile")) {} OctoPrint::~OctoPrint() {} bool OctoPrint::test(wxString &msg) const { - // Since the request is performed synchronously here, - // it is ok to refer to `msg` from within the closure + // Since the request is performed synchronously here, + // it is ok to refer to `msg` from within the closure - bool res = true; - auto url = make_url("api/version"); + bool res = true; + auto url = make_url("api/version"); - BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; - auto http = Http::get(std::move(url)); - set_auth(http); - http.on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1%, HTTP %2%, body: `%3%`") % error % status % body; - res = false; - msg = format_error(body, error, status); - }) - .on_complete([&](std::string body, unsigned) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - }) - .perform_sync(); + auto http = Http::get(std::move(url)); + set_auth(http); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1%, HTTP %2%, body: `%3%`") % error % status % body; + res = false; + msg = format_error(body, error, status); + }) + .on_complete([&](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - return res; + // TODO: parse body, call validate_version_text + + }) + .perform_sync(); + + return res; } wxString OctoPrint::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); + return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); } wxString OctoPrint::get_test_failed_msg (wxString &msg) const { - return wxString::Format("%s: %s\n\n%s", - _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); + return wxString::Format("%s: %s\n\n%s", + _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { - const auto upload_filename = upload_data.upload_path.filename(); - const auto upload_parent_path = upload_data.upload_path.parent_path(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); - wxString test_msg; - if (! test(test_msg)) { + wxString test_msg; + if (! test(test_msg)) { - // TODO: + // TODO: - // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - // GUI::show_error(&progress_dialog, std::move(errormsg)); - // return false; - } + // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); + // GUI::show_error(&progress_dialog, std::move(errormsg)); + // return false; + } - bool res = true; + bool res = true; - auto url = make_url("api/files/local"); + auto url = make_url("api/files/local"); - BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % upload_data.source_path.string() - % url - % upload_filename.string() - % upload_parent_path.string() - % upload_data.start_print; + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") + % upload_data.source_path.string() + % url + % upload_filename.string() + % upload_parent_path.string() + % upload_data.start_print; - auto http = Http::post(std::move(url)); - set_auth(http); - http.form_add("print", upload_data.start_print ? "true" : "false") - .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? - .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) - .on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; - }) - .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - error_fn(std::move(body), std::move(error), status); - res = false; - }) - .on_progress([&](Http::Progress progress, bool &cancel) { - prorgess_fn(std::move(progress), cancel); - if (cancel) { - // Upload was canceled - BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; - res = false; - } - }) - .perform_sync(); + auto http = Http::post(std::move(url)); + set_auth(http); + http.form_add("print", upload_data.start_print ? "true" : "false") + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; + error_fn(std::move(body), std::move(error), status); + res = false; + }) + .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; + res = false; + } + }) + .perform_sync(); - return res; + return res; } bool OctoPrint::has_auto_discovery() const { - return true; + return true; } bool OctoPrint::can_test() const { - return true; + return true; +} + +bool OctoPrint::validate_version_text(const std::string &version_text) +{ + // FIXME + return true; } void OctoPrint::set_auth(Http &http) const { - http.header("X-Api-Key", apikey); + http.header("X-Api-Key", apikey); - if (! cafile.empty()) { - http.ca_file(cafile); - } + if (! cafile.empty()) { + http.ca_file(cafile); + } } std::string OctoPrint::make_url(const std::string &path) const { - if (host.find("http://") == 0 || host.find("https://") == 0) { - if (host.back() == '/') { - return (boost::format("%1%%2%") % host % path).str(); - } else { - return (boost::format("%1%/%2%") % host % path).str(); - } - } else { - return (boost::format("http://%1%/%2%") % host % path).str(); - } + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return (boost::format("%1%%2%") % host % path).str(); + } else { + return (boost::format("%1%/%2%") % host % path).str(); + } + } else { + return (boost::format("http://%1%/%2%") % host % path).str(); + } } wxString OctoPrint::format_error(const std::string &body, const std::string &error, unsigned status) { - if (status != 0) { - auto wxbody = wxString::FromUTF8(body.data()); - return wxString::Format("HTTP %u: %s", status, wxbody); - } else { - return wxString::FromUTF8(error.data()); - } + if (status != 0) { + auto wxbody = wxString::FromUTF8(body.data()); + return wxString::Format("HTTP %u: %s", status, wxbody); + } else { + return wxString::FromUTF8(error.data()); + } +} + + +// SL1 + +SL1Host::~SL1Host() {} + +bool SL1Host::validate_version_text(const std::string &version_text) +{ + // FIXME + return true; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 9267b4c83..4d6555e13 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -16,24 +16,39 @@ class Http; class OctoPrint : public PrintHost { public: - OctoPrint(DynamicPrintConfig *config); - virtual ~OctoPrint(); + OctoPrint(DynamicPrintConfig *config); + virtual ~OctoPrint(); + + bool test(wxString &curl_msg) const; + wxString get_test_ok_msg () const; + wxString get_test_failed_msg (wxString &msg) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + bool has_auto_discovery() const; + bool can_test() const; + virtual std::string get_host() const { return host; } + +protected: + virtual bool validate_version_text(const std::string &version_text); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; - virtual std::string get_host() const { return host; } private: - std::string host; - std::string apikey; - std::string cafile; + std::string host; + std::string apikey; + std::string cafile; - void set_auth(Http &http) const; - std::string make_url(const std::string &path) const; - static wxString format_error(const std::string &body, const std::string &error, unsigned status); + void set_auth(Http &http) const; + std::string make_url(const std::string &path) const; + static wxString format_error(const std::string &body, const std::string &error, unsigned status); +}; + + +class SL1Host: public OctoPrint +{ +public: + SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} + virtual ~SL1Host(); + +protected: + virtual bool validate_version_text(const std::string &version_text); }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 48f504884..863da5d43 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -24,13 +24,15 @@ PrintHost::~PrintHost() {} PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) { - PrintHostType kind = config->option>("host_type")->value; - if (kind == htOctoPrint) { - return new OctoPrint(config); - } else if (kind == htDuet) { - return new Duet(config); + const auto opt = config->option>("host_type"); + if (opt == nullptr) { return nullptr; } + + switch (opt->value) { + case htOctoPrint: return new OctoPrint(config); + case htSL1: return new SL1Host(config); + case htDuet: return new Duet(config); + default: return nullptr; } - return nullptr; } From 7d1fb201e725a2c83ceb5a226ed76e652154fb4a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Dec 2018 13:22:22 +0100 Subject: [PATCH 46/57] Implemented updating of the settings values for PointCtrl and Choice. * wx_EVT_KILL_FOCES doesn't handled on OSX, so values are updating on wx_EVT_TEXT like a temporary workaround. --- src/slic3r/GUI/Field.cpp | 59 ++++++++++++++++++++++++++++++++------- src/slic3r/GUI/Field.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index fc428ac0d..0283b7c29 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -193,9 +193,10 @@ void Field::get_value_by_opt_type(wxString& str) } } -bool TextCtrl::is_defined_input_value() const +template +bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type) { - if (static_cast(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings) + if (static_cast(win)->GetValue().empty() && type != coString && type != coStrings) return false; return true; } @@ -274,7 +275,7 @@ void TextCtrl::BUILD() { temp->GetToolTip()->Enable(true); #endif // __WXGTK__ // if (!is_defined_input_value()) - if (is_defined_input_value()) + if (is_defined_input_value(window, m_opt.type)) on_change_field(); else on_kill_focus(e); @@ -399,6 +400,9 @@ void SpinCtrl::BUILD() { 0, min_val, max_val, default_value); // temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); + + // #ys_FIXME_KILL_FOCUS + // wxEVT_KILL_FOCUS doesn't handled on OSX now temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { if (tmp_value < 0) @@ -408,6 +412,7 @@ void SpinCtrl::BUILD() { on_change_field(); } }), temp->GetId()); + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value @@ -420,9 +425,15 @@ void SpinCtrl::BUILD() { tmp_value = std::stoi(value); else tmp_value = -9999; // on_change_field(); -// # We don't reset tmp_value here because _on_change might put callbacks -// # in the CallAfter queue, and we want the tmp value to be available from -// # them as well. +#ifdef __WXOSX__ + // #ys_FIXME_KILL_FOCUS so call on_change_field() inside wxEVT_TEXT + if (tmp_value < 0) { + if (m_on_kill_focus != nullptr) + m_on_kill_focus(m_opt_id); + } + else + on_change_field(); +#endif }), temp->GetId()); temp->SetToolTip(get_tooltip_text(text_value)); @@ -454,9 +465,24 @@ void Choice::BUILD() { } set_selection(); } - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); +// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); + if (temp->GetWindowStyle() != wxCB_READONLY) { + temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { + e.Skip(); + double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; + if (is_defined_input_value(window, m_opt.type)) { + if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + return; + else + on_change_field(); + } + else + on_kill_focus(e); + }), temp->GetId()); + } + temp->SetToolTip(get_tooltip_text(temp->GetValue())); } @@ -666,7 +692,7 @@ boost::any& Choice::get_value() if (ret_enum < 0 || m_opt.enum_values.empty()) get_value_by_opt_type(ret_str); else - m_value = m_opt.enum_values[ret_enum]; + m_value = atof(m_opt.enum_values[ret_enum].c_str()); } else get_value_by_opt_type(ret_str); @@ -733,8 +759,11 @@ void PointCtrl::BUILD() temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0); temp->Add(y_textctrl); - x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); - y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); +// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); +// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); + + x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId()); + y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId()); // // recast as a wxWindow to fit the calling convention sizer = dynamic_cast(temp); @@ -743,6 +772,16 @@ void PointCtrl::BUILD() y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); } +void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win) +{ + e.Skip(); + if (!win->GetValue().empty()) { + on_change_field(); + } + else + on_kill_focus(e); +} + void PointCtrl::set_value(const Vec2d& value, bool change_event) { m_disable_change_event = !change_event; diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 0097d3ec0..4a19b6103 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -266,7 +266,6 @@ public: } boost::any& get_value() override; - bool is_defined_input_value() const ; virtual void enable(); virtual void disable(); @@ -395,6 +394,7 @@ public: void BUILD() override; + void OnKillFocus(wxEvent& e, wxTextCtrl* win); void set_value(const Vec2d& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); boost::any& get_value() override; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 51735fe67..ab7141e91 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -484,7 +484,7 @@ Sidebar::Sidebar(Plater *parent) : wxPanel(parent), p(new priv(parent)) { p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1)); - p->scrolled->SetScrollbars(0, 1, 1, 1); + p->scrolled->SetScrollbars(0, 20, 1, 2); // Sizer in the scrolled area auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f46ad2142..0b59a21ab 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str auto panel = this; #endif PageShp page(new Page(panel, title, icon_idx)); - page->SetScrollbars(1, 1, 1, 2); + page->SetScrollbars(1, 20, 1, 2); page->Hide(); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); From 1dc3145e69ea522228b7ce18795f28578ab44260 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Dec 2018 15:29:09 +0100 Subject: [PATCH 47/57] Suppressed selection's update if SettingsItem for the current object/part is selected --- src/slic3r/GUI/GUI_ObjectList.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cba42470a..e7b84e289 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1399,6 +1399,20 @@ void ObjectList::update_selections() auto& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; + // We doesn't update selection if SettingsItem for the current object/part is selected + if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) + { + const auto item = GetSelection(); + if (selection.is_single_full_object() && + m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx()) + return; + if (selection.is_single_volume() || selection.is_modifier()) { + const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin()); + if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx()) + return; + } + } + if (selection.is_single_full_object()) { sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); From 1e6900afa22f7a650a1a6801017b597cb1656407 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 15:55:45 +0100 Subject: [PATCH 48/57] Logging of memory usage for the GCodeAnalyzer and GCodePreviewData. --- src/libslic3r/GCode.cpp | 7 +++-- src/libslic3r/GCode/Analyzer.cpp | 11 ++++++++ src/libslic3r/GCode/Analyzer.hpp | 3 +++ src/libslic3r/GCode/PreviewData.cpp | 38 ++++++++++++++++++++++++++++ src/libslic3r/GCode/PreviewData.hpp | 12 +++++++++ src/libslic3r/GCodeTimeEstimator.cpp | 10 ++------ src/libslic3r/Utils.hpp | 7 ++++- 7 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ef143a3e8..e738662cb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1644,8 +1644,11 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); _write(file, gcode); - BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " + - format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << + ", time estimator memory: " << + format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) << + ", analyzer memory: " << + format_memsize_MB(m_analyzer.memory_used()); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c56f02753..8212b1703 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -4,6 +4,7 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" +#include "../Utils.hpp" #include "Print.hpp" #include "Analyzer.hpp" @@ -852,6 +853,16 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_ } } +// Return an estimate of the memory consumed by the time estimator. +size_t GCodeAnalyzer::memory_used() const +{ + size_t out = sizeof(*this); + for (const std::pair &kvp : m_moves_map) + out += sizeof(kvp) + SLIC3R_STDVEC_MEMSIZE(kvp.second, GCodeMove); + out += m_process_output.size(); + return out; +} + GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) { return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index f50138b56..389c11cec 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -120,6 +120,9 @@ public: // Calculates all data needed for gcode visualization void calc_gcode_preview_data(GCodePreviewData& preview_data); + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_valid_extrusion_role(ExtrusionRole role); private: diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3f2df9532..d4aa9bc02 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -2,6 +2,7 @@ #include "PreviewData.hpp" #include #include +#include "Utils.hpp" #include @@ -205,6 +206,18 @@ bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, Extrusion return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0; } +size_t GCodePreviewData::Extrusion::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); + for (const Layer &layer : this->layers) { + out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); + for (const ExtrusionPath &path : layer.paths) + out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); + } + return out; +} + const float GCodePreviewData::Travel::Default_Width = 0.075f; const float GCodePreviewData::Travel::Default_Height = 0.075f; const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] = @@ -224,6 +237,15 @@ void GCodePreviewData::Travel::set_default() is_visible = false; } +size_t GCodePreviewData::Travel::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->polylines, Polyline); + for (const Polyline &polyline : this->polylines) + out += SLIC3R_STDVEC_MEMSIZE(polyline.polyline.points, Vec3crd); + return out; +} + const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f); GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height) @@ -239,6 +261,11 @@ void GCodePreviewData::Retraction::set_default() is_visible = false; } +size_t GCodePreviewData::Retraction::memory_used() const +{ + return sizeof(*this) + SLIC3R_STDVEC_MEMSIZE(this->positions, Position); +} + void GCodePreviewData::Shell::set_default() { is_visible = false; @@ -483,4 +510,15 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: return items; } +// Return an estimate of the memory consumed by the time estimator. +size_t GCodePreviewData::memory_used() const +{ + return + this->extrusion.memory_used() + + this->travel.memory_used() + + this->retraction.memory_used() + + this->unretraction.memory_used() + + sizeof(shell) + sizeof(ranges); +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 9f882788d..8ed5e91c7 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -99,6 +99,9 @@ public: void set_default(); bool is_role_flag_set(ExtrusionRole role) const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_role_flag_set(unsigned int flags, ExtrusionRole role); }; @@ -144,6 +147,9 @@ public: size_t color_print_idx; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Retraction @@ -166,6 +172,9 @@ public: bool is_visible; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Shell @@ -199,6 +208,9 @@ public: std::string get_legend_title() const; LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const; + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 0ab49a345..81119c513 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -672,14 +672,8 @@ namespace Slic3r { size_t GCodeTimeEstimator::memory_used() const { size_t out = sizeof(*this); -#if WIN32 - #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) -#else - #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) -#endif - out += STDVEC_MEMSIZE(this->_blocks, Block); - out += STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); -#undef STDVEC_MEMSIZE + out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block); + out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); return out; } diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index f4f05ae66..047e03bf2 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -187,7 +187,12 @@ public: void reset() { closure = Closure(); } }; - } // namespace Slic3r +#if WIN32 + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) +#else + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) +#endif + #endif // slic3r_Utils_hpp_ From e2d7fd941f6d53ba9dc40c8643f54f7529edc6eb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 16:32:11 +0100 Subject: [PATCH 49/57] Fixed OSX/Linux builds --- src/libslic3r/utils.cpp | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 8081828c7..18e27f34b 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -366,6 +366,32 @@ std::string xml_escape(std::string text) return text; } +std::string format_memsize_MB(size_t n) +{ + std::string out; + size_t n2 = 0; + size_t scale = 1; + // Round to MB + n += 500000; + n /= 1000000; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; + } + char buf[8]; + sprintf(buf, "%d", n); + out = buf; + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + sprintf(buf, ",%03d", n); + out += buf; + } + return out + "MB"; +} + #ifdef WIN32 #ifndef PROCESS_MEMORY_COUNTERS_EX @@ -385,32 +411,6 @@ std::string xml_escape(std::string text) } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; #endif /* PROCESS_MEMORY_COUNTERS_EX */ -std::string format_memsize_MB(size_t n) -{ - std::string out; - size_t n2 = 0; - size_t scale = 1; - // Round to MB - n += 500000; - n /= 1000000; - while (n >= 1000) { - n2 = n2 + scale * (n % 1000); - n /= 1000; - scale *= 1000; - } - char buf[8]; - sprintf(buf, "%d", n); - out = buf; - while (scale != 1) { - scale /= 1000; - n = n2 / scale; - n2 = n2 % scale; - sprintf(buf, ",%03d", n); - out += buf; - } - return out + "MB"; -} - std::string log_memory_info() { std::string out; From 8bc04e640aae1b45de9f987b826e2e04aa0c601f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 17:34:21 +0100 Subject: [PATCH 50/57] The G-code export was reshuffled a bit to reduce peak memory consumption. Namely, the time estimate memory is released before the G-code preview data is created from the G-code analyser data. --- src/libslic3r/GCode.cpp | 40 ++++++++++++++++++++++------------------ src/libslic3r/GCode.hpp | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e738662cb..b13c38742 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -435,9 +435,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); + m_enable_analyzer = preview_data != nullptr; + try { m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); + this->_do_export(*print, file); fflush(file); if (ferror(file)) { fclose(file); @@ -453,15 +455,6 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } fclose(file); - if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; - m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; - m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - } - } - if (! m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; @@ -475,6 +468,24 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } + if (print->config().remaining_times.value) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; + m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_normal_time_estimator.reset(); + if (m_silent_time_estimator_enabled) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; + m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_silent_time_estimator.reset(); + } + } + + // starts analyzer calculations + if (m_enable_analyzer) { + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; + m_analyzer.calc_gcode_preview_data(*preview_data); + m_analyzer.reset(); + } + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + @@ -488,7 +499,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } -void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) +void GCode::_do_export(Print &print, FILE *file) { PROFILE_FUNC(); @@ -558,7 +569,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // resets analyzer m_analyzer.reset(); - m_enable_analyzer = preview_data != nullptr; // resets analyzer's tracking data m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -1034,12 +1044,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, full_config); } print.throw_if_canceled(); - - // starts analyzer calculations - if (preview_data != nullptr) { - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; - m_analyzer.calc_gcode_preview_data(*preview_data); - } } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index bf65311db..32a705751 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -180,7 +180,7 @@ public: static void append_full_config(const Print& print, std::string& str); protected: - void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data); + void _do_export(Print &print, FILE *file); // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint From bb5caf2e08ca3be318501ff0580242e84d1e4355 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 18:41:20 +0100 Subject: [PATCH 51/57] Fixed scaling of the object, if it was loaded too big. Here the large object was not scaled uniformly, and the Z height of the bed was set incorrectly to one. --- src/slic3r/GUI/Plater.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ab7141e91..a261e7e05 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1526,9 +1526,8 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode if (max_ratio > 10000) { // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh - // const Vec3d inverse = ratio.cwiseInverse(); - // object->scale(inverse); - object->scale(ratio.cwiseInverse()); + double inv = 1. / max_ratio; + object->scale(Vec3d(inv, inv, inv)); scaled_down = true; } else if (max_ratio > 5) { const Vec3d inverse = ratio.cwiseInverse(); From ec9caae6227d8a151bd673d1e1214f197c803d1a Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 18 Dec 2018 18:40:35 +0100 Subject: [PATCH 52/57] Http & ErrorDialog: Improve error reporting --- src/libslic3r/Utils.hpp | 1 + src/libslic3r/utils.cpp | 12 ++++++++++++ src/slic3r/GUI/MsgDialog.cpp | 22 ++++++++++++++++++++-- src/slic3r/GUI/MsgDialog.hpp | 6 +++++- src/slic3r/Utils/Http.cpp | 25 ++++++++++++++----------- src/slic3r/Utils/PrintHost.cpp | 11 ++++++++++- 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 047e03bf2..cfae9edd1 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -11,6 +11,7 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); +extern unsigned get_logging_level(); extern void trace(unsigned int level, const char *message); // Format memory allocated, separate thousands by comma. extern std::string format_memsize_MB(size_t n); diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 18e27f34b..f48abfd89 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -59,6 +59,18 @@ void set_logging_level(unsigned int level) ); } +unsigned get_logging_level() +{ + switch (logSeverity) { + case boost::log::trivial::fatal : return 0; + case boost::log::trivial::error : return 1; + case boost::log::trivial::warning : return 2; + case boost::log::trivial::info : return 3; + case boost::log::trivial::debug : return 4; + default: return 1; + } +} + // Force set_logging_level(<=error) after loading of the DLL. // Switch boost::filesystem to utf8. static struct RunOnInit { diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index ae7b40484..d6b8b438e 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" @@ -61,8 +62,11 @@ MsgDialog::~MsgDialog() {} // ErrorDialog -ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : - MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG)) +ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) + : MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), + wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), + wxID_NONE) + , msg(msg) { auto *panel = new wxScrolledWindow(this); auto *p_sizer = new wxBoxSizer(wxVERTICAL); @@ -77,6 +81,20 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : content_sizer->Add(panel, 1, wxEXPAND); + auto *btn_copy = new wxButton(this, wxID_ANY, _(L("Copy to clipboard"))); + btn_copy->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + if (wxTheClipboard->Open()) { + wxTheClipboard->SetData(new wxTextDataObject(this->msg)); // Note: the clipboard takes ownership of the pointer + wxTheClipboard->Close(); + } + }); + + auto *btn_ok = new wxButton(this, wxID_OK); + btn_ok->SetFocus(); + + btn_sizer->Add(btn_copy, 0, wxRIGHT, HORIZ_SPACING); + btn_sizer->Add(btn_ok); + SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT)); Fit(); } diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index ca349eb5c..6064d2a9f 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -50,14 +50,18 @@ protected: // Generic error dialog, used for displaying exceptions -struct ErrorDialog : MsgDialog +class ErrorDialog : public MsgDialog { +public: ErrorDialog(wxWindow *parent, const wxString &msg); ErrorDialog(ErrorDialog &&) = delete; ErrorDialog(const ErrorDialog &) = delete; ErrorDialog &operator=(ErrorDialog &&) = delete; ErrorDialog &operator=(const ErrorDialog &) = delete; virtual ~ErrorDialog(); + +private: + wxString msg; }; diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 67c24f3f4..6e6c9ed44 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -11,6 +11,7 @@ #include #include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" namespace fs = boost::filesystem; @@ -44,6 +45,7 @@ struct Http::priv // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion std::deque form_files; std::string postfields; + std::string error_buffer; // Used for CURLOPT_ERRORBUFFER size_t limit; bool cancel; @@ -69,13 +71,14 @@ struct Http::priv void http_perform(); }; -Http::priv::priv(const std::string &url) : - curl(::curl_easy_init()), - form(nullptr), - form_end(nullptr), - headerlist(nullptr), - limit(0), - cancel(false) +Http::priv::priv(const std::string &url) + : curl(::curl_easy_init()) + , form(nullptr) + , form_end(nullptr) + , headerlist(nullptr) + , error_buffer(CURL_ERROR_SIZE + 1, '\0') + , limit(0) + , cancel(false) { if (curl == nullptr) { throw std::runtime_error(std::string("Could not construct Curl object")); @@ -83,6 +86,7 @@ Http::priv::priv(const std::string &url) : ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); + ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); } Http::priv::~priv() @@ -199,9 +203,10 @@ void Http::priv::set_post_body(const fs::path &path) std::string Http::priv::curl_error(CURLcode curlcode) { - return (boost::format("%1% (%2%)") + return (boost::format("%1% (%2%): %3%") % ::curl_easy_strerror(curlcode) % curlcode + % error_buffer ).str(); } @@ -227,9 +232,7 @@ void Http::priv::http_perform() ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast(this)); #endif -#ifndef NDEBUG - ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); -#endif + ::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 4); if (headerlist != nullptr) { ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 863da5d43..cdd0c107e 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,10 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`") + % the_job.upload_data.upload_path + % the_job.printhost->get_host(); + const fs::path gcode_path = the_job.upload_data.source_path; the_job.printhost->upload(std::move(the_job.upload_data), @@ -154,7 +159,11 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); wxQueueEvent(queue_dialog, evt); - fs::remove(gcode_path); // XXX: error handling + boost::system::error_code ec; + fs::remove(gcode_path, ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % gcode_path % ec; + } } void PrintHostJobQueue::enqueue(PrintHostJob job) From 76c922bf9ab80a4d40ca10d1cd54443a4dcecf69 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 19:12:59 +0100 Subject: [PATCH 53/57] Fixed a crash when trying to delete a wipe tower with the delete key. --- src/slic3r/GUI/GUI_ObjectList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index e7b84e289..5e20ceaab 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -982,6 +982,10 @@ void ObjectList::del_instances_from_object(const int obj_idx) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) { + if (obj_idx == 1000) + // Cannot delete a wipe tower. + return false; + if (type == itVolume) { const auto volume = (*m_objects)[obj_idx]->volumes[idx]; From 9d8e78636b452af89182976a0ad969f76b90fe0a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 09:54:15 +0100 Subject: [PATCH 54/57] Fixed loading of huge models --- src/libslic3r/Model.cpp | 16 ++++++++++++++++ src/libslic3r/Model.hpp | 5 +++++ src/slic3r/GUI/Plater.cpp | 6 ++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7ebcbd815..e56a4b6e1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -983,6 +983,16 @@ void ModelObject::mirror(Axis axis) this->invalidate_bounding_box(); } +void ModelObject::scale_mesh(const Vec3d &versor) +{ + for (ModelVolume *v : this->volumes) + { + v->scale_geometry(versor); + v->set_offset(versor.cwiseProduct(v->get_offset())); + } + this->invalidate_bounding_box(); +} + size_t ModelObject::materials_count() const { std::set material_ids; @@ -1514,6 +1524,12 @@ void ModelVolume::mirror(Axis axis) #endif // ENABLE_MODELVOLUME_TRANSFORM } +void ModelVolume::scale_geometry(const Vec3d& versor) +{ + mesh.scale(versor); + m_convex_hull.scale(versor); +} + #if !ENABLE_MODELVOLUME_TRANSFORM void ModelInstance::set_rotation(const Vec3d& rotation) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b396bd1db..c26eb0f7a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -237,6 +237,9 @@ public: void rotate(double angle, Axis axis); void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + + void scale_mesh(const Vec3d& versor); + size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; @@ -331,6 +334,8 @@ public: void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + void scale_geometry(const Vec3d& versor); + #if ENABLE_MODELVOLUME_TRANSFORM // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box void center_geometry(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a261e7e05..118fb41c8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1494,7 +1494,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode #if !ENABLE_MODELVOLUME_TRANSFORM const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); #endif // !ENABLE_MODELVOLUME_TRANSFORM - const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); + const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); bool need_arrange = false; bool scaled_down = false; @@ -1527,7 +1527,9 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh double inv = 1. / max_ratio; - object->scale(Vec3d(inv, inv, inv)); + object->scale_mesh(Vec3d(inv, inv, inv)); + object->origin_translation = Vec3d::Zero(); + object->center_around_origin(); scaled_down = true; } else if (max_ratio > 5) { const Vec3d inverse = ratio.cwiseInverse(); From d922260b73b90432cdbf5e9df8d49104068694ac Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 11:59:59 +0100 Subject: [PATCH 55/57] Do not clear selection if left-clicking out of objects while shift is down --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 32bbb32d6..e01e1dbbe 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5200,7 +5200,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) 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 (m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) + if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) { m_selection.clear(); m_selection.set_mode(Selection::Instance); From d414ef33950565230d756e7c2a099d5f3132d60c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 19 Dec 2018 12:02:17 +0100 Subject: [PATCH 56/57] Fix of G-code remaining times export, that I broke with my optimizations. --- src/libslic3r/GCodeTimeEstimator.cpp | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 81119c513..461b4cd35 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -290,7 +290,8 @@ namespace Slic3r { // buffer line to export only when greater than 64K to reduce writing calls std::string export_line; char time_line[64]; - while (std::getline(in, gcode_line)) + G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); + while (std::getline(in, gcode_line)) { if (!in.good()) { @@ -309,7 +310,6 @@ namespace Slic3r { gcode_line += "\n"; // add remaining time lines where needed - G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); _parser.parse_line(gcode_line, [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) { @@ -317,23 +317,23 @@ namespace Slic3r { { ++g1_lines_count; - if (!line.has_e()) - return; + assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count); - if ((it_line_id != _g1_line_ids.end()) && (it_line_id->first == g1_lines_count) && (it_line_id->second < (unsigned int)_blocks.size())) - { - const Block& block = _blocks[it_line_id->second]; - ++ it_line_id; - if (block.elapsed_time != -1.0f) + const Block *block = nullptr; + if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) { + if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size()) + block = &_blocks[it_line_id->second]; + ++it_line_id; + } + + if (block != nullptr && block->elapsed_time != -1.0f) { + float block_remaining_time = _time - block->elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval) { - float block_remaining_time = _time - block.elapsed_time; - if (std::abs(last_recorded_time - block_remaining_time) > interval) - { - sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); - gcode_line += time_line; + sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += time_line; - last_recorded_time = block_remaining_time; - } + last_recorded_time = block_remaining_time; } } } From a6a1a866d811b405f201d770334f0fbc44e02296 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 19 Dec 2018 12:07:45 +0100 Subject: [PATCH 57/57] Fix of SPE-695 Minus key should not delete object --- src/slic3r/GUI/Plater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a261e7e05..90a2744ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1186,7 +1186,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) + { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); @@ -1212,7 +1213,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) + { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); });