2018-09-17 12:15:11 +02:00
#include "Plater.hpp"
#include <cstddef>
#include <algorithm>
2019-06-26 17:09:26 +02:00
#include <numeric>
2018-09-17 12:15:11 +02:00
#include <vector>
#include <string>
#include <regex>
2019-05-13 18:58:56 +02:00
#include <future>
2019-05-17 16:27:00 +02:00
#include <boost/algorithm/string.hpp>
2018-10-04 11:12:55 +02:00
#include <boost/optional.hpp>
2018-09-17 12:15:11 +02:00
#include <boost/filesystem/path.hpp>
2019-09-19 09:09:11 +02:00
#include <boost/filesystem/operations.hpp>
2019-06-18 17:10:13 +02:00
#include <boost/log/trivial.hpp>
2020-05-01 08:57:24 +02:00
#include <boost/nowide/convert.hpp>
2018-09-17 12:15:11 +02:00
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/bmpcbox.h>
#include <wx/statbox.h>
#include <wx/statbmp.h>
#include <wx/filedlg.h>
#include <wx/dnd.h>
#include <wx/progdlg.h>
2018-10-01 16:48:08 +02:00
#include <wx/wupdlock.h>
2018-10-10 13:53:45 +02:00
#include <wx/colordlg.h>
2018-10-18 15:13:38 +02:00
#include <wx/numdlg.h>
#include <wx/debug.h>
2020-01-21 14:07:13 +01:00
#include <wx/busyinfo.h>
2018-09-17 12:15:11 +02:00
2018-10-09 09:35:19 +02:00
#include "libslic3r/libslic3r.h"
2018-10-04 11:12:55 +02:00
#include "libslic3r/Format/STL.hpp"
2018-10-08 19:55:30 +02:00
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
2018-11-26 14:41:58 +01:00
#include "libslic3r/GCode/PreviewData.hpp"
2019-10-24 12:09:09 +02:00
#include "libslic3r/GCode/ThumbnailData.hpp"
2018-11-26 14:41:58 +01:00
#include "libslic3r/Model.hpp"
2019-11-11 11:41:14 +01:00
#include "libslic3r/SLA/Hollowing.hpp"
#include "libslic3r/SLA/SupportPoint.hpp"
2020-05-12 15:52:33 +02:00
#include "libslic3r/SLA/ReprojectPointsOnMesh.hpp"
2018-11-26 14:41:58 +01:00
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Utils.hpp"
2018-09-17 12:15:11 +02:00
#include "GUI.hpp"
#include "GUI_App.hpp"
2018-10-04 16:43:10 +02:00
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
2019-05-28 16:38:04 +02:00
#include "GUI_ObjectLayers.hpp"
2018-10-04 11:12:55 +02:00
#include "GUI_Utils.hpp"
2018-10-17 12:17:25 +02:00
#include "wxExtensions.hpp"
2018-09-17 12:15:11 +02:00
#include "MainFrame.hpp"
2020-04-01 10:30:33 +02:00
#include "format.hpp"
2018-09-17 12:15:11 +02:00
#include "3DScene.hpp"
2018-10-03 11:34:39 +02:00
#include "GLCanvas3D.hpp"
2019-03-19 13:30:21 +01:00
#include "Selection.hpp"
2018-09-17 12:15:11 +02:00
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
2019-02-19 15:15:27 +01:00
#include "3DBed.hpp"
2019-03-06 15:46:19 +01:00
#include "Camera.hpp"
2019-09-27 14:52:19 +02:00
#include "Mouse3DController.hpp"
2018-09-17 12:15:11 +02:00
#include "Tab.hpp"
2020-04-23 18:56:09 +02:00
#include "Jobs/ArrangeJob.hpp"
#include "Jobs/RotoptimizeJob.hpp"
2020-04-23 19:45:55 +02:00
#include "Jobs/SLAImportJob.hpp"
2018-09-17 12:15:11 +02:00
#include "PresetBundle.hpp"
#include "BackgroundSlicingProcess.hpp"
#include "ProgressStatusBar.hpp"
2018-12-11 10:33:11 +01:00
#include "PrintHostDialogs.hpp"
2019-03-22 15:45:51 +01:00
#include "ConfigWizard.hpp"
2018-11-26 14:41:58 +01:00
#include "../Utils/ASCIIFolding.hpp"
2018-12-11 10:33:11 +01:00
#include "../Utils/PrintHost.hpp"
2018-11-07 13:40:24 +01:00
#include "../Utils/FixModelByWin10.hpp"
2019-07-02 16:42:23 +02:00
#include "../Utils/UndoRedo.hpp"
2019-11-27 13:30:45 +01:00
#include "RemovableDriveManager.hpp"
2020-04-29 10:50:28 +02:00
#include "InstanceCheck.hpp"
2020-03-02 12:45:04 +01:00
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
#endif // __APPLE__
2018-09-17 12:15:11 +02:00
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
2018-10-03 15:14:52 +02:00
#include "WipeTowerDialog.hpp"
2020-01-23 15:07:31 +01:00
#include "libslic3r/CustomGCode.hpp"
2018-09-17 12:15:11 +02:00
2018-10-04 11:12:55 +02:00
using boost :: optional ;
2018-09-17 12:15:11 +02:00
namespace fs = boost :: filesystem ;
using Slic3r :: _3DScene ;
using Slic3r :: Preset ;
2018-12-11 10:33:11 +01:00
using Slic3r :: PrintHostJob ;
2020-04-01 10:08:03 +02:00
using Slic3r :: GUI :: format_wxstr ;
2018-09-17 12:15:11 +02:00
2019-10-24 10:06:31 +02:00
static const std :: pair < unsigned int , unsigned int > THUMBNAIL_SIZE_3MF = { 256 , 256 };
2018-09-17 12:15:11 +02:00
namespace Slic3r {
namespace GUI {
2018-11-26 14:41:58 +01:00
wxDEFINE_EVENT ( EVT_SCHEDULE_BACKGROUND_PROCESS , SimpleEvent );
wxDEFINE_EVENT ( EVT_SLICING_UPDATE , SlicingStatusEvent );
wxDEFINE_EVENT ( EVT_SLICING_COMPLETED , wxCommandEvent );
wxDEFINE_EVENT ( EVT_PROCESS_COMPLETED , wxCommandEvent );
2018-09-17 12:15:11 +02:00
// Sidebar widgets
// struct InfoBox : public wxStaticBox
// {
// InfoBox(wxWindow *parent, const wxString &label) :
// wxStaticBox(parent, wxID_ANY, label)
// {
// SetFont(GUI::small_font().Bold());
// }
// };
class ObjectInfo : public wxStaticBoxSizer
{
public :
ObjectInfo ( wxWindow * parent );
2018-10-05 23:29:15 +02:00
wxStaticBitmap * manifold_warning_icon ;
2018-09-17 12:15:11 +02:00
wxStaticText * info_size ;
wxStaticText * info_volume ;
wxStaticText * info_facets ;
wxStaticText * info_materials ;
wxStaticText * info_manifold ;
2019-02-19 12:14:13 +01:00
wxStaticText * label_volume ;
wxStaticText * label_materials ;
2019-02-21 15:46:04 +01:00
std :: vector < wxStaticText *> sla_hidden_items ;
2019-02-19 12:14:13 +01:00
2018-11-01 12:33:56 +01:00
bool showing_manifold_warning_icon ;
void show_sizer ( bool show );
2019-04-25 01:45:00 +02:00
void msw_rescale ();
2018-09-17 12:15:11 +02:00
};
ObjectInfo :: ObjectInfo ( wxWindow * parent ) :
2020-04-01 09:48:56 +02:00
wxStaticBoxSizer ( new wxStaticBox ( parent , wxID_ANY , _L ( "Info" )), wxVERTICAL )
2018-09-17 12:15:11 +02:00
{
2018-10-01 15:09:31 +02:00
GetStaticBox () -> SetFont ( wxGetApp (). bold_font ());
2018-09-17 12:15:11 +02:00
2019-04-13 23:46:52 +02:00
auto * grid_sizer = new wxFlexGridSizer ( 4 , 5 , 15 );
2018-09-17 12:15:11 +02:00
grid_sizer -> SetFlexibleDirection ( wxHORIZONTAL );
2019-04-13 23:46:52 +02:00
// grid_sizer->AddGrowableCol(1, 1);
// grid_sizer->AddGrowableCol(3, 1);
2018-09-17 12:15:11 +02:00
auto init_info_label = [ parent , grid_sizer ]( wxStaticText ** info_label , wxString text_label ) {
2019-01-21 12:34:28 +01:00
auto * text = new wxStaticText ( parent , wxID_ANY , text_label + ":" );
2018-10-01 15:09:31 +02:00
text -> SetFont ( wxGetApp (). small_font ());
2018-09-17 12:15:11 +02:00
* info_label = new wxStaticText ( parent , wxID_ANY , "" );
2018-10-01 15:09:31 +02:00
( * info_label ) -> SetFont ( wxGetApp (). small_font ());
2018-09-17 12:15:11 +02:00
grid_sizer -> Add ( text , 0 );
grid_sizer -> Add ( * info_label , 0 );
2019-02-19 12:14:13 +01:00
return text ;
2018-09-17 12:15:11 +02:00
};
2020-04-01 09:48:56 +02:00
init_info_label ( & info_size , _L ( "Size" ));
label_volume = init_info_label ( & info_volume , _L ( "Volume" ));
init_info_label ( & info_facets , _L ( "Facets" ));
label_materials = init_info_label ( & info_materials , _L ( "Materials" ));
2018-11-01 12:33:56 +01:00
Add ( grid_sizer , 0 , wxEXPAND );
2018-09-17 12:15:11 +02:00
2020-04-01 09:48:56 +02:00
auto * info_manifold_text = new wxStaticText ( parent , wxID_ANY , _L ( "Manifold" ) + ":" );
2018-10-01 15:09:31 +02:00
info_manifold_text -> SetFont ( wxGetApp (). small_font ());
2018-09-17 12:15:11 +02:00
info_manifold = new wxStaticText ( parent , wxID_ANY , "" );
2018-10-01 15:09:31 +02:00
info_manifold -> SetFont ( wxGetApp (). small_font ());
2020-01-31 16:50:11 +01:00
manifold_warning_icon = new wxStaticBitmap ( parent , wxID_ANY , create_scaled_bitmap ( "exclamation" ));
2018-09-17 12:15:11 +02:00
auto * sizer_manifold = new wxBoxSizer ( wxHORIZONTAL );
sizer_manifold -> Add ( info_manifold_text , 0 );
sizer_manifold -> Add ( manifold_warning_icon , 0 , wxLEFT , 2 );
sizer_manifold -> Add ( info_manifold , 0 , wxLEFT , 2 );
2018-11-01 12:33:56 +01:00
Add ( sizer_manifold , 0 , wxEXPAND | wxTOP , 4 );
2019-02-19 12:14:13 +01:00
2019-02-21 15:46:04 +01:00
sla_hidden_items = { label_volume , info_volume , label_materials , info_materials };
2018-11-01 12:33:56 +01:00
}
2018-09-17 12:15:11 +02:00
2018-11-01 12:33:56 +01:00
void ObjectInfo :: show_sizer ( bool show )
{
Show ( show );
if ( show )
manifold_warning_icon -> Show ( showing_manifold_warning_icon && show );
2018-09-17 12:15:11 +02:00
}
2019-04-25 01:45:00 +02:00
void ObjectInfo :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2020-01-31 16:50:11 +01:00
manifold_warning_icon -> SetBitmap ( create_scaled_bitmap ( "exclamation" ));
2019-04-13 23:46:52 +02:00
}
2019-11-05 11:37:40 +01:00
enum SlicedInfoIdx
2018-10-31 12:26:57 +01:00
{
siFilament_m ,
siFilament_mm3 ,
siFilament_g ,
2019-02-12 16:34:42 +01:00
siMateril_unit ,
2018-10-31 12:26:57 +01:00
siCost ,
2018-11-20 10:33:54 +01:00
siEstimatedTime ,
2018-10-31 12:26:57 +01:00
siWTNumbetOfToolchanges ,
siCount
};
2018-09-17 12:15:11 +02:00
class SlicedInfo : public wxStaticBoxSizer
{
public :
SlicedInfo ( wxWindow * parent );
2019-11-05 11:37:40 +01:00
void SetTextAndShow ( SlicedInfoIdx idx , const wxString & text , const wxString & new_label = "" );
2018-09-17 12:15:11 +02:00
private :
2018-10-31 12:26:57 +01:00
std :: vector < std :: pair < wxStaticText * , wxStaticText *>> info_vec ;
2018-09-17 12:15:11 +02:00
};
SlicedInfo :: SlicedInfo ( wxWindow * parent ) :
2020-04-01 09:48:56 +02:00
wxStaticBoxSizer ( new wxStaticBox ( parent , wxID_ANY , _L ( "Sliced Info" )), wxVERTICAL )
2018-09-17 12:15:11 +02:00
{
2018-10-01 15:09:31 +02:00
GetStaticBox () -> SetFont ( wxGetApp (). bold_font ());
2018-09-17 12:15:11 +02:00
2018-11-20 10:33:54 +01:00
auto * grid_sizer = new wxFlexGridSizer ( 2 , 5 , 15 );
grid_sizer -> SetFlexibleDirection ( wxVERTICAL );
2018-09-17 12:15:11 +02:00
2018-10-31 12:26:57 +01:00
info_vec . reserve ( siCount );
auto init_info_label = [ this , parent , grid_sizer ]( wxString text_label ) {
2018-09-17 12:15:11 +02:00
auto * text = new wxStaticText ( parent , wxID_ANY , text_label );
2018-10-01 15:09:31 +02:00
text -> SetFont ( wxGetApp (). small_font ());
2018-10-31 12:26:57 +01:00
auto info_label = new wxStaticText ( parent , wxID_ANY , "N/A" );
2018-10-01 15:09:31 +02:00
info_label -> SetFont ( wxGetApp (). small_font ());
2018-09-17 12:15:11 +02:00
grid_sizer -> Add ( text , 0 );
grid_sizer -> Add ( info_label , 0 );
2018-10-31 12:26:57 +01:00
info_vec . push_back ( std :: pair < wxStaticText * , wxStaticText *> ( text , info_label ));
2018-09-17 12:15:11 +02:00
};
2020-04-01 09:48:56 +02:00
init_info_label ( _L ( "Used Filament (m)" ));
init_info_label ( _L ( "Used Filament (mm³)" ));
init_info_label ( _L ( "Used Filament (g)" ));
init_info_label ( _L ( "Used Material (unit)" ));
init_info_label ( _L ( "Cost (money)" ));
init_info_label ( _L ( "Estimated printing time" ));
init_info_label ( _L ( "Number of tool changes" ));
2018-09-17 12:15:11 +02:00
Add ( grid_sizer , 0 , wxEXPAND );
2018-10-31 12:26:57 +01:00
this -> Show ( false );
}
2019-11-05 11:37:40 +01:00
void SlicedInfo :: SetTextAndShow ( SlicedInfoIdx idx , const wxString & text , const wxString & new_label /*=""*/ )
2018-10-31 12:26:57 +01:00
{
const bool show = text != "N/A" ;
if ( show )
info_vec [ idx ]. second -> SetLabelText ( text );
2018-11-20 10:33:54 +01:00
if ( ! new_label . IsEmpty ())
info_vec [ idx ]. first -> SetLabelText ( new_label );
2018-10-31 12:26:57 +01:00
info_vec [ idx ]. first -> Show ( show );
info_vec [ idx ]. second -> Show ( show );
2018-09-17 12:15:11 +02:00
}
PresetComboBox :: PresetComboBox ( wxWindow * parent , Preset :: Type preset_type ) :
2020-01-31 16:50:11 +01:00
PresetBitmapComboBox ( parent , wxSize ( 15 * wxGetApp (). em_unit (), - 1 )),
2018-09-17 12:15:11 +02:00
preset_type ( preset_type ),
2019-04-23 16:33:06 +02:00
last_selected ( wxNOT_FOUND ),
m_em_unit ( wxGetApp (). em_unit ())
2018-09-17 12:15:11 +02:00
{
2019-07-10 15:55:53 +02:00
SetFont ( wxGetApp (). normal_font ());
2020-02-27 11:44:01 +01:00
#ifdef _WIN32
// Workaround for ignoring CBN_EDITCHANGE events, which are processed after the content of the combo box changes, so that
// the index of the item inside CBN_EDITCHANGE may no more be valid.
EnableTextChangedEvents ( false );
#endif /* _WIN32 */
2018-09-17 12:15:11 +02:00
Bind ( wxEVT_COMBOBOX , [ this ]( wxCommandEvent & evt ) {
2020-03-23 22:47:35 +01:00
auto selected_item = evt . GetSelection ();
2018-09-17 12:15:11 +02:00
auto marker = reinterpret_cast < Marker > ( this -> GetClientData ( selected_item ));
2019-06-04 18:01:41 +02:00
if ( marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX ) {
2018-09-17 12:15:11 +02:00
this -> SetSelection ( this -> last_selected );
evt . StopPropagation ();
2019-06-04 18:01:41 +02:00
if ( marker >= LABEL_ITEM_WIZARD_PRINTERS ) {
ConfigWizard :: StartPage sp = ConfigWizard :: SP_WELCOME ;
switch ( marker ) {
case LABEL_ITEM_WIZARD_PRINTERS : sp = ConfigWizard :: SP_PRINTERS ; break ;
case LABEL_ITEM_WIZARD_FILAMENTS : sp = ConfigWizard :: SP_FILAMENTS ; break ;
case LABEL_ITEM_WIZARD_MATERIALS : sp = ConfigWizard :: SP_MATERIALS ; break ;
}
wxTheApp -> CallAfter ([ sp ]() { wxGetApp (). run_wizard ( ConfigWizard :: RR_USER , sp ); });
}
2019-08-06 18:16:02 +02:00
} else if ( this -> last_selected != selected_item ||
2018-12-21 10:58:00 +01:00
wxGetApp (). get_tab ( this -> preset_type ) -> get_presets () -> current_is_dirty () ) {
2018-09-17 12:15:11 +02:00
this -> last_selected = selected_item ;
evt . SetInt ( this -> preset_type );
2018-10-09 12:41:05 +02:00
evt . Skip ();
2018-09-17 12:15:11 +02:00
} else {
evt . StopPropagation ();
}
});
2019-08-06 18:16:02 +02:00
2018-10-10 13:53:45 +02:00
if ( preset_type == Slic3r :: Preset :: TYPE_FILAMENT )
{
Bind ( wxEVT_LEFT_DOWN , [ this ]( wxMouseEvent & event ) {
2020-03-06 14:22:14 +01:00
PresetBundle * preset_bundle = wxGetApp (). preset_bundle ;
const Preset * selected_preset = preset_bundle -> filaments . find_preset ( preset_bundle -> filament_presets [ extruder_idx ]);
// Wide icons are shown if the currently selected preset is not compatible with the current printer,
// and red flag is drown in front of the selected preset.
bool wide_icons = selected_preset != nullptr && ! selected_preset -> is_compatible ;
2019-05-23 10:55:57 +02:00
float scale = m_em_unit * 0.1f ;
2020-03-06 14:22:14 +01:00
int shifl_Left = wide_icons ? int ( scale * 16 + 0.5 ) : 0 ;
2019-05-23 10:55:57 +02:00
#if defined(wxBITMAPCOMBOBOX_OWNERDRAWN_BASED)
2020-03-06 14:22:14 +01:00
shifl_Left += int ( scale * 4 + 0.5f ); // IMAGE_SPACING_RIGHT = 4 for wxBitmapComboBox -> Space left of image
2019-05-23 10:55:57 +02:00
#endif
2020-03-06 14:22:14 +01:00
int icon_right_pos = shifl_Left + int ( scale * ( 24 + 4 ) + 0.5 );
2019-05-23 10:55:57 +02:00
int mouse_pos = event . GetLogicalPosition ( wxClientDC ( this )). x ;
2020-03-06 14:22:14 +01:00
if ( mouse_pos < shifl_Left || mouse_pos > icon_right_pos ) {
2018-10-10 13:53:45 +02:00
// Let the combo box process the mouse click.
event . Skip ();
return ;
}
2019-08-06 18:16:02 +02:00
2018-10-10 13:53:45 +02:00
// Swallow the mouse click and open the color picker.
2019-05-23 12:39:55 +02:00
// get current color
DynamicPrintConfig * cfg = wxGetApp (). get_tab ( Preset :: TYPE_PRINTER ) -> get_config ();
auto colors = static_cast < ConfigOptionStrings *> ( cfg -> option ( "extruder_colour" ) -> clone ());
wxColour clr ( colors -> values [ extruder_idx ]);
if ( ! clr . IsOk ())
2019-08-20 15:26:57 +02:00
clr = wxColour ( 0 , 0 , 0 ); // Don't set alfa to transparence
2019-05-23 12:39:55 +02:00
2018-10-10 13:53:45 +02:00
auto data = new wxColourData ();
data -> SetChooseFull ( 1 );
2019-05-23 12:39:55 +02:00
data -> SetColour ( clr );
2018-10-10 13:53:45 +02:00
2019-08-15 12:52:56 +02:00
wxColourDialog dialog ( this , data );
dialog . CenterOnParent ();
if ( dialog . ShowModal () == wxID_OK )
2019-05-23 12:39:55 +02:00
{
2020-02-21 11:58:18 +01:00
colors -> values [ extruder_idx ] = dialog . GetColourData (). GetColour (). GetAsString ( wxC2S_HTML_SYNTAX ). ToStdString ();
2018-10-10 13:53:45 +02:00
2019-08-06 18:16:02 +02:00
DynamicPrintConfig cfg_new = * cfg ;
2019-05-23 12:39:55 +02:00
cfg_new . set_key_value ( "extruder_colour" , colors );
2018-10-10 13:53:45 +02:00
2019-05-23 12:39:55 +02:00
wxGetApp (). get_tab ( Preset :: TYPE_PRINTER ) -> load_config ( cfg_new );
2020-03-06 14:22:14 +01:00
preset_bundle -> update_plater_filament_ui ( extruder_idx , this );
2019-05-23 12:39:55 +02:00
wxGetApp (). plater () -> on_config_change ( cfg_new );
2018-10-10 13:53:45 +02:00
}
});
}
2019-03-18 15:10:40 +01:00
2019-04-25 01:45:00 +02:00
edit_btn = new ScalableButton ( parent , wxID_ANY , "cog" );
2020-04-01 09:48:56 +02:00
edit_btn -> SetToolTip ( _L ( "Click to edit preset" ));
2019-03-18 15:10:40 +01:00
edit_btn -> Bind ( wxEVT_BUTTON , ([ preset_type , this ]( wxCommandEvent )
{
Tab * tab = wxGetApp (). get_tab ( preset_type );
if ( ! tab )
return ;
int page_id = wxGetApp (). tab_panel () -> FindPage ( tab );
if ( page_id == wxNOT_FOUND )
return ;
2020-05-04 22:30:38 +02:00
wxGetApp (). tab_panel () -> SetSelection ( page_id );
2019-03-18 15:10:40 +01:00
2020-04-23 12:41:38 +02:00
// Switch to Settings NotePad
2020-05-04 22:30:38 +02:00
wxGetApp (). mainframe -> select_tab ();
2020-04-23 12:41:38 +02:00
2019-08-06 18:16:02 +02:00
/* In a case of a multi-material printing, for editing another Filament Preset
* it's needed to select this preset for the "Filament settings" Tab
2019-03-19 14:36:32 +01:00
*/
2019-08-06 18:16:02 +02:00
if ( preset_type == Preset :: TYPE_FILAMENT && wxGetApp (). extruders_edited_cnt () > 1 )
2019-03-19 14:36:32 +01:00
{
const std :: string & selected_preset = GetString ( GetSelection ()). ToUTF8 (). data ();
2019-03-18 15:10:40 +01:00
2019-08-06 18:16:02 +02:00
// Call select_preset() only if there is new preset and not just modified
2019-03-20 10:14:49 +01:00
if ( ! boost :: algorithm :: ends_with ( selected_preset , Preset :: suffix_modified ()) )
2019-11-25 15:57:36 +01:00
{
const std :: string & preset_name = wxGetApp (). preset_bundle -> filaments . get_preset_name_by_alias ( selected_preset );
tab -> select_preset ( /*selected_preset*/ preset_name );
}
2019-03-19 14:36:32 +01:00
}
2019-03-18 15:10:40 +01:00
}));
2018-09-17 12:15:11 +02:00
}
2019-03-18 15:10:40 +01:00
PresetComboBox ::~ PresetComboBox ()
{
if ( edit_btn )
edit_btn -> Destroy ();
}
2018-09-17 12:15:11 +02:00
2019-03-22 15:45:51 +01:00
void PresetComboBox :: set_label_marker ( int item , LabelItemType label_item_type )
2018-10-09 12:41:05 +02:00
{
2019-03-22 15:45:51 +01:00
this -> SetClientData ( item , ( void * ) label_item_type );
2018-10-09 12:41:05 +02:00
}
2020-03-23 22:47:35 +01:00
void PresetComboBox :: check_selection ( int selection )
2018-12-03 15:16:33 +01:00
{
2020-03-23 22:47:35 +01:00
this -> last_selected = selection ;
2018-12-03 15:16:33 +01:00
}
2019-04-25 01:45:00 +02:00
void PresetComboBox :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-04-23 16:33:06 +02:00
m_em_unit = wxGetApp (). em_unit ();
2019-04-25 01:45:00 +02:00
edit_btn -> msw_rescale ();
2019-04-13 23:46:52 +02:00
}
2018-10-04 16:43:10 +02:00
// Frequently changed parameters
class FreqChangedParams : public OG_Settings
{
double m_brim_width = 0.0 ;
wxButton * m_wiping_dialog_button { nullptr };
2019-01-15 09:31:53 +01:00
wxSizer * m_sizer { nullptr };
std :: shared_ptr < ConfigOptionsGroup > m_og_sla ;
2019-07-10 17:50:24 +02:00
std :: vector < ScalableButton *> m_empty_buttons ;
2018-10-04 16:43:10 +02:00
public :
2019-07-10 15:55:53 +02:00
FreqChangedParams ( wxWindow * parent );
2018-10-04 16:43:10 +02:00
~ FreqChangedParams () {}
wxButton * get_wiping_dialog_button () { return m_wiping_dialog_button ; }
2019-01-15 09:31:53 +01:00
wxSizer * get_sizer () override ;
ConfigOptionsGroup * get_og ( const bool is_fff );
void Show ( const bool is_fff );
2019-07-10 17:50:24 +02:00
void msw_rescale ();
2018-10-04 16:43:10 +02:00
};
2019-07-10 17:50:24 +02:00
void FreqChangedParams :: msw_rescale ()
{
m_og -> msw_rescale ();
m_og_sla -> msw_rescale ();
for ( auto btn : m_empty_buttons )
btn -> msw_rescale ();
}
2019-07-10 15:55:53 +02:00
FreqChangedParams :: FreqChangedParams ( wxWindow * parent ) :
2018-10-04 16:43:10 +02:00
OG_Settings ( parent , false )
{
DynamicPrintConfig * config = & wxGetApp (). preset_bundle -> prints . get_edited_preset (). config ;
2019-01-15 09:31:53 +01:00
// Frequently changed parameters for FFF_technology
2018-10-04 16:43:10 +02:00
m_og -> set_config ( config );
2019-07-10 15:55:53 +02:00
m_og -> hide_labels ();
2018-10-04 16:43:10 +02:00
2018-10-31 12:56:08 +01:00
m_og -> m_on_change = [ config , this ]( t_config_option_key opt_key , boost :: any value ) {
2019-01-15 09:31:53 +01:00
Tab * tab_print = wxGetApp (). get_tab ( Preset :: TYPE_PRINT );
if ( ! tab_print ) return ;
2018-10-04 16:43:10 +02:00
2018-10-31 12:56:08 +01:00
if ( opt_key == "fill_density" ) {
2018-10-04 16:43:10 +02:00
value = m_og -> get_config_value ( * config , opt_key );
tab_print -> set_value ( opt_key , value );
tab_print -> update ();
}
else {
DynamicPrintConfig new_conf = * config ;
2018-10-31 12:56:08 +01:00
if ( opt_key == "brim" ) {
2018-10-04 16:43:10 +02:00
double new_val ;
double brim_width = config -> opt_float ( "brim_width" );
if ( boost :: any_cast < bool > ( value ) == true )
{
2018-12-11 16:33:43 +01:00
new_val = m_brim_width == 0.0 ? 5 :
2018-10-04 16:43:10 +02:00
m_brim_width < 0.0 ? m_brim_width * ( - 1 ) :
m_brim_width ;
}
2018-12-11 16:33:43 +01:00
else {
2018-10-04 16:43:10 +02:00
m_brim_width = brim_width * ( - 1 );
new_val = 0 ;
}
new_conf . set_key_value ( "brim_width" , new ConfigOptionFloat ( new_val ));
}
2019-05-03 18:01:39 +02:00
else {
assert ( opt_key == "support" );
2018-10-04 16:43:10 +02:00
const wxString & selection = boost :: any_cast < wxString > ( value );
2019-05-03 18:01:39 +02:00
PrinterTechnology printer_technology = wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ();
2018-10-04 16:43:10 +02:00
auto support_material = selection == _ ( "None" ) ? false : true ;
new_conf . set_key_value ( "support_material" , new ConfigOptionBool ( support_material ));
2019-05-03 18:01:39 +02:00
if ( selection == _ ( "Everywhere" )) {
2018-10-04 16:43:10 +02:00
new_conf . set_key_value ( "support_material_buildplate_only" , new ConfigOptionBool ( false ));
2019-05-03 18:01:39 +02:00
if ( printer_technology == ptFFF )
new_conf . set_key_value ( "support_material_auto" , new ConfigOptionBool ( true ));
} else if ( selection == _ ( "Support on build plate only" )) {
2018-10-04 16:43:10 +02:00
new_conf . set_key_value ( "support_material_buildplate_only" , new ConfigOptionBool ( true ));
2019-05-03 18:01:39 +02:00
if ( printer_technology == ptFFF )
new_conf . set_key_value ( "support_material_auto" , new ConfigOptionBool ( true ));
} else if ( selection == _ ( "For support enforcers only" )) {
assert ( printer_technology == ptFFF );
new_conf . set_key_value ( "support_material_buildplate_only" , new ConfigOptionBool ( false ));
new_conf . set_key_value ( "support_material_auto" , new ConfigOptionBool ( false ));
}
2018-10-04 16:43:10 +02:00
}
tab_print -> load_config ( new_conf );
}
tab_print -> update_dirty ();
};
2019-08-06 18:16:02 +02:00
2019-02-07 14:44:05 +01:00
Line line = Line { "" , "" };
2018-10-04 16:43:10 +02:00
2019-03-11 16:00:13 +01:00
ConfigOptionDef support_def ;
support_def . label = L ( "Supports" );
support_def . type = coStrings ;
support_def . gui_type = "select_open" ;
support_def . tooltip = L ( "Select what kind of support do you need" );
support_def . enum_labels . push_back ( L ( "None" ));
support_def . enum_labels . push_back ( L ( "Support on build plate only" ));
2019-05-03 18:01:39 +02:00
support_def . enum_labels . push_back ( L ( "For support enforcers only" ));
2019-03-11 16:00:13 +01:00
support_def . enum_labels . push_back ( L ( "Everywhere" ));
2019-05-03 18:01:39 +02:00
support_def . set_default_value ( new ConfigOptionStrings { "None" });
2019-03-11 16:00:13 +01:00
Option option = Option ( support_def , "support" );
2018-10-04 16:43:10 +02:00
option . opt . full_width = true ;
2019-02-07 14:44:05 +01:00
line . append_option ( option );
2019-07-10 17:50:24 +02:00
/* Not a best solution, but
* Temporary workaround for right border alignment
*/
auto empty_widget = [ this ] ( wxWindow * parent ) {
auto sizer = new wxBoxSizer ( wxHORIZONTAL );
auto btn = new ScalableButton ( parent , wxID_ANY , "mirroring_transparent.png" , wxEmptyString ,
wxDefaultSize , wxDefaultPosition , wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW );
sizer -> Add ( btn , 0 , wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT , int ( 0.3 * wxGetApp (). em_unit ()));
m_empty_buttons . push_back ( btn );
return sizer ;
};
line . append_widget ( empty_widget );
2019-02-07 14:44:05 +01:00
m_og -> append_line ( line );
2019-08-06 18:16:02 +02:00
2019-02-07 14:44:05 +01:00
line = Line { "" , "" };
option = m_og -> get_option ( "fill_density" );
option . opt . label = L ( "Infill" );
2019-05-13 18:21:17 +02:00
option . opt . width = 7 /*6*/ ;
2019-07-13 10:37:21 +02:00
option . opt . sidetext = " " ;
2019-02-07 14:44:05 +01:00
line . append_option ( option );
2018-10-04 16:43:10 +02:00
m_brim_width = config -> opt_float ( "brim_width" );
2019-03-11 16:00:13 +01:00
ConfigOptionDef def ;
2018-10-04 16:43:10 +02:00
def . label = L ( "Brim" );
def . type = coBool ;
def . tooltip = L ( "This flag enables the brim that will be printed around each object on the first layer." );
def . gui_type = "" ;
2019-05-03 18:01:39 +02:00
def . set_default_value ( new ConfigOptionBool { m_brim_width > 0.0 ? true : false });
2018-10-04 16:43:10 +02:00
option = Option ( def , "brim" );
2019-07-13 10:37:21 +02:00
option . opt . sidetext = "" ;
2019-02-07 14:44:05 +01:00
line . append_option ( option );
2018-10-04 16:43:10 +02:00
2019-04-26 10:16:08 +02:00
auto wiping_dialog_btn = [ this ]( wxWindow * parent ) {
2020-04-01 09:48:56 +02:00
m_wiping_dialog_button = new wxButton ( parent , wxID_ANY , _L ( "Purging volumes" ) + dots , wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT );
2019-07-10 15:55:53 +02:00
m_wiping_dialog_button -> SetFont ( wxGetApp (). normal_font ());
2018-10-04 16:43:10 +02:00
auto sizer = new wxBoxSizer ( wxHORIZONTAL );
2019-07-10 17:50:24 +02:00
sizer -> Add ( m_wiping_dialog_button , 0 , wxALIGN_CENTER_VERTICAL );
2018-10-04 16:43:10 +02:00
m_wiping_dialog_button -> Bind ( wxEVT_BUTTON , ([ parent ]( wxCommandEvent & e )
{
2019-04-26 10:16:08 +02:00
auto & project_config = wxGetApp (). preset_bundle -> project_config ;
const std :: vector < double > & init_matrix = ( project_config . option < ConfigOptionFloats > ( "wiping_volumes_matrix" )) -> values ;
const std :: vector < double > & init_extruders = ( project_config . option < ConfigOptionFloats > ( "wiping_volumes_extruders" )) -> values ;
2018-10-04 16:43:10 +02:00
2019-09-30 14:03:50 +02:00
const std :: vector < std :: string > extruder_colours = wxGetApp (). plater () -> get_extruder_colors_from_plater_config ();
2019-04-26 10:16:08 +02:00
WipingDialog dlg ( parent , cast < float > ( init_matrix ), cast < float > ( init_extruders ), extruder_colours );
2018-10-04 16:43:10 +02:00
if ( dlg . ShowModal () == wxID_OK ) {
std :: vector < float > matrix = dlg . get_matrix ();
std :: vector < float > extruders = dlg . get_extruders ();
2019-04-26 10:16:08 +02:00
( project_config . option < ConfigOptionFloats > ( "wiping_volumes_matrix" )) -> values = std :: vector < double > ( matrix . begin (), matrix . end ());
( project_config . option < ConfigOptionFloats > ( "wiping_volumes_extruders" )) -> values = std :: vector < double > ( extruders . begin (), extruders . end ());
2019-02-07 14:44:05 +01:00
wxPostEvent ( parent , SimpleEvent ( EVT_SCHEDULE_BACKGROUND_PROCESS , parent ));
2018-10-04 16:43:10 +02:00
}
}));
2019-07-10 17:50:24 +02:00
2019-08-06 18:16:02 +02:00
auto btn = new ScalableButton ( parent , wxID_ANY , "mirroring_transparent.png" , wxEmptyString ,
2019-07-10 17:50:24 +02:00
wxDefaultSize , wxDefaultPosition , wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW );
sizer -> Add ( btn , 0 , wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT ,
int ( 0.3 * wxGetApp (). em_unit ()));
m_empty_buttons . push_back ( btn );
2018-10-04 16:43:10 +02:00
return sizer ;
};
2019-02-07 14:44:05 +01:00
line . append_widget ( wiping_dialog_btn );
2019-01-15 09:31:53 +01:00
2019-02-07 14:44:05 +01:00
m_og -> append_line ( line );
2019-01-15 09:31:53 +01:00
2019-03-11 16:00:13 +01:00
2019-01-15 09:31:53 +01:00
// Frequently changed parameters for SLA_technology
m_og_sla = std :: make_shared < ConfigOptionsGroup > ( parent , "" );
2019-07-10 15:55:53 +02:00
m_og_sla -> hide_labels ();
2019-01-15 09:31:53 +01:00
DynamicPrintConfig * config_sla = & wxGetApp (). preset_bundle -> sla_prints . get_edited_preset (). config ;
m_og_sla -> set_config ( config_sla );
m_og_sla -> m_on_change = [ config_sla , this ]( t_config_option_key opt_key , boost :: any value ) {
Tab * tab = wxGetApp (). get_tab ( Preset :: TYPE_SLA_PRINT );
if ( ! tab ) return ;
2019-08-06 18:16:02 +02:00
DynamicPrintConfig new_conf = * config_sla ;
if ( opt_key == "pad" ) {
const wxString & selection = boost :: any_cast < wxString > ( value );
const bool pad_enable = selection == _ ( "None" ) ? false : true ;
new_conf . set_key_value ( "pad_enable" , new ConfigOptionBool ( pad_enable ));
if ( selection == _ ( "Below object" ))
2019-08-28 11:32:49 +02:00
new_conf . set_key_value ( "pad_around_object" , new ConfigOptionBool ( false ));
2019-08-06 18:16:02 +02:00
else if ( selection == _ ( "Around object" ))
2019-08-28 11:32:49 +02:00
new_conf . set_key_value ( "pad_around_object" , new ConfigOptionBool ( true ));
2019-03-11 16:00:13 +01:00
}
2019-05-03 18:01:39 +02:00
else
2019-03-11 16:00:13 +01:00
{
2019-05-03 18:01:39 +02:00
assert ( opt_key == "support" );
2019-03-11 16:00:13 +01:00
const wxString & selection = boost :: any_cast < wxString > ( value );
const bool supports_enable = selection == _ ( "None" ) ? false : true ;
new_conf . set_key_value ( "supports_enable" , new ConfigOptionBool ( supports_enable ));
if ( selection == _ ( "Everywhere" ))
new_conf . set_key_value ( "support_buildplate_only" , new ConfigOptionBool ( false ));
else if ( selection == _ ( "Support on build plate only" ))
new_conf . set_key_value ( "support_buildplate_only" , new ConfigOptionBool ( true ));
}
2019-01-15 09:31:53 +01:00
2019-08-06 18:16:02 +02:00
tab -> load_config ( new_conf );
2019-01-15 09:31:53 +01:00
tab -> update_dirty ();
2019-03-11 16:00:13 +01:00
};
2019-02-07 14:44:05 +01:00
line = Line { "" , "" };
2019-05-03 18:01:39 +02:00
ConfigOptionDef support_def_sla = support_def ;
support_def_sla . set_default_value ( new ConfigOptionStrings { "None" });
assert ( support_def_sla . enum_labels [ 2 ] == L ( "For support enforcers only" ));
support_def_sla . enum_labels . erase ( support_def_sla . enum_labels . begin () + 2 );
option = Option ( support_def_sla , "support" );
2019-03-11 16:00:13 +01:00
option . opt . full_width = true ;
2019-08-06 18:16:02 +02:00
line . append_option ( option );
2019-07-10 17:50:24 +02:00
line . append_widget ( empty_widget );
2019-03-11 16:00:13 +01:00
m_og_sla -> append_line ( line );
line = Line { "" , "" };
2019-02-07 14:44:05 +01:00
2019-08-06 18:16:02 +02:00
ConfigOptionDef pad_def ;
pad_def . label = L ( "Pad" );
pad_def . type = coStrings ;
pad_def . gui_type = "select_open" ;
pad_def . tooltip = L ( "Select what kind of pad do you need" );
pad_def . enum_labels . push_back ( L ( "None" ));
pad_def . enum_labels . push_back ( L ( "Below object" ));
pad_def . enum_labels . push_back ( L ( "Around object" ));
pad_def . set_default_value ( new ConfigOptionStrings { "Below object" });
option = Option ( pad_def , "pad" );
option . opt . full_width = true ;
2019-02-07 14:44:05 +01:00
line . append_option ( option );
2019-08-06 18:16:02 +02:00
line . append_widget ( empty_widget );
2019-02-07 14:44:05 +01:00
m_og_sla -> append_line ( line );
2019-01-15 09:31:53 +01:00
m_sizer = new wxBoxSizer ( wxVERTICAL );
m_sizer -> Add ( m_og -> sizer , 0 , wxEXPAND );
2019-02-07 14:44:05 +01:00
m_sizer -> Add ( m_og_sla -> sizer , 0 , wxEXPAND );
2018-10-04 16:43:10 +02:00
}
2019-01-15 09:31:53 +01:00
wxSizer * FreqChangedParams :: get_sizer ()
2018-10-24 12:57:23 +02:00
{
2019-01-15 09:31:53 +01:00
return m_sizer ;
}
void FreqChangedParams :: Show ( const bool is_fff )
{
const bool is_wdb_shown = m_wiping_dialog_button -> IsShown ();
m_og -> Show ( is_fff );
m_og_sla -> Show ( ! is_fff );
2018-10-24 12:57:23 +02:00
2019-08-06 18:16:02 +02:00
// correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden
2019-01-15 09:31:53 +01:00
if ( is_fff && ! is_wdb_shown )
2018-10-24 12:57:23 +02:00
m_wiping_dialog_button -> Hide ();
}
2019-01-15 09:31:53 +01:00
ConfigOptionsGroup * FreqChangedParams :: get_og ( const bool is_fff )
{
return is_fff ? m_og . get () : m_og_sla . get ();
}
2018-09-17 12:15:11 +02:00
// Sidebar / private
2019-03-01 11:00:34 +01:00
enum class ActionButtonType : int {
abReslice ,
abExport ,
abSendGCode
};
2018-09-17 12:15:11 +02:00
struct Sidebar :: priv
{
2018-10-04 11:12:55 +02:00
Plater * plater ;
2018-09-17 12:15:11 +02:00
wxScrolledWindow * scrolled ;
2019-03-13 13:13:18 +01:00
wxPanel * presets_panel ; // Used for MSW better layouts
2018-09-17 12:15:11 +02:00
2019-04-25 01:45:00 +02:00
ModeSizer * mode_sizer ;
2018-09-17 12:15:11 +02:00
wxFlexGridSizer * sizer_presets ;
PresetComboBox * combo_print ;
std :: vector < PresetComboBox *> combos_filament ;
wxBoxSizer * sizer_filaments ;
2018-11-16 17:36:23 +01:00
PresetComboBox * combo_sla_print ;
2018-09-17 12:15:11 +02:00
PresetComboBox * combo_sla_material ;
PresetComboBox * combo_printer ;
wxBoxSizer * sizer_params ;
2019-06-27 15:16:36 +02:00
FreqChangedParams * frequently_changed_parameters { nullptr };
ObjectList * object_list { nullptr };
ObjectManipulation * object_manipulation { nullptr };
ObjectSettings * object_settings { nullptr };
2019-06-29 12:38:03 +02:00
ObjectLayers * object_layers { nullptr };
2018-09-17 12:15:11 +02:00
ObjectInfo * object_info ;
SlicedInfo * sliced_info ;
wxButton * btn_export_gcode ;
wxButton * btn_reslice ;
2019-12-13 13:23:55 +01:00
ScalableButton * btn_send_gcode ;
ScalableButton * btn_remove_device ;
2020-01-10 16:25:32 +01:00
ScalableButton * btn_export_gcode_removable ; //exports to removable drives (appears only if removable drive is connected)
2018-10-04 11:12:55 +02:00
2020-03-28 22:07:56 +01:00
bool is_collapsed { false };
2020-04-13 17:55:38 +02:00
Search :: OptionsSearcher searcher ;
2020-03-28 19:39:24 +01:00
2018-10-04 11:12:55 +02:00
priv ( Plater * plater ) : plater ( plater ) {}
2019-06-27 15:16:36 +02:00
~ priv ();
2018-10-09 08:30:48 +02:00
2018-10-10 13:53:45 +02:00
void show_preset_comboboxes ();
2018-09-17 12:15:11 +02:00
};
2019-06-27 15:16:36 +02:00
Sidebar :: priv ::~ priv ()
{
if ( object_manipulation != nullptr )
delete object_manipulation ;
if ( object_settings != nullptr )
delete object_settings ;
if ( frequently_changed_parameters != nullptr )
delete frequently_changed_parameters ;
2019-06-29 12:38:03 +02:00
if ( object_layers != nullptr )
delete object_layers ;
2019-06-27 15:16:36 +02:00
}
2018-10-10 13:53:45 +02:00
void Sidebar :: priv :: show_preset_comboboxes ()
{
2019-02-22 09:38:56 +01:00
const bool showSLA = wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptSLA ;
2018-10-10 13:53:45 +02:00
2018-11-15 16:22:25 +01:00
for ( size_t i = 0 ; i < 4 ; ++ i )
sizer_presets -> Show ( i , ! showSLA );
2018-10-10 13:53:45 +02:00
2018-11-16 17:36:23 +01:00
for ( size_t i = 4 ; i < 8 ; ++ i ) {
2018-11-09 18:39:07 +01:00
if ( sizer_presets -> IsShown ( i ) != showSLA )
sizer_presets -> Show ( i , showSLA );
}
2018-10-10 13:53:45 +02:00
2018-11-15 16:22:25 +01:00
frequently_changed_parameters -> Show ( ! showSLA );
2018-10-10 13:53:45 +02:00
2018-11-15 11:15:24 +01:00
scrolled -> GetParent () -> Layout ();
scrolled -> Refresh ();
2018-10-10 13:53:45 +02:00
}
2018-09-17 12:15:11 +02:00
// Sidebar / public
2018-10-04 11:12:55 +02:00
Sidebar :: Sidebar ( Plater * parent )
2019-04-13 23:46:52 +02:00
: wxPanel ( parent , wxID_ANY , wxDefaultPosition , wxSize ( 40 * wxGetApp (). em_unit (), - 1 )), p ( new priv ( parent ))
2018-09-17 12:15:11 +02:00
{
2019-08-23 16:04:58 +02:00
p -> scrolled = new wxScrolledWindow ( this );
p -> scrolled -> SetScrollbars ( 0 , 100 , 1 , 2 );
2018-11-09 18:39:07 +01:00
2020-04-23 12:41:38 +02:00
SetFont ( wxGetApp (). normal_font ());
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
2019-03-12 11:39:16 +01:00
2018-11-09 18:39:07 +01:00
// Sizer in the scrolled area
auto * scrolled_sizer = new wxBoxSizer ( wxVERTICAL );
p -> scrolled -> SetSizer ( scrolled_sizer );
2018-09-17 12:15:11 +02:00
2019-01-10 11:05:58 +01:00
// Sizer with buttons for mode changing
2019-07-25 16:23:32 +02:00
p -> mode_sizer = new ModeSizer ( p -> scrolled );
2019-01-10 11:05:58 +01:00
2018-09-17 12:15:11 +02:00
// The preset chooser
2019-02-07 14:44:05 +01:00
p -> sizer_presets = new wxFlexGridSizer ( 10 , 1 , 1 , 2 );
p -> sizer_presets -> AddGrowableCol ( 0 , 1 );
2018-10-10 13:53:45 +02:00
p -> sizer_presets -> SetFlexibleDirection ( wxBOTH );
2019-03-13 13:13:18 +01:00
bool is_msw = false ;
#ifdef __WINDOWS__
p -> scrolled -> SetDoubleBuffered ( true );
p -> presets_panel = new wxPanel ( p -> scrolled , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
p -> presets_panel -> SetSizer ( p -> sizer_presets );
is_msw = true ;
#else
2019-03-13 13:37:36 +01:00
p -> presets_panel = p -> scrolled ;
2019-03-13 13:13:18 +01:00
#endif //__WINDOWS__
2018-09-17 12:15:11 +02:00
p -> sizer_filaments = new wxBoxSizer ( wxVERTICAL );
auto init_combo = [ this ]( PresetComboBox ** combo , wxString label , Preset :: Type preset_type , bool filament ) {
2019-03-13 13:13:18 +01:00
auto * text = new wxStaticText ( p -> presets_panel , wxID_ANY , label + " :" );
2018-10-01 15:09:31 +02:00
text -> SetFont ( wxGetApp (). small_font ());
2019-03-13 13:13:18 +01:00
* combo = new PresetComboBox ( p -> presets_panel , preset_type );
2018-09-17 12:15:11 +02:00
2019-03-18 15:10:40 +01:00
auto combo_and_btn_sizer = new wxBoxSizer ( wxHORIZONTAL );
combo_and_btn_sizer -> Add ( * combo , 1 , wxEXPAND );
if (( * combo ) -> edit_btn )
2019-08-06 18:16:02 +02:00
combo_and_btn_sizer -> Add (( * combo ) -> edit_btn , 0 , wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT ,
2019-05-17 15:49:37 +02:00
int ( 0.3 * wxGetApp (). em_unit ()));
2019-03-18 15:10:40 +01:00
2018-09-17 12:15:11 +02:00
auto * sizer_presets = this -> p -> sizer_presets ;
auto * sizer_filaments = this -> p -> sizer_filaments ;
2019-02-07 14:44:05 +01:00
sizer_presets -> Add ( text , 0 , wxALIGN_LEFT | wxEXPAND | wxRIGHT , 4 );
2018-09-17 12:15:11 +02:00
if ( ! filament ) {
2019-03-18 15:10:40 +01:00
sizer_presets -> Add ( combo_and_btn_sizer , 0 , wxEXPAND | wxBOTTOM , 1 );
2018-09-17 12:15:11 +02:00
} else {
2019-03-18 15:10:40 +01:00
sizer_filaments -> Add ( combo_and_btn_sizer , 0 , wxEXPAND | wxBOTTOM , 1 );
2018-10-10 13:53:45 +02:00
( * combo ) -> set_extruder_idx ( 0 );
2018-09-17 12:15:11 +02:00
sizer_presets -> Add ( sizer_filaments , 1 , wxEXPAND );
}
};
p -> combos_filament . push_back ( nullptr );
2020-04-01 09:48:56 +02:00
init_combo ( & p -> combo_print , _L ( "Print settings" ), Preset :: TYPE_PRINT , false );
init_combo ( & p -> combos_filament [ 0 ], _L ( "Filament" ), Preset :: TYPE_FILAMENT , true );
init_combo ( & p -> combo_sla_print , _L ( "SLA print settings" ), Preset :: TYPE_SLA_PRINT , false );
init_combo ( & p -> combo_sla_material , _L ( "SLA material" ), Preset :: TYPE_SLA_MATERIAL , false );
init_combo ( & p -> combo_printer , _L ( "Printer" ), Preset :: TYPE_PRINTER , false );
2018-09-17 12:15:11 +02:00
2019-02-07 14:44:05 +01:00
const int margin_5 = int ( 0.5 * wxGetApp (). em_unit ()); // 5;
2018-10-04 16:43:10 +02:00
p -> sizer_params = new wxBoxSizer ( wxVERTICAL );
2018-10-01 15:09:31 +02:00
2018-09-17 12:15:11 +02:00
// Frequently changed parameters
2019-07-10 15:55:53 +02:00
p -> frequently_changed_parameters = new FreqChangedParams ( p -> scrolled );
p -> sizer_params -> Add ( p -> frequently_changed_parameters -> get_sizer (), 0 , wxEXPAND | wxTOP | wxBOTTOM , wxOSX ? 1 : margin_5 );
2019-08-06 18:16:02 +02:00
2018-10-03 15:14:52 +02:00
// Object List
2018-10-04 16:43:10 +02:00
p -> object_list = new ObjectList ( p -> scrolled );
2019-02-07 14:44:05 +01:00
p -> sizer_params -> Add ( p -> object_list -> get_sizer (), 1 , wxEXPAND );
2019-08-06 18:16:02 +02:00
2018-11-09 18:39:07 +01:00
// Object Manipulations
2018-10-04 16:43:10 +02:00
p -> object_manipulation = new ObjectManipulation ( p -> scrolled );
2018-11-09 18:39:07 +01:00
p -> object_manipulation -> Hide ();
2019-02-07 14:44:05 +01:00
p -> sizer_params -> Add ( p -> object_manipulation -> get_sizer (), 0 , wxEXPAND | wxTOP , margin_5 );
2018-09-17 12:15:11 +02:00
2018-11-09 18:39:07 +01:00
// Frequently Object Settings
p -> object_settings = new ObjectSettings ( p -> scrolled );
p -> object_settings -> Hide ();
2019-02-07 14:44:05 +01:00
p -> sizer_params -> Add ( p -> object_settings -> get_sizer (), 0 , wxEXPAND | wxTOP , margin_5 );
2019-08-06 18:16:02 +02:00
2019-05-28 16:38:04 +02:00
// Object Layers
p -> object_layers = new ObjectLayers ( p -> scrolled );
p -> object_layers -> Hide ();
p -> sizer_params -> Add ( p -> object_layers -> get_sizer (), 0 , wxEXPAND | wxTOP , margin_5 );
2018-11-09 18:39:07 +01:00
2018-09-17 12:15:11 +02:00
// Info boxes
2018-10-01 15:09:31 +02:00
p -> object_info = new ObjectInfo ( p -> scrolled );
p -> sliced_info = new SlicedInfo ( p -> scrolled );
2018-09-17 12:15:11 +02:00
// Sizer in the scrolled area
2019-02-07 14:44:05 +01:00
scrolled_sizer -> Add ( p -> mode_sizer , 0 , wxALIGN_CENTER_HORIZONTAL /*RIGHT | wxBOTTOM | wxRIGHT, 5*/ );
2019-03-13 13:13:18 +01:00
is_msw ?
2019-08-06 18:16:02 +02:00
scrolled_sizer -> Add ( p -> presets_panel , 0 , wxEXPAND | wxLEFT , margin_5 ) :
2019-03-13 13:13:18 +01:00
scrolled_sizer -> Add ( p -> sizer_presets , 0 , wxEXPAND | wxLEFT , margin_5 );
2019-02-07 14:44:05 +01:00
scrolled_sizer -> Add ( p -> sizer_params , 1 , wxEXPAND | wxLEFT , margin_5 );
scrolled_sizer -> Add ( p -> object_info , 0 , wxEXPAND | wxTOP | wxLEFT , margin_5 );
scrolled_sizer -> Add ( p -> sliced_info , 0 , wxEXPAND | wxTOP | wxLEFT , margin_5 );
2018-09-17 12:15:11 +02:00
// Buttons underneath the scrolled area
2019-04-13 23:46:52 +02:00
2019-12-13 13:23:55 +01:00
// rescalable bitmap buttons "Send to printer" and "Remove device"
2019-12-10 17:31:27 +01:00
2019-12-13 13:23:55 +01:00
auto init_scalable_btn = [ this ]( ScalableButton ** btn , const std :: string & icon_name , wxString tooltip = wxEmptyString )
{
2019-12-13 17:56:04 +01:00
#ifdef __APPLE__
int bmp_px_cnt = 16 ;
#else
int bmp_px_cnt = 32 ;
#endif //__APPLE__
ScalableBitmap bmp = ScalableBitmap ( this , icon_name , bmp_px_cnt );
2019-12-13 13:23:55 +01:00
* btn = new ScalableButton ( this , wxID_ANY , bmp , "" , wxBU_EXACTFIT );
( * btn ) -> SetToolTip ( tooltip );
( * btn ) -> Hide ();
2019-04-13 23:46:52 +02:00
};
2020-04-01 09:48:56 +02:00
init_scalable_btn ( & p -> btn_send_gcode , "export_gcode" , _L ( "Send to printer" ) + " \t Ctrl+Shift+G" );
init_scalable_btn ( & p -> btn_remove_device , "eject_sd" , _L ( "Remove device" ) + " \t Ctrl+T" );
init_scalable_btn ( & p -> btn_export_gcode_removable , "export_to_sd" , _L ( "Export to SD card / Flash drive" ) + " \t Ctrl+U" );
2019-12-10 17:31:27 +01:00
2019-12-13 13:23:55 +01:00
// regular buttons "Slice now" and "Export G-code"
2019-12-13 17:56:04 +01:00
const int scaled_height = p -> btn_remove_device -> GetBitmapHeight () + 4 ;
2019-12-13 13:23:55 +01:00
auto init_btn = [ this ]( wxButton ** btn , wxString label , const int button_height ) {
2019-08-06 18:16:02 +02:00
* btn = new wxButton ( this , wxID_ANY , label , wxDefaultPosition ,
2019-12-13 13:23:55 +01:00
wxSize ( - 1 , button_height ), wxBU_EXACTFIT );
2019-04-13 23:46:52 +02:00
( * btn ) -> SetFont ( wxGetApp (). bold_font ());
};
2020-04-01 09:48:56 +02:00
init_btn ( & p -> btn_export_gcode , _L ( "Export G-code" ) + dots , scaled_height );
init_btn ( & p -> btn_reslice , _L ( "Slice now" ) , scaled_height );
2019-12-10 17:31:27 +01:00
2018-12-04 10:28:05 +01:00
enable_buttons ( false );
2018-09-17 12:15:11 +02:00
auto * btns_sizer = new wxBoxSizer ( wxVERTICAL );
2019-12-10 17:31:27 +01:00
auto * complect_btns_sizer = new wxBoxSizer ( wxHORIZONTAL );
complect_btns_sizer -> Add ( p -> btn_export_gcode , 1 , wxEXPAND );
2019-12-13 17:56:04 +01:00
complect_btns_sizer -> Add ( p -> btn_send_gcode );
2020-01-02 16:30:28 +01:00
complect_btns_sizer -> Add ( p -> btn_export_gcode_removable );
2019-12-13 13:23:55 +01:00
complect_btns_sizer -> Add ( p -> btn_remove_device );
2020-01-10 16:25:32 +01:00
2019-12-10 17:31:27 +01:00
2019-02-07 14:44:05 +01:00
btns_sizer -> Add ( p -> btn_reslice , 0 , wxEXPAND | wxTOP , margin_5 );
2019-12-10 17:31:27 +01:00
btns_sizer -> Add ( complect_btns_sizer , 0 , wxEXPAND | wxTOP , margin_5 );
2018-09-17 12:15:11 +02:00
auto * sizer = new wxBoxSizer ( wxVERTICAL );
2019-02-11 15:36:05 +01:00
sizer -> Add ( p -> scrolled , 1 , wxEXPAND );
2019-02-07 14:44:05 +01:00
sizer -> Add ( btns_sizer , 0 , wxEXPAND | wxLEFT , margin_5 );
2018-09-17 12:15:11 +02:00
SetSizer ( sizer );
2018-10-04 11:12:55 +02:00
// Events
2020-01-10 16:25:32 +01:00
p -> btn_export_gcode -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> export_gcode ( false ); });
2019-03-01 12:03:14 +01:00
p -> btn_reslice -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & )
{
const bool export_gcode_after_slicing = wxGetKeyState ( WXK_SHIFT );
if ( export_gcode_after_slicing )
p -> plater -> export_gcode ();
2019-03-12 09:41:57 +01:00
else
p -> plater -> reslice ();
2019-08-06 18:16:02 +02:00
p -> plater -> select_view_3D ( "Preview" );
2019-03-01 12:03:14 +01:00
});
2018-10-04 11:12:55 +02:00
p -> btn_send_gcode -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> send_gcode (); });
2019-12-13 13:23:55 +01:00
p -> btn_remove_device -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> eject_drive (); });
2020-01-10 16:25:32 +01:00
p -> btn_export_gcode_removable -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> export_gcode ( true ); });
2018-09-17 12:15:11 +02:00
}
Sidebar ::~ Sidebar () {}
2018-10-10 13:53:45 +02:00
void Sidebar :: init_filament_combo ( PresetComboBox ** combo , const int extr_idx ) {
2019-03-13 13:13:18 +01:00
* combo = new PresetComboBox ( p -> presets_panel , Slic3r :: Preset :: TYPE_FILAMENT );
2018-10-10 13:53:45 +02:00
// # copy icons from first choice
// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
( * combo ) -> set_extruder_idx ( extr_idx );
2019-03-18 15:10:40 +01:00
auto combo_and_btn_sizer = new wxBoxSizer ( wxHORIZONTAL );
combo_and_btn_sizer -> Add ( * combo , 1 , wxEXPAND );
2019-05-17 16:05:32 +02:00
combo_and_btn_sizer -> Add (( * combo ) -> edit_btn , 0 , wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT ,
2019-05-17 15:49:37 +02:00
int ( 0.3 * wxGetApp (). em_unit ()));
2019-03-18 15:10:40 +01:00
2018-10-10 13:53:45 +02:00
auto /***/ sizer_filaments = this -> p -> sizer_filaments ;
2019-03-18 15:10:40 +01:00
sizer_filaments -> Add ( combo_and_btn_sizer , 1 , wxEXPAND | wxBOTTOM , 1 );
2018-10-10 13:53:45 +02:00
}
2019-09-06 17:46:55 +02:00
void Sidebar :: remove_unused_filament_combos ( const size_t current_extruder_count )
2018-10-10 13:53:45 +02:00
{
if ( current_extruder_count >= p -> combos_filament . size ())
return ;
auto sizer_filaments = this -> p -> sizer_filaments ;
while ( p -> combos_filament . size () > current_extruder_count ) {
const int last = p -> combos_filament . size () - 1 ;
sizer_filaments -> Remove ( last );
( * p -> combos_filament [ last ]). Destroy ();
p -> combos_filament . pop_back ();
}
}
2019-04-10 09:56:32 +02:00
void Sidebar :: update_all_preset_comboboxes ()
{
PresetBundle & preset_bundle = * wxGetApp (). preset_bundle ;
const auto print_tech = preset_bundle . printers . get_edited_preset (). printer_technology ();
// Update the print choosers to only contain the compatible presets, update the dirty flags.
if ( print_tech == ptFFF )
2019-12-02 22:48:01 -05:00
preset_bundle . prints . update_plater_ui ( p -> combo_print );
2019-04-10 09:56:32 +02:00
else {
2019-12-02 22:48:01 -05:00
preset_bundle . sla_prints . update_plater_ui ( p -> combo_sla_print );
preset_bundle . sla_materials . update_plater_ui ( p -> combo_sla_material );
2019-04-10 09:56:32 +02:00
}
// Update the printer choosers, update the dirty flags.
2019-12-02 22:48:01 -05:00
preset_bundle . printers . update_plater_ui ( p -> combo_printer );
2019-04-10 09:56:32 +02:00
// Update the filament choosers to only contain the compatible presets, update the color preview,
// update the dirty flags.
if ( print_tech == ptFFF ) {
for ( size_t i = 0 ; i < p -> combos_filament . size (); ++ i )
2019-12-02 22:48:01 -05:00
preset_bundle . update_plater_filament_ui ( i , p -> combos_filament [ i ]);
2019-04-10 09:56:32 +02:00
}
}
2018-09-17 12:15:11 +02:00
void Sidebar :: update_presets ( Preset :: Type preset_type )
{
2019-08-06 18:16:02 +02:00
PresetBundle & preset_bundle = * wxGetApp (). preset_bundle ;
2019-02-22 09:38:56 +01:00
const auto print_tech = preset_bundle . printers . get_edited_preset (). printer_technology ();
2018-10-31 16:22:36 +01:00
2018-09-17 12:15:11 +02:00
switch ( preset_type ) {
2019-08-06 18:16:02 +02:00
case Preset :: TYPE_FILAMENT :
2019-01-15 15:54:20 +01:00
{
2019-09-06 17:46:55 +02:00
const size_t extruder_cnt = print_tech != ptFFF ? 1 :
2019-01-15 15:54:20 +01:00
dynamic_cast < ConfigOptionFloats *> ( preset_bundle . printers . get_edited_preset (). config . option ( "nozzle_diameter" )) -> values . size ();
2019-09-06 17:46:55 +02:00
const size_t filament_cnt = p -> combos_filament . size () > extruder_cnt ? extruder_cnt : p -> combos_filament . size ();
2019-01-15 15:54:20 +01:00
if ( filament_cnt == 1 ) {
2018-09-17 12:15:11 +02:00
// Single filament printer, synchronize the filament presets.
2019-05-22 11:31:36 +02:00
const std :: string & name = preset_bundle . filaments . get_selected_preset_name ();
2019-01-15 15:54:20 +01:00
preset_bundle . set_filament_preset ( 0 , name );
2018-09-17 12:15:11 +02:00
}
2019-01-15 15:54:20 +01:00
for ( size_t i = 0 ; i < filament_cnt ; i ++ ) {
2019-12-02 22:48:01 -05:00
preset_bundle . update_plater_filament_ui ( i , p -> combos_filament [ i ]);
2018-09-17 12:15:11 +02:00
}
2018-10-03 16:27:02 +02:00
2018-09-17 12:15:11 +02:00
break ;
2019-01-15 15:54:20 +01:00
}
2018-09-17 12:15:11 +02:00
case Preset :: TYPE_PRINT :
2019-12-02 22:48:01 -05:00
preset_bundle . prints . update_plater_ui ( p -> combo_print );
2018-09-17 12:15:11 +02:00
break ;
2018-11-16 17:36:23 +01:00
case Preset :: TYPE_SLA_PRINT :
2019-12-02 22:48:01 -05:00
preset_bundle . sla_prints . update_plater_ui ( p -> combo_sla_print );
2018-11-16 17:36:23 +01:00
break ;
2018-09-17 12:15:11 +02:00
case Preset :: TYPE_SLA_MATERIAL :
2019-12-02 22:48:01 -05:00
preset_bundle . sla_materials . update_plater_ui ( p -> combo_sla_material );
2018-09-17 12:15:11 +02:00
break ;
2019-08-06 18:16:02 +02:00
case Preset :: TYPE_PRINTER :
{
2019-04-10 09:56:32 +02:00
update_all_preset_comboboxes ();
2019-08-06 18:16:02 +02:00
p -> show_preset_comboboxes ();
break ;
}
2018-09-17 12:15:11 +02:00
default : break ;
}
// Synchronize config.ini with the current selections.
2018-10-03 16:27:02 +02:00
wxGetApp (). preset_bundle -> export_selections ( * wxGetApp (). app_config );
2018-09-17 12:15:11 +02:00
}
2019-03-01 12:03:14 +01:00
void Sidebar :: update_mode_sizer () const
2019-01-10 11:05:58 +01:00
{
2019-03-01 12:03:14 +01:00
p -> mode_sizer -> SetMode ( m_mode );
}
void Sidebar :: update_reslice_btn_tooltip () const
{
2019-03-26 14:00:53 +01:00
wxString tooltip = wxString ( "Slice" ) + " [" + GUI :: shortkey_ctrl_prefix () + "R]" ;
if ( m_mode != comSimple )
2020-04-01 09:48:56 +02:00
tooltip += wxString ( " \n " ) + _L ( "Hold Shift to Slice & Export G-code" );
2019-03-01 12:03:14 +01:00
p -> btn_reslice -> SetToolTip ( tooltip );
2019-01-10 11:05:58 +01:00
}
2019-08-06 18:16:02 +02:00
void Sidebar :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
SetMinSize ( wxSize ( 40 * wxGetApp (). em_unit (), - 1 ));
2019-04-25 01:45:00 +02:00
p -> mode_sizer -> msw_rescale ();
2019-04-13 23:46:52 +02:00
2019-04-23 16:33:06 +02:00
// Rescale preset comboboxes in respect to the current em_unit ...
2019-04-13 23:46:52 +02:00
for ( PresetComboBox * combo : std :: vector < PresetComboBox *> { p -> combo_print ,
p -> combo_sla_print ,
p -> combo_sla_material ,
p -> combo_printer } )
2019-04-25 01:45:00 +02:00
combo -> msw_rescale ();
2019-04-13 23:46:52 +02:00
for ( PresetComboBox * combo : p -> combos_filament )
2019-04-25 01:45:00 +02:00
combo -> msw_rescale ();
2019-04-13 23:46:52 +02:00
2019-04-23 16:33:06 +02:00
// ... then refill them and set min size to correct layout of the sidebar
update_all_preset_comboboxes ();
2019-07-10 17:50:24 +02:00
p -> frequently_changed_parameters -> msw_rescale ();
2019-04-25 01:45:00 +02:00
p -> object_list -> msw_rescale ();
2019-04-29 15:27:59 +02:00
p -> object_manipulation -> msw_rescale ();
2019-04-25 01:45:00 +02:00
p -> object_settings -> msw_rescale ();
2019-05-28 16:38:04 +02:00
p -> object_layers -> msw_rescale ();
2019-04-13 23:46:52 +02:00
2019-04-25 01:45:00 +02:00
p -> object_info -> msw_rescale ();
2019-04-13 23:46:52 +02:00
2019-12-13 13:23:55 +01:00
p -> btn_send_gcode -> msw_rescale ();
p -> btn_remove_device -> msw_rescale ();
2020-01-02 16:30:28 +01:00
p -> btn_export_gcode_removable -> msw_rescale ();
2019-12-13 13:23:55 +01:00
const int scaled_height = p -> btn_remove_device -> GetBitmap (). GetHeight () + 4 ;
p -> btn_export_gcode -> SetMinSize ( wxSize ( - 1 , scaled_height ));
p -> btn_reslice -> SetMinSize ( wxSize ( - 1 , scaled_height ));
2019-04-13 23:46:52 +02:00
p -> scrolled -> Layout ();
}
2020-04-24 11:01:14 +02:00
void Sidebar :: search ()
2020-03-28 19:39:24 +01:00
{
2020-04-24 11:01:14 +02:00
p -> searcher . search ();
2020-03-28 19:39:24 +01:00
}
void Sidebar :: jump_to_option ( size_t selected )
{
2020-04-13 17:55:38 +02:00
const Search :: Option & opt = p -> searcher . get_option ( selected );
2020-05-01 08:57:24 +02:00
wxGetApp (). get_tab ( opt . type ) -> activate_option ( boost :: nowide :: narrow ( opt . opt_key ), boost :: nowide :: narrow ( opt . category ));
2020-04-23 12:41:38 +02:00
2020-05-04 22:30:38 +02:00
// Switch to the Settings NotePad
wxGetApp (). mainframe -> select_tab ();
2020-03-28 19:39:24 +01:00
}
2018-10-04 16:43:10 +02:00
ObjectManipulation * Sidebar :: obj_manipul ()
{
return p -> object_manipulation ;
}
2018-10-05 23:29:15 +02:00
ObjectList * Sidebar :: obj_list ()
2018-10-03 15:14:52 +02:00
{
2018-10-05 23:29:15 +02:00
return p -> object_list ;
2018-10-03 15:14:52 +02:00
}
2018-11-09 18:39:07 +01:00
ObjectSettings * Sidebar :: obj_settings ()
{
return p -> object_settings ;
}
2019-05-28 16:38:04 +02:00
ObjectLayers * Sidebar :: obj_layers ()
{
return p -> object_layers ;
}
2018-11-09 18:39:07 +01:00
wxScrolledWindow * Sidebar :: scrolled_panel ()
{
return p -> scrolled ;
}
2019-03-13 13:13:18 +01:00
wxPanel * Sidebar :: presets_panel ()
{
return p -> presets_panel ;
}
2019-01-15 09:31:53 +01:00
ConfigOptionsGroup * Sidebar :: og_freq_chng_params ( const bool is_fff )
2018-10-05 23:29:15 +02:00
{
2019-01-15 09:31:53 +01:00
return p -> frequently_changed_parameters -> get_og ( is_fff );
2018-10-03 15:14:52 +02:00
}
wxButton * Sidebar :: get_wiping_dialog_button ()
{
2018-10-04 16:43:10 +02:00
return p -> frequently_changed_parameters -> get_wiping_dialog_button ();
}
2019-09-06 17:46:55 +02:00
void Sidebar :: update_objects_list_extruder_column ( size_t extruders_count )
2018-10-04 16:43:10 +02:00
{
p -> object_list -> update_objects_list_extruder_column ( extruders_count );
}
2018-11-13 12:19:56 +01:00
void Sidebar :: show_info_sizer ()
2018-10-05 23:29:15 +02:00
{
2018-11-13 12:19:56 +01:00
if ( ! p -> plater -> is_single_full_object_selection () ||
2019-01-10 11:05:58 +01:00
m_mode < comExpert ||
2018-11-14 16:24:36 +01:00
p -> plater -> model (). objects . empty ()) {
2018-11-01 12:33:56 +01:00
p -> object_info -> Show ( false );
return ;
}
2018-11-09 18:39:07 +01:00
int obj_idx = p -> plater -> get_selected_object_idx ();
2018-11-14 16:24:36 +01:00
const ModelObject * model_object = p -> plater -> model (). objects [ obj_idx ];
2018-11-13 15:24:05 +01:00
// hack to avoid crash when deleting the last object on the bed
if ( model_object -> volumes . empty ())
{
p -> object_info -> Show ( false );
return ;
}
2020-04-29 19:10:13 +02:00
bool imperial_units = wxGetApp (). app_config -> get ( "use_inches" ) == "1" ;
double koef = imperial_units ? ObjectManipulation :: mm_to_in : 1.0f ;
2018-11-15 11:15:24 +01:00
auto size = model_object -> bounding_box (). size ();
2020-04-29 19:10:13 +02:00
p -> object_info -> info_size -> SetLabel ( wxString :: Format ( "%.2f x %.2f x %.2f" , size ( 0 ) * koef , size ( 1 ) * koef , size ( 2 ) * koef ));
2018-11-01 12:33:56 +01:00
p -> object_info -> info_materials -> SetLabel ( wxString :: Format ( "%d" , static_cast < int > ( model_object -> materials_count ())));
2019-04-24 16:04:47 +02:00
const auto & stats = model_object -> get_object_stl_stats (); //model_object->volumes.front()->mesh.stl.stats;
2020-04-29 19:10:13 +02:00
p -> object_info -> info_volume -> SetLabel ( wxString :: Format ( "%.2f" , stats . volume * pow ( koef , 3 )));
2020-04-01 09:48:56 +02:00
p -> object_info -> info_facets -> SetLabel ( wxString :: Format ( _L ( "%d (%d shells)" ), static_cast < int > ( model_object -> facets_count ()), stats . number_of_parts ));
2018-11-01 12:33:56 +01:00
int errors = stats . degenerate_facets + stats . edges_fixed + stats . facets_removed +
stats . facets_added + stats . facets_reversed + stats . backwards_edges ;
if ( errors > 0 ) {
2020-04-01 09:48:56 +02:00
wxString tooltip = wxString :: Format ( _L ( "Auto-repaired (%d errors)" ), errors );
2018-11-01 12:33:56 +01:00
p -> object_info -> info_manifold -> SetLabel ( tooltip );
2019-08-06 18:16:02 +02:00
2020-04-01 09:48:56 +02:00
tooltip += ": \n " + wxString :: Format ( _L ( "%d degenerate facets, %d edges fixed, %d facets removed, "
"%d facets added, %d facets reversed, %d backwards edges" ),
2018-11-01 12:33:56 +01:00
stats . degenerate_facets , stats . edges_fixed , stats . facets_removed ,
stats . facets_added , stats . facets_reversed , stats . backwards_edges );
p -> object_info -> showing_manifold_warning_icon = true ;
p -> object_info -> info_manifold -> SetToolTip ( tooltip );
p -> object_info -> manifold_warning_icon -> SetToolTip ( tooltip );
2019-08-06 18:16:02 +02:00
}
2018-11-01 12:33:56 +01:00
else {
2020-04-01 09:48:56 +02:00
p -> object_info -> info_manifold -> SetLabel ( _L ( "Yes" ));
2018-11-01 12:33:56 +01:00
p -> object_info -> showing_manifold_warning_icon = false ;
p -> object_info -> info_manifold -> SetToolTip ( "" );
p -> object_info -> manifold_warning_icon -> SetToolTip ( "" );
}
p -> object_info -> show_sizer ( true );
2019-02-19 12:14:13 +01:00
if ( p -> plater -> printer_technology () == ptSLA ) {
2019-02-21 15:46:04 +01:00
for ( auto item : p -> object_info -> sla_hidden_items )
2019-02-19 12:14:13 +01:00
item -> Show ( false );
}
2018-10-31 12:26:57 +01:00
}
2019-09-26 16:33:55 +02:00
void Sidebar :: update_sliced_info_sizer ()
2018-10-31 12:26:57 +01:00
{
2019-09-26 16:33:55 +02:00
if ( p -> sliced_info -> IsShown ( size_t ( 0 )))
{
2019-02-12 16:34:42 +01:00
if ( p -> plater -> printer_technology () == ptSLA )
{
const SLAPrintStatistics & ps = p -> plater -> sla_print (). print_statistics ();
2020-04-01 09:48:56 +02:00
wxString new_label = _L ( "Used Material (ml)" ) + ":" ;
2019-02-13 15:35:41 +01:00
const bool is_supports = ps . support_used_material > 0.0 ;
if ( is_supports )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " \n - %s \n - %s" , _L ( "object(s)" ), _L ( "supports and pad" ));
2019-02-13 15:35:41 +01:00
wxString info_text = is_supports ?
2019-02-18 12:28:58 +01:00
wxString :: Format ( "%.2f \n %.2f \n %.2f" , ( ps . objects_used_material + ps . support_used_material ) / 1000 ,
ps . objects_used_material / 1000 ,
ps . support_used_material / 1000 ) :
2019-02-18 13:18:47 +01:00
wxString :: Format ( "%.2f" , ( ps . objects_used_material + ps . support_used_material ) / 1000 );
2019-02-13 15:35:41 +01:00
p -> sliced_info -> SetTextAndShow ( siMateril_unit , info_text , new_label );
2019-09-26 16:33:55 +02:00
wxString str_total_cost = "N/A" ;
DynamicPrintConfig * cfg = wxGetApp (). get_tab ( Preset :: TYPE_SLA_MATERIAL ) -> get_config ();
if ( cfg -> option ( "bottle_cost" ) -> getFloat () > 0.0 &&
cfg -> option ( "bottle_volume" ) -> getFloat () > 0.0 )
{
double material_cost = cfg -> option ( "bottle_cost" ) -> getFloat () /
cfg -> option ( "bottle_volume" ) -> getFloat ();
2020-01-08 15:23:46 +01:00
str_total_cost = wxString :: Format ( "%.3f" , material_cost * ( ps . objects_used_material + ps . support_used_material ) / 1000 );
2019-09-26 16:33:55 +02:00
}
2020-01-08 15:23:46 +01:00
p -> sliced_info -> SetTextAndShow ( siCost , str_total_cost , "Cost" );
2019-09-26 16:33:55 +02:00
2019-09-11 12:13:59 +02:00
wxString t_est = std :: isnan ( ps . estimated_print_time ) ? "N/A" : get_time_dhms ( float ( ps . estimated_print_time ));
2020-04-01 09:48:56 +02:00
p -> sliced_info -> SetTextAndShow ( siEstimatedTime , t_est , _L ( "Estimated printing time" ) + ":" );
2019-02-12 16:34:42 +01:00
// Hide non-SLA sliced info parameters
p -> sliced_info -> SetTextAndShow ( siFilament_m , "N/A" );
p -> sliced_info -> SetTextAndShow ( siFilament_mm3 , "N/A" );
p -> sliced_info -> SetTextAndShow ( siFilament_g , "N/A" );
p -> sliced_info -> SetTextAndShow ( siWTNumbetOfToolchanges , "N/A" );
}
else
2019-08-06 18:16:02 +02:00
{
2019-02-21 15:46:04 +01:00
const PrintStatistics & ps = p -> plater -> fff_print (). print_statistics ();
const bool is_wipe_tower = ps . total_wipe_tower_filament > 0 ;
2018-10-31 12:26:57 +01:00
2020-04-29 19:10:13 +02:00
bool imperial_units = wxGetApp (). app_config -> get ( "use_inches" ) == "1" ;
double koef = imperial_units ? ObjectManipulation :: in_to_mm : 1000.0 ;
wxString new_label = imperial_units ? _L ( "Used Filament (in)" ) : _L ( "Used Filament (m)" );
2019-02-21 15:46:04 +01:00
if ( is_wipe_tower )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( ": \n - %1% \n - %2%" , _L ( "objects" ), _L ( "wipe tower" ));
2018-11-20 10:33:54 +01:00
2019-02-21 15:46:04 +01:00
wxString info_text = is_wipe_tower ?
2020-04-29 19:10:13 +02:00
wxString :: Format ( "%.2f \n %.2f \n %.2f" , ps . total_used_filament / /*1000*/ koef ,
( ps . total_used_filament - ps . total_wipe_tower_filament ) / /*1000*/ koef ,
ps . total_wipe_tower_filament / /*1000*/ koef ) :
wxString :: Format ( "%.2f" , ps . total_used_filament / /*1000*/ koef );
2019-02-21 15:46:04 +01:00
p -> sliced_info -> SetTextAndShow ( siFilament_m , info_text , new_label );
2018-11-20 10:33:54 +01:00
2020-04-29 19:10:13 +02:00
koef = imperial_units ? pow ( ObjectManipulation :: mm_to_in , 3 ) : 1.0f ;
new_label = imperial_units ? _L ( "Used Filament (in³)" ) : _L ( "Used Filament (mm³)" );
info_text = wxString :: Format ( "%.2f" , imperial_units ? ps . total_extruded_volume * koef : ps . total_extruded_volume );
p -> sliced_info -> SetTextAndShow ( siFilament_mm3 , info_text , new_label );
2020-02-17 11:51:22 +01:00
p -> sliced_info -> SetTextAndShow ( siFilament_g , ps . total_weight == 0.0 ? "N/A" : wxString :: Format ( "%.2f" , ps . total_weight ));
2018-11-20 10:33:54 +01:00
2020-04-01 09:48:56 +02:00
new_label = _L ( "Cost" );
2019-02-21 15:46:04 +01:00
if ( is_wipe_tower )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( ": \n - %1% \n - %2%" , _L ( "objects" ), _L ( "wipe tower" ));
2019-08-06 18:16:02 +02:00
2020-02-17 11:51:22 +01:00
info_text = ps . total_cost == 0.0 ? "N/A" :
is_wipe_tower ?
2019-02-21 15:46:04 +01:00
wxString :: Format ( "%.2f \n %.2f \n %.2f" , ps . total_cost ,
2019-08-06 18:16:02 +02:00
( ps . total_cost - ps . total_wipe_tower_cost ),
2019-02-21 15:46:04 +01:00
ps . total_wipe_tower_cost ) :
wxString :: Format ( "%.2f" , ps . total_cost );
2020-02-17 11:51:22 +01:00
p -> sliced_info -> SetTextAndShow ( siCost , info_text , new_label );
2018-11-20 10:33:54 +01:00
2019-02-21 15:46:04 +01:00
if ( ps . estimated_normal_print_time == "N/A" && ps . estimated_silent_print_time == "N/A" )
p -> sliced_info -> SetTextAndShow ( siEstimatedTime , "N/A" );
else {
2020-04-01 09:48:56 +02:00
new_label = _L ( "Estimated printing time" ) + ":" ;
2019-02-21 15:46:04 +01:00
info_text = "" ;
2020-04-01 09:48:56 +02:00
wxString str_color = _L ( "Color" );
wxString str_pause = _L ( "Pause" );
2020-02-20 17:32:46 +01:00
auto fill_labels = [ str_color , str_pause ]( const std :: vector < std :: pair < CustomGcodeType , std :: string >>& times ,
wxString & new_label , wxString & info_text )
{
int color_change_count = 0 ;
for ( auto time : times )
if ( time . first == cgtColorChange )
color_change_count ++ ;
for ( int i = ( int ) times . size () - 1 ; i >= 0 ; -- i )
2019-07-08 08:40:20 +02:00
{
2020-02-20 17:32:46 +01:00
if ( i == 0 || times [ i - 1 ]. first == cgtPausePrint )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " \n - %1%%2%" , str_color + " " , color_change_count );
2020-02-20 17:32:46 +01:00
else if ( times [ i - 1 ]. first == cgtColorChange )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " \n - %1%%2%" , str_color + " " , color_change_count -- );
2020-02-20 17:32:46 +01:00
if ( i != ( int ) times . size () - 1 && times [ i ]. first == cgtPausePrint )
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " -> %1%" , str_pause );
2020-02-20 17:32:46 +01:00
2020-04-01 09:48:56 +02:00
info_text += format_wxstr ( " \n %1%" , times [ i ]. second );
2019-07-08 08:40:20 +02:00
}
2020-02-20 17:32:46 +01:00
};
if ( ps . estimated_normal_print_time != "N/A" ) {
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " \n - %1%" , _L ( "normal mode" ));
info_text += format_wxstr ( " \n %1%" , ps . estimated_normal_print_time );
2020-02-20 17:32:46 +01:00
fill_labels ( ps . estimated_normal_custom_gcode_print_times , new_label , info_text );
2019-02-21 15:46:04 +01:00
}
if ( ps . estimated_silent_print_time != "N/A" ) {
2020-04-01 09:48:56 +02:00
new_label += format_wxstr ( " \n - %1%" , _L ( "stealth mode" ));
info_text += format_wxstr ( " \n %1%" , ps . estimated_silent_print_time );
2020-02-20 17:32:46 +01:00
fill_labels ( ps . estimated_silent_custom_gcode_print_times , new_label , info_text );
2019-02-21 15:46:04 +01:00
}
p -> sliced_info -> SetTextAndShow ( siEstimatedTime , info_text , new_label );
2018-11-20 10:33:54 +01:00
}
2018-10-31 12:26:57 +01:00
2019-02-21 15:46:04 +01:00
// if there is a wipe tower, insert number of toolchanges info into the array:
2019-11-05 11:37:40 +01:00
p -> sliced_info -> SetTextAndShow ( siWTNumbetOfToolchanges , is_wipe_tower ? wxString :: Format ( "%.d" , ps . total_toolchanges ) : "N/A" );
2019-02-12 16:34:42 +01:00
2019-02-21 15:46:04 +01:00
// Hide non-FFF sliced info parameters
p -> sliced_info -> SetTextAndShow ( siMateril_unit , "N/A" );
2019-08-06 18:16:02 +02:00
}
2018-10-31 12:26:57 +01:00
}
2019-09-26 16:33:55 +02:00
}
void Sidebar :: show_sliced_info_sizer ( const bool show )
{
wxWindowUpdateLocker freeze_guard ( this );
p -> sliced_info -> Show ( show );
if ( show )
update_sliced_info_sizer ();
2018-10-31 12:26:57 +01:00
2018-11-13 12:19:56 +01:00
Layout ();
2018-11-15 11:15:24 +01:00
p -> scrolled -> Refresh ();
2018-10-05 23:29:15 +02:00
}
2018-10-08 19:14:55 +02:00
void Sidebar :: enable_buttons ( bool enable )
{
p -> btn_reslice -> Enable ( enable );
p -> btn_export_gcode -> Enable ( enable );
p -> btn_send_gcode -> Enable ( enable );
2019-12-13 13:23:55 +01:00
p -> btn_remove_device -> Enable ( enable );
2020-01-02 16:30:28 +01:00
p -> btn_export_gcode_removable -> Enable ( enable );
2018-10-08 19:14:55 +02:00
}
2020-01-02 16:30:28 +01:00
bool Sidebar :: show_reslice ( bool show ) const { return p -> btn_reslice -> Show ( show ); }
bool Sidebar :: show_export ( bool show ) const { return p -> btn_export_gcode -> Show ( show ); }
bool Sidebar :: show_send ( bool show ) const { return p -> btn_send_gcode -> Show ( show ); }
bool Sidebar :: show_disconnect ( bool show ) const { return p -> btn_remove_device -> Show ( show ); }
bool Sidebar :: show_export_removable ( bool show ) const { return p -> btn_export_gcode_removable -> Show ( show ); }
2018-10-15 10:53:47 +02:00
2018-10-09 12:41:05 +02:00
bool Sidebar :: is_multifilament ()
{
2018-11-01 15:58:14 +01:00
return p -> combos_filament . size () > 1 ;
2018-10-09 12:41:05 +02:00
}
2020-04-13 17:55:38 +02:00
static std :: vector < Search :: InputInfo > get_search_inputs ( ConfigOptionMode mode )
2020-03-13 22:37:18 +01:00
{
2020-04-13 17:55:38 +02:00
std :: vector < Search :: InputInfo > ret {};
2020-03-13 22:37:18 +01:00
auto & tabs_list = wxGetApp (). tabs_list ;
auto print_tech = wxGetApp (). preset_bundle -> printers . get_selected_preset (). printer_technology ();
for ( auto tab : tabs_list )
if ( tab -> supports_printer_technology ( print_tech ))
2020-04-13 17:55:38 +02:00
ret . emplace_back ( Search :: InputInfo { tab -> get_config (), tab -> type (), mode });
2020-03-13 22:37:18 +01:00
return ret ;
}
2020-04-13 17:55:38 +02:00
void Sidebar :: update_searcher ()
2020-04-03 10:01:23 +02:00
{
2020-04-13 17:55:38 +02:00
p -> searcher . init ( get_search_inputs ( m_mode ));
2020-03-13 22:37:18 +01:00
}
2018-10-10 13:53:45 +02:00
2019-03-01 12:03:14 +01:00
void Sidebar :: update_mode ()
{
m_mode = wxGetApp (). get_mode ();
update_reslice_btn_tooltip ();
update_mode_sizer ();
2020-04-13 17:55:38 +02:00
update_searcher ();
2019-03-01 12:03:14 +01:00
wxWindowUpdateLocker noUpdates ( this );
p -> object_list -> get_sizer () -> Show ( m_mode > comSimple );
p -> object_list -> unselect_objects ();
p -> object_list -> update_selections ();
p -> object_list -> update_object_menu ();
2019-08-06 18:16:02 +02:00
2019-03-01 12:03:14 +01:00
Layout ();
}
2020-03-16 12:49:39 +01:00
bool Sidebar :: is_collapsed () { return p -> is_collapsed ; }
void Sidebar :: collapse ( bool collapse )
{
p -> is_collapsed = collapse ;
this -> Show ( ! collapse );
p -> plater -> Layout ();
// save collapsing state to the AppConfig
wxGetApp (). app_config -> set ( "collapsed_sidebar" , collapse ? "1" : "0" );
}
2020-04-29 19:10:13 +02:00
void Sidebar :: update_ui_from_settings ()
{
p -> object_manipulation -> update_ui_from_settings ();
show_info_sizer ();
update_sliced_info_sizer ();
}
2018-10-10 13:53:45 +02:00
std :: vector < PresetComboBox *>& Sidebar :: combos_filament ()
{
return p -> combos_filament ;
}
2020-04-13 17:55:38 +02:00
Search :: OptionsSearcher & Sidebar :: get_searcher ()
2020-03-28 19:39:24 +01:00
{
2020-04-13 17:55:38 +02:00
return p -> searcher ;
2020-03-28 19:39:24 +01:00
}
std :: string & Sidebar :: get_search_line ()
{
2020-04-24 11:01:14 +02:00
return p -> searcher . search_string ();
2020-03-28 19:39:24 +01:00
}
2018-09-17 12:15:11 +02:00
// Plater::DropTarget
class PlaterDropTarget : public wxFileDropTarget
{
public :
2019-08-06 18:16:02 +02:00
PlaterDropTarget ( Plater * plater ) : plater ( plater ) { this -> SetDefaultAction ( wxDragCopy ); }
2018-09-17 12:15:11 +02:00
virtual bool OnDropFiles ( wxCoord x , wxCoord y , const wxArrayString & filenames );
private :
Plater * plater ;
2018-10-08 19:14:55 +02:00
static const std :: regex pattern_drop ;
2018-09-17 12:15:11 +02:00
};
2018-10-22 11:52:13 +02:00
const std :: regex PlaterDropTarget :: pattern_drop ( ".*[.](stl|obj|amf|3mf|prusa)" , std :: regex :: icase );
2018-10-08 19:14:55 +02:00
2018-09-17 12:15:11 +02:00
bool PlaterDropTarget :: OnDropFiles ( wxCoord x , wxCoord y , const wxArrayString & filenames )
{
2018-10-08 19:14:55 +02:00
std :: vector < fs :: path > paths ;
for ( const auto & filename : filenames ) {
2019-01-02 15:11:05 +01:00
fs :: path path ( into_path ( filename ));
2018-10-08 19:14:55 +02:00
if ( std :: regex_match ( path . string (), pattern_drop )) {
paths . push_back ( std :: move ( path ));
} else {
return false ;
}
}
2019-08-06 18:16:02 +02:00
wxString snapshot_label ;
assert ( ! paths . empty ());
if ( paths . size () == 1 ) {
2020-04-01 09:48:56 +02:00
snapshot_label = _L ( "Load File" );
2019-08-06 18:16:02 +02:00
snapshot_label += ": " ;
snapshot_label += wxString :: FromUTF8 ( paths . front (). filename (). string (). c_str ());
} else {
2020-04-01 09:48:56 +02:00
snapshot_label = _L ( "Load Files" );
2019-08-06 18:16:02 +02:00
snapshot_label += ": " ;
snapshot_label += wxString :: FromUTF8 ( paths . front (). filename (). string (). c_str ());
for ( size_t i = 1 ; i < paths . size (); ++ i ) {
snapshot_label += ", " ;
snapshot_label += wxString :: FromUTF8 ( paths [ i ]. filename (). string (). c_str ());
}
}
Plater :: TakeSnapshot snapshot ( plater , snapshot_label );
2019-07-17 15:48:53 +02:00
2019-05-15 14:09:16 +02:00
// FIXME: when drag and drop is done on a .3mf or a .amf file we should clear the plater for consistence with the open project command
// (the following call to plater->load_files() will load the config data, if present)
2019-09-02 15:15:28 +02:00
std :: vector < size_t > res = plater -> load_files ( paths );
2019-05-15 14:09:16 +02:00
// because right now the plater is not cleared, we set the project file (from the latest imported .3mf or .amf file)
// only if not set yet
2019-09-02 15:15:28 +02:00
// if res is empty no data has been loaded
if ( ! res . empty () && plater -> get_project_filename (). empty ())
2019-05-15 14:09:16 +02:00
{
for ( std :: vector < fs :: path >:: const_reverse_iterator it = paths . rbegin (); it != paths . rend (); ++ it )
{
std :: string filename = ( * it ). filename (). string ();
if ( boost :: algorithm :: iends_with ( filename , ".3mf" ) || boost :: algorithm :: iends_with ( filename , ".amf" ))
{
plater -> set_project_filename ( from_path ( * it ));
break ;
}
}
}
2018-10-08 19:14:55 +02:00
return true ;
2018-09-17 12:15:11 +02:00
}
// Plater / private
struct Plater :: priv
{
// PIMPL back pointer ("Q-Pointer")
Plater * q ;
MainFrame * main_frame ;
2018-10-17 12:17:25 +02:00
// Object popup menu
2019-04-25 01:45:00 +02:00
MenuWithSeparators object_menu ;
2018-12-07 17:50:48 +01:00
// Part popup menu
2019-04-25 01:45:00 +02:00
MenuWithSeparators part_menu ;
2018-12-07 17:50:48 +01:00
// SLA-Object popup menu
2019-04-25 01:45:00 +02:00
MenuWithSeparators sla_object_menu ;
2019-09-20 09:57:27 +02:00
// Default popup menu (when nothing is selected on 3DScene)
MenuWithSeparators default_menu ;
2019-01-25 10:34:32 +01:00
2019-03-19 09:51:50 +01:00
// Removed/Prepended Items according to the view mode
std :: vector < wxMenuItem *> items_increase ;
std :: vector < wxMenuItem *> items_decrease ;
std :: vector < wxMenuItem *> items_set_number_of_copies ;
enum MenuIdentifier {
miObjectFFF = 0 ,
miObjectSLA
};
2018-09-17 12:15:11 +02:00
// Data
2018-12-05 15:22:03 +01:00
Slic3r :: DynamicPrintConfig * config ; // FIXME: leak?
2018-12-03 13:14:28 +01:00
Slic3r :: Print fff_print ;
2019-08-06 18:16:02 +02:00
Slic3r :: SLAPrint sla_print ;
2018-11-13 17:45:44 +01:00
Slic3r :: Model model ;
PrinterTechnology printer_technology = ptFFF ;
Slic3r :: GCodePreviewData gcode_preview_data ;
2019-07-26 13:44:33 +02:00
2018-09-17 12:15:11 +02:00
// GUI elements
2019-03-05 10:54:03 +01:00
wxSizer * panel_sizer { nullptr };
wxPanel * current_panel { nullptr };
2018-12-04 13:55:25 +01:00
std :: vector < wxPanel *> panels ;
2018-09-17 12:15:11 +02:00
Sidebar * sidebar ;
2019-02-19 15:15:27 +01:00
Bed3D bed ;
2019-03-06 15:46:19 +01:00
Camera camera ;
2019-09-27 14:52:19 +02:00
Mouse3DController mouse3d_controller ;
2018-12-04 13:55:25 +01:00
View3D * view3D ;
2018-12-17 10:55:14 +01:00
GLToolbar view_toolbar ;
2018-10-04 16:43:10 +02:00
Preview * preview ;
2018-11-15 15:27:39 +01:00
2018-10-17 11:12:38 +02:00
BackgroundSlicingProcess background_process ;
2019-06-26 09:48:52 +02:00
bool suppressed_backround_processing_update { false };
2019-08-06 18:16:02 +02:00
2019-06-18 16:24:30 +02:00
// Jobs defined inside the group class will be managed so that only one can
// run at a time. Also, the background process will be stopped if a job is
2020-04-23 18:47:51 +02:00
// started. It is up the the plater to ensure that the background slicing
// can't be restarted while a ui job is still running.
class Jobs : public ExclusiveJobGroup
{
priv * m ;
2020-04-23 19:45:55 +02:00
size_t m_arrange_id , m_rotoptimize_id , m_sla_import_id ;
2020-04-23 18:47:51 +02:00
void before_start () override { m -> background_process . stop (); }
2019-05-13 18:58:56 +02:00
public :
2020-04-23 18:47:51 +02:00
Jobs ( priv * _m ) : m ( _m )
2019-06-18 16:24:30 +02:00
{
2020-04-23 18:47:51 +02:00
m_arrange_id = add_job ( std :: make_unique < ArrangeJob > ( m -> statusbar (), m -> q ));
m_rotoptimize_id = add_job ( std :: make_unique < RotoptimizeJob > ( m -> statusbar (), m -> q ));
2020-04-23 19:45:55 +02:00
m_sla_import_id = add_job ( std :: make_unique < SLAImportJob > ( m -> statusbar (), m -> q ));
2019-06-18 16:24:30 +02:00
}
2020-04-23 18:47:51 +02:00
void arrange ()
2019-06-18 16:24:30 +02:00
{
2020-04-23 18:47:51 +02:00
m -> take_snapshot ( _ ( L ( "Arrange" )));
start ( m_arrange_id );
2019-06-18 16:24:30 +02:00
}
2020-04-23 18:47:51 +02:00
void optimize_rotation ()
{
m -> take_snapshot ( _ ( L ( "Optimize Rotation" )));
start ( m_rotoptimize_id );
}
2020-04-23 19:45:55 +02:00
void import_sla_arch ()
{
m -> take_snapshot ( _ ( L ( "Import SLA archive" )));
start ( m_sla_import_id );
}
2020-04-23 18:47:51 +02:00
} m_ui_jobs ;
2019-06-18 16:24:30 +02:00
2018-11-23 12:47:32 +01:00
bool delayed_scene_refresh ;
2019-02-19 14:57:59 +01:00
std :: string delayed_error_message ;
2018-11-08 16:01:21 +01:00
2018-10-17 11:12:38 +02:00
wxTimer background_process_timer ;
2018-09-17 12:15:11 +02:00
2019-03-01 11:00:34 +01:00
std :: string label_btn_export ;
2019-03-12 09:41:57 +01:00
std :: string label_btn_send ;
2019-03-01 11:00:34 +01:00
2018-09-17 12:15:11 +02:00
static const std :: regex pattern_bundle ;
static const std :: regex pattern_3mf ;
static const std :: regex pattern_zip_amf ;
2019-01-03 12:04:14 +01:00
static const std :: regex pattern_any_amf ;
2019-01-18 16:01:43 +01:00
static const std :: regex pattern_prusa ;
2018-09-17 12:15:11 +02:00
priv ( Plater * q , MainFrame * main_frame );
2019-06-27 15:23:03 +02:00
~ priv ();
2018-09-17 12:15:11 +02:00
2020-04-23 18:47:51 +02:00
enum class UpdateParams {
FORCE_FULL_SCREEN_REFRESH = 1 ,
FORCE_BACKGROUND_PROCESSING_UPDATE = 2 ,
POSTPONE_VALIDATION_ERROR_MESSAGE = 4 ,
2019-08-23 15:53:45 +02:00
};
void update ( unsigned int flags = 0 );
2018-10-17 12:59:58 +02:00
void select_view ( const std :: string & direction );
2018-12-04 13:55:25 +01:00
void select_view_3D ( const std :: string & name );
2018-12-21 10:43:22 +01:00
void select_next_view_3D ();
2019-08-20 09:01:09 +02:00
bool is_preview_shown () const { return current_panel == preview ; }
bool is_preview_loaded () const { return preview -> is_loaded (); }
bool is_view3D_shown () const { return current_panel == view3D ; }
2020-02-04 14:42:26 +01:00
bool are_view3D_labels_shown () const { return ( current_panel == view3D ) && view3D -> get_canvas3d () -> are_labels_shown (); }
void show_view3D_labels ( bool show ) { if ( current_panel == view3D ) view3D -> get_canvas3d () -> show_labels ( show ); }
2020-03-16 12:49:39 +01:00
bool is_sidebar_collapsed () const { return sidebar -> is_collapsed (); }
2020-04-29 15:58:57 +02:00
void collapse_sidebar ( bool show ) { sidebar -> collapse ( show ); }
2020-03-16 12:49:39 +01:00
2020-03-13 15:09:07 +01:00
#if ENABLE_SLOPE_RENDERING
bool is_view3D_slope_shown () const { return ( current_panel == view3D ) && view3D -> get_canvas3d () -> is_slope_shown (); }
void show_view3D_slope ( bool show ) { if ( current_panel == view3D ) view3D -> get_canvas3d () -> show_slope ( show ); }
bool is_view3D_layers_editing_enabled () const { return ( current_panel == view3D ) && view3D -> get_canvas3d () -> is_layers_editing_enabled (); }
#endif // ENABLE_SLOPE_RENDERING
2020-01-03 11:41:29 +01:00
void set_current_canvas_as_dirty ();
2020-01-21 14:07:13 +01:00
GLCanvas3D * get_current_canvas3D ();
2020-03-04 15:19:48 +01:00
void unbind_canvas_event_handlers ();
2020-04-01 09:58:31 +02:00
void reset_canvas_volumes ();
2020-01-03 11:41:29 +01:00
2019-12-10 10:56:21 +01:00
bool init_view_toolbar ();
2019-03-27 14:16:38 +01:00
void reset_all_gizmos ();
2018-09-17 12:15:11 +02:00
void update_ui_from_settings ();
2020-05-05 17:23:58 +02:00
void update_main_toolbar_tooltips ();
2019-12-16 09:47:31 +01:00
std :: shared_ptr < ProgressStatusBar > statusbar ();
2018-10-01 16:48:08 +02:00
std :: string get_config ( const std :: string & key ) const ;
2018-10-03 16:18:23 +02:00
BoundingBoxf bed_shape_bb () const ;
BoundingBox scaled_bed_shape_bb () const ;
2019-08-06 18:16:02 +02:00
2020-04-29 19:10:13 +02:00
std :: vector < size_t > load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config , bool used_inches = false );
2018-10-01 16:48:08 +02:00
std :: vector < size_t > load_model_objects ( const ModelObjectPtrs & model_objects );
2019-04-04 11:31:26 +02:00
wxString get_export_file ( GUI :: FileType file_type );
2018-10-04 11:12:55 +02:00
2019-03-19 13:30:21 +01:00
const Selection & get_selection () const ;
Selection & get_selection ();
2018-10-17 09:30:07 +02:00
int get_selected_object_idx () const ;
2018-12-06 14:49:14 +01:00
int get_selected_volume_idx () const ;
2018-10-04 11:12:55 +02:00
void selection_changed ();
2018-10-08 19:14:55 +02:00
void object_list_changed ();
2018-10-04 11:12:55 +02:00
2018-11-21 15:28:35 +01:00
void select_all ();
2019-05-14 11:57:39 +02:00
void deselect_all ();
2018-10-08 19:14:55 +02:00
void remove ( size_t obj_idx );
2018-11-13 14:17:35 +01:00
void delete_object_from_model ( size_t obj_idx );
2018-10-04 11:12:55 +02:00
void reset ();
2018-10-18 15:09:41 +02:00
void mirror ( Axis axis );
2018-10-09 09:35:19 +02:00
void split_object ();
2018-10-24 12:55:38 +02:00
void split_volume ();
2019-05-22 14:42:38 +02:00
void scale_selection_to_fit_print_volume ();
2019-07-02 16:42:23 +02:00
2019-07-26 13:44:33 +02:00
// Return the active Undo/Redo stack. It may be either the main stack or the Gimzo stack.
Slic3r :: UndoRedo :: Stack & undo_redo_stack () { assert ( m_undo_redo_stack_active != nullptr ); return * m_undo_redo_stack_active ; }
Slic3r :: UndoRedo :: Stack & undo_redo_stack_main () { return m_undo_redo_stack_main ; }
void enter_gizmos_stack ();
void leave_gizmos_stack ();
2019-08-06 18:16:02 +02:00
void take_snapshot ( const std :: string & snapshot_name );
void take_snapshot ( const wxString & snapshot_name ) { this -> take_snapshot ( std :: string ( snapshot_name . ToUTF8 (). data ())); }
2019-07-09 20:45:00 +02:00
int get_active_snapshot_index ();
2019-07-26 13:44:33 +02:00
2019-07-04 10:45:41 +02:00
void undo ();
void redo ();
2019-07-18 17:41:47 +02:00
void undo_redo_to ( size_t time_to_load );
2019-07-11 18:42:02 +02:00
void suppress_snapshots () { this -> m_prevent_snapshots ++ ; }
void allow_snapshots () { this -> m_prevent_snapshots -- ; }
2019-07-02 16:42:23 +02:00
2019-05-22 14:42:38 +02:00
bool background_processing_enabled () const { return this -> get_config ( "background_processing" ) == "1" ; }
2018-12-03 13:14:28 +01:00
void update_print_volume_state ();
2018-10-09 09:35:19 +02:00
void schedule_background_process ();
2018-11-13 17:45:44 +01:00
// Update background processing thread from the current config and Model.
enum UpdateBackgroundProcessReturnState {
// update_background_process() reports, that the Print / SLAPrint was updated in a way,
// that the background process was invalidated and it needs to be re-run.
UPDATE_BACKGROUND_PROCESS_RESTART = 1 ,
// update_background_process() reports, that the Print / SLAPrint was updated in a way,
2018-11-23 12:47:32 +01:00
// that a scene needs to be refreshed (you should call _3DScene::reload_scene(canvas3Dwidget, false))
2019-08-06 18:16:02 +02:00
UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE = 2 ,
2018-11-13 17:45:44 +01:00
// update_background_process() reports, that the Print / SLAPrint is invalid, and the error message
// was sent to the status line.
UPDATE_BACKGROUND_PROCESS_INVALID = 4 ,
2018-12-22 10:02:42 +01:00
// Restart even if the background processing is disabled.
UPDATE_BACKGROUND_PROCESS_FORCE_RESTART = 8 ,
// Restart for G-code (or SLA zip) export or upload.
UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT = 16 ,
2018-11-13 17:45:44 +01:00
};
// returns bit mask of UpdateBackgroundProcessReturnState
2019-08-23 15:53:45 +02:00
unsigned int update_background_process ( bool force_validation = false , bool postpone_error_messages = false );
2018-12-22 10:02:42 +01:00
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
bool restart_background_process ( unsigned int state );
2019-08-06 18:16:02 +02:00
// returns bit mask of UpdateBackgroundProcessReturnState
unsigned int update_restart_background_process ( bool force_scene_update , bool force_preview_update );
2019-08-23 15:53:45 +02:00
void show_delayed_error_message () {
if ( ! this -> delayed_error_message . empty ()) {
std :: string msg = std :: move ( this -> delayed_error_message );
this -> delayed_error_message . clear ();
GUI :: show_error ( this -> q , msg );
}
}
2020-03-12 12:43:01 +01:00
void export_gcode ( fs :: path output_path , bool output_path_on_removable_media , PrintHostJob upload_job );
2018-10-09 09:35:19 +02:00
void reload_from_disk ();
2020-01-31 12:15:04 +01:00
void reload_all_from_disk ();
2019-01-30 16:27:07 +01:00
void fix_through_netfabb ( const int obj_idx , const int vol_idx = - 1 );
2018-09-17 12:15:11 +02:00
2018-12-04 13:55:25 +01:00
void set_current_panel ( wxPanel * panel );
2019-01-03 12:59:06 +01:00
2018-10-08 19:55:30 +02:00
void on_select_preset ( wxCommandEvent & );
2018-11-20 13:22:26 +01:00
void on_slicing_update ( SlicingStatusEvent & );
void on_slicing_completed ( wxCommandEvent & );
2018-10-08 19:55:30 +02:00
void on_process_completed ( wxCommandEvent & );
2018-09-17 12:15:11 +02:00
void on_layer_editing_toggled ( bool enable );
2018-10-08 19:55:30 +02:00
2018-10-03 11:34:39 +02:00
void on_action_add ( SimpleEvent & );
2018-10-24 12:55:38 +02:00
void on_action_split_objects ( SimpleEvent & );
void on_action_split_volumes ( SimpleEvent & );
2018-10-04 11:12:55 +02:00
void on_action_layersediting ( SimpleEvent & );
2018-10-03 14:25:35 +02:00
2018-10-16 16:04:19 +02:00
void on_object_select ( SimpleEvent & );
2019-09-20 09:57:27 +02:00
void on_right_click ( RBtnEvent & );
2018-10-08 19:55:30 +02:00
void on_wipetower_moved ( Vec3dEvent & );
2019-04-26 15:34:26 +02:00
void on_wipetower_rotated ( Vec3dEvent & );
2018-10-08 19:55:30 +02:00
void on_update_geometry ( Vec3dsEvent < 2 >& );
2018-11-23 12:47:32 +01:00
void on_3dcanvas_mouse_dragging_finished ( SimpleEvent & );
2018-10-17 12:17:25 +02:00
2018-12-07 17:50:48 +01:00
void update_object_menu ();
2019-03-01 11:00:34 +01:00
void show_action_buttons ( const bool is_ready_to_slice ) const ;
2018-12-07 17:50:48 +01:00
2019-02-19 15:15:27 +01:00
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
2019-07-24 14:02:36 +02:00
void set_bed_shape ( const Pointfs & shape , const std :: string & custom_texture , const std :: string & custom_model );
2019-02-19 15:15:27 +01:00
2019-03-14 13:54:05 +01:00
bool can_delete () const ;
bool can_delete_all () const ;
bool can_increase_instances () const ;
bool can_decrease_instances () const ;
bool can_split_to_objects () const ;
bool can_split_to_volumes () const ;
bool can_arrange () const ;
bool can_layers_editing () const ;
2019-04-24 16:04:47 +02:00
bool can_fix_through_netfabb () const ;
2019-05-13 14:27:51 +02:00
bool can_set_instance_to_object () const ;
bool can_mirror () const ;
2019-09-19 09:09:11 +02:00
bool can_reload_from_disk () const ;
2019-03-14 13:54:05 +01:00
2019-11-28 14:18:24 +01:00
void generate_thumbnail ( ThumbnailData & data , unsigned int w , unsigned int h , bool printable_only , bool parts_only , bool show_bed , bool transparent_background );
void generate_thumbnails ( ThumbnailsList & thumbnails , const Vec2ds & sizes , bool printable_only , bool parts_only , bool show_bed , bool transparent_background );
2019-03-14 13:54:05 +01:00
2019-05-03 09:44:19 +02:00
void msw_rescale_object_menu ();
2019-05-15 14:09:16 +02:00
// returns the path to project file with the given extension (none if extension == wxEmptyString)
// extension should contain the leading dot, i.e.: ".3mf"
wxString get_project_filename ( const wxString & extension = wxEmptyString ) const ;
2019-05-14 15:00:24 +02:00
void set_project_filename ( const wxString & filename );
2020-03-06 15:10:58 +01:00
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
mutable bool ready_to_slice = { false };
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
bool writing_to_removable_device = { false };
2020-03-20 13:41:36 +01:00
bool inside_snapshot_capture () { return m_prevent_snapshots != 0 ; }
2020-03-06 15:10:58 +01:00
2018-10-17 12:17:25 +02:00
private :
bool init_object_menu ();
2018-12-07 17:50:48 +01:00
bool init_common_menu ( wxMenu * menu , const bool is_part = false );
bool complit_init_object_menu ();
bool complit_init_sla_object_menu ();
bool complit_init_part_menu ();
2018-10-18 09:27:37 +02:00
2018-12-17 15:44:30 +01:00
bool can_split () const ;
2018-10-18 09:27:37 +02:00
bool layers_height_allowed () const ;
2018-11-23 12:47:32 +01:00
2018-12-06 15:40:41 +01:00
void update_fff_scene ();
2018-11-23 12:47:32 +01:00
void update_sla_scene ();
2019-08-06 18:16:02 +02:00
void undo_redo_to ( std :: vector < UndoRedo :: Snapshot >:: const_iterator it_snapshot );
2019-08-22 16:44:49 +02:00
void update_after_undo_redo ( const UndoRedo :: Snapshot & snapshot , bool temp_snapshot_was_taken = false );
2019-05-14 15:00:24 +02:00
2019-05-15 14:09:16 +02:00
// path to project file stored with no extension
2019-07-26 13:44:33 +02:00
wxString m_project_filename ;
Slic3r :: UndoRedo :: Stack m_undo_redo_stack_main ;
Slic3r :: UndoRedo :: Stack m_undo_redo_stack_gizmos ;
Slic3r :: UndoRedo :: Stack * m_undo_redo_stack_active = & m_undo_redo_stack_main ;
2019-08-06 18:16:02 +02:00
int m_prevent_snapshots = 0 ; /* Used for avoid of excess "snapshoting".
2019-07-26 13:44:33 +02:00
* Like for "delete selected" or "set numbers of copies"
2019-08-06 18:16:02 +02:00
* we should call tack_snapshot just ones
2019-07-26 13:44:33 +02:00
* instead of calls for each action separately
* */
2020-03-06 15:10:58 +01:00
std :: string m_last_fff_printer_profile_name ;
std :: string m_last_sla_printer_profile_name ;
2018-09-17 12:15:11 +02:00
};
2018-10-22 11:52:13 +02:00
const std :: regex Plater :: priv :: pattern_bundle ( ".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)" , std :: regex :: icase );
const std :: regex Plater :: priv :: pattern_3mf ( ".*3mf" , std :: regex :: icase );
const std :: regex Plater :: priv :: pattern_zip_amf ( ".*[.]zip[.]amf" , std :: regex :: icase );
2019-01-03 12:04:14 +01:00
const std :: regex Plater :: priv :: pattern_any_amf ( ".*[.](amf|amf[.]xml|zip[.]amf)" , std :: regex :: icase );
2019-01-18 16:01:43 +01:00
const std :: regex Plater :: priv :: pattern_prusa ( ".*prusa" , std :: regex :: icase );
2019-01-03 12:04:14 +01:00
2018-10-18 15:13:38 +02:00
Plater :: priv :: priv ( Plater * q , MainFrame * main_frame )
: q ( q )
, main_frame ( main_frame )
, config ( Slic3r :: DynamicPrintConfig :: new_from_defaults_keys ({
2019-07-18 14:39:19 +02:00
"bed_shape" , "bed_custom_texture" , "bed_custom_model" , "complete_objects" , "duplicate_distance" , "extruder_clearance_radius" , "skirts" , "skirt_distance" ,
2018-09-17 12:15:11 +02:00
"brim_width" , "variable_layer_height" , "serial_port" , "serial_speed" , "host_type" , "print_host" ,
"printhost_apikey" , "printhost_cafile" , "nozzle_diameter" , "single_extruder_multi_material" ,
"wipe_tower" , "wipe_tower_x" , "wipe_tower_y" , "wipe_tower_width" , "wipe_tower_rotation_angle" ,
2019-01-21 10:06:51 +01:00
"extruder_colour" , "filament_colour" , "max_print_height" , "printer_model" , "printer_technology" ,
2019-01-21 17:02:16 +01:00
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
"layer_height" , "first_layer_height" , "min_layer_height" , "max_layer_height" ,
2020-02-05 16:53:26 +01:00
"brim_width" , "perimeters" , "perimeter_extruder" , "fill_density" , "infill_extruder" , "top_solid_layers" ,
2019-01-21 17:02:16 +01:00
"support_material" , "support_material_extruder" , "support_material_interface_extruder" , "support_material_contact_distance" , "raft_layers"
2018-10-18 15:13:38 +02:00
}))
, sidebar ( new Sidebar ( q ))
2020-04-23 18:47:51 +02:00
, m_ui_jobs ( this )
2018-11-23 12:47:32 +01:00
, delayed_scene_refresh ( false )
2019-02-26 12:56:13 +01:00
, view_toolbar ( GLToolbar :: Radio , "View" )
2019-05-14 15:00:24 +02:00
, m_project_filename ( wxEmptyString )
2018-09-17 12:15:11 +02:00
{
2019-08-06 18:16:02 +02:00
this -> q -> SetFont ( Slic3r :: GUI :: wxGetApp (). normal_font ());
2019-05-06 18:28:23 +02:00
2018-12-03 13:14:28 +01:00
background_process . set_fff_print ( & fff_print );
2019-08-06 18:16:02 +02:00
background_process . set_sla_print ( & sla_print );
2018-09-17 12:15:11 +02:00
background_process . set_gcode_preview_data ( & gcode_preview_data );
2019-11-28 14:18:24 +01:00
background_process . set_thumbnail_cb ([ this ]( ThumbnailsList & thumbnails , const Vec2ds & sizes , bool printable_only , bool parts_only , bool show_bed , bool transparent_background )
2019-11-22 12:39:03 +01:00
{
2019-11-28 14:18:24 +01:00
std :: packaged_task < void ( ThumbnailsList & , const Vec2ds & , bool , bool , bool , bool ) > task ([ this ]( ThumbnailsList & thumbnails , const Vec2ds & sizes , bool printable_only , bool parts_only , bool show_bed , bool transparent_background ) {
generate_thumbnails ( thumbnails , sizes , printable_only , parts_only , show_bed , transparent_background );
2019-11-22 12:39:03 +01:00
});
std :: future < void > result = task . get_future ();
2019-11-28 14:18:24 +01:00
wxTheApp -> CallAfter ([ & ]() { task ( thumbnails , sizes , printable_only , parts_only , show_bed , transparent_background ); });
2019-11-22 12:39:03 +01:00
result . wait ();
});
2018-11-20 13:22:26 +01:00
background_process . set_slicing_completed_event ( EVT_SLICING_COMPLETED );
2018-09-17 12:15:11 +02:00
background_process . set_finished_event ( EVT_PROCESS_COMPLETED );
2019-08-06 18:16:02 +02:00
// Default printer technology for default config.
2018-11-13 17:45:44 +01:00
background_process . select_technology ( this -> printer_technology );
2019-12-02 22:48:01 -05:00
// Register progress callback from the Print class to the Plater.
2018-11-13 11:53:54 +01:00
2018-11-20 16:27:30 +01:00
auto statuscb = [ this ]( const Slic3r :: PrintBase :: SlicingStatus & status ) {
2018-11-20 13:22:26 +01:00
wxQueueEvent ( this -> q , new Slic3r :: SlicingStatusEvent ( EVT_SLICING_UPDATE , 0 , status ));
2018-11-13 11:53:54 +01:00
};
2018-12-03 13:14:28 +01:00
fff_print . set_status_callback ( statuscb );
2018-11-13 11:53:54 +01:00
sla_print . set_status_callback ( statuscb );
2018-11-20 13:22:26 +01:00
this -> q -> Bind ( EVT_SLICING_UPDATE , & priv :: on_slicing_update , this );
2018-09-17 12:15:11 +02:00
2020-03-02 10:58:46 +01:00
view3D = new View3D ( q , & model , config , & background_process );
preview = new Preview ( q , & model , config , & background_process , & gcode_preview_data , [ this ]() { schedule_background_process (); });
2020-03-02 12:45:04 +01:00
#ifdef __APPLE__
// set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
2020-03-02 13:09:12 +01:00
view_toolbar . set_icons_size ( GLGizmosManager :: Default_Icons_Size );
2020-03-02 12:45:04 +01:00
#endif // __APPLE__
2019-02-19 15:15:27 +01:00
2018-12-04 13:55:25 +01:00
panels . push_back ( view3D );
panels . push_back ( preview );
2018-09-17 12:15:11 +02:00
2018-10-23 15:27:31 +02:00
this -> background_process_timer . SetOwner ( this -> q , 0 );
2019-06-26 09:48:52 +02:00
this -> q -> Bind ( wxEVT_TIMER , [ this ]( wxTimerEvent & evt )
{
if ( ! this -> suppressed_backround_processing_update )
this -> update_restart_background_process ( false , false );
});
2018-09-17 12:15:11 +02:00
update ();
auto * hsizer = new wxBoxSizer ( wxHORIZONTAL );
2018-12-04 13:55:25 +01:00
panel_sizer = new wxBoxSizer ( wxHORIZONTAL );
panel_sizer -> Add ( view3D , 1 , wxEXPAND | wxALL , 0 );
panel_sizer -> Add ( preview , 1 , wxEXPAND | wxALL , 0 );
hsizer -> Add ( panel_sizer , 1 , wxEXPAND | wxALL , 0 );
2018-09-17 12:15:11 +02:00
hsizer -> Add ( sidebar , 0 , wxEXPAND | wxLEFT | wxRIGHT , 0 );
q -> SetSizer ( hsizer );
2018-10-17 12:17:25 +02:00
init_object_menu ();
2018-09-17 12:15:11 +02:00
// Events:
// Preset change event
sidebar -> Bind ( wxEVT_COMBOBOX , & priv :: on_select_preset , this );
2018-11-01 12:33:56 +01:00
sidebar -> Bind ( EVT_OBJ_LIST_OBJECT_SELECT , [ this ]( wxEvent & ) { priv :: selection_changed (); });
2018-12-04 11:14:39 +01:00
sidebar -> Bind ( EVT_SCHEDULE_BACKGROUND_PROCESS , [ this ]( SimpleEvent & ) { this -> schedule_background_process (); });
2018-11-01 12:33:56 +01:00
2018-12-04 13:55:25 +01:00
wxGLCanvas * view3D_canvas = view3D -> get_wxglcanvas ();
2020-04-23 18:47:51 +02:00
2018-12-04 13:55:25 +01:00
// 3DScene events:
view3D_canvas -> Bind ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS , [ this ]( SimpleEvent & ) { this -> schedule_background_process (); });
view3D_canvas -> Bind ( EVT_GLCANVAS_OBJECT_SELECT , & priv :: on_object_select , this );
view3D_canvas -> Bind ( EVT_GLCANVAS_RIGHT_CLICK , & priv :: on_right_click , this );
view3D_canvas -> Bind ( EVT_GLCANVAS_REMOVE_OBJECT , [ q ]( SimpleEvent & ) { q -> remove_selected (); });
2020-04-23 18:47:51 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_ARRANGE , [ this ]( SimpleEvent & ) { this -> q -> arrange (); });
2019-02-03 14:06:13 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_SELECT_ALL , [ this ]( SimpleEvent & ) { this -> q -> select_all (); });
2020-04-23 18:47:51 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_QUESTION_MARK , []( SimpleEvent & ) { wxGetApp (). keyboard_shortcuts (); });
2019-08-06 18:16:02 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INCREASE_INSTANCES , [ this ]( Event < int > & evt )
2018-12-19 12:07:45 +01:00
{ if ( evt . data == 1 ) this -> q -> increase_instances (); else if ( this -> can_decrease_instances ()) this -> q -> decrease_instances (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INSTANCE_MOVED , [ this ]( SimpleEvent & ) { update (); });
2020-02-04 13:03:28 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_FORCE_UPDATE , [ this ]( SimpleEvent & ) { update (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_WIPETOWER_MOVED , & priv :: on_wipetower_moved , this );
2019-04-26 15:34:26 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_WIPETOWER_ROTATED , & priv :: on_wipetower_rotated , this );
2019-01-03 11:24:03 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INSTANCE_ROTATED , [ this ]( SimpleEvent & ) { update (); });
view3D_canvas -> Bind ( EVT_GLCANVAS_INSTANCE_SCALED , [ this ]( SimpleEvent & ) { update (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , [ this ]( Event < bool > & evt ) { this -> sidebar -> enable_buttons ( evt . data ); });
view3D_canvas -> Bind ( EVT_GLCANVAS_UPDATE_GEOMETRY , & priv :: on_update_geometry , this );
view3D_canvas -> Bind ( EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED , & priv :: on_3dcanvas_mouse_dragging_finished , this );
2019-02-21 11:54:18 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_TAB , [ this ]( SimpleEvent & ) { select_next_view_3D (); });
2019-03-27 14:16:38 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_RESETGIZMOS , [ this ]( SimpleEvent & ) { reset_all_gizmos (); });
2019-07-04 10:45:41 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_UNDO , [ this ]( SimpleEvent & ) { this -> undo (); });
view3D_canvas -> Bind ( EVT_GLCANVAS_REDO , [ this ]( SimpleEvent & ) { this -> redo (); });
2020-04-29 15:58:57 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_COLLAPSE_SIDEBAR , [ this ]( SimpleEvent & ) { this -> q -> collapse_sidebar ( ! this -> q -> is_sidebar_collapsed ()); });
2019-11-29 11:01:51 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE , [ this ]( SimpleEvent & ) { this -> view3D -> get_canvas3d () -> reset_layer_height_profile (); });
view3D_canvas -> Bind ( EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE , [ this ]( Event < float >& evt ) { this -> view3D -> get_canvas3d () -> adaptive_layer_height_profile ( evt . data ); });
view3D_canvas -> Bind ( EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE , [ this ]( HeightProfileSmoothEvent & evt ) { this -> view3D -> get_canvas3d () -> smooth_layer_height_profile ( evt . data ); });
2020-02-03 15:47:09 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_RELOAD_FROM_DISK , [ this ]( SimpleEvent & ) { this -> reload_all_from_disk (); });
2019-04-26 15:34:26 +02:00
2018-12-04 13:55:25 +01:00
// 3DScene/Toolbar:
view3D_canvas -> Bind ( EVT_GLTOOLBAR_ADD , & priv :: on_action_add , this );
view3D_canvas -> Bind ( EVT_GLTOOLBAR_DELETE , [ q ]( SimpleEvent & ) { q -> remove_selected (); });
2019-05-02 13:52:13 +02:00
view3D_canvas -> Bind ( EVT_GLTOOLBAR_DELETE_ALL , [ q ]( SimpleEvent & ) { q -> reset_with_confirm (); });
2020-04-23 18:47:51 +02:00
view3D_canvas -> Bind ( EVT_GLTOOLBAR_ARRANGE , [ this ]( SimpleEvent & ) { this -> q -> arrange (); });
2019-04-11 13:20:34 +02:00
view3D_canvas -> Bind ( EVT_GLTOOLBAR_COPY , [ q ]( SimpleEvent & ) { q -> copy_selection_to_clipboard (); });
view3D_canvas -> Bind ( EVT_GLTOOLBAR_PASTE , [ q ]( SimpleEvent & ) { q -> paste_from_clipboard (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLTOOLBAR_MORE , [ q ]( SimpleEvent & ) { q -> increase_instances (); });
view3D_canvas -> Bind ( EVT_GLTOOLBAR_FEWER , [ q ]( SimpleEvent & ) { q -> decrease_instances (); });
view3D_canvas -> Bind ( EVT_GLTOOLBAR_SPLIT_OBJECTS , & priv :: on_action_split_objects , this );
view3D_canvas -> Bind ( EVT_GLTOOLBAR_SPLIT_VOLUMES , & priv :: on_action_split_volumes , this );
view3D_canvas -> Bind ( EVT_GLTOOLBAR_LAYERSEDITING , & priv :: on_action_layersediting , this );
2019-07-18 14:39:19 +02:00
view3D_canvas -> Bind ( EVT_GLCANVAS_UPDATE_BED_SHAPE , [ this ]( SimpleEvent & )
{
set_bed_shape ( config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ,
2019-07-24 14:02:36 +02:00
config -> option < ConfigOptionString > ( "bed_custom_texture" ) -> value ,
config -> option < ConfigOptionString > ( "bed_custom_model" ) -> value );
2019-07-18 14:39:19 +02:00
});
2018-10-04 11:12:55 +02:00
2018-10-08 19:55:30 +02:00
// Preview events:
2018-12-22 10:02:42 +01:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_QUESTION_MARK , [ this ]( SimpleEvent & ) { wxGetApp (). keyboard_shortcuts (); });
2019-07-18 14:39:19 +02:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_UPDATE_BED_SHAPE , [ this ]( SimpleEvent & )
{
set_bed_shape ( config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ,
2019-07-24 14:02:36 +02:00
config -> option < ConfigOptionString > ( "bed_custom_texture" ) -> value ,
config -> option < ConfigOptionString > ( "bed_custom_model" ) -> value );
2019-07-18 14:39:19 +02:00
});
2019-02-21 11:54:18 +01:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_TAB , [ this ]( SimpleEvent & ) { select_next_view_3D (); });
2019-06-13 16:10:33 +02:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_MOVE_DOUBLE_SLIDER , [ this ]( wxKeyEvent & evt ) { preview -> move_double_slider ( evt ); });
2019-06-18 11:40:26 +02:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_EDIT_COLOR_CHANGE , [ this ]( wxKeyEvent & evt ) { preview -> edit_double_slider ( evt ); });
2019-01-03 12:59:06 +01:00
2018-11-20 13:22:26 +01:00
q -> Bind ( EVT_SLICING_COMPLETED , & priv :: on_slicing_completed , this );
2018-09-17 12:15:11 +02:00
q -> Bind ( EVT_PROCESS_COMPLETED , & priv :: on_process_completed , this );
2018-12-06 10:38:19 +01:00
q -> Bind ( EVT_GLVIEWTOOLBAR_3D , [ q ]( SimpleEvent & ) { q -> select_view_3D ( "3D" ); });
q -> Bind ( EVT_GLVIEWTOOLBAR_PREVIEW , [ q ]( SimpleEvent & ) { q -> select_view_3D ( "Preview" ); });
2018-09-17 12:15:11 +02:00
// Drop target:
q -> SetDropTarget ( new PlaterDropTarget ( q )); // if my understanding is right, wxWindow takes the owenership
q -> Layout ();
2018-12-20 12:51:42 +01:00
set_current_panel ( view3D );
2019-06-20 10:02:52 +02:00
// updates camera type from .ini file
2019-06-24 15:55:14 +02:00
camera . set_type ( get_config ( "use_perspective_camera" ));
2019-07-02 16:42:23 +02:00
2020-03-04 11:36:36 +01:00
// Load the 3DConnexion device database.
mouse3d_controller . load_config ( * wxGetApp (). app_config );
// Start the background thread to detect and connect to a HID device (Windows and Linux).
2020-03-13 14:19:02 +01:00
// Connect to a 3DConnextion driver (OSX).
2019-09-27 14:52:19 +02:00
mouse3d_controller . init ();
2020-03-13 14:19:02 +01:00
#ifdef _WIN32
// Register an USB HID (Human Interface Device) attach event. evt contains Win32 path to the USB device containing VID, PID and other info.
// This event wakes up the Mouse3DController's background thread to enumerate HID devices, if the VID of the callback event
// is one of the 3D Mouse vendors (3DConnexion or Logitech).
this -> q -> Bind ( EVT_HID_DEVICE_ATTACHED , [ this ]( HIDDeviceAttachedEvent & evt ) {
mouse3d_controller . device_attached ( evt . data );
});
#endif /* _WIN32 */
2019-09-27 14:52:19 +02:00
2020-03-06 15:10:58 +01:00
this -> q -> Bind ( EVT_REMOVABLE_DRIVE_EJECTED , [ this ]( RemovableDriveEjectEvent & evt ) {
2020-03-12 09:38:46 +01:00
if ( evt . data . second ) {
this -> show_action_buttons ( this -> ready_to_slice );
2020-04-01 09:48:56 +02:00
Slic3r :: GUI :: show_info ( this -> q , format_wxstr ( _L ( "Unmounting successful. The device %s(%s) can now be safely removed from the computer." ),
evt . data . first . name , evt . data . first . path ));
2020-03-12 09:38:46 +01:00
} else
2020-04-01 09:48:56 +02:00
Slic3r :: GUI :: show_info ( this -> q , format_wxstr ( _L ( "Ejecting of device %s(%s) has failed." ),
evt . data . first . name , evt . data . first . path ));
2020-03-06 15:10:58 +01:00
});
this -> q -> Bind ( EVT_REMOVABLE_DRIVES_CHANGED , [ this ]( RemovableDrivesChangedEvent & ) { this -> show_action_buttons ( this -> ready_to_slice ); });
// Start the background thread and register this window as a target for update events.
wxGetApp (). removable_drive_manager () -> init ( this -> q );
2020-03-13 14:19:02 +01:00
#ifdef _WIN32
// Trigger enumeration of removable media on Win32 notification.
this -> q -> Bind ( EVT_VOLUME_ATTACHED , [ this ]( VolumeAttachedEvent & evt ) { wxGetApp (). removable_drive_manager () -> volumes_changed (); });
this -> q -> Bind ( EVT_VOLUME_DETACHED , [ this ]( VolumeDetachedEvent & evt ) { wxGetApp (). removable_drive_manager () -> volumes_changed (); });
#endif /* _WIN32 */
2020-03-06 15:10:58 +01:00
2019-07-05 19:06:19 +02:00
// Initialize the Undo / Redo stack with a first snapshot.
2020-04-01 09:48:56 +02:00
this -> take_snapshot ( _L ( "New Project" ));
2020-04-29 10:50:28 +02:00
this -> q -> Bind ( EVT_LOAD_MODEL_OTHER_INSTANCE , [ this ]( LoadFromOtherInstanceEvent & evt ) {
BOOST_LOG_TRIVIAL ( debug ) << "received load from other instance event " ;
this -> load_files ( evt . data , true , true );
});
this -> q -> Bind ( EVT_INSTANCE_GO_TO_FRONT , [ this ]( InstanceGoToFrontEvent & ) {
BOOST_LOG_TRIVIAL ( debug ) << "prusaslicer window going forward" ;
//this code maximize app window on Fedora
2020-05-14 12:15:02 +02:00
{
wxGetApp (). mainframe -> Iconize ( false );
if ( wxGetApp (). mainframe -> IsMaximized ())
wxGetApp (). mainframe -> Maximize ( true );
else
wxGetApp (). mainframe -> Maximize ( false );
}
//this code maximize window on Ubuntu
{
wxGetApp (). mainframe -> Restore ();
wxGetApp (). GetTopWindow () -> SetFocus (); // focus on my window
wxGetApp (). GetTopWindow () -> Raise (); // bring window to front
wxGetApp (). GetTopWindow () -> Show ( true ); // show the window
}
2020-04-29 10:50:28 +02:00
});
wxGetApp (). other_instance_message_handler () -> init ( this -> q );
2020-04-29 14:56:31 +02:00
2020-03-16 12:49:39 +01:00
// collapse sidebar according to saved value
2020-05-05 17:23:58 +02:00
bool is_collapsed = wxGetApp (). app_config -> get ( "collapsed_sidebar" ) == "1" ;
sidebar -> collapse ( is_collapsed );
// Update an enable of the collapse_toolbar: if sidebar is collapsed, then collapse_toolbar should be visible
if ( is_collapsed )
wxGetApp (). app_config -> set ( "show_collapse_button" , "1" );
2018-09-17 12:15:11 +02:00
}
2019-06-27 15:23:03 +02:00
Plater :: priv ::~ priv ()
{
if ( config != nullptr )
delete config ;
}
2019-08-23 15:53:45 +02:00
void Plater :: priv :: update ( unsigned int flags )
2018-09-17 12:15:11 +02:00
{
2019-06-19 11:05:34 +02:00
// the following line, when enabled, causes flickering on NVIDIA graphics cards
// wxWindowUpdateLocker freeze_guard(q);
2018-11-21 13:32:24 +01:00
if ( get_config ( "autocenter" ) == "1" ) {
2018-10-01 16:48:08 +02:00
// auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
// const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values);
// const BoundingBox bed_shape_bb = bed_shape.bounding_box();
2018-10-03 16:18:23 +02:00
const Vec2d & bed_center = bed_shape_bb (). center ();
2018-10-01 16:48:08 +02:00
model . center_instances_around_point ( bed_center );
}
2018-12-22 10:02:42 +01:00
unsigned int update_status = 0 ;
2019-08-23 15:53:45 +02:00
if ( this -> printer_technology == ptSLA || ( flags & ( unsigned int ) UpdateParams :: FORCE_BACKGROUND_PROCESSING_UPDATE ))
2018-11-13 17:45:44 +01:00
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
2019-08-23 15:53:45 +02:00
update_status = this -> update_background_process ( false , flags & ( unsigned int ) UpdateParams :: POSTPONE_VALIDATION_ERROR_MESSAGE );
this -> view3D -> reload_scene ( false , flags & ( unsigned int ) UpdateParams :: FORCE_FULL_SCREEN_REFRESH );
2019-08-06 18:16:02 +02:00
this -> preview -> reload_print ();
2018-12-22 10:02:42 +01:00
if ( this -> printer_technology == ptSLA )
this -> restart_background_process ( update_status );
else
this -> schedule_background_process ();
2018-09-17 12:15:11 +02:00
}
2018-12-04 13:55:25 +01:00
void Plater :: priv :: select_view ( const std :: string & direction )
{
if ( current_panel == view3D )
view3D -> select_view ( direction );
else if ( current_panel == preview )
preview -> select_view ( direction );
}
void Plater :: priv :: select_view_3D ( const std :: string & name )
{
if ( name == "3D" )
set_current_panel ( view3D );
else if ( name == "Preview" )
set_current_panel ( preview );
}
2018-12-21 10:43:22 +01:00
void Plater :: priv :: select_next_view_3D ()
{
if ( current_panel == view3D )
set_current_panel ( preview );
else if ( current_panel == preview )
set_current_panel ( view3D );
}
2018-10-17 12:59:58 +02:00
2019-03-27 14:16:38 +01:00
void Plater :: priv :: reset_all_gizmos ()
{
view3D -> get_canvas3d () -> reset_all_gizmos ();
}
2018-12-04 11:14:39 +01:00
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
2018-09-17 12:15:11 +02:00
void Plater :: priv :: update_ui_from_settings ()
{
2020-03-09 15:23:29 +01:00
camera . set_type ( wxGetApp (). app_config -> get ( "use_perspective_camera" ));
if ( wxGetApp (). app_config -> get ( "use_free_camera" ) != "1" )
2020-03-10 13:02:39 +01:00
camera . recover_from_free_camera ();
2020-03-09 15:23:29 +01:00
2019-01-24 13:16:46 +01:00
view3D -> get_canvas3d () -> update_ui_from_settings ();
preview -> get_canvas3d () -> update_ui_from_settings ();
2020-04-29 19:10:13 +02:00
sidebar -> update_ui_from_settings ();
2018-09-17 12:15:11 +02:00
}
2020-05-05 17:23:58 +02:00
// Called after the print technology was changed.
// Update the tooltips for "Switch to Settings" button in maintoolbar
void Plater :: priv :: update_main_toolbar_tooltips ()
{
view3D -> get_canvas3d () -> update_tooltip_for_settings_item_in_main_toolbar ();
}
2019-12-16 09:47:31 +01:00
std :: shared_ptr < ProgressStatusBar > Plater :: priv :: statusbar ()
2018-09-17 12:15:11 +02:00
{
2019-12-16 09:47:31 +01:00
return main_frame -> m_statusbar ;
2018-09-17 12:15:11 +02:00
}
2018-10-01 16:48:08 +02:00
std :: string Plater :: priv :: get_config ( const std :: string & key ) const
{
return wxGetApp (). app_config -> get ( key );
}
2018-10-03 16:18:23 +02:00
BoundingBoxf Plater :: priv :: bed_shape_bb () const
{
BoundingBox bb = scaled_bed_shape_bb ();
return BoundingBoxf ( unscale ( bb . min ), unscale ( bb . max ));
}
BoundingBox Plater :: priv :: scaled_bed_shape_bb () const
2018-10-01 16:48:08 +02:00
{
const auto * bed_shape_opt = config -> opt < ConfigOptionPoints > ( "bed_shape" );
const auto bed_shape = Slic3r :: Polygon :: new_scale ( bed_shape_opt -> values );
return bed_shape . bounding_box ();
}
2020-04-29 19:10:13 +02:00
std :: vector < size_t > Plater :: priv :: load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config , bool imperial_units /* = false*/ )
2018-09-17 12:15:11 +02:00
{
2018-10-04 11:12:55 +02:00
if ( input_files . empty ()) { return std :: vector < size_t > (); }
2018-09-17 12:15:11 +02:00
auto * nozzle_dmrs = config -> opt < ConfigOptionFloats > ( "nozzle_diameter" );
2020-02-14 14:44:17 +01:00
bool one_by_one = input_files . size () == 1 || printer_technology == ptSLA || nozzle_dmrs -> values . size () <= 1 ;
2018-09-17 12:15:11 +02:00
if ( ! one_by_one ) {
for ( const auto & path : input_files ) {
if ( std :: regex_match ( path . string (), pattern_bundle )) {
one_by_one = true ;
break ;
}
}
}
2020-04-01 09:48:56 +02:00
const auto loading = _L ( "Loading" ) + dots ;
2018-09-17 12:15:11 +02:00
wxProgressDialog dlg ( loading , loading );
dlg . Pulse ();
2018-11-16 09:26:41 +01:00
auto * new_model = ( ! load_model || one_by_one ) ? nullptr : new Slic3r :: Model ();
2018-10-01 16:48:08 +02:00
std :: vector < size_t > obj_idxs ;
2018-09-17 12:15:11 +02:00
for ( size_t i = 0 ; i < input_files . size (); i ++ ) {
const auto & path = input_files [ i ];
const auto filename = path . filename ();
2020-04-01 09:48:56 +02:00
const auto dlg_info = format_wxstr ( _L ( "Processing input file %s" ), from_path ( filename )) + " \n " ;
2018-09-17 12:15:11 +02:00
dlg . Update ( 100 * i / input_files . size (), dlg_info );
const bool type_3mf = std :: regex_match ( path . string (), pattern_3mf );
const bool type_zip_amf = ! type_3mf && std :: regex_match ( path . string (), pattern_zip_amf );
2019-01-03 12:04:14 +01:00
const bool type_any_amf = ! type_3mf && std :: regex_match ( path . string (), pattern_any_amf );
2019-01-18 16:01:43 +01:00
const bool type_prusa = std :: regex_match ( path . string (), pattern_prusa );
2018-09-17 12:15:11 +02:00
Slic3r :: Model model ;
2019-05-22 15:30:35 +02:00
bool is_project_file = type_prusa ;
2018-09-17 12:15:11 +02:00
try {
if ( type_3mf || type_zip_amf ) {
DynamicPrintConfig config ;
2018-11-06 19:09:54 +01:00
{
DynamicPrintConfig config_loaded ;
2019-08-23 13:12:31 +02:00
model = Slic3r :: Model :: read_from_archive ( path . string (), & config_loaded , false , load_config );
2018-11-16 09:26:41 +01:00
if ( load_config && ! config_loaded . empty ()) {
// Based on the printer technology field found in the loaded config, select the base for the config,
2019-08-06 18:16:02 +02:00
PrinterTechnology printer_technology = Preset :: printer_technology ( config_loaded );
2019-06-17 17:04:19 +02:00
// We can't to load SLA project if there is at least one multi-part object on the bed
if ( printer_technology == ptSLA )
{
const ModelObjectPtrs & objects = q -> model (). objects ;
for ( auto object : objects )
if ( object -> volumes . size () > 1 )
{
Slic3r :: GUI :: show_info ( nullptr ,
2020-04-01 09:48:56 +02:00
_L ( "You cannot load SLA project with a multi-part object on the bed" ) + " \n\n " +
_L ( "Please check your object list before preset changing." ),
_L ( "Attention!" ));
2019-06-17 17:04:19 +02:00
return obj_idxs ;
}
}
2019-08-06 18:16:02 +02:00
config . apply ( printer_technology == ptFFF ?
static_cast < const ConfigBase &> ( FullPrintConfig :: defaults ()) :
2018-11-06 19:09:54 +01:00
static_cast < const ConfigBase &> ( SLAFullPrintConfig :: defaults ()));
// and place the loaded config over the base.
config += std :: move ( config_loaded );
}
2019-11-11 16:01:34 +01:00
2019-12-17 08:37:50 +01:00
this -> model . custom_gcode_per_print_z = model . custom_gcode_per_print_z ;
2018-11-06 19:09:54 +01:00
}
2018-12-03 15:06:02 +01:00
2018-11-16 09:26:41 +01:00
if ( load_config )
{
if ( ! config . empty ()) {
Preset :: normalize ( config );
wxGetApp (). preset_bundle -> load_config_model ( filename . string (), std :: move ( config ));
wxGetApp (). load_current_presets ();
2019-05-22 15:30:35 +02:00
is_project_file = true ;
2018-11-16 09:26:41 +01:00
}
wxGetApp (). app_config -> update_config_dir ( path . parent_path (). string ());
}
}
else {
2019-08-23 13:12:31 +02:00
model = Slic3r :: Model :: read_from_file ( path . string (), nullptr , false , load_config );
2018-10-19 11:02:50 +02:00
for ( auto obj : model . objects )
if ( obj -> name . empty ())
obj -> name = fs :: path ( obj -> input_file ). filename (). string ();
2018-09-17 12:15:11 +02:00
}
2018-12-03 13:14:28 +01:00
} catch ( const std :: exception & e ) {
2018-09-17 12:15:11 +02:00
GUI :: show_error ( q , e . what ());
continue ;
}
2018-11-16 09:26:41 +01:00
if ( load_model )
{
// The model should now be initialized
2018-09-17 12:15:11 +02:00
2020-04-29 19:10:13 +02:00
auto convert_from_imperial_units = []( Model & model ) {
model . convert_from_imperial_units ();
wxGetApp (). app_config -> set ( "use_inches" , "1" );
wxGetApp (). sidebar (). update_ui_from_settings ();
};
if ( imperial_units )
convert_from_imperial_units ( model );
else if ( model . looks_like_imperial_units ()) {
wxMessageDialog msg_dlg ( q , _L (
"This model looks like saved in inches. \n "
"Should I consider this model as a saved in inches and convert it?" ) + " \n " ,
_L ( "Saved in inches object detected" ), wxICON_WARNING | wxYES | wxNO );
if ( msg_dlg . ShowModal () == wxID_YES )
convert_from_imperial_units ( model );
}
2019-05-22 15:30:35 +02:00
if ( ! is_project_file ) {
2019-01-18 12:52:09 +01:00
if ( model . looks_like_multipart_object ()) {
2020-04-01 09:48:56 +02:00
wxMessageDialog msg_dlg ( q , _L (
2019-12-04 11:12:28 +01:00
"This file contains several objects positioned at multiple heights. \n "
2019-01-21 12:34:28 +01:00
"Instead of considering them as multiple objects, should I consider \n "
2020-04-01 09:48:56 +02:00
"this file as a single object having multiple parts?" ) + " \n " ,
_L ( "Multi-part object detected" ), wxICON_WARNING | wxYES | wxNO );
2019-09-03 10:27:16 +02:00
if ( msg_dlg . ShowModal () == wxID_YES ) {
2019-01-21 14:43:57 +01:00
model . convert_multipart_object ( nozzle_dmrs -> values . size ());
}
2019-01-18 12:52:09 +01:00
}
2018-09-17 12:15:11 +02:00
}
2019-07-18 17:41:47 +02:00
else if (( wxGetApp (). get_mode () == comSimple ) && ( type_3mf || type_any_amf ) && model_has_advanced_features ( model )) {
2020-04-01 09:48:56 +02:00
wxMessageDialog msg_dlg ( q , _L ( "This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?" ) + " \n " ,
_L ( "Detected advanced data" ), wxICON_WARNING | wxYES | wxNO );
2019-09-03 10:27:16 +02:00
if ( msg_dlg . ShowModal () == wxID_YES )
2019-04-04 12:02:13 +02:00
{
2019-07-18 17:41:47 +02:00
Slic3r :: GUI :: wxGetApp (). save_mode ( comAdvanced );
view3D -> set_as_dirty ();
2019-04-04 12:02:13 +02:00
}
2019-07-18 17:41:47 +02:00
else
return obj_idxs ;
2019-04-04 12:02:13 +02:00
}
2018-09-17 12:15:11 +02:00
2019-07-18 17:41:47 +02:00
for ( ModelObject * model_object : model . objects ) {
2019-09-17 12:50:54 +02:00
if ( ! type_3mf && ! type_zip_amf )
model_object -> center_around_origin ( false );
2019-07-18 17:41:47 +02:00
model_object -> ensure_on_bed ();
}
2018-09-17 12:15:11 +02:00
2018-12-10 13:39:56 +01:00
// check multi-part object adding for the SLA-printing
if ( printer_technology == ptSLA )
{
for ( auto obj : model . objects )
if ( obj -> volumes . size () > 1 ) {
2019-08-06 18:16:02 +02:00
Slic3r :: GUI :: show_error ( nullptr ,
2020-04-01 09:48:56 +02:00
format_wxstr ( _L ( "You can't to add the object(s) from %s because of one or some of them is(are) multi-part" ),
from_path ( filename )));
2019-04-04 12:02:13 +02:00
return obj_idxs ;
2018-12-10 13:39:56 +01:00
}
}
2018-11-16 09:26:41 +01:00
if ( one_by_one ) {
auto loaded_idxs = load_model_objects ( model . objects );
obj_idxs . insert ( obj_idxs . end (), loaded_idxs . begin (), loaded_idxs . end ());
} else {
// This must be an .stl or .obj file, which may contain a maximum of one volume.
for ( const ModelObject * model_object : model . objects ) {
new_model -> add_object ( * model_object );
}
2018-09-17 12:15:11 +02:00
}
}
}
2019-08-05 17:49:21 +02:00
if ( new_model != nullptr && new_model -> objects . size () > 1 ) {
2020-04-01 09:48:56 +02:00
wxMessageDialog msg_dlg ( q , _L (
2018-09-17 12:15:11 +02:00
"Multiple objects were loaded for a multi-material printer. \n "
"Instead of considering them as multiple objects, should I consider \n "
2020-04-01 09:48:56 +02:00
"these files to represent a single object having multiple parts?" ) + " \n " ,
_L ( "Multi-part object detected" ), wxICON_WARNING | wxYES | wxNO );
2019-09-03 10:27:16 +02:00
if ( msg_dlg . ShowModal () == wxID_YES ) {
2018-09-17 12:15:11 +02:00
new_model -> convert_multipart_object ( nozzle_dmrs -> values . size ());
}
2018-10-19 11:02:50 +02:00
auto loaded_idxs = load_model_objects ( new_model -> objects );
2018-10-01 16:48:08 +02:00
obj_idxs . insert ( obj_idxs . end (), loaded_idxs . begin (), loaded_idxs . end ());
2018-09-17 12:15:11 +02:00
}
2018-11-16 09:26:41 +01:00
if ( load_model )
{
wxGetApp (). app_config -> update_skein_dir ( input_files [ input_files . size () - 1 ]. parent_path (). string ());
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
2020-04-01 09:48:56 +02:00
statusbar () -> set_status_text ( _L ( "Loaded" ));
2018-11-16 09:26:41 +01:00
}
2018-12-03 15:06:02 +01:00
2019-01-03 09:12:50 +01:00
// automatic selection of added objects
if ( ! obj_idxs . empty () && ( view3D != nullptr ))
{
2019-08-06 14:54:38 +02:00
// update printable state for new volumes on canvas3D
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_objects ( obj_idxs );
2019-03-19 13:30:21 +01:00
Selection & selection = view3D -> get_canvas3d () -> get_selection ();
2019-01-03 09:12:50 +01:00
selection . clear ();
for ( size_t idx : obj_idxs )
{
selection . add_object (( unsigned int ) idx , false );
}
2019-09-09 15:55:04 +02:00
2020-03-14 09:42:24 +01:00
if ( view3D -> get_canvas3d () -> get_gizmos_manager (). is_enabled ())
2019-09-09 15:55:04 +02:00
// this is required because the selected object changed and the flatten on face an sla support gizmos need to be updated accordingly
view3D -> get_canvas3d () -> update_gizmos_on_off_state ();
2019-01-03 09:12:50 +01:00
}
2018-10-01 16:48:08 +02:00
return obj_idxs ;
2018-09-17 12:15:11 +02:00
}
2019-01-28 10:56:02 +01:00
// #define AUTOPLACEMENT_ON_LOAD
2018-10-04 11:12:55 +02:00
std :: vector < size_t > Plater :: priv :: load_model_objects ( const ModelObjectPtrs & model_objects )
2018-09-17 12:15:11 +02:00
{
2018-10-03 16:18:23 +02:00
const BoundingBoxf bed_shape = bed_shape_bb ();
2018-12-19 09:54:15 +01:00
const Vec3d bed_size = Slic3r :: to_3d ( bed_shape . size (). cast < double > (), 1.0 ) - 2.0 * Vec3d :: Ones ();
2018-09-17 12:15:11 +02:00
2019-01-28 10:56:02 +01:00
#ifndef AUTOPLACEMENT_ON_LOAD
bool need_arrange = false ;
#endif /* AUTOPLACEMENT_ON_LOAD */
2018-09-17 12:15:11 +02:00
bool scaled_down = false ;
2018-10-01 16:48:08 +02:00
std :: vector < size_t > obj_idxs ;
2018-10-19 11:02:50 +02:00
unsigned int obj_count = model . objects . size ();
2018-09-17 12:15:11 +02:00
2019-01-28 10:56:02 +01:00
#ifdef AUTOPLACEMENT_ON_LOAD
2019-01-22 17:50:33 +01:00
ModelInstancePtrs new_instances ;
2019-01-28 10:56:02 +01:00
#endif /* AUTOPLACEMENT_ON_LOAD */
2018-10-01 16:48:08 +02:00
for ( ModelObject * model_object : model_objects ) {
2018-09-17 12:15:11 +02:00
auto * object = model . add_object ( * model_object );
std :: string object_name = object -> name . empty () ? fs :: path ( object -> input_file ). filename (). string () : object -> name ;
2018-10-17 09:30:07 +02:00
obj_idxs . push_back ( obj_count ++ );
2018-09-17 12:15:11 +02:00
2018-10-04 11:12:55 +02:00
if ( model_object -> instances . empty ()) {
2019-01-28 10:56:02 +01:00
#ifdef AUTOPLACEMENT_ON_LOAD
2019-01-22 17:50:33 +01:00
object -> center_around_origin ();
new_instances . emplace_back ( object -> add_instance ());
2019-01-28 10:56:02 +01:00
#else /* AUTOPLACEMENT_ON_LOAD */
2019-03-25 12:07:43 +01:00
// if object has no defined position(s) we need to rearrange everything after loading
need_arrange = true ;
2019-08-06 18:16:02 +02:00
// add a default instance and center object around origin
object -> center_around_origin (); // also aligns object to Z = 0
ModelInstance * instance = object -> add_instance ();
2019-01-28 10:56:02 +01:00
instance -> set_offset ( Slic3r :: to_3d ( bed_shape . center (). cast < double > (), - object -> origin_translation ( 2 )));
#endif /* AUTOPLACEMENT_ON_LOAD */
2018-09-17 12:15:11 +02:00
}
2018-10-01 16:48:08 +02:00
const Vec3d size = object -> bounding_box (). size ();
const Vec3d ratio = size . cwiseQuotient ( bed_size );
const double max_ratio = std :: max ( ratio ( 0 ), ratio ( 1 ));
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
2019-08-06 18:16:02 +02:00
double inv = 1. / max_ratio ;
2019-07-02 16:42:23 +02:00
object -> scale_mesh_after_creation ( Vec3d ( inv , inv , inv ));
2018-12-19 09:54:15 +01:00
object -> origin_translation = Vec3d :: Zero ();
object -> center_around_origin ();
2018-10-01 16:48:08 +02:00
scaled_down = true ;
} else if ( max_ratio > 5 ) {
2019-01-25 13:30:01 +01:00
const Vec3d inverse = 1.0 / max_ratio * Vec3d :: Ones ();
for ( ModelInstance * instance : object -> instances ) {
2018-10-01 16:48:08 +02:00
instance -> set_scaling_factor ( inverse );
}
2019-01-25 13:30:01 +01:00
scaled_down = true ;
2018-10-01 16:48:08 +02:00
}
2018-10-10 14:43:07 +02:00
2018-10-30 16:03:03 +01:00
object -> ensure_on_bed ();
2018-09-17 12:15:11 +02:00
}
2019-01-28 10:56:02 +01:00
#ifdef AUTOPLACEMENT_ON_LOAD
2019-01-23 13:12:44 +01:00
// FIXME distance should be a config value /////////////////////////////////
2019-01-22 17:50:33 +01:00
auto min_obj_distance = static_cast < coord_t > ( 6 / SCALING_FACTOR );
const auto * bed_shape_opt = config -> opt < ConfigOptionPoints > ( "bed_shape" );
assert ( bed_shape_opt );
auto & bedpoints = bed_shape_opt -> values ;
Polyline bed ; bed . points . reserve ( bedpoints . size ());
for ( auto & v : bedpoints ) bed . append ( Point :: new_scale ( v ( 0 ), v ( 1 )));
2019-08-06 18:16:02 +02:00
2019-06-27 21:13:44 +02:00
std :: pair < bool , GLCanvas3D :: WipeTowerInfo > wti = view3D -> get_canvas3d () -> get_wipe_tower_info ();
2019-08-06 18:16:02 +02:00
2019-04-29 14:32:02 +02:00
arr :: find_new_position ( model , new_instances , min_obj_distance , bed , wti );
// it remains to move the wipe tower:
view3D -> get_canvas3d () -> arrange_wipe_tower ( wti );
2019-01-28 10:56:02 +01:00
#endif /* AUTOPLACEMENT_ON_LOAD */
2019-01-22 17:50:33 +01:00
2018-10-01 16:48:08 +02:00
if ( scaled_down ) {
GUI :: show_info ( q ,
2020-04-01 09:48:56 +02:00
_L ( "Your object appears to be too large, so it was automatically scaled down to fit your print bed." ),
_L ( "Object too large?" ));
2018-10-01 16:48:08 +02:00
}
2019-09-23 15:35:49 +02:00
for ( const size_t idx : obj_idxs ) {
wxGetApp (). obj_list () -> add_object_to_list ( idx );
2018-10-01 16:48:08 +02:00
}
update ();
2018-10-11 13:22:36 +02:00
object_list_changed ();
2018-10-17 11:12:38 +02:00
this -> schedule_background_process ();
2018-10-01 16:48:08 +02:00
return obj_idxs ;
2018-09-17 12:15:11 +02:00
}
2019-04-04 11:31:26 +02:00
wxString Plater :: priv :: get_export_file ( GUI :: FileType file_type )
2018-10-04 11:12:55 +02:00
{
wxString wildcard ;
switch ( file_type ) {
case FT_STL :
case FT_AMF :
case FT_3MF :
2018-10-19 16:52:41 +02:00
case FT_GCODE :
2019-08-20 09:01:09 +02:00
case FT_OBJ :
2018-12-06 17:32:49 +01:00
wildcard = file_wildcards ( file_type );
2018-10-04 11:12:55 +02:00
break ;
default :
2018-12-06 17:32:49 +01:00
wildcard = file_wildcards ( FT_MODEL );
2018-10-04 11:12:55 +02:00
break ;
}
2018-12-03 13:14:28 +01:00
// Update printbility state of each of the ModelInstances.
this -> update_print_volume_state ();
2019-04-04 14:00:31 +02:00
const Selection & selection = get_selection ();
int obj_idx = selection . get_object_idx ();
fs :: path output_file ;
2019-05-14 15:00:24 +02:00
if ( file_type == FT_3MF )
// for 3mf take the path from the project filename, if any
2019-05-15 14:09:16 +02:00
output_file = into_path ( get_project_filename ( ".3mf" ));
2019-04-04 14:00:31 +02:00
if ( output_file . empty ())
2019-05-14 15:00:24 +02:00
{
// first try to get the file name from the current selection
if (( 0 <= obj_idx ) && ( obj_idx < ( int ) this -> model . objects . size ()))
output_file = this -> model . objects [ obj_idx ] -> get_export_filename ();
if ( output_file . empty ())
// Find the file name of the first printable object.
output_file = this -> model . propose_export_file_name_and_path ();
2019-09-19 14:01:17 +02:00
if ( output_file . empty () && ! model . objects . empty ())
// Find the file name of the first object.
output_file = this -> model . objects [ 0 ] -> get_export_filename ();
2019-05-14 15:00:24 +02:00
}
2018-10-04 11:12:55 +02:00
2019-04-04 11:31:26 +02:00
wxString dlg_title ;
2018-10-04 11:12:55 +02:00
switch ( file_type ) {
2019-04-04 11:31:26 +02:00
case FT_STL :
{
output_file . replace_extension ( "stl" );
2020-04-01 09:48:56 +02:00
dlg_title = _L ( "Export STL file:" );
2019-04-04 11:31:26 +02:00
break ;
}
case FT_AMF :
{
// XXX: Problem on OS X with double extension?
output_file . replace_extension ( "zip.amf" );
2020-04-01 09:48:56 +02:00
dlg_title = _L ( "Export AMF file:" );
2019-04-04 11:31:26 +02:00
break ;
}
case FT_3MF :
{
output_file . replace_extension ( "3mf" );
2020-04-01 09:48:56 +02:00
dlg_title = _L ( "Save file as:" );
2019-04-04 11:31:26 +02:00
break ;
}
2019-08-20 09:01:09 +02:00
case FT_OBJ :
{
output_file . replace_extension ( "obj" );
2020-04-01 09:48:56 +02:00
dlg_title = _L ( "Export OBJ file:" );
2019-08-20 09:01:09 +02:00
break ;
}
2018-10-04 11:12:55 +02:00
default : break ;
}
2019-08-15 12:52:56 +02:00
wxFileDialog dlg ( q , dlg_title ,
2019-04-04 11:31:26 +02:00
from_path ( output_file . parent_path ()), from_path ( output_file . filename ()),
wildcard , wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2018-10-04 11:12:55 +02:00
2019-08-15 12:52:56 +02:00
if ( dlg . ShowModal () != wxID_OK )
2019-04-04 11:31:26 +02:00
return wxEmptyString ;
2018-10-04 11:12:55 +02:00
2019-08-15 12:52:56 +02:00
wxString out_path = dlg . GetPath ();
2019-04-04 11:31:26 +02:00
fs :: path path ( into_path ( out_path ));
2018-10-04 11:12:55 +02:00
wxGetApp (). app_config -> update_last_output_dir ( path . parent_path (). string ());
2019-04-04 11:31:26 +02:00
return out_path ;
2018-10-04 11:12:55 +02:00
}
2019-03-19 13:30:21 +01:00
const Selection & Plater :: priv :: get_selection () const
2018-10-17 09:30:07 +02:00
{
2018-12-04 13:55:25 +01:00
return view3D -> get_canvas3d () -> get_selection ();
2018-10-17 09:30:07 +02:00
}
2019-03-19 13:30:21 +01:00
Selection & Plater :: priv :: get_selection ()
2018-10-17 09:30:07 +02:00
{
2018-12-04 13:55:25 +01:00
return view3D -> get_canvas3d () -> get_selection ();
2018-10-17 09:30:07 +02:00
}
int Plater :: priv :: get_selected_object_idx () const
{
int idx = get_selection (). get_object_idx ();
return (( 0 <= idx ) && ( idx < 1000 )) ? idx : - 1 ;
}
2018-10-04 11:12:55 +02:00
2018-12-06 14:49:14 +01:00
int Plater :: priv :: get_selected_volume_idx () const
{
auto & selection = get_selection ();
int idx = selection . get_object_idx ();
2019-08-06 18:16:02 +02:00
if (( 0 > idx ) || ( idx > 1000 ))
2018-12-06 14:49:14 +01:00
return - 1 ;
const GLVolume * v = selection . get_volume ( * selection . get_volume_idxs (). begin ());
if ( model . objects [ idx ] -> volumes . size () > 1 )
return v -> volume_idx ();
return - 1 ;
}
2018-10-04 11:12:55 +02:00
void Plater :: priv :: selection_changed ()
{
2019-01-24 15:50:06 +01:00
// if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running
bool enable_layer_editing = layers_height_allowed ();
2019-01-25 08:37:06 +01:00
if ( ! enable_layer_editing && view3D -> is_layers_editing_enabled ()) {
SimpleEvent evt ( EVT_GLTOOLBAR_LAYERSEDITING );
on_action_layersediting ( evt );
}
2019-01-24 15:50:06 +01:00
2018-12-04 13:55:25 +01:00
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
view3D -> render ();
2018-10-04 11:12:55 +02:00
}
2018-10-08 19:14:55 +02:00
void Plater :: priv :: object_list_changed ()
2018-10-04 11:12:55 +02:00
{
2018-10-23 15:27:31 +02:00
const bool export_in_progress = this -> background_process . is_export_scheduled (); // || ! send_gcode_file.empty());
2018-10-08 19:14:55 +02:00
// XXX: is this right?
2018-12-04 13:55:25 +01:00
const bool model_fits = view3D -> check_volumes_outside_state () == ModelInstance :: PVS_Inside ;
2018-10-08 19:14:55 +02:00
2018-10-18 15:09:41 +02:00
sidebar -> enable_buttons ( ! model . objects . empty () && ! export_in_progress && model_fits );
2018-10-08 19:14:55 +02:00
}
2018-11-21 15:28:35 +01:00
void Plater :: priv :: select_all ()
{
2018-12-04 13:55:25 +01:00
view3D -> select_all ();
2018-12-03 13:29:07 +01:00
this -> sidebar -> obj_list () -> update_selections ();
2018-11-21 15:28:35 +01:00
}
2019-05-14 11:57:39 +02:00
void Plater :: priv :: deselect_all ()
{
view3D -> deselect_all ();
}
2018-10-08 19:14:55 +02:00
void Plater :: priv :: remove ( size_t obj_idx )
{
// Prevent toolpaths preview from rendering while we modify the Print object
preview -> set_enabled ( false );
2018-12-04 13:55:25 +01:00
if ( view3D -> is_layers_editing_enabled ())
view3D -> enable_layers_editing ( false );
2018-10-22 15:18:56 +02:00
2018-10-08 19:14:55 +02:00
model . delete_object ( obj_idx );
update ();
2019-05-14 16:37:32 +02:00
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
sidebar -> obj_list () -> delete_object_from_list ( obj_idx );
2019-05-08 10:37:45 +02:00
object_list_changed ();
2018-10-04 11:12:55 +02:00
}
2018-11-13 14:17:35 +01:00
void Plater :: priv :: delete_object_from_model ( size_t obj_idx )
{
2020-04-01 09:48:56 +02:00
wxString snapshot_label = _L ( "Delete Object" );
2019-08-06 18:16:02 +02:00
if ( ! model . objects [ obj_idx ] -> name . empty ())
snapshot_label += ": " + wxString :: FromUTF8 ( model . objects [ obj_idx ] -> name . c_str ());
Plater :: TakeSnapshot snapshot ( q , snapshot_label );
2018-11-13 14:17:35 +01:00
model . delete_object ( obj_idx );
2018-11-14 11:22:13 +01:00
update ();
2019-05-08 10:37:45 +02:00
object_list_changed ();
2018-11-13 14:17:35 +01:00
}
2018-10-04 11:12:55 +02:00
void Plater :: priv :: reset ()
{
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( q , _L ( "Reset Project" ));
2019-07-02 16:42:23 +02:00
2019-05-14 15:00:24 +02:00
set_project_filename ( wxEmptyString );
2018-11-15 15:27:39 +01:00
2018-10-04 11:12:55 +02:00
// Prevent toolpaths preview from rendering while we modify the Print object
preview -> set_enabled ( false );
2018-12-04 13:55:25 +01:00
if ( view3D -> is_layers_editing_enabled ())
view3D -> enable_layers_editing ( false );
2018-10-22 15:18:56 +02:00
2018-11-08 14:23:17 +01:00
// Stop and reset the Print content.
this -> background_process . reset ();
2018-10-04 11:12:55 +02:00
model . clear_objects ();
update ();
2019-05-14 16:37:32 +02:00
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
sidebar -> obj_list () -> delete_all_objects_from_list ();
2019-05-08 10:37:45 +02:00
object_list_changed ();
2018-11-28 16:02:29 +01:00
2019-01-30 10:00:38 +01:00
// The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
this -> sidebar -> show_sliced_info_sizer ( false );
2018-11-28 16:02:29 +01:00
2020-01-16 16:01:19 +01:00
model . custom_gcode_per_print_z . gcodes . clear ();
2018-10-04 11:12:55 +02:00
}
2018-10-18 15:09:41 +02:00
void Plater :: priv :: mirror ( Axis axis )
2018-10-09 09:35:19 +02:00
{
2018-12-04 13:55:25 +01:00
view3D -> mirror_selection ( axis );
2018-10-09 09:35:19 +02:00
}
2020-04-23 18:47:51 +02:00
void Plater :: find_new_position ( const ModelInstancePtrs & instances ,
2019-07-16 18:33:42 +02:00
coord_t min_d )
2019-07-15 18:18:34 +02:00
{
arrangement :: ArrangePolygons movable , fixed ;
2020-04-23 18:47:51 +02:00
for ( const ModelObject * mo : p -> model . objects )
2019-07-15 18:18:34 +02:00
for ( const ModelInstance * inst : mo -> instances ) {
auto it = std :: find ( instances . begin (), instances . end (), inst );
auto arrpoly = inst -> get_arrange_polygon ();
2019-08-06 18:16:02 +02:00
2019-07-15 18:18:34 +02:00
if ( it == instances . end ())
fixed . emplace_back ( std :: move ( arrpoly ));
else
movable . emplace_back ( std :: move ( arrpoly ));
}
2020-04-23 18:47:51 +02:00
if ( p -> view3D -> get_canvas3d () -> get_wipe_tower_info ())
fixed . emplace_back ( get_wipe_tower_arrangepoly ( * this ));
arrangement :: arrange ( movable , fixed , get_bed_shape ( * config ()),
arrangement :: ArrangeParams { min_d });
2019-08-06 18:16:02 +02:00
2019-07-15 18:18:34 +02:00
for ( size_t i = 0 ; i < instances . size (); ++ i )
if ( movable [ i ]. bed_idx == 0 )
2020-03-30 13:26:24 +02:00
instances [ i ] -> apply_arrange_result ( movable [ i ]. translation . cast < double > (),
2019-07-17 13:32:31 +02:00
movable [ i ]. rotation );
2019-07-15 18:18:34 +02:00
}
2018-10-09 09:35:19 +02:00
void Plater :: priv :: split_object ()
{
2018-10-19 15:27:19 +02:00
int obj_idx = get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
2019-11-11 16:10:46 +01:00
2018-10-19 15:27:19 +02:00
// we clone model object because split_object() adds the split volumes
// into the same model object, thus causing duplicates when we call load_model_objects()
Model new_model = model ;
ModelObject * current_model_object = new_model . objects [ obj_idx ];
if ( current_model_object -> volumes . size () > 1 )
{
2020-04-01 09:48:56 +02:00
Slic3r :: GUI :: warning_catcher ( q , _L ( "The selected object can't be split because it contains more than one volume/material." ));
2018-10-19 15:27:19 +02:00
return ;
}
2019-02-05 20:23:24 +01:00
wxBusyCursor wait ;
2018-10-19 15:27:19 +02:00
ModelObjectPtrs new_objects ;
current_model_object -> split ( & new_objects );
if ( new_objects . size () == 1 )
2020-04-01 09:48:56 +02:00
Slic3r :: GUI :: warning_catcher ( q , _L ( "The selected object couldn't be split because it contains only one part." ));
2018-10-19 15:27:19 +02:00
else
{
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( q , _L ( "Split to Objects" ));
2019-07-04 17:33:19 +02:00
2018-10-24 12:55:38 +02:00
unsigned int counter = 1 ;
2018-10-19 15:27:19 +02:00
for ( ModelObject * m : new_objects )
2018-10-24 12:55:38 +02:00
m -> name = current_model_object -> name + "_" + std :: to_string ( counter ++ );
2018-10-19 15:27:19 +02:00
remove ( obj_idx );
// load all model objects at once, otherwise the plate would be rearranged after each one
// causing original positions not to be kept
2018-10-31 11:24:56 +01:00
std :: vector < size_t > idxs = load_model_objects ( new_objects );
// select newly added objects
for ( size_t idx : idxs )
{
get_selection (). add_object (( unsigned int ) idx , false );
}
2018-10-19 15:27:19 +02:00
}
2018-10-09 09:35:19 +02:00
}
2018-10-24 12:55:38 +02:00
void Plater :: priv :: split_volume ()
{
2018-12-06 14:49:14 +01:00
wxGetApp (). obj_list () -> split ();
2018-10-24 12:55:38 +02:00
}
2019-05-22 14:42:38 +02:00
void Plater :: priv :: scale_selection_to_fit_print_volume ()
{
this -> view3D -> get_canvas3d () -> get_selection (). scale_to_fit_print_volume ( * config );
}
2018-10-09 09:35:19 +02:00
void Plater :: priv :: schedule_background_process ()
{
2019-02-22 11:59:40 +01:00
delayed_error_message . clear ();
2018-10-17 11:12:38 +02:00
// Trigger the timer event after 0.5s
this -> background_process_timer . Start ( 500 , wxTIMER_ONE_SHOT );
2019-01-21 17:02:16 +01:00
// Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff.
this -> view3D -> get_canvas3d () -> set_config ( this -> config );
2018-10-09 09:35:19 +02:00
}
2018-12-03 13:14:28 +01:00
void Plater :: priv :: update_print_volume_state ()
{
BoundingBox bed_box_2D = get_extents ( Polygon :: new_scale ( this -> config -> opt < ConfigOptionPoints > ( "bed_shape" ) -> values ));
BoundingBoxf3 print_volume ( unscale ( bed_box_2D . min ( 0 ), bed_box_2D . min ( 1 ), 0.0 ), unscale ( bed_box_2D . max ( 0 ), bed_box_2D . max ( 1 ), scale_ ( this -> config -> opt_float ( "max_print_height" ))));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume . min ( 2 ) = - 1e10 ;
this -> q -> model (). update_print_volume_state ( print_volume );
}
2018-11-13 17:45:44 +01:00
// Update background processing thread from the current config and Model.
// Returns a bitmask of UpdateBackgroundProcessReturnState.
2019-08-23 15:53:45 +02:00
unsigned int Plater :: priv :: update_background_process ( bool force_validation , bool postpone_error_messages )
2018-10-09 09:35:19 +02:00
{
2018-11-13 17:45:44 +01:00
// bitmap of enum UpdateBackgroundProcessReturnState
unsigned int return_state = 0 ;
2019-08-06 18:16:02 +02:00
// If the update_background_process() was not called by the timer, kill the timer,
2018-12-22 10:02:42 +01:00
// so the update_restart_background_process() will not be called again in vain.
2018-11-13 17:45:44 +01:00
this -> background_process_timer . Stop ();
2018-12-03 13:14:28 +01:00
// Update the "out of print bed" state of ModelInstances.
this -> update_print_volume_state ();
2018-10-17 11:12:38 +02:00
// Apply new config to the possibly running background task.
2018-12-22 10:02:42 +01:00
bool was_running = this -> background_process . running ();
2018-12-03 13:14:28 +01:00
Print :: ApplyStatus invalidated = this -> background_process . apply ( this -> q -> model (), wxGetApp (). preset_bundle -> full_config ());
2018-11-08 16:01:21 +01:00
2018-10-17 11:12:38 +02:00
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
2018-12-04 13:55:25 +01:00
if ( view3D -> is_layers_editing_enabled ())
view3D -> get_wxglcanvas () -> Refresh ();
2018-10-23 22:53:43 +02:00
if ( invalidated == Print :: APPLY_STATUS_INVALIDATED ) {
// Some previously calculated data on the Print was invalidated.
// Hide the slicing results, as the current slicing status is no more valid.
2018-10-31 12:26:57 +01:00
this -> sidebar -> show_sliced_info_sizer ( false );
2018-10-23 22:53:43 +02:00
// Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
// Otherwise they will be just refreshed.
2019-08-06 18:16:02 +02:00
if ( this -> preview != nullptr )
// If the preview is not visible, the following line just invalidates the preview,
// but the G-code paths or SLA preview are calculated first once the preview is made visible.
this -> preview -> reload_print ();
// In FDM mode, we need to reload the 3D scene because of the wipe tower preview box.
// In SLA mode, we need to reload the 3D scene every time to show the support structures.
if ( this -> printer_technology == ptSLA || ( this -> printer_technology == ptFFF && this -> config -> opt_bool ( "wipe_tower" )))
return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE ;
2018-10-17 11:12:38 +02:00
}
2018-11-13 17:45:44 +01:00
2019-01-10 16:06:24 +01:00
if (( invalidated != Print :: APPLY_STATUS_UNCHANGED || force_validation ) && ! this -> background_process . empty ()) {
2019-08-23 15:53:45 +02:00
// The delayed error message is no more valid.
this -> delayed_error_message . clear ();
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
2018-11-13 17:45:44 +01:00
std :: string err = this -> background_process . validate ();
if ( err . empty ()) {
2019-08-06 18:16:02 +02:00
if ( invalidated != Print :: APPLY_STATUS_UNCHANGED && this -> background_processing_enabled ())
2018-11-13 17:45:44 +01:00
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART ;
} else {
// The print is not valid.
2019-02-19 14:57:59 +01:00
// Only show the error message immediately, if the top level parent of this window is active.
auto p = dynamic_cast < wxWindow *> ( this -> q );
while ( p -> GetParent ())
p = p -> GetParent ();
auto * top_level_wnd = dynamic_cast < wxTopLevelWindow *> ( p );
2019-08-23 15:53:45 +02:00
if ( ! postpone_error_messages && top_level_wnd && top_level_wnd -> IsActive ()) {
2019-02-19 14:57:59 +01:00
// The error returned from the Print needs to be translated into the local language.
2020-02-21 13:38:06 +01:00
GUI :: show_error ( this -> q , err );
2019-02-19 14:57:59 +01:00
} else {
// Show the error message once the main window gets activated.
2020-02-21 13:38:06 +01:00
this -> delayed_error_message = err ;
2019-02-19 14:57:59 +01:00
}
2018-11-13 17:45:44 +01:00
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID ;
}
2019-08-23 15:53:45 +02:00
} else if ( ! this -> delayed_error_message . empty ()) {
// Reusing the old state.
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID ;
2018-11-13 17:45:44 +01:00
}
2018-12-22 10:02:42 +01:00
2019-01-10 16:06:24 +01:00
if ( invalidated != Print :: APPLY_STATUS_UNCHANGED && was_running && ! this -> background_process . running () &&
2018-12-22 10:02:42 +01:00
( return_state & UPDATE_BACKGROUND_PROCESS_RESTART ) == 0 ) {
2019-08-06 18:16:02 +02:00
// The background processing was killed and it will not be restarted.
wxCommandEvent evt ( EVT_PROCESS_COMPLETED );
evt . SetInt ( - 1 );
// Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages.
wxQueueEvent ( GUI :: wxGetApp (). mainframe -> m_plater , evt . Clone ());
}
2018-12-22 10:02:42 +01:00
2019-03-01 11:00:34 +01:00
if (( return_state & UPDATE_BACKGROUND_PROCESS_INVALID ) != 0 )
{
2019-03-12 09:41:57 +01:00
// Validation of the background data failed.
2020-04-01 09:48:56 +02:00
const wxString invalid_str = _L ( "Invalid data" );
2019-03-01 11:00:34 +01:00
for ( auto btn : { ActionButtonType :: abReslice , ActionButtonType :: abSendGCode , ActionButtonType :: abExport })
sidebar -> set_btn_label ( btn , invalid_str );
}
else
{
2019-03-12 09:41:57 +01:00
// Background data is valid.
2019-03-01 11:00:34 +01:00
if (( return_state & UPDATE_BACKGROUND_PROCESS_RESTART ) != 0 ||
( return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE ) != 0 )
2020-04-01 09:48:56 +02:00
this -> statusbar () -> set_status_text ( _L ( "Ready to slice" ));
2019-03-01 11:00:34 +01:00
sidebar -> set_btn_label ( ActionButtonType :: abExport , _ ( label_btn_export ));
2019-03-12 09:41:57 +01:00
sidebar -> set_btn_label ( ActionButtonType :: abSendGCode , _ ( label_btn_send ));
2019-08-06 18:16:02 +02:00
const wxString slice_string = background_process . running () && wxGetApp (). get_mode () == comSimple ?
2020-04-01 09:48:56 +02:00
_L ( "Slicing" ) + dots : _L ( "Slice now" );
2019-03-01 11:00:34 +01:00
sidebar -> set_btn_label ( ActionButtonType :: abReslice , slice_string );
if ( background_process . finished ())
show_action_buttons ( false );
2019-08-06 18:16:02 +02:00
else if ( ! background_process . empty () &&
2019-03-11 10:23:59 +01:00
! background_process . running ()) /* Do not update buttons if background process is running
2019-08-06 18:16:02 +02:00
* This condition is important for SLA mode especially,
* when this function is called several times during calculations
2019-03-11 10:23:59 +01:00
* */
2019-03-01 11:00:34 +01:00
show_action_buttons ( true );
}
2018-11-13 17:45:44 +01:00
return return_state ;
2018-10-09 09:35:19 +02:00
}
2018-12-22 10:02:42 +01:00
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
bool Plater :: priv :: restart_background_process ( unsigned int state )
{
2019-06-18 16:24:30 +02:00
if ( m_ui_jobs . is_any_running ()) {
2019-04-17 14:56:00 +02:00
// Avoid a race condition
return false ;
}
2019-08-06 18:16:02 +02:00
if ( ! this -> background_process . empty () &&
( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ) == 0 &&
( (( state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART ) != 0 && ! this -> background_process . finished ()) ||
2018-12-22 10:02:42 +01:00
( state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT ) != 0 ||
( state & UPDATE_BACKGROUND_PROCESS_RESTART ) != 0 ) ) {
// The print is valid and it can be started.
if ( this -> background_process . start ()) {
this -> statusbar () -> set_cancel_callback ([ this ]() {
2020-04-01 09:48:56 +02:00
this -> statusbar () -> set_status_text ( _L ( "Cancelling" ));
2018-12-22 10:02:42 +01:00
this -> background_process . stop ();
});
return true ;
}
}
return false ;
}
2020-03-12 12:43:01 +01:00
void Plater :: priv :: export_gcode ( fs :: path output_path , bool output_path_on_removable_media , PrintHostJob upload_job )
2018-12-11 10:33:11 +01:00
{
wxCHECK_RET ( ! ( output_path . empty () && upload_job . empty ()), "export_gcode: output_path and upload_job empty" );
if ( model . objects . empty ())
return ;
if ( background_process . is_export_scheduled ()) {
2020-04-01 09:48:56 +02:00
GUI :: show_error ( q , _L ( "Another export job is currently running." ));
2018-12-11 10:33:11 +01:00
return ;
}
// bitmask of UpdateBackgroundProcessReturnState
2019-01-10 16:06:24 +01:00
unsigned int state = update_background_process ( true );
2018-12-11 10:33:11 +01:00
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE )
view3D -> reload_scene ( false );
2019-01-03 12:59:06 +01:00
2018-12-11 10:33:11 +01:00
if (( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ) != 0 )
return ;
if ( ! output_path . empty ()) {
2020-03-12 12:43:01 +01:00
background_process . schedule_export ( output_path . string (), output_path_on_removable_media );
2018-12-11 10:33:11 +01:00
} else {
background_process . schedule_upload ( std :: move ( upload_job ));
}
2019-02-25 10:21:12 +01:00
// If the SLA processing of just a single object's supports is running, restart slicing for the whole object.
this -> background_process . set_task ( PrintBase :: TaskParams ());
2018-12-22 10:02:42 +01:00
this -> restart_background_process ( priv :: UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT );
2018-12-11 10:33:11 +01:00
}
2019-02-03 10:41:14 +01:00
unsigned int Plater :: priv :: update_restart_background_process ( bool force_update_scene , bool force_update_preview )
2018-10-09 09:35:19 +02:00
{
2018-11-13 17:45:44 +01:00
// bitmask of UpdateBackgroundProcessReturnState
2019-01-10 16:06:24 +01:00
unsigned int state = this -> update_background_process ( false );
2018-12-22 10:02:42 +01:00
if ( force_update_scene || ( state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE ) != 0 )
2018-12-04 13:55:25 +01:00
view3D -> reload_scene ( false );
2019-01-03 12:59:06 +01:00
2018-12-22 10:02:42 +01:00
if ( force_update_preview )
this -> preview -> reload_print ();
this -> restart_background_process ( state );
2019-08-06 18:16:02 +02:00
return state ;
2018-10-09 09:35:19 +02:00
}
2018-12-06 15:40:41 +01:00
void Plater :: priv :: update_fff_scene ()
{
if ( this -> preview != nullptr )
this -> preview -> reload_print ();
// In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
2019-08-06 18:16:02 +02:00
view3D -> reload_scene ( true );
2019-12-12 15:43:14 +01:00
2018-12-06 15:40:41 +01:00
}
2018-11-23 12:47:32 +01:00
void Plater :: priv :: update_sla_scene ()
{
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
delayed_scene_refresh = false ;
2018-12-22 10:02:42 +01:00
this -> update_restart_background_process ( true , true );
2018-11-23 12:47:32 +01:00
}
2018-10-09 09:35:19 +02:00
void Plater :: priv :: reload_from_disk ()
{
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( q , _L ( "Reload from disk" ));
2019-07-05 19:46:48 +02:00
2019-09-19 09:09:11 +02:00
const Selection & selection = get_selection ();
2018-10-09 09:35:19 +02:00
2019-09-19 09:09:11 +02:00
if ( selection . is_wipe_tower ())
2019-09-06 14:36:47 +02:00
return ;
2018-12-05 15:22:03 +01:00
2019-09-19 09:09:11 +02:00
// struct to hold selected ModelVolumes by their indices
struct SelectedVolume
2019-09-11 15:02:57 +02:00
{
2019-09-19 09:09:11 +02:00
int object_idx ;
int volume_idx ;
2019-09-10 11:16:59 +02:00
2019-09-19 09:09:11 +02:00
// operators needed by std::algorithms
bool operator < ( const SelectedVolume & other ) const { return ( object_idx < other . object_idx ) || (( object_idx == other . object_idx ) && ( volume_idx < other . volume_idx )); }
bool operator == ( const SelectedVolume & other ) const { return ( object_idx == other . object_idx ) && ( volume_idx == other . volume_idx ); }
};
std :: vector < SelectedVolume > selected_volumes ;
// collects selected ModelVolumes
const std :: set < unsigned int >& selected_volumes_idxs = selection . get_volume_idxs ();
for ( unsigned int idx : selected_volumes_idxs )
{
const GLVolume * v = selection . get_volume ( idx );
int v_idx = v -> volume_idx ();
2019-11-25 14:42:36 +01:00
if ( v_idx >= 0 )
{
int o_idx = v -> object_idx ();
if (( 0 <= o_idx ) && ( o_idx < ( int ) model . objects . size ()))
selected_volumes . push_back ({ o_idx , v_idx });
}
2019-09-19 09:09:11 +02:00
}
std :: sort ( selected_volumes . begin (), selected_volumes . end ());
selected_volumes . erase ( std :: unique ( selected_volumes . begin (), selected_volumes . end ()), selected_volumes . end ());
// collects paths of files to load
std :: vector < fs :: path > input_paths ;
2019-12-10 14:42:28 +01:00
std :: vector < fs :: path > missing_input_paths ;
2019-09-19 09:09:11 +02:00
for ( const SelectedVolume & v : selected_volumes )
{
2019-12-10 14:42:28 +01:00
const ModelObject * object = model . objects [ v . object_idx ];
const ModelVolume * volume = object -> volumes [ v . volume_idx ];
if ( ! volume -> source . input_file . empty ())
{
if ( fs :: exists ( volume -> source . input_file ))
input_paths . push_back ( volume -> source . input_file );
else
missing_input_paths . push_back ( volume -> source . input_file );
}
2020-02-26 08:56:05 +01:00
else if ( ! object -> input_file . empty () && volume -> is_model_part () && ! volume -> name . empty ())
2020-01-21 14:07:13 +01:00
missing_input_paths . push_back ( volume -> name );
2019-09-19 09:09:11 +02:00
}
2019-12-10 14:42:28 +01:00
std :: sort ( missing_input_paths . begin (), missing_input_paths . end ());
missing_input_paths . erase ( std :: unique ( missing_input_paths . begin (), missing_input_paths . end ()), missing_input_paths . end ());
while ( ! missing_input_paths . empty ())
{
// ask user to select the missing file
2019-12-19 09:02:50 +01:00
fs :: path search = missing_input_paths . back ();
2020-04-01 09:48:56 +02:00
wxString title = _L ( "Please select the file to reload" );
2019-12-18 15:32:25 +01:00
#if defined(__APPLE__)
2020-01-21 14:07:13 +01:00
title += " (" + from_u8 ( search . filename (). string ()) + ")" ;
2019-12-18 15:32:25 +01:00
#endif // __APPLE__
2020-01-21 14:07:13 +01:00
title += ":" ;
2019-12-19 09:02:50 +01:00
wxFileDialog dialog ( q , title , "" , from_u8 ( search . filename (). string ()), file_wildcards ( FT_MODEL ), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2019-12-10 14:42:28 +01:00
if ( dialog . ShowModal () != wxID_OK )
return ;
std :: string sel_filename_path = dialog . GetPath (). ToUTF8 (). data ();
std :: string sel_filename = fs :: path ( sel_filename_path ). filename (). string ();
2019-12-19 09:02:50 +01:00
if ( boost :: algorithm :: iequals ( search . filename (). string (), sel_filename ))
2019-12-10 14:42:28 +01:00
{
input_paths . push_back ( sel_filename_path );
missing_input_paths . pop_back ();
2019-12-16 10:24:08 +01:00
fs :: path sel_path = fs :: path ( sel_filename_path ). remove_filename (). string ();
2019-12-10 14:42:28 +01:00
std :: vector < fs :: path >:: iterator it = missing_input_paths . begin ();
while ( it != missing_input_paths . end ())
{
// try to use the path of the selected file with all remaining missing files
2019-12-16 10:24:08 +01:00
fs :: path repathed_filename = sel_path ;
repathed_filename /= it -> filename ();
2019-12-10 14:42:28 +01:00
if ( fs :: exists ( repathed_filename ))
{
2019-12-16 10:24:08 +01:00
input_paths . push_back ( repathed_filename . string ());
2019-12-10 14:42:28 +01:00
it = missing_input_paths . erase ( it );
}
else
++ it ;
}
}
2019-12-16 10:24:08 +01:00
else
{
2020-04-01 09:48:56 +02:00
wxString message = _L ( "It is not allowed to change the file to reload" ) + " (" + from_u8 ( search . filename (). string ()) + "). \n " + _L ( "Do you want to retry" ) + " ?" ;
2019-12-16 10:24:08 +01:00
wxMessageDialog dlg ( q , message , wxMessageBoxCaptionStr , wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION );
if ( dlg . ShowModal () != wxID_YES )
return ;
}
2019-12-10 14:42:28 +01:00
}
2019-09-19 09:09:11 +02:00
std :: sort ( input_paths . begin (), input_paths . end ());
input_paths . erase ( std :: unique ( input_paths . begin (), input_paths . end ()), input_paths . end ());
2020-01-21 14:07:13 +01:00
std :: vector < wxString > fail_list ;
2019-09-19 09:09:11 +02:00
// load one file at a time
for ( size_t i = 0 ; i < input_paths . size (); ++ i )
{
const auto & path = input_paths [ i ]. string ();
2020-01-21 14:07:13 +01:00
wxBusyCursor wait ;
2020-04-01 09:48:56 +02:00
wxBusyInfo info ( _L ( "Reload from:" ) + " " + from_u8 ( path ), q -> get_current_canvas3D () -> get_wxglcanvas ());
2020-01-21 14:07:13 +01:00
2019-09-19 09:09:11 +02:00
Model new_model ;
try
2019-09-11 15:02:57 +02:00
{
2019-09-19 09:09:11 +02:00
new_model = Model :: read_from_file ( path , nullptr , true , false );
for ( ModelObject * model_object : new_model . objects )
2019-09-11 15:02:57 +02:00
{
2019-09-19 09:09:11 +02:00
model_object -> center_around_origin ();
model_object -> ensure_on_bed ();
2018-12-05 15:22:03 +01:00
}
}
2019-09-19 09:09:11 +02:00
catch ( std :: exception & )
{
// error while loading
return ;
}
2018-12-05 15:22:03 +01:00
2019-09-19 09:09:11 +02:00
// update the selected volumes whose source is the current file
2020-01-21 14:07:13 +01:00
for ( const SelectedVolume & sel_v : selected_volumes )
2019-09-19 09:09:11 +02:00
{
2020-01-21 14:07:13 +01:00
ModelObject * old_model_object = model . objects [ sel_v . object_idx ];
ModelVolume * old_volume = old_model_object -> volumes [ sel_v . volume_idx ];
bool has_source = ! old_volume -> source . input_file . empty () && boost :: algorithm :: iequals ( fs :: path ( old_volume -> source . input_file ). filename (). string (), fs :: path ( path ). filename (). string ());
bool has_name = ! old_volume -> name . empty () && boost :: algorithm :: iequals ( old_volume -> name , fs :: path ( path ). filename (). string ());
if ( has_source || has_name )
2019-09-19 09:09:11 +02:00
{
2020-01-21 14:07:13 +01:00
int new_volume_idx = - 1 ;
int new_object_idx = - 1 ;
if ( has_source )
{
// take idxs from source
new_volume_idx = old_volume -> source . volume_idx ;
new_object_idx = old_volume -> source . object_idx ;
}
else
{
// take idxs from the 1st matching volume
for ( size_t o = 0 ; o < new_model . objects . size (); ++ o )
{
ModelObject * obj = new_model . objects [ o ];
bool found = false ;
for ( size_t v = 0 ; v < obj -> volumes . size (); ++ v )
{
if ( obj -> volumes [ v ] -> name == old_volume -> name )
{
new_volume_idx = ( int ) v ;
new_object_idx = ( int ) o ;
found = true ;
break ;
}
}
if ( found )
break ;
}
}
if (( new_object_idx < 0 ) && (( int ) new_model . objects . size () <= new_object_idx ))
{
fail_list . push_back ( from_u8 ( has_source ? old_volume -> source . input_file : old_volume -> name ));
continue ;
}
2019-12-10 14:42:28 +01:00
ModelObject * new_model_object = new_model . objects [ new_object_idx ];
2020-01-21 14:07:13 +01:00
if (( new_volume_idx < 0 ) && (( int ) new_model . objects . size () <= new_volume_idx ))
{
fail_list . push_back ( from_u8 ( has_source ? old_volume -> source . input_file : old_volume -> name ));
continue ;
}
2019-12-10 14:42:28 +01:00
if ( new_volume_idx < ( int ) new_model_object -> volumes . size ())
2019-09-19 09:09:11 +02:00
{
2019-12-10 14:42:28 +01:00
old_model_object -> add_volume ( * new_model_object -> volumes [ new_volume_idx ]);
ModelVolume * new_volume = old_model_object -> volumes . back ();
new_volume -> set_new_unique_id ();
new_volume -> config . apply ( old_volume -> config );
new_volume -> set_type ( old_volume -> type ());
new_volume -> set_material_id ( old_volume -> material_id ());
2019-12-20 12:11:58 +01:00
new_volume -> set_transformation ( old_volume -> get_transformation () * old_volume -> source . transform );
2019-12-10 14:42:28 +01:00
new_volume -> translate ( new_volume -> get_transformation (). get_matrix ( true ) * ( new_volume -> source . mesh_offset - old_volume -> source . mesh_offset ));
2020-01-21 14:07:13 +01:00
std :: swap ( old_model_object -> volumes [ sel_v . volume_idx ], old_model_object -> volumes . back ());
2019-12-10 14:42:28 +01:00
old_model_object -> delete_volume ( old_model_object -> volumes . size () - 1 );
2019-12-20 16:02:56 +01:00
old_model_object -> ensure_on_bed ();
2020-05-12 15:52:33 +02:00
2020-05-13 14:53:05 +02:00
sla :: reproject_points_and_holes ( old_model_object );
2019-09-19 09:09:11 +02:00
}
}
}
2018-12-05 15:22:03 +01:00
}
2020-01-21 14:07:13 +01:00
if ( ! fail_list . empty ())
{
2020-04-01 09:48:56 +02:00
wxString message = _L ( "Unable to reload:" ) + " \n " ;
2020-01-21 14:07:13 +01:00
for ( const wxString & s : fail_list )
{
message += s + " \n " ;
}
2020-04-01 09:48:56 +02:00
wxMessageDialog dlg ( q , message , _L ( "Error during reload" ), wxOK | wxOK_DEFAULT | wxICON_WARNING );
2020-01-21 14:07:13 +01:00
dlg . ShowModal ();
}
2019-09-19 09:09:11 +02:00
// update 3D scene
update ();
2019-09-06 13:11:20 +02:00
2019-09-10 11:16:59 +02:00
// new GLVolumes have been created at this point, so update their printable state
for ( size_t i = 0 ; i < model . objects . size (); ++ i )
{
view3D -> get_canvas3d () -> update_instance_printable_state_for_object ( i );
}
2018-10-09 09:35:19 +02:00
}
2020-01-31 12:15:04 +01:00
void Plater :: priv :: reload_all_from_disk ()
{
2020-02-03 15:47:09 +01:00
if ( model . objects . empty ())
return ;
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( q , _L ( "Reload all from disk" ));
2020-01-31 12:15:04 +01:00
Plater :: SuppressSnapshots suppress ( q );
Selection & selection = get_selection ();
Selection :: IndicesList curr_idxs = selection . get_volume_idxs ();
// reload from disk uses selection
select_all ();
reload_from_disk ();
// restore previous selection
selection . clear ();
for ( unsigned int idx : curr_idxs )
{
selection . add ( idx , false );
}
}
2019-01-30 16:27:07 +01:00
void Plater :: priv :: fix_through_netfabb ( const int obj_idx , const int vol_idx /* = -1*/ )
2018-10-09 09:35:19 +02:00
{
2018-11-07 13:40:24 +01:00
if ( obj_idx < 0 )
return ;
2019-07-04 17:33:19 +02:00
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( q , _L ( "Fix Throught NetFabb" ));
2019-07-04 17:33:19 +02:00
2019-02-03 22:14:34 +01:00
fix_model_by_win10_sdk_gui ( * model . objects [ obj_idx ], vol_idx );
2020-05-13 14:53:05 +02:00
sla :: reproject_points_and_holes ( model . objects [ obj_idx ]);
2019-02-03 22:14:34 +01:00
this -> update ();
2019-05-08 10:37:45 +02:00
this -> object_list_changed ();
2019-02-03 22:14:34 +01:00
this -> schedule_background_process ();
2018-10-09 09:35:19 +02:00
}
2018-12-04 13:55:25 +01:00
void Plater :: priv :: set_current_panel ( wxPanel * panel )
{
if ( std :: find ( panels . begin (), panels . end (), panel ) == panels . end ())
return ;
2019-03-08 08:29:54 +01:00
#ifdef __WXMAC__
2019-03-07 15:44:25 +01:00
bool force_render = ( current_panel != nullptr );
2019-03-08 08:29:54 +01:00
#endif // __WXMAC__
2019-03-07 15:44:25 +01:00
2018-12-04 13:55:25 +01:00
if ( current_panel == panel )
return ;
current_panel = panel ;
2018-12-06 13:44:38 +01:00
// to reduce flickering when changing view, first set as visible the new current panel
2018-12-04 13:55:25 +01:00
for ( wxPanel * p : panels )
{
2018-12-06 13:44:38 +01:00
if ( p == current_panel )
2019-03-07 15:44:25 +01:00
{
2019-03-08 08:29:54 +01:00
#ifdef __WXMAC__
// On Mac we need also to force a render to avoid flickering when changing view
2019-03-07 15:44:25 +01:00
if ( force_render )
{
if ( p == view3D )
dynamic_cast < View3D *> ( p ) -> get_canvas3d () -> render ();
else if ( p == preview )
dynamic_cast < Preview *> ( p ) -> get_canvas3d () -> render ();
}
2019-03-08 08:29:54 +01:00
#endif // __WXMAC__
2018-12-06 13:44:38 +01:00
p -> Show ();
2019-03-07 15:44:25 +01:00
}
2018-12-06 13:44:38 +01:00
}
// then set to invisible the other
for ( wxPanel * p : panels )
{
if ( p != current_panel )
p -> Hide ();
2018-12-04 13:55:25 +01:00
}
panel_sizer -> Layout ();
if ( current_panel == view3D )
{
if ( view3D -> is_reload_delayed ())
{
// Delayed loading of the 3D scene.
if ( this -> printer_technology == ptSLA )
{
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
2018-12-22 10:02:42 +01:00
this -> update_restart_background_process ( true , false );
} else
view3D -> reload_scene ( true );
2018-12-04 13:55:25 +01:00
}
2019-09-27 14:52:19 +02:00
2018-12-04 13:55:25 +01:00
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
view3D -> set_as_dirty ();
2018-12-21 10:43:22 +01:00
view_toolbar . select_item ( "3D" );
2018-12-04 13:55:25 +01:00
}
else if ( current_panel == preview )
{
2019-08-29 15:38:21 +02:00
// see: Plater::priv::object_list_changed()
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this -> background_process . is_export_scheduled ();
bool model_fits = view3D -> check_volumes_outside_state () != ModelInstance :: PVS_Partly_Outside ;
if ( ! model . objects . empty () && ! export_in_progress && model_fits )
this -> q -> reslice ();
2019-02-28 15:27:03 +01:00
// keeps current gcode preview, if any
2019-03-18 14:07:55 +01:00
preview -> reload_print ( true );
2019-09-27 14:52:19 +02:00
2020-01-03 11:41:29 +01:00
preview -> set_as_dirty ();
2018-12-21 10:43:22 +01:00
view_toolbar . select_item ( "Preview" );
2018-12-04 13:55:25 +01:00
}
2018-12-21 11:39:37 +01:00
current_panel -> SetFocusFromKbd ();
2018-12-04 13:55:25 +01:00
}
2018-09-17 12:15:11 +02:00
void Plater :: priv :: on_select_preset ( wxCommandEvent & evt )
{
auto preset_type = static_cast < Preset :: Type > ( evt . GetInt ());
2018-10-10 13:53:45 +02:00
auto * combo = static_cast < PresetComboBox *> ( evt . GetEventObject ());
2018-09-17 12:15:11 +02:00
2020-03-23 22:47:35 +01:00
// see https://github.com/prusa3d/PrusaSlicer/issues/3889
// Under OSX: in case of use of a same names written in different case (like "ENDER" and "Ender"),
// m_presets_choice->GetSelection() will return first item, because search in PopupListCtrl is case-insensitive.
// So, use GetSelection() from event parameter
// But in this function we couldn't use evt.GetSelection(), because m_commandInt is used for preset_type
// Thus, get selection in this way:
int selection = combo -> FindString ( evt . GetString (), true );
2018-10-10 13:53:45 +02:00
auto idx = combo -> get_extruder_idx ();
2018-10-09 12:41:05 +02:00
2019-08-06 18:16:02 +02:00
//! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox,
2018-10-15 11:39:48 +02:00
//! but the OSX version derived from wxOwnerDrawnCombo.
2019-08-06 18:16:02 +02:00
//! So, to get selected string we do
//! combo->GetString(combo->GetSelection())
//! instead of
//! combo->GetStringSelection().ToUTF8().data());
2018-10-15 11:39:48 +02:00
2020-03-16 09:57:20 +01:00
const std :: string preset_name = wxGetApp (). preset_bundle -> get_preset_name_by_alias ( preset_type ,
2020-03-23 22:47:35 +01:00
Preset :: remove_suffix_modified ( combo -> GetString ( selection ). ToUTF8 (). data ()));
2018-10-15 11:39:48 +02:00
2018-09-17 12:15:11 +02:00
if ( preset_type == Preset :: TYPE_FILAMENT ) {
2019-11-29 11:02:30 +01:00
wxGetApp (). preset_bundle -> set_filament_preset ( idx , preset_name );
2018-09-17 12:15:11 +02:00
}
// TODO: ?
2018-10-09 12:41:05 +02:00
if ( preset_type == Preset :: TYPE_FILAMENT && sidebar -> is_multifilament ()) {
2019-12-02 22:48:01 -05:00
// Only update the plater UI for the 2nd and other filaments.
wxGetApp (). preset_bundle -> update_plater_filament_ui ( idx , combo );
2019-08-06 18:16:02 +02:00
}
2018-10-10 13:53:45 +02:00
else {
2019-03-13 13:13:18 +01:00
wxWindowUpdateLocker noUpdates ( sidebar -> presets_panel ());
2019-11-29 11:02:30 +01:00
wxGetApp (). get_tab ( preset_type ) -> select_preset ( preset_name );
2018-09-17 12:15:11 +02:00
}
2018-10-15 11:39:48 +02:00
// update plater with new config
2020-04-23 18:47:51 +02:00
q -> on_config_change ( wxGetApp (). preset_bundle -> full_config ());
2020-03-13 22:37:18 +01:00
if ( preset_type == Preset :: TYPE_PRINTER ) {
2019-06-10 10:48:43 +02:00
/* Settings list can be changed after printer preset changing, so
* update all settings items for all item had it.
2019-08-06 18:16:02 +02:00
* Furthermore, Layers editing is implemented only for FFF printers
2019-06-10 10:48:43 +02:00
* and for SLA presets they should be deleted
*/
wxGetApp (). obj_list () -> update_object_list_by_printer_technology ();
2020-03-13 22:37:18 +01:00
}
2018-09-17 12:15:11 +02:00
}
2018-11-20 13:22:26 +01:00
void Plater :: priv :: on_slicing_update ( SlicingStatusEvent & evt )
2018-10-09 09:35:19 +02:00
{
2019-03-14 15:11:27 +01:00
if ( evt . status . percent >= - 1 ) {
2019-06-18 16:24:30 +02:00
if ( m_ui_jobs . is_any_running ()) {
2019-04-17 14:56:00 +02:00
// Avoid a race condition
return ;
}
2019-03-14 15:11:27 +01:00
this -> statusbar () -> set_progress ( evt . status . percent );
2019-05-04 02:07:07 +02:00
this -> statusbar () -> set_status_text ( _ ( evt . status . text ) + wxString :: FromUTF8 ( "…" ));
2019-03-14 15:11:27 +01:00
}
2019-09-25 12:06:38 +02:00
if ( evt . status . flags & ( PrintBase :: SlicingStatus :: RELOAD_SCENE | PrintBase :: SlicingStatus :: RELOAD_SLA_SUPPORT_POINTS )) {
2018-11-20 13:22:26 +01:00
switch ( this -> printer_technology ) {
case ptFFF :
2018-12-06 15:40:41 +01:00
this -> update_fff_scene ();
2018-11-20 13:22:26 +01:00
break ;
case ptSLA :
2019-04-10 13:36:15 +02:00
// If RELOAD_SLA_SUPPORT_POINTS, then the SLA gizmo is updated (reload_scene calls update_gizmos_data)
2018-12-04 13:55:25 +01:00
if ( view3D -> is_dragging ())
2018-11-23 12:47:32 +01:00
delayed_scene_refresh = true ;
else
this -> update_sla_scene ();
2018-11-20 13:22:26 +01:00
break ;
2019-09-03 10:27:16 +02:00
default : break ;
2018-11-20 13:22:26 +01:00
}
2019-04-10 13:36:15 +02:00
} else if ( evt . status . flags & PrintBase :: SlicingStatus :: RELOAD_SLA_PREVIEW ) {
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
2019-03-14 15:11:27 +01:00
this -> preview -> reload_print ();
}
2018-10-09 09:35:19 +02:00
}
2018-11-20 13:22:26 +01:00
void Plater :: priv :: on_slicing_completed ( wxCommandEvent & )
2018-09-17 12:15:11 +02:00
{
2018-11-20 13:22:26 +01:00
switch ( this -> printer_technology ) {
case ptFFF :
2018-12-06 15:40:41 +01:00
this -> update_fff_scene ();
2018-11-20 13:22:26 +01:00
break ;
case ptSLA :
2018-12-04 13:55:25 +01:00
if ( view3D -> is_dragging ())
2018-11-23 12:47:32 +01:00
delayed_scene_refresh = true ;
else
this -> update_sla_scene ();
2018-11-20 13:22:26 +01:00
break ;
2019-09-03 10:27:16 +02:00
default : break ;
2018-11-20 13:22:26 +01:00
}
2018-09-17 12:15:11 +02:00
}
2018-10-23 15:27:31 +02:00
void Plater :: priv :: on_process_completed ( wxCommandEvent & evt )
2018-09-17 12:15:11 +02:00
{
2018-10-18 18:06:40 +02:00
// Stop the background task, wait until the thread goes into the "Idle" state.
// At this point of time the thread should be either finished or canceled,
// so the following call just confirms, that the produced data were consumed.
this -> background_process . stop ();
2018-10-23 15:27:31 +02:00
this -> statusbar () -> reset_cancel_callback ();
this -> statusbar () -> stop_busy ();
2019-08-06 18:16:02 +02:00
2019-03-11 09:48:24 +01:00
const bool canceled = evt . GetInt () < 0 ;
2019-08-06 18:16:02 +02:00
const bool error = evt . GetInt () == 0 ;
2019-03-11 09:48:24 +01:00
const bool success = evt . GetInt () > 0 ;
2018-10-23 15:27:31 +02:00
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
this -> background_process . reset_export ();
2019-03-11 09:48:24 +01:00
if ( error ) {
2018-10-23 15:27:31 +02:00
wxString message = evt . GetString ();
if ( message . IsEmpty ())
2020-04-01 09:48:56 +02:00
message = _L ( "Export failed" );
2020-03-12 16:05:39 +01:00
if ( q -> m_tracking_popup_menu )
// We don't want to pop-up a message box when tracking a pop-up menu.
// We postpone the error message instead.
q -> m_tracking_popup_menu_error_message = message ;
else
show_error ( q , message );
2018-10-23 15:27:31 +02:00
this -> statusbar () -> set_status_text ( message );
2018-10-18 18:06:40 +02:00
}
2019-08-06 18:16:02 +02:00
if ( canceled )
2020-04-01 09:48:56 +02:00
this -> statusbar () -> set_status_text ( _L ( "Cancelled" ));
2018-10-18 18:06:40 +02:00
2018-10-31 12:26:57 +01:00
this -> sidebar -> show_sliced_info_sizer ( success );
2018-10-18 18:06:40 +02:00
2018-12-04 11:14:39 +01:00
// This updates the "Slice now", "Export G-code", "Arrange" buttons status.
// Namely, it refreshes the "Out of print bed" property of all the ModelObjects, and it enables
// the "Slice now" and "Export G-code" buttons based on their "out of bed" status.
this -> object_list_changed ();
2019-08-06 18:16:02 +02:00
2018-10-18 18:06:40 +02:00
// refresh preview
2018-11-13 17:45:44 +01:00
switch ( this -> printer_technology ) {
case ptFFF :
2018-12-06 15:40:41 +01:00
this -> update_fff_scene ();
2018-11-13 17:45:44 +01:00
break ;
case ptSLA :
2018-12-04 13:55:25 +01:00
if ( view3D -> is_dragging ())
2018-11-23 12:47:32 +01:00
delayed_scene_refresh = true ;
else
this -> update_sla_scene ();
2018-11-13 17:45:44 +01:00
break ;
2019-09-03 10:27:16 +02:00
default : break ;
2018-11-12 17:35:57 +01:00
}
2019-12-13 18:02:25 +01:00
2019-03-01 11:00:34 +01:00
2019-03-08 15:40:28 +01:00
if ( canceled ) {
if ( wxGetApp (). get_mode () == comSimple )
sidebar -> set_btn_label ( ActionButtonType :: abReslice , "Slice now" );
show_action_buttons ( true );
}
2020-03-06 15:10:58 +01:00
else if ( this -> writing_to_removable_device || wxGetApp (). get_mode () == comSimple )
show_action_buttons ( false );
this -> writing_to_removable_device = false ;
2018-09-17 12:15:11 +02:00
}
void Plater :: priv :: on_layer_editing_toggled ( bool enable )
{
2018-12-04 13:55:25 +01:00
view3D -> enable_layers_editing ( enable );
view3D -> set_as_dirty ();
2018-09-17 12:15:11 +02:00
}
2018-10-03 11:34:39 +02:00
void Plater :: priv :: on_action_add ( SimpleEvent & )
2018-09-17 12:15:11 +02:00
{
2018-10-17 12:17:25 +02:00
if ( q != nullptr )
2018-11-15 15:27:39 +01:00
q -> add_model ();
2018-09-17 12:15:11 +02:00
}
2018-10-24 12:55:38 +02:00
void Plater :: priv :: on_action_split_objects ( SimpleEvent & )
2018-10-04 11:12:55 +02:00
{
2018-10-19 15:27:19 +02:00
split_object ();
2018-10-04 11:12:55 +02:00
}
2018-10-24 12:55:38 +02:00
void Plater :: priv :: on_action_split_volumes ( SimpleEvent & )
{
split_volume ();
}
2018-10-04 11:12:55 +02:00
void Plater :: priv :: on_action_layersediting ( SimpleEvent & )
{
2019-03-14 13:54:05 +01:00
view3D -> enable_layers_editing ( ! view3D -> is_layers_editing_enabled ());
2018-10-04 11:12:55 +02:00
}
2018-10-16 16:04:19 +02:00
void Plater :: priv :: on_object_select ( SimpleEvent & evt )
{
wxGetApp (). obj_list () -> update_selections ();
2018-12-17 15:44:30 +01:00
selection_changed ();
2018-10-16 16:04:19 +02:00
}
2018-10-09 17:14:59 +02:00
2019-09-20 09:57:27 +02:00
void Plater :: priv :: on_right_click ( RBtnEvent & evt )
2018-10-08 19:55:30 +02:00
{
2018-10-17 12:17:25 +02:00
int obj_idx = get_selected_object_idx ();
2019-09-20 09:57:27 +02:00
wxMenu * menu = nullptr ;
2019-12-11 16:07:00 +01:00
if ( obj_idx == - 1 ) // no one or several object are selected
{
if ( evt . data . second ) // right button was clicked on empty space
menu = & default_menu ;
else
2019-12-18 09:06:21 +01:00
{
sidebar -> obj_list () -> show_multi_selection_menu ();
2019-12-11 16:07:00 +01:00
return ;
2019-12-18 09:06:21 +01:00
}
2019-12-11 16:07:00 +01:00
}
2019-09-20 09:57:27 +02:00
else
2019-03-28 16:28:34 +01:00
{
2019-09-20 09:57:27 +02:00
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
if ( evt . data . second )
return ;
2019-12-11 16:07:00 +01:00
if ( printer_technology == ptSLA )
menu = & sla_object_menu ;
else
{
// show "Object menu" for each one or several FullInstance instead of FullObject
const bool is_some_full_instances = get_selection (). is_single_full_instance () ||
get_selection (). is_single_full_object () ||
get_selection (). is_multiple_full_instance ();
menu = is_some_full_instances ? & object_menu : & part_menu ;
}
2019-09-20 09:57:27 +02:00
sidebar -> obj_list () -> append_menu_item_settings ( menu );
if ( printer_technology != ptSLA )
sidebar -> obj_list () -> append_menu_item_change_extruder ( menu );
if ( menu != & part_menu )
{
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
* Suppress to show those items for a Simple mode
*/
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF ;
if ( wxGetApp (). get_mode () == comSimple ) {
2020-04-01 09:48:56 +02:00
if ( menu -> FindItem ( _L ( "Add instance" )) != wxNOT_FOUND )
2019-09-20 09:57:27 +02:00
{
/* Detach an items from the menu, but don't delete them
* so that they can be added back later
* (after switching to the Advanced/Expert mode)
*/
menu -> Remove ( items_increase [ id ]);
menu -> Remove ( items_decrease [ id ]);
menu -> Remove ( items_set_number_of_copies [ id ]);
}
2019-03-28 16:28:34 +01:00
}
2019-09-20 09:57:27 +02:00
else {
2020-04-01 09:48:56 +02:00
if ( menu -> FindItem ( _L ( "Add instance" )) == wxNOT_FOUND )
2019-09-20 09:57:27 +02:00
{
// Prepend items to the menu, if those aren't not there
menu -> Prepend ( items_set_number_of_copies [ id ]);
menu -> Prepend ( items_decrease [ id ]);
menu -> Prepend ( items_increase [ id ]);
}
2019-03-28 16:28:34 +01:00
}
2019-03-19 09:51:50 +01:00
}
}
2019-09-20 09:57:27 +02:00
if ( q != nullptr && menu ) {
2019-02-25 10:35:16 +01:00
#ifdef __linux__
// For some reason on Linux the menu isn't displayed if position is specified
// (even though the position is sane).
q -> PopupMenu ( menu );
#else
2019-09-20 09:57:27 +02:00
q -> PopupMenu ( menu , ( int ) evt . data . first . x (), ( int ) evt . data . first . y ());
2019-02-25 10:35:16 +01:00
#endif
}
2018-10-08 19:55:30 +02:00
}
2018-10-09 17:14:59 +02:00
void Plater :: priv :: on_wipetower_moved ( Vec3dEvent & evt )
2018-10-08 19:55:30 +02:00
{
2018-10-09 17:14:59 +02:00
DynamicPrintConfig cfg ;
cfg . opt < ConfigOptionFloat > ( "wipe_tower_x" , true ) -> value = evt . data ( 0 );
cfg . opt < ConfigOptionFloat > ( "wipe_tower_y" , true ) -> value = evt . data ( 1 );
2018-11-19 13:17:14 +01:00
wxGetApp (). get_tab ( Preset :: TYPE_PRINT ) -> load_config ( cfg );
2018-10-08 19:55:30 +02:00
}
2019-04-26 15:34:26 +02:00
void Plater :: priv :: on_wipetower_rotated ( Vec3dEvent & evt )
{
DynamicPrintConfig cfg ;
cfg . opt < ConfigOptionFloat > ( "wipe_tower_x" , true ) -> value = evt . data ( 0 );
cfg . opt < ConfigOptionFloat > ( "wipe_tower_y" , true ) -> value = evt . data ( 1 );
cfg . opt < ConfigOptionFloat > ( "wipe_tower_rotation_angle" , true ) -> value = Geometry :: rad2deg ( evt . data ( 2 ));
wxGetApp (). get_tab ( Preset :: TYPE_PRINT ) -> load_config ( cfg );
}
2018-10-08 19:55:30 +02:00
void Plater :: priv :: on_update_geometry ( Vec3dsEvent < 2 >& )
{
// TODO
}
2019-08-06 18:16:02 +02:00
// Update the scene from the background processing,
2018-11-23 12:47:32 +01:00
// if the update message was received during mouse manipulation.
void Plater :: priv :: on_3dcanvas_mouse_dragging_finished ( SimpleEvent & )
{
if ( this -> delayed_scene_refresh ) {
this -> delayed_scene_refresh = false ;
this -> update_sla_scene ();
}
}
2018-10-17 12:17:25 +02:00
bool Plater :: priv :: init_object_menu ()
{
2019-03-19 09:51:50 +01:00
items_increase . reserve ( 2 );
items_decrease . reserve ( 2 );
items_set_number_of_copies . reserve ( 2 );
2018-12-07 17:50:48 +01:00
init_common_menu ( & object_menu );
complit_init_object_menu ();
init_common_menu ( & sla_object_menu );
complit_init_sla_object_menu ();
init_common_menu ( & part_menu , true );
complit_init_part_menu ();
2019-09-20 09:57:27 +02:00
sidebar -> obj_list () -> create_default_popupmenu ( & default_menu );
2018-12-07 17:50:48 +01:00
return true ;
}
2019-11-28 14:18:24 +01:00
void Plater :: priv :: generate_thumbnail ( ThumbnailData & data , unsigned int w , unsigned int h , bool printable_only , bool parts_only , bool show_bed , bool transparent_background )
2019-10-23 13:31:24 +02:00
{
2019-11-28 14:18:24 +01:00
view3D -> get_canvas3d () -> render_thumbnail ( data , w , h , printable_only , parts_only , show_bed , transparent_background );
2019-10-23 13:31:24 +02:00
}
2019-11-22 12:39:03 +01:00
2019-11-28 14:18:24 +01:00
void Plater :: priv :: generate_thumbnails ( ThumbnailsList & thumbnails , const Vec2ds & sizes , bool printable_only , bool parts_only , bool show_bed , bool transparent_background )
2019-11-22 12:39:03 +01:00
{
thumbnails . clear ();
for ( const Vec2d & size : sizes )
{
thumbnails . push_back ( ThumbnailData ());
Point isize ( size ); // round to ints
2019-11-28 14:18:24 +01:00
generate_thumbnail ( thumbnails . back (), isize . x (), isize . y (), printable_only , parts_only , show_bed , transparent_background );
2019-11-22 12:39:03 +01:00
if ( ! thumbnails . back (). is_valid ())
thumbnails . pop_back ();
}
2019-10-23 13:31:24 +02:00
}
2019-05-03 09:44:19 +02:00
void Plater :: priv :: msw_rescale_object_menu ()
{
2019-09-20 09:57:27 +02:00
for ( MenuWithSeparators * menu : { & object_menu , & sla_object_menu , & part_menu , & default_menu })
2019-05-03 09:44:19 +02:00
msw_rescale_menu ( dynamic_cast < wxMenu *> ( menu ));
}
2019-05-15 14:09:16 +02:00
wxString Plater :: priv :: get_project_filename ( const wxString & extension ) const
2019-05-14 15:00:24 +02:00
{
2019-05-15 14:55:51 +02:00
return m_project_filename . empty () ? "" : m_project_filename + extension ;
2019-05-14 15:00:24 +02:00
}
void Plater :: priv :: set_project_filename ( const wxString & filename )
{
2019-05-15 14:09:16 +02:00
boost :: filesystem :: path full_path = into_path ( filename );
2019-05-17 16:27:00 +02:00
boost :: filesystem :: path ext = full_path . extension ();
if ( boost :: iequals ( ext . string (), ".amf" )) {
// Remove the first extension.
full_path . replace_extension ( "" );
// It may be ".zip.amf".
if ( boost :: iequals ( full_path . extension (). string (), ".zip" ))
// Remove the 2nd extension.
full_path . replace_extension ( "" );
} else {
// Remove just one extension.
2019-05-15 14:09:16 +02:00
full_path . replace_extension ( "" );
}
2019-05-15 10:23:02 +02:00
m_project_filename = from_path ( full_path );
2019-05-14 15:00:24 +02:00
wxGetApp (). mainframe -> update_title ();
2019-07-12 15:36:01 +02:00
if ( ! filename . empty ())
wxGetApp (). mainframe -> add_to_recent_projects ( filename );
2019-05-14 15:00:24 +02:00
}
2018-12-07 17:50:48 +01:00
bool Plater :: priv :: init_common_menu ( wxMenu * menu , const bool is_part /* = false*/ )
{
2019-03-08 17:00:56 +01:00
if ( is_part ) {
2020-04-01 09:48:56 +02:00
append_menu_item ( menu , wxID_ANY , _L ( "Delete" ) + " \t Del" , _L ( "Remove the selected object" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { q -> remove_selected (); }, "delete" , nullptr , [ this ]() { return can_delete (); }, q );
2019-04-04 15:13:43 +02:00
2020-04-01 09:48:56 +02:00
append_menu_item ( menu , wxID_ANY , _L ( "Reload from disk" ), _L ( "Reload the selected volumes from disk" ),
2019-09-19 09:09:11 +02:00
[ this ]( wxCommandEvent & ) { q -> reload_from_disk (); }, "" , menu , [ this ]() { return can_reload_from_disk (); }, q );
2019-04-04 15:13:43 +02:00
sidebar -> obj_list () -> append_menu_item_export_stl ( menu );
}
else {
2020-04-01 09:48:56 +02:00
wxMenuItem * item_increase = append_menu_item ( menu , wxID_ANY , _L ( "Add instance" ) + " \t +" , _L ( "Add one more instance of the selected object" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { q -> increase_instances (); }, "add_copies" , nullptr , [ this ]() { return can_increase_instances (); }, q );
2020-04-01 09:48:56 +02:00
wxMenuItem * item_decrease = append_menu_item ( menu , wxID_ANY , _L ( "Remove instance" ) + " \t -" , _L ( "Remove one instance of the selected object" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { q -> decrease_instances (); }, "remove_copies" , nullptr , [ this ]() { return can_decrease_instances (); }, q );
2020-04-01 09:48:56 +02:00
wxMenuItem * item_set_number_of_copies = append_menu_item ( menu , wxID_ANY , _L ( "Set number of instances" ) + dots , _L ( "Change the number of instances of the selected object" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { q -> set_number_of_copies (); }, "number_of_copies" , nullptr , [ this ]() { return can_increase_instances (); }, q );
2019-03-19 09:51:50 +01:00
items_increase . push_back ( item_increase );
items_decrease . push_back ( item_decrease );
items_set_number_of_copies . push_back ( item_set_number_of_copies );
2019-03-08 17:00:56 +01:00
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
2020-04-01 09:48:56 +02:00
append_menu_item ( menu , wxID_ANY , _L ( "Delete" ) + " \t Del" , _L ( "Remove the selected object" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { q -> remove_selected (); }, "delete" , nullptr , [ this ]() { return can_delete (); }, q );
2019-01-22 16:40:10 +01:00
menu -> AppendSeparator ();
2019-05-13 14:27:51 +02:00
sidebar -> obj_list () -> append_menu_item_instance_to_object ( menu , q );
2018-12-07 18:20:22 +01:00
menu -> AppendSeparator ();
2019-07-31 10:12:13 +02:00
wxMenuItem * menu_item_printable = sidebar -> obj_list () -> append_menu_item_printable ( menu , q );
menu -> AppendSeparator ();
2020-04-01 09:48:56 +02:00
append_menu_item ( menu , wxID_ANY , _L ( "Reload from disk" ), _L ( "Reload the selected object from disk" ),
2019-09-19 09:09:11 +02:00
[ this ]( wxCommandEvent & ) { reload_from_disk (); }, "" , nullptr , [ this ]() { return can_reload_from_disk (); }, q );
2018-12-07 18:20:22 +01:00
2020-04-01 09:48:56 +02:00
append_menu_item ( menu , wxID_ANY , _L ( "Export as STL" ) + dots , _L ( "Export the selected object as STL file" ),
2019-12-11 16:07:00 +01:00
[ this ]( wxCommandEvent & ) { q -> export_stl ( false , true ); }, "" , nullptr ,
[ this ]() {
const Selection & selection = get_selection ();
return selection . is_single_full_instance () || selection . is_single_full_object ();
}, q );
2019-04-04 15:13:43 +02:00
menu -> AppendSeparator ();
2019-07-31 10:12:13 +02:00
2020-05-20 20:07:31 +02:00
// "Scale to print volume" makes a sense just for whole object
sidebar -> obj_list () -> append_menu_item_scale_selection_to_fit_print_volume ( menu );
2019-07-31 10:12:13 +02:00
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) {
const Selection & selection = get_selection ();
int instance_idx = selection . get_instance_idx ();
2019-12-11 16:07:00 +01:00
evt . Enable ( selection . is_single_full_instance () || selection . is_single_full_object ());
2019-07-31 10:12:13 +02:00
if ( instance_idx != - 1 )
2019-07-31 11:01:50 +02:00
{
2019-07-31 10:12:13 +02:00
evt . Check ( model . objects [ selection . get_object_idx ()] -> instances [ instance_idx ] -> printable );
2019-07-31 11:01:50 +02:00
view3D -> set_as_dirty ();
}
2019-07-31 10:12:13 +02:00
}, menu_item_printable -> GetId ());
2018-12-07 17:50:48 +01:00
}
2018-10-17 14:26:13 +02:00
2020-05-20 20:07:31 +02:00
sidebar -> obj_list () -> append_menu_items_convert_unit ( menu );
2019-05-13 14:27:51 +02:00
sidebar -> obj_list () -> append_menu_item_fix_through_netfabb ( menu );
2019-01-30 16:27:07 +01:00
2018-10-18 14:42:21 +02:00
wxMenu * mirror_menu = new wxMenu ();
if ( mirror_menu == nullptr )
return false ;
2020-04-01 09:48:56 +02:00
append_menu_item ( mirror_menu , wxID_ANY , _L ( "Along X axis" ), _L ( "Mirror the selected object along the X axis" ),
2019-04-12 17:10:04 +02:00
[ this ]( wxCommandEvent & ) { mirror ( X ); }, "mark_X" , menu );
2020-04-01 09:48:56 +02:00
append_menu_item ( mirror_menu , wxID_ANY , _L ( "Along Y axis" ), _L ( "Mirror the selected object along the Y axis" ),
2019-04-12 17:10:04 +02:00
[ this ]( wxCommandEvent & ) { mirror ( Y ); }, "mark_Y" , menu );
2020-04-01 09:48:56 +02:00
append_menu_item ( mirror_menu , wxID_ANY , _L ( "Along Z axis" ), _L ( "Mirror the selected object along the Z axis" ),
2019-04-12 17:10:04 +02:00
[ this ]( wxCommandEvent & ) { mirror ( Z ); }, "mark_Z" , menu );
2018-10-18 14:42:21 +02:00
2020-04-01 09:48:56 +02:00
append_submenu ( menu , mirror_menu , wxID_ANY , _L ( "Mirror" ), _L ( "Mirror the selected object" ), "" ,
2019-05-13 14:27:51 +02:00
[ this ]() { return can_mirror (); }, q );
2018-12-07 17:50:48 +01:00
return true ;
}
bool Plater :: priv :: complit_init_object_menu ()
{
2018-10-24 12:55:38 +02:00
wxMenu * split_menu = new wxMenu ();
if ( split_menu == nullptr )
return false ;
2020-04-01 09:48:56 +02:00
append_menu_item ( split_menu , wxID_ANY , _L ( "To objects" ), _L ( "Split the selected object into individual objects" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split_object (); }, "split_object_SMALL" , & object_menu , [ this ]() { return can_split (); }, q );
2020-04-01 09:48:56 +02:00
append_menu_item ( split_menu , wxID_ANY , _L ( "To parts" ), _L ( "Split the selected object into individual sub-parts" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split_volume (); }, "split_parts_SMALL" , & object_menu , [ this ]() { return can_split (); }, q );
2018-10-24 12:55:38 +02:00
2020-04-01 09:48:56 +02:00
append_submenu ( & object_menu , split_menu , wxID_ANY , _L ( "Split" ), _L ( "Split the selected object" ), "" ,
2019-05-13 14:27:51 +02:00
[ this ]() { return can_split () && wxGetApp (). get_mode () > comSimple ; }, q );
2018-12-07 17:50:48 +01:00
object_menu . AppendSeparator ();
2018-11-22 16:04:21 +01:00
2019-05-27 13:07:37 +02:00
// Layers Editing for object
2019-12-11 16:07:00 +01:00
sidebar -> obj_list () -> append_menu_item_layers_editing ( & object_menu , q );
2019-05-27 13:07:37 +02:00
object_menu . AppendSeparator ();
2019-01-25 10:34:32 +01:00
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
2018-11-22 16:04:21 +01:00
2018-12-07 17:50:48 +01:00
return true ;
}
bool Plater :: priv :: complit_init_sla_object_menu ()
{
2020-04-01 09:48:56 +02:00
append_menu_item ( & sla_object_menu , wxID_ANY , _L ( "Split" ), _L ( "Split the selected object into individual objects" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split_object (); }, "split_object_SMALL" , nullptr , [ this ]() { return can_split (); }, q );
2018-12-07 17:50:48 +01:00
2019-01-25 10:34:32 +01:00
sla_object_menu . AppendSeparator ();
2018-12-07 17:50:48 +01:00
// Add the automatic rotation sub-menu
2020-04-23 18:47:51 +02:00
append_menu_item (
& sla_object_menu , wxID_ANY , _ ( L ( "Optimize orientation" )),
_ ( L ( "Optimize the rotation of the object for better print results." )),
[ this ]( wxCommandEvent & ) {
m_ui_jobs . optimize_rotation ();
});
2018-12-07 17:50:48 +01:00
return true ;
}
bool Plater :: priv :: complit_init_part_menu ()
{
2020-04-01 09:48:56 +02:00
append_menu_item ( & part_menu , wxID_ANY , _L ( "Split" ), _L ( "Split the selected object into individual sub-parts" ),
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split_volume (); }, "split_parts_SMALL" , nullptr , [ this ]() { return can_split (); }, q );
2018-12-07 17:50:48 +01:00
2019-01-25 10:34:32 +01:00
part_menu . AppendSeparator ();
2018-12-07 17:50:48 +01:00
auto obj_list = sidebar -> obj_list ();
2020-02-14 13:27:01 +01:00
obj_list -> append_menu_item_change_type ( & part_menu , q );
2018-12-07 17:50:48 +01:00
2018-10-17 12:17:25 +02:00
return true ;
}
2018-10-08 19:55:30 +02:00
2020-01-03 11:41:29 +01:00
void Plater :: priv :: set_current_canvas_as_dirty ()
{
if ( current_panel == view3D )
view3D -> set_as_dirty ();
else if ( current_panel == preview )
preview -> set_as_dirty ();
}
2020-01-21 14:07:13 +01:00
GLCanvas3D * Plater :: priv :: get_current_canvas3D ()
{
return ( current_panel == view3D ) ? view3D -> get_canvas3d () : (( current_panel == preview ) ? preview -> get_canvas3d () : nullptr );
}
2020-03-04 15:19:48 +01:00
void Plater :: priv :: unbind_canvas_event_handlers ()
{
if ( view3D != nullptr )
view3D -> get_canvas3d () -> unbind_event_handlers ();
if ( preview != nullptr )
preview -> get_canvas3d () -> unbind_event_handlers ();
}
2020-04-01 09:58:31 +02:00
void Plater :: priv :: reset_canvas_volumes ()
{
if ( view3D != nullptr )
view3D -> get_canvas3d () -> reset_volumes ();
if ( preview != nullptr )
preview -> get_canvas3d () -> reset_volumes ();
}
2020-03-04 15:19:48 +01:00
2019-12-10 10:56:21 +01:00
bool Plater :: priv :: init_view_toolbar ()
2018-12-06 10:38:19 +01:00
{
2019-12-10 10:56:21 +01:00
if ( view_toolbar . get_items_count () > 0 )
// already initialized
return true ;
2018-12-17 10:55:14 +01:00
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 ;
2019-02-26 09:56:23 +01:00
if ( ! view_toolbar . init ( background_data ))
2019-12-10 10:56:21 +01:00
return false ;
2018-12-06 10:38:19 +01:00
2019-07-20 12:02:29 +02:00
view_toolbar . set_horizontal_orientation ( GLToolbar :: Layout :: HO_Left );
view_toolbar . set_vertical_orientation ( GLToolbar :: Layout :: VO_Bottom );
2018-12-17 10:55:14 +01:00
view_toolbar . set_border ( 5.0f );
view_toolbar . set_gap_size ( 1.0f );
GLToolbarItem :: Data item ;
item . name = "3D" ;
2019-03-04 14:21:52 +01:00
item . icon_filename = "editor.svg" ;
2019-05-09 17:18:03 +02:00
item . tooltip = _utf8 ( L ( "3D editor view" )) + " [" + GUI :: shortkey_ctrl_prefix () + "5]" ;
2018-12-17 10:55:14 +01:00
item . sprite_id = 0 ;
2019-07-12 09:26:19 +02:00
item . left . action_callback = [ this ]() { if ( this -> q != nullptr ) wxPostEvent ( this -> q , SimpleEvent ( EVT_GLVIEWTOOLBAR_3D )); };
2018-12-17 10:55:14 +01:00
if ( ! view_toolbar . add_item ( item ))
2019-12-10 10:56:21 +01:00
return false ;
2018-12-17 10:55:14 +01:00
item . name = "Preview" ;
2019-03-04 14:21:52 +01:00
item . icon_filename = "preview.svg" ;
2019-05-09 17:18:03 +02:00
item . tooltip = _utf8 ( L ( "Preview" )) + " [" + GUI :: shortkey_ctrl_prefix () + "6]" ;
2018-12-17 10:55:14 +01:00
item . sprite_id = 1 ;
2019-07-12 09:26:19 +02:00
item . left . action_callback = [ this ]() { if ( this -> q != nullptr ) wxPostEvent ( this -> q , SimpleEvent ( EVT_GLVIEWTOOLBAR_PREVIEW )); };
2018-12-17 10:55:14 +01:00
if ( ! view_toolbar . add_item ( item ))
2019-12-10 10:56:21 +01:00
return false ;
2018-12-17 10:55:14 +01:00
view_toolbar . select_item ( "3D" );
view_toolbar . set_enabled ( true );
2019-12-10 10:56:21 +01:00
return true ;
2018-12-06 10:38:19 +01:00
}
2019-01-22 16:40:10 +01:00
bool Plater :: priv :: can_set_instance_to_object () const
{
const int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && ( model . objects [ obj_idx ] -> instances . size () > 1 );
}
2018-12-17 15:44:30 +01:00
bool Plater :: priv :: can_split () const
{
return sidebar -> obj_list () -> is_splittable ();
}
2018-10-18 09:27:37 +02:00
bool Plater :: priv :: layers_height_allowed () const
{
2019-04-05 09:51:58 +02:00
if ( printer_technology != ptFFF )
return false ;
2018-10-22 15:18:56 +02:00
int obj_idx = get_selected_object_idx ();
2018-12-04 13:55:25 +01:00
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && config -> opt_bool ( "variable_layer_height" ) && view3D -> is_layers_editing_allowed ();
2018-10-18 09:27:37 +02:00
}
2018-10-18 15:09:41 +02:00
bool Plater :: priv :: can_mirror () const
{
return get_selection (). is_from_single_instance ();
}
2018-10-08 19:55:30 +02:00
2019-09-19 09:09:11 +02:00
bool Plater :: priv :: can_reload_from_disk () const
{
// struct to hold selected ModelVolumes by their indices
struct SelectedVolume
{
int object_idx ;
int volume_idx ;
// operators needed by std::algorithms
bool operator < ( const SelectedVolume & other ) const { return ( object_idx < other . object_idx ) || (( object_idx == other . object_idx ) && ( volume_idx < other . volume_idx )); }
bool operator == ( const SelectedVolume & other ) const { return ( object_idx == other . object_idx ) && ( volume_idx == other . volume_idx ); }
};
std :: vector < SelectedVolume > selected_volumes ;
const Selection & selection = get_selection ();
// collects selected ModelVolumes
const std :: set < unsigned int >& selected_volumes_idxs = selection . get_volume_idxs ();
for ( unsigned int idx : selected_volumes_idxs )
{
const GLVolume * v = selection . get_volume ( idx );
int v_idx = v -> volume_idx ();
2019-10-10 14:44:52 +02:00
if ( v_idx >= 0 )
2020-01-02 09:07:33 +01:00
{
int o_idx = v -> object_idx ();
if (( 0 <= o_idx ) && ( o_idx < ( int ) model . objects . size ()))
selected_volumes . push_back ({ o_idx , v_idx });
}
2019-09-19 09:09:11 +02:00
}
std :: sort ( selected_volumes . begin (), selected_volumes . end ());
selected_volumes . erase ( std :: unique ( selected_volumes . begin (), selected_volumes . end ()), selected_volumes . end ());
// collects paths of files to load
std :: vector < fs :: path > paths ;
for ( const SelectedVolume & v : selected_volumes )
{
2020-02-07 09:49:48 +01:00
const ModelObject * object = model . objects [ v . object_idx ];
const ModelVolume * volume = object -> volumes [ v . volume_idx ];
2019-12-10 14:42:28 +01:00
if ( ! volume -> source . input_file . empty ())
2019-09-19 09:09:11 +02:00
paths . push_back ( volume -> source . input_file );
2020-02-07 09:49:48 +01:00
else if ( ! object -> input_file . empty () && ! volume -> name . empty ())
paths . push_back ( volume -> name );
2019-09-19 09:09:11 +02:00
}
std :: sort ( paths . begin (), paths . end ());
paths . erase ( std :: unique ( paths . begin (), paths . end ()), paths . end ());
return ! paths . empty ();
}
2019-07-24 14:02:36 +02:00
void Plater :: priv :: set_bed_shape ( const Pointfs & shape , const std :: string & custom_texture , const std :: string & custom_model )
2019-02-19 15:15:27 +01:00
{
2019-07-24 14:02:36 +02:00
bool new_shape = bed . set_shape ( shape , custom_texture , custom_model );
2019-02-19 15:15:27 +01:00
if ( new_shape )
{
if ( view3D ) view3D -> bed_shape_changed ();
if ( preview ) preview -> bed_shape_changed ();
}
}
2019-03-14 13:54:05 +01:00
bool Plater :: priv :: can_delete () const
{
2019-06-28 17:03:50 +02:00
return ! get_selection (). is_empty () && ! get_selection (). is_wipe_tower () && ! m_ui_jobs . is_any_running ();
2019-03-14 13:54:05 +01:00
}
bool Plater :: priv :: can_delete_all () const
{
return ! model . objects . empty ();
}
2019-04-24 16:04:47 +02:00
bool Plater :: priv :: can_fix_through_netfabb () const
{
int obj_idx = get_selected_object_idx ();
if ( obj_idx < 0 )
return false ;
return model . objects [ obj_idx ] -> get_mesh_errors_count () > 0 ;
}
2019-03-14 13:54:05 +01:00
bool Plater :: priv :: can_increase_instances () const
{
2019-06-18 16:24:30 +02:00
if ( m_ui_jobs . is_any_running ()) {
2019-04-17 14:56:00 +02:00
return false ;
}
2019-03-14 13:54:05 +01:00
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ());
}
bool Plater :: priv :: can_decrease_instances () const
{
2019-06-18 16:24:30 +02:00
if ( m_ui_jobs . is_any_running ()) {
2019-04-17 14:56:00 +02:00
return false ;
}
2019-03-14 13:54:05 +01:00
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && ( model . objects [ obj_idx ] -> instances . size () > 1 );
}
bool Plater :: priv :: can_split_to_objects () const
{
return can_split ();
}
bool Plater :: priv :: can_split_to_volumes () const
{
return ( printer_technology != ptSLA ) && can_split ();
}
bool Plater :: priv :: can_arrange () const
{
2019-06-18 16:24:30 +02:00
return ! model . objects . empty () && ! m_ui_jobs . is_any_running ();
2019-03-14 13:54:05 +01:00
}
bool Plater :: priv :: can_layers_editing () const
{
return layers_height_allowed ();
}
2018-12-07 17:50:48 +01:00
void Plater :: priv :: update_object_menu ()
{
2019-01-25 13:16:32 +01:00
sidebar -> obj_list () -> append_menu_items_add_volume ( & object_menu );
2018-12-07 17:50:48 +01:00
}
2020-03-06 15:10:58 +01:00
void Plater :: priv :: show_action_buttons ( const bool ready_to_slice ) const
2019-03-01 11:00:34 +01:00
{
2020-03-06 15:10:58 +01:00
// Cache this value, so that the callbacks from the RemovableDriveManager may repeat that value when calling show_action_buttons().
this -> ready_to_slice = ready_to_slice ;
2019-03-01 11:00:34 +01:00
wxWindowUpdateLocker noUpdater ( sidebar );
const auto prin_host_opt = config -> option < ConfigOptionString > ( "print_host" );
const bool send_gcode_shown = prin_host_opt != nullptr && ! prin_host_opt -> value . empty ();
2019-12-10 17:31:27 +01:00
2019-08-06 18:16:02 +02:00
// when a background processing is ON, export_btn and/or send_btn are showing
2019-03-01 11:00:34 +01:00
if ( wxGetApp (). app_config -> get ( "background_processing" ) == "1" )
{
2020-03-06 15:10:58 +01:00
RemovableDriveManager :: RemovableDrivesStatus removable_media_status = wxGetApp (). removable_drive_manager () -> status ();
2020-01-02 16:30:28 +01:00
if ( sidebar -> show_reslice ( false ) |
sidebar -> show_export ( true ) |
sidebar -> show_send ( send_gcode_shown ) |
2020-03-06 15:10:58 +01:00
sidebar -> show_export_removable ( removable_media_status . has_removable_drives ) |
sidebar -> show_disconnect ( removable_media_status . has_eject ))
2019-08-06 18:16:02 +02:00
sidebar -> Layout ();
}
2019-03-01 11:00:34 +01:00
else
{
2020-03-06 15:10:58 +01:00
RemovableDriveManager :: RemovableDrivesStatus removable_media_status ;
if ( ! ready_to_slice )
removable_media_status = wxGetApp (). removable_drive_manager () -> status ();
if ( sidebar -> show_reslice ( ready_to_slice ) |
sidebar -> show_export ( ! ready_to_slice ) |
sidebar -> show_send ( send_gcode_shown && ! ready_to_slice ) |
sidebar -> show_export_removable ( ! ready_to_slice && removable_media_status . has_removable_drives ) |
sidebar -> show_disconnect ( ! ready_to_slice && removable_media_status . has_eject ))
2019-08-06 18:16:02 +02:00
sidebar -> Layout ();
}
2019-03-01 11:00:34 +01:00
}
2019-07-26 13:44:33 +02:00
void Plater :: priv :: enter_gizmos_stack ()
{
2019-08-06 18:16:02 +02:00
assert ( m_undo_redo_stack_active == & m_undo_redo_stack_main );
if ( m_undo_redo_stack_active == & m_undo_redo_stack_main ) {
m_undo_redo_stack_active = & m_undo_redo_stack_gizmos ;
assert ( m_undo_redo_stack_active -> empty ());
// Take the initial snapshot of the gizmos.
// Not localized on purpose, the text will never be shown to the user.
this -> take_snapshot ( std :: string ( "Gizmos-Initial" ));
}
2019-07-26 13:44:33 +02:00
}
void Plater :: priv :: leave_gizmos_stack ()
{
2019-08-06 18:16:02 +02:00
assert ( m_undo_redo_stack_active == & m_undo_redo_stack_gizmos );
if ( m_undo_redo_stack_active == & m_undo_redo_stack_gizmos ) {
assert ( ! m_undo_redo_stack_active -> empty ());
m_undo_redo_stack_active -> clear ();
m_undo_redo_stack_active = & m_undo_redo_stack_main ;
}
2019-07-26 13:44:33 +02:00
}
2019-07-09 20:45:00 +02:00
int Plater :: priv :: get_active_snapshot_index ()
{
2019-07-26 13:44:33 +02:00
const size_t active_snapshot_time = this -> undo_redo_stack (). active_snapshot_time ();
const std :: vector < UndoRedo :: Snapshot >& ss_stack = this -> undo_redo_stack (). snapshots ();
2019-07-09 20:45:00 +02:00
const auto it = std :: lower_bound ( ss_stack . begin (), ss_stack . end (), UndoRedo :: Snapshot ( active_snapshot_time ));
return it - ss_stack . begin ();
}
2019-07-19 10:29:06 +02:00
void Plater :: priv :: take_snapshot ( const std :: string & snapshot_name )
{
2019-08-06 18:16:02 +02:00
if ( this -> m_prevent_snapshots > 0 )
2019-07-19 10:29:06 +02:00
return ;
assert ( this -> m_prevent_snapshots >= 0 );
2019-07-22 16:52:47 +02:00
UndoRedo :: SnapshotData snapshot_data ;
snapshot_data . printer_technology = this -> printer_technology ;
2019-07-19 10:29:06 +02:00
if ( this -> view3D -> is_layers_editing_enabled ())
2019-08-06 18:16:02 +02:00
snapshot_data . flags |= UndoRedo :: SnapshotData :: VARIABLE_LAYER_EDITING_ACTIVE ;
2019-07-23 18:17:57 +02:00
if ( this -> sidebar -> obj_list () -> is_selected ( itSettings )) {
snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_SETTINGS_ON_SIDEBAR ;
2019-07-24 12:32:38 +02:00
snapshot_data . layer_range_idx = this -> sidebar -> obj_list () -> get_selected_layers_range_idx ();
2019-07-23 18:17:57 +02:00
}
else if ( this -> sidebar -> obj_list () -> is_selected ( itLayer )) {
snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_LAYER_ON_SIDEBAR ;
2019-07-24 12:32:38 +02:00
snapshot_data . layer_range_idx = this -> sidebar -> obj_list () -> get_selected_layers_range_idx ();
2019-07-23 18:17:57 +02:00
}
else if ( this -> sidebar -> obj_list () -> is_selected ( itLayerRoot ))
snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_LAYERROOT_ON_SIDEBAR ;
2019-08-22 16:44:49 +02:00
// If SLA gizmo is active, ask it if it wants to trigger support generation
// on loading this snapshot.
if ( view3D -> get_canvas3d () -> get_gizmos_manager (). wants_reslice_supports_on_undo ())
snapshot_data . flags |= UndoRedo :: SnapshotData :: RECALCULATE_SLA_SUPPORTS ;
2019-07-19 15:29:04 +02:00
//FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if ( this -> printer_technology == ptFFF ) {
2019-07-19 15:47:10 +02:00
const DynamicPrintConfig & config = wxGetApp (). preset_bundle -> prints . get_edited_preset (). config ;
model . wipe_tower . position = Vec2d ( config . opt_float ( "wipe_tower_x" ), config . opt_float ( "wipe_tower_y" ));
model . wipe_tower . rotation = config . opt_float ( "wipe_tower_rotation_angle" );
}
2019-07-26 13:44:33 +02:00
this -> undo_redo_stack (). take_snapshot ( snapshot_name , model , view3D -> get_canvas3d () -> get_selection (), view3D -> get_canvas3d () -> get_gizmos_manager (), snapshot_data );
this -> undo_redo_stack (). release_least_recently_used ();
2019-07-19 10:29:06 +02:00
// Save the last active preset name of a particular printer technology.
(( this -> printer_technology == ptFFF ) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name ) = wxGetApp (). preset_bundle -> printers . get_selected_preset_name ();
2019-08-06 18:16:02 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r :: format_memsize_MB ( this -> undo_redo_stack (). memsize ()) << log_memory_info ();
2019-07-19 10:29:06 +02:00
}
2019-07-04 10:45:41 +02:00
void Plater :: priv :: undo ()
{
2019-08-06 18:16:02 +02:00
const std :: vector < UndoRedo :: Snapshot > & snapshots = this -> undo_redo_stack (). snapshots ();
auto it_current = std :: lower_bound ( snapshots . begin (), snapshots . end (), UndoRedo :: Snapshot ( this -> undo_redo_stack (). active_snapshot_time ()));
if ( -- it_current != snapshots . begin ())
this -> undo_redo_to ( it_current );
2019-07-04 10:45:41 +02:00
}
void Plater :: priv :: redo ()
2019-08-06 18:16:02 +02:00
{
const std :: vector < UndoRedo :: Snapshot > & snapshots = this -> undo_redo_stack (). snapshots ();
auto it_current = std :: lower_bound ( snapshots . begin (), snapshots . end (), UndoRedo :: Snapshot ( this -> undo_redo_stack (). active_snapshot_time ()));
if ( ++ it_current != snapshots . end ())
this -> undo_redo_to ( it_current );
2019-07-04 14:35:04 +02:00
}
2019-07-18 17:41:47 +02:00
void Plater :: priv :: undo_redo_to ( size_t time_to_load )
2019-07-09 20:45:00 +02:00
{
2019-08-06 18:16:02 +02:00
const std :: vector < UndoRedo :: Snapshot > & snapshots = this -> undo_redo_stack (). snapshots ();
auto it_current = std :: lower_bound ( snapshots . begin (), snapshots . end (), UndoRedo :: Snapshot ( time_to_load ));
assert ( it_current != snapshots . end ());
this -> undo_redo_to ( it_current );
2019-07-09 20:45:00 +02:00
}
2019-07-18 17:41:47 +02:00
void Plater :: priv :: undo_redo_to ( std :: vector < UndoRedo :: Snapshot >:: const_iterator it_snapshot )
{
2019-08-23 01:36:56 +02:00
// Make sure that no updating function calls take_snapshot until we are done.
SuppressSnapshots snapshot_supressor ( q );
2019-08-06 18:16:02 +02:00
bool temp_snapshot_was_taken = this -> undo_redo_stack (). temp_snapshot_active ();
PrinterTechnology new_printer_technology = it_snapshot -> snapshot_data . printer_technology ;
bool printer_technology_changed = this -> printer_technology != new_printer_technology ;
if ( printer_technology_changed ) {
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
std :: string s_pt = ( it_snapshot -> snapshot_data . printer_technology == ptFFF ) ? "FFF" : "SLA" ;
2020-04-01 09:48:56 +02:00
if ( ! wxGetApp (). check_unsaved_changes ( format_wxstr ( _L (
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets." ), s_pt )))
2019-08-06 18:16:02 +02:00
// Don't switch the profiles.
return ;
}
2019-07-18 17:41:47 +02:00
// Save the last active preset name of a particular printer technology.
(( this -> printer_technology == ptFFF ) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name ) = wxGetApp (). preset_bundle -> printers . get_selected_preset_name ();
2019-07-19 15:29:04 +02:00
//FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if ( this -> printer_technology == ptFFF ) {
2019-07-19 15:47:10 +02:00
const DynamicPrintConfig & config = wxGetApp (). preset_bundle -> prints . get_edited_preset (). config ;
model . wipe_tower . position = Vec2d ( config . opt_float ( "wipe_tower_x" ), config . opt_float ( "wipe_tower_y" ));
model . wipe_tower . rotation = config . opt_float ( "wipe_tower_rotation_angle" );
}
2019-07-25 17:24:00 +02:00
const int layer_range_idx = it_snapshot -> snapshot_data . layer_range_idx ;
2019-07-19 10:29:06 +02:00
// Flags made of Snapshot::Flags enum values.
2019-07-22 16:52:47 +02:00
unsigned int new_flags = it_snapshot -> snapshot_data . flags ;
2019-08-06 18:16:02 +02:00
UndoRedo :: SnapshotData top_snapshot_data ;
2019-07-22 16:52:47 +02:00
top_snapshot_data . printer_technology = this -> printer_technology ;
2019-07-19 10:29:06 +02:00
if ( this -> view3D -> is_layers_editing_enabled ())
2019-08-06 18:16:02 +02:00
top_snapshot_data . flags |= UndoRedo :: SnapshotData :: VARIABLE_LAYER_EDITING_ACTIVE ;
2019-07-23 18:17:57 +02:00
if ( this -> sidebar -> obj_list () -> is_selected ( itSettings )) {
2019-08-06 18:16:02 +02:00
top_snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_SETTINGS_ON_SIDEBAR ;
2019-07-24 12:32:38 +02:00
top_snapshot_data . layer_range_idx = this -> sidebar -> obj_list () -> get_selected_layers_range_idx ();
2019-07-23 18:17:57 +02:00
}
else if ( this -> sidebar -> obj_list () -> is_selected ( itLayer )) {
top_snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_LAYER_ON_SIDEBAR ;
2019-07-24 12:32:38 +02:00
top_snapshot_data . layer_range_idx = this -> sidebar -> obj_list () -> get_selected_layers_range_idx ();
2019-07-23 18:17:57 +02:00
}
else if ( this -> sidebar -> obj_list () -> is_selected ( itLayerRoot ))
top_snapshot_data . flags |= UndoRedo :: SnapshotData :: SELECTED_LAYERROOT_ON_SIDEBAR ;
2019-08-06 18:16:02 +02:00
bool new_variable_layer_editing_active = ( new_flags & UndoRedo :: SnapshotData :: VARIABLE_LAYER_EDITING_ACTIVE ) != 0 ;
2019-07-23 18:17:57 +02:00
bool new_selected_settings_on_sidebar = ( new_flags & UndoRedo :: SnapshotData :: SELECTED_SETTINGS_ON_SIDEBAR ) != 0 ;
bool new_selected_layer_on_sidebar = ( new_flags & UndoRedo :: SnapshotData :: SELECTED_LAYER_ON_SIDEBAR ) != 0 ;
bool new_selected_layerroot_on_sidebar = ( new_flags & UndoRedo :: SnapshotData :: SELECTED_LAYERROOT_ON_SIDEBAR ) != 0 ;
2019-08-22 16:44:49 +02:00
if ( this -> view3D -> get_canvas3d () -> get_gizmos_manager (). wants_reslice_supports_on_undo ())
top_snapshot_data . flags |= UndoRedo :: SnapshotData :: RECALCULATE_SLA_SUPPORTS ;
2019-08-06 18:16:02 +02:00
// Disable layer editing before the Undo / Redo jump.
2019-07-19 15:36:55 +02:00
if ( ! new_variable_layer_editing_active && view3D -> is_layers_editing_enabled ())
view3D -> get_canvas3d () -> force_main_toolbar_left_action ( view3D -> get_canvas3d () -> get_main_toolbar_item_id ( "layersediting" ));
2019-11-27 11:08:10 +01:00
2019-08-22 16:44:49 +02:00
// Make a copy of the snapshot, undo/redo could invalidate the iterator
const UndoRedo :: Snapshot snapshot_copy = * it_snapshot ;
2019-07-18 17:41:47 +02:00
// Do the jump in time.
2019-07-26 13:44:33 +02:00
if ( it_snapshot -> timestamp < this -> undo_redo_stack (). active_snapshot_time () ?
2019-08-06 18:16:02 +02:00
this -> undo_redo_stack (). undo ( model , this -> view3D -> get_canvas3d () -> get_selection (), this -> view3D -> get_canvas3d () -> get_gizmos_manager (), top_snapshot_data , it_snapshot -> timestamp ) :
this -> undo_redo_stack (). redo ( model , this -> view3D -> get_canvas3d () -> get_gizmos_manager (), it_snapshot -> timestamp )) {
if ( printer_technology_changed ) {
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
AppConfig * app_config = wxGetApp (). app_config ;
app_config -> set ( "presets" , "printer" , ( new_printer_technology == ptFFF ) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name );
wxGetApp (). preset_bundle -> load_presets ( * app_config );
2019-08-27 12:15:29 +02:00
// load_current_presets() calls Tab::load_current_preset() -> TabPrint::update() -> Object_list::update_and_show_object_settings_item(),
// but the Object list still keeps pointer to the old Model. Avoid a crash by removing selection first.
this -> sidebar -> obj_list () -> unselect_objects ();
2019-08-06 18:16:02 +02:00
// Load the currently selected preset into the GUI, update the preset selection box.
// This also switches the printer technology based on the printer technology of the active printer profile.
wxGetApp (). load_current_presets ();
2019-07-18 17:41:47 +02:00
}
2019-07-19 15:47:10 +02:00
//FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if ( this -> printer_technology == ptFFF ) {
const DynamicPrintConfig & current_config = wxGetApp (). preset_bundle -> prints . get_edited_preset (). config ;
Vec2d current_position ( current_config . opt_float ( "wipe_tower_x" ), current_config . opt_float ( "wipe_tower_y" ));
double current_rotation = current_config . opt_float ( "wipe_tower_rotation_angle" );
if ( current_position != model . wipe_tower . position || current_rotation != model . wipe_tower . rotation ) {
DynamicPrintConfig new_config ;
new_config . set_key_value ( "wipe_tower_x" , new ConfigOptionFloat ( model . wipe_tower . position . x ()));
new_config . set_key_value ( "wipe_tower_y" , new ConfigOptionFloat ( model . wipe_tower . position . y ()));
new_config . set_key_value ( "wipe_tower_rotation_angle" , new ConfigOptionFloat ( model . wipe_tower . rotation ));
Tab * tab_print = wxGetApp (). get_tab ( Preset :: TYPE_PRINT );
tab_print -> load_config ( new_config );
tab_print -> update_dirty ();
}
}
2019-07-23 18:17:57 +02:00
// set selection mode for ObjectList on sidebar
this -> sidebar -> obj_list () -> set_selection_mode ( new_selected_settings_on_sidebar ? ObjectList :: SELECTION_MODE :: smSettings :
new_selected_layer_on_sidebar ? ObjectList :: SELECTION_MODE :: smLayer :
2019-08-06 18:16:02 +02:00
new_selected_layerroot_on_sidebar ? ObjectList :: SELECTION_MODE :: smLayerRoot :
2019-07-23 18:17:57 +02:00
ObjectList :: SELECTION_MODE :: smUndef );
if ( new_selected_settings_on_sidebar || new_selected_layer_on_sidebar )
2019-07-25 17:24:00 +02:00
this -> sidebar -> obj_list () -> set_selected_layers_range_idx ( layer_range_idx );
2019-07-23 18:17:57 +02:00
2019-08-22 16:44:49 +02:00
this -> update_after_undo_redo ( snapshot_copy , temp_snapshot_was_taken );
2019-08-06 18:16:02 +02:00
// Enable layer editing after the Undo / Redo jump.
if ( ! view3D -> is_layers_editing_enabled () && this -> layers_height_allowed () && new_variable_layer_editing_active )
2019-07-19 15:36:55 +02:00
view3D -> get_canvas3d () -> force_main_toolbar_left_action ( view3D -> get_canvas3d () -> get_main_toolbar_item_id ( "layersediting" ));
}
2019-07-09 20:45:00 +02:00
}
2019-08-22 16:44:49 +02:00
void Plater :: priv :: update_after_undo_redo ( const UndoRedo :: Snapshot & snapshot , bool /* temp_snapshot_was_taken */ )
2019-07-04 14:35:04 +02:00
{
2019-08-06 18:16:02 +02:00
this -> view3D -> get_canvas3d () -> get_selection (). clear ();
// Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies).
2019-08-23 15:53:45 +02:00
this -> update (( unsigned int ) UpdateParams :: FORCE_BACKGROUND_PROCESSING_UPDATE | ( unsigned int ) UpdateParams :: POSTPONE_VALIDATION_ERROR_MESSAGE );
2019-08-06 18:16:02 +02:00
// Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot.
//if (temp_snapshot_was_taken)
// Release the old snapshots always, as it may have happened, that some of the triangle meshes got deserialized from the snapshot, while some
// triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size.
this -> undo_redo_stack (). release_least_recently_used ();
//YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
2019-07-26 13:44:33 +02:00
this -> view3D -> get_canvas3d () -> get_selection (). set_deserialized ( GUI :: Selection :: EMode ( this -> undo_redo_stack (). selection_deserialized (). mode ), this -> undo_redo_stack (). selection_deserialized (). volumes_and_instances );
2019-08-22 16:44:49 +02:00
this -> view3D -> get_canvas3d () -> get_gizmos_manager (). update_after_undo_redo ( snapshot );
2019-07-04 17:33:19 +02:00
2019-07-11 18:42:02 +02:00
wxGetApp (). obj_list () -> update_after_undo_redo ();
2019-07-18 17:41:47 +02:00
if ( wxGetApp (). get_mode () == comSimple && model_has_advanced_features ( this -> model )) {
2019-08-06 18:16:02 +02:00
// If the user jumped to a snapshot that require user interface with advanced features, switch to the advanced mode without asking.
// There is a little risk of surprising the user, as he already must have had the advanced or expert mode active for such a snapshot to be taken.
2019-07-18 17:41:47 +02:00
Slic3r :: GUI :: wxGetApp (). save_mode ( comAdvanced );
view3D -> set_as_dirty ();
}
2019-08-23 15:53:45 +02:00
// this->update() above was called with POSTPONE_VALIDATION_ERROR_MESSAGE, so that if an error message was generated when updating the back end, it would not open immediately,
// but it would be saved to be show later. Let's do it now. We do not want to display the message box earlier, because on Windows & OSX the message box takes over the message
// queue pump, which in turn executes the rendering function before a full update after the Undo / Redo jump.
this -> show_delayed_error_message ();
2019-08-06 18:16:02 +02:00
//FIXME what about the state of the manipulators?
//FIXME what about the focus? Cursor in the side panel?
2019-07-17 15:48:53 +02:00
2019-07-26 13:44:33 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Undo / Redo snapshot reloaded. Undo / Redo stack memory: " << Slic3r :: format_memsize_MB ( this -> undo_redo_stack (). memsize ()) << log_memory_info ();
2019-07-04 10:45:41 +02:00
}
2019-03-01 11:00:34 +01:00
void Sidebar :: set_btn_label ( const ActionButtonType btn_type , const wxString & label ) const
{
switch ( btn_type )
{
case ActionButtonType :: abReslice : p -> btn_reslice -> SetLabelText ( label ); break ;
case ActionButtonType :: abExport : p -> btn_export_gcode -> SetLabelText ( label ); break ;
2019-12-10 17:31:27 +01:00
case ActionButtonType :: abSendGCode : /*p->btn_send_gcode->SetLabelText(label);*/ break ;
2019-03-01 11:00:34 +01:00
}
}
2018-09-17 12:15:11 +02:00
// Plater / Public
Plater :: Plater ( wxWindow * parent , MainFrame * main_frame )
2020-05-18 15:56:33 +02:00
: wxPanel ( parent , wxID_ANY , wxDefaultPosition , wxSize ( 76 * wxGetApp (). em_unit (), 49 * wxGetApp (). em_unit ()))
2020-03-02 10:58:46 +01:00
, p ( new priv ( this , main_frame ))
2018-09-17 12:15:11 +02:00
{
// Initialization performed in the private c-tor
}
Plater ::~ Plater ()
{
}
2018-12-03 13:14:28 +01:00
Sidebar & Plater :: sidebar () { return * p -> sidebar ; }
Model & Plater :: model () { return p -> model ; }
const Print & Plater :: fff_print () const { return p -> fff_print ; }
Print & Plater :: fff_print () { return p -> fff_print ; }
const SLAPrint & Plater :: sla_print () const { return p -> sla_print ; }
SLAPrint & Plater :: sla_print () { return p -> sla_print ; }
2018-09-17 12:15:11 +02:00
2019-05-14 10:30:14 +02:00
void Plater :: new_project ()
{
2019-06-18 14:45:10 +02:00
p -> select_view_3D ( "3D" );
2019-05-14 15:00:24 +02:00
wxPostEvent ( p -> view3D -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_DELETE_ALL ));
2019-05-14 10:30:14 +02:00
}
2018-11-15 15:27:39 +01:00
void Plater :: load_project ()
{
2019-07-17 15:48:53 +02:00
// Ask user for a project file name.
2018-11-15 15:27:39 +01:00
wxString input_file ;
wxGetApp (). load_project ( this , input_file );
2019-07-17 15:48:53 +02:00
// And finally load the new project.
2019-07-12 15:36:01 +02:00
load_project ( input_file );
}
2018-11-15 15:27:39 +01:00
2019-07-12 15:36:01 +02:00
void Plater :: load_project ( const wxString & filename )
{
if ( filename . empty ())
2018-11-15 15:27:39 +01:00
return ;
2019-07-23 12:57:58 +02:00
// Take the Undo / Redo snapshot.
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Load Project" ) + ": " + wxString :: FromUTF8 ( into_path ( filename ). stem (). string (). c_str ()));
2019-07-23 12:57:58 +02:00
2018-11-15 15:27:39 +01:00
p -> reset ();
std :: vector < fs :: path > input_paths ;
2019-07-12 15:36:01 +02:00
input_paths . push_back ( into_path ( filename ));
2019-09-02 15:15:28 +02:00
std :: vector < size_t > res = load_files ( input_paths );
// if res is empty no data has been loaded
if ( ! res . empty ())
p -> set_project_filename ( filename );
2018-11-15 15:27:39 +01:00
}
2020-04-29 19:10:13 +02:00
void Plater :: add_model ( bool imperial_units /* = false*/ )
2018-10-17 12:17:25 +02:00
{
wxArrayString input_files ;
2018-11-15 15:27:39 +01:00
wxGetApp (). import_model ( this , input_files );
if ( input_files . empty ())
return ;
2018-10-17 12:17:25 +02:00
2019-07-17 15:48:53 +02:00
std :: vector < fs :: path > paths ;
for ( const auto & file : input_files )
paths . push_back ( into_path ( file ));
2019-07-04 17:33:19 +02:00
2019-08-06 18:16:02 +02:00
wxString snapshot_label ;
assert ( ! paths . empty ());
if ( paths . size () == 1 ) {
2020-04-01 09:48:56 +02:00
snapshot_label = _L ( "Import Object" );
2019-08-06 18:16:02 +02:00
snapshot_label += ": " ;
snapshot_label += wxString :: FromUTF8 ( paths . front (). filename (). string (). c_str ());
} else {
2020-04-01 09:48:56 +02:00
snapshot_label = _L ( "Import Objects" );
2019-08-06 18:16:02 +02:00
snapshot_label += ": " ;
snapshot_label += wxString :: FromUTF8 ( paths . front (). filename (). string (). c_str ());
for ( size_t i = 1 ; i < paths . size (); ++ i ) {
snapshot_label += ", " ;
snapshot_label += wxString :: FromUTF8 ( paths [ i ]. filename (). string (). c_str ());
}
}
2019-07-17 15:48:53 +02:00
2019-08-06 18:16:02 +02:00
Plater :: TakeSnapshot snapshot ( this , snapshot_label );
2020-04-29 19:10:13 +02:00
load_files ( paths , true , false , imperial_units );
2018-10-17 12:17:25 +02:00
}
2020-04-13 12:31:37 +02:00
void Plater :: import_sl1_archive ()
{
2020-04-23 19:45:55 +02:00
p -> m_ui_jobs . import_sla_arch ();
2020-04-13 12:31:37 +02:00
}
2018-11-16 09:26:41 +01:00
void Plater :: extract_config_from_project ()
{
wxString input_file ;
wxGetApp (). load_project ( this , input_file );
if ( input_file . empty ())
return ;
std :: vector < fs :: path > input_paths ;
2019-01-02 15:11:05 +01:00
input_paths . push_back ( into_path ( input_file ));
2018-11-16 09:26:41 +01:00
load_files ( input_paths , false , true );
}
2020-04-29 19:10:13 +02:00
std :: vector < size_t > Plater :: load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config , bool imperial_units /*= false*/ ) { return p -> load_files ( input_files , load_model , load_config , imperial_units ); }
2018-09-17 12:15:11 +02:00
2019-01-09 10:43:17 +01:00
// To be called when providing a list of files to the GUI slic3r on command line.
2020-04-29 19:10:13 +02:00
std :: vector < size_t > Plater :: load_files ( const std :: vector < std :: string >& input_files , bool load_model , bool load_config , bool imperial_units /*= false*/ )
2019-08-06 18:16:02 +02:00
{
2019-01-09 10:43:17 +01:00
std :: vector < fs :: path > paths ;
paths . reserve ( input_files . size ());
2019-09-02 15:15:28 +02:00
for ( const std :: string & path : input_files )
2019-01-09 10:43:17 +01:00
paths . emplace_back ( path );
2020-04-29 19:10:13 +02:00
return p -> load_files ( paths , load_model , load_config , imperial_units );
2019-01-09 10:43:17 +01:00
}
2018-11-21 13:32:24 +01:00
void Plater :: update () { p -> update (); }
2018-10-17 12:59:58 +02:00
2019-06-18 16:24:30 +02:00
void Plater :: stop_jobs () { p -> m_ui_jobs . stop_all (); }
2019-05-13 18:58:56 +02:00
2018-12-04 11:14:39 +01:00
void Plater :: update_ui_from_settings () { p -> update_ui_from_settings (); }
2018-10-17 12:59:58 +02:00
void Plater :: select_view ( const std :: string & direction ) { p -> select_view ( direction ); }
2018-12-04 13:55:25 +01:00
void Plater :: select_view_3D ( const std :: string & name ) { p -> select_view_3D ( name ); }
2019-08-20 09:01:09 +02:00
bool Plater :: is_preview_shown () const { return p -> is_preview_shown (); }
bool Plater :: is_preview_loaded () const { return p -> is_preview_loaded (); }
bool Plater :: is_view3D_shown () const { return p -> is_view3D_shown (); }
2020-02-04 14:42:26 +01:00
bool Plater :: are_view3D_labels_shown () const { return p -> are_view3D_labels_shown (); }
void Plater :: show_view3D_labels ( bool show ) { p -> show_view3D_labels ( show ); }
2020-03-16 12:49:39 +01:00
bool Plater :: is_sidebar_collapsed () const { return p -> is_sidebar_collapsed (); }
2020-04-29 15:58:57 +02:00
void Plater :: collapse_sidebar ( bool show ) { p -> collapse_sidebar ( show ); }
2020-03-16 12:49:39 +01:00
2020-03-13 15:09:07 +01:00
#if ENABLE_SLOPE_RENDERING
bool Plater :: is_view3D_slope_shown () const { return p -> is_view3D_slope_shown (); }
void Plater :: show_view3D_slope ( bool show ) { p -> show_view3D_slope ( show ); }
bool Plater :: is_view3D_layers_editing_enabled () const { return p -> is_view3D_layers_editing_enabled (); }
#endif // ENABLE_SLOPE_RENDERING
2018-11-21 15:28:35 +01:00
void Plater :: select_all () { p -> select_all (); }
2019-05-14 11:57:39 +02:00
void Plater :: deselect_all () { p -> deselect_all (); }
2018-11-21 15:28:35 +01:00
2018-10-08 19:14:55 +02:00
void Plater :: remove ( size_t obj_idx ) { p -> remove ( obj_idx ); }
2018-11-22 11:31:53 +01:00
void Plater :: reset () { p -> reset (); }
2019-05-02 13:52:13 +02:00
void Plater :: reset_with_confirm ()
{
2020-04-01 09:48:56 +02:00
if ( wxMessageDialog (( wxWindow * ) this , _L ( "All objects will be removed, continue?" ), wxString ( SLIC3R_APP_NAME ) + " - " + _L ( "Delete all" ), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE ). ShowModal () == wxID_YES )
2019-05-02 13:52:13 +02:00
reset ();
}
2018-11-22 11:31:53 +01:00
2018-11-13 14:17:35 +01:00
void Plater :: delete_object_from_model ( size_t obj_idx ) { p -> delete_object_from_model ( obj_idx ); }
2018-10-04 11:12:55 +02:00
void Plater :: remove_selected ()
{
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Delete Selected Objects" ));
2018-12-04 13:55:25 +01:00
this -> p -> view3D -> delete_selected ();
2018-10-04 11:12:55 +02:00
}
2018-10-18 15:09:41 +02:00
void Plater :: increase_instances ( size_t num )
2018-10-11 10:03:38 +02:00
{
2019-04-17 14:56:00 +02:00
if ( ! can_increase_instances ()) { return ; }
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Increase Instances" ));
2019-07-02 16:42:23 +02:00
2018-10-17 09:30:07 +02:00
int obj_idx = p -> get_selected_object_idx ();
ModelObject * model_object = p -> model . objects [ obj_idx ];
ModelInstance * model_instance = model_object -> instances . back ();
2018-10-11 10:03:38 +02:00
2018-11-07 13:40:24 +01:00
bool was_one_instance = model_object -> instances . size () == 1 ;
2019-08-06 18:16:02 +02:00
2019-04-12 15:31:33 +02:00
double offset_base = canvas3D () -> get_size_proportional_to_max_bed_size ( 0.05 );
double offset = offset_base ;
for ( size_t i = 0 ; i < num ; i ++ , offset += offset_base ) {
2018-10-11 10:03:38 +02:00
Vec3d offset_vec = model_instance -> get_offset () + Vec3d ( offset , offset , 0.0 );
2019-01-03 15:04:29 +01:00
model_object -> add_instance ( offset_vec , model_instance -> get_scaling_factor (), model_instance -> get_rotation (), model_instance -> get_mirror ());
2018-10-23 15:27:31 +02:00
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
2018-10-11 10:03:38 +02:00
}
2019-09-24 09:11:27 +02:00
if ( p -> get_config ( "autocenter" ) == "1" )
2020-04-23 18:47:51 +02:00
arrange ();
2019-09-24 09:11:27 +02:00
p -> update ();
2018-10-11 10:03:38 +02:00
2018-10-23 09:19:41 +02:00
p -> get_selection (). add_instance ( obj_idx , ( int ) model_object -> instances . size () - 1 );
2020-04-29 13:35:35 +02:00
sidebar (). obj_list () -> increase_object_instances ( obj_idx , was_one_instance ? num + 1 : num );
2018-10-11 10:03:38 +02:00
2020-04-29 13:35:35 +02:00
p -> selection_changed ();
2018-10-17 11:12:38 +02:00
this -> p -> schedule_background_process ();
2018-10-11 10:03:38 +02:00
}
2018-10-18 15:09:41 +02:00
void Plater :: decrease_instances ( size_t num )
2018-10-11 10:03:38 +02:00
{
2019-04-17 14:56:00 +02:00
if ( ! can_decrease_instances ()) { return ; }
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Decrease Instances" ));
2019-07-02 16:42:23 +02:00
2018-10-17 09:30:07 +02:00
int obj_idx = p -> get_selected_object_idx ();
ModelObject * model_object = p -> model . objects [ obj_idx ];
2018-10-11 10:03:38 +02:00
if ( model_object -> instances . size () > num ) {
2018-11-08 14:23:17 +01:00
for ( size_t i = 0 ; i < num ; ++ i )
2018-10-11 10:03:38 +02:00
model_object -> delete_last_instance ();
2019-05-14 16:37:32 +02:00
p -> update ();
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
2018-10-18 11:41:29 +02:00
sidebar (). obj_list () -> decrease_object_instances ( obj_idx , num );
2018-10-17 09:30:07 +02:00
}
else {
remove ( obj_idx );
2018-10-11 10:03:38 +02:00
}
2018-10-23 09:19:41 +02:00
if ( ! model_object -> instances . empty ())
p -> get_selection (). add_instance ( obj_idx , ( int ) model_object -> instances . size () - 1 );
2018-10-17 09:40:04 +02:00
p -> selection_changed ();
2018-10-23 15:27:31 +02:00
this -> p -> schedule_background_process ();
2018-10-11 10:03:38 +02:00
}
2018-10-25 21:13:45 +02:00
void Plater :: set_number_of_copies ( /*size_t num*/ )
2018-10-11 10:03:38 +02:00
{
2018-10-17 09:30:07 +02:00
int obj_idx = p -> get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
ModelObject * model_object = p -> model . objects [ obj_idx ];
2018-10-11 10:03:38 +02:00
2020-04-01 09:48:56 +02:00
const int num = wxGetNumberFromUser ( " " , _L ( "Enter the number of copies:" ),
_L ( "Copies of the selected object" ), model_object -> instances . size (), 0 , 1000 , this );
2018-10-25 21:13:45 +02:00
if ( num < 0 )
return ;
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , wxString :: Format ( _L ( "Set numbers of copies to %d" ), num ));
2019-07-04 17:33:19 +02:00
2019-09-20 16:45:43 +02:00
int diff = num - ( int ) model_object -> instances . size ();
2018-10-18 15:09:41 +02:00
if ( diff > 0 )
increase_instances ( diff );
else if ( diff < 0 )
decrease_instances ( - diff );
2018-10-11 10:03:38 +02:00
}
2018-10-04 11:12:55 +02:00
2018-11-21 15:47:41 +01:00
bool Plater :: is_selection_empty () const
{
2019-04-10 12:16:17 +02:00
return p -> get_selection (). is_empty () || p -> get_selection (). is_wipe_tower ();
2018-11-21 15:47:41 +01:00
}
2019-05-22 14:42:38 +02:00
void Plater :: scale_selection_to_fit_print_volume ()
{
p -> scale_selection_to_fit_print_volume ();
}
2020-05-20 20:07:31 +02:00
void Plater :: convert_unit ( bool from_imperial_unit )
{
std :: vector < int > obj_idxs , volume_idxs ;
wxGetApp (). obj_list () -> get_selection_indexes ( obj_idxs , volume_idxs );
if ( obj_idxs . empty () && volume_idxs . empty ())
return ;
TakeSnapshot snapshot ( this , from_imperial_unit ? _L ( "Convert from imperial units" ) : _L ( "Convert to imperial units" ));
wxBusyCursor wait ;
ModelObjectPtrs objects ;
for ( int obj_idx : obj_idxs ) {
ModelObject * object = p -> model . objects [ obj_idx ];
object -> convert_units ( objects , from_imperial_unit , volume_idxs );
remove ( obj_idx );
}
p -> load_model_objects ( objects );
Selection & selection = p -> view3D -> get_canvas3d () -> get_selection ();
size_t last_obj_idx = p -> model . objects . size () - 1 ;
if ( volume_idxs . empty ()) {
for ( size_t i = 0 ; i < objects . size (); ++ i )
selection . add_object (( unsigned int )( last_obj_idx - i ), i == 0 );
}
else {
for ( int vol_idx : volume_idxs )
selection . add_volume ( last_obj_idx , vol_idx , 0 , false );
}
}
2018-11-26 10:56:07 +01:00
void Plater :: cut ( size_t obj_idx , size_t instance_idx , coordf_t z , bool keep_upper , bool keep_lower , bool rotate_lower )
2018-10-18 15:13:38 +02:00
{
wxCHECK_RET ( obj_idx < p -> model . objects . size (), "obj_idx out of bounds" );
auto * object = p -> model . objects [ obj_idx ];
wxCHECK_RET ( instance_idx < object -> instances . size (), "instance_idx out of bounds" );
2019-01-18 15:02:07 +01:00
if ( ! keep_upper && ! keep_lower ) {
return ;
}
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Cut by Plane" ));
2019-07-04 17:33:19 +02:00
2019-02-05 20:23:24 +01:00
wxBusyCursor wait ;
2018-11-26 10:56:07 +01:00
const auto new_objects = object -> cut ( instance_idx , z , keep_upper , keep_lower , rotate_lower );
2018-10-18 15:13:38 +02:00
remove ( obj_idx );
p -> load_model_objects ( new_objects );
2019-12-19 10:06:46 +01:00
Selection & selection = p -> get_selection ();
size_t last_id = p -> model . objects . size () - 1 ;
for ( size_t i = 0 ; i < new_objects . size (); ++ i )
{
selection . add_object (( unsigned int )( last_id - i ), i == 0 );
}
2018-10-18 15:13:38 +02:00
}
2020-01-02 16:30:28 +01:00
void Plater :: export_gcode ( bool prefer_removable )
2018-10-04 11:12:55 +02:00
{
2018-10-23 15:27:31 +02:00
if ( p -> model . objects . empty ())
return ;
2018-09-17 12:15:11 +02:00
2019-02-03 10:41:14 +01:00
// If possible, remove accents from accented latin characters.
// This function is useful for generating file names to be processed by legacy firmwares.
fs :: path default_output_file ;
try {
2019-08-06 18:16:02 +02:00
// Update the background processing, so that the placeholder parser will get the correct values for the ouput file template.
// Also if there is something wrong with the current configuration, a pop-up dialog will be shown and the export will not be performed.
unsigned int state = this -> p -> update_restart_background_process ( false , false );
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID )
return ;
2019-05-17 16:27:00 +02:00
default_output_file = this -> p -> background_process . output_filepath_for_project ( into_path ( get_project_filename ( ".3mf" )));
2019-05-15 14:09:16 +02:00
}
catch ( const std :: exception & ex ) {
2019-02-03 10:41:14 +01:00
show_error ( this , ex . what ());
return ;
2018-09-17 12:15:11 +02:00
}
2019-02-03 10:41:14 +01:00
default_output_file = fs :: path ( Slic3r :: fold_utf8_to_ascii ( default_output_file . string ()));
2020-03-06 15:10:58 +01:00
AppConfig & appconfig = * wxGetApp (). app_config ;
RemovableDriveManager & removable_drive_manager = * wxGetApp (). removable_drive_manager ();
// Get a last save path, either to removable media or to an internal media.
std :: string start_dir = appconfig . get_last_output_dir ( default_output_file . parent_path (). string (), prefer_removable );
if ( prefer_removable ) {
// Returns a path to a removable media if it exists, prefering start_dir. Update the internal removable drives database.
start_dir = removable_drive_manager . get_removable_drive_path ( start_dir );
if ( start_dir . empty ())
// Direct user to the last internal media.
start_dir = appconfig . get_last_output_dir ( default_output_file . parent_path (). string (), false );
2019-11-27 13:30:45 +01:00
}
2019-02-03 10:41:14 +01:00
fs :: path output_path ;
2020-03-06 15:10:58 +01:00
{
2020-04-01 09:48:56 +02:00
wxFileDialog dlg ( this , ( printer_technology () == ptFFF ) ? _L ( "Save G-code file as:" ) : _L ( "Save SL1 file as:" ),
2020-03-06 15:10:58 +01:00
start_dir ,
from_path ( default_output_file . filename ()),
GUI :: file_wildcards (( printer_technology () == ptFFF ) ? FT_GCODE : FT_PNGZIP , default_output_file . extension (). string ()),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if ( dlg . ShowModal () == wxID_OK )
output_path = into_path ( dlg . GetPath ());
2019-02-03 10:41:14 +01:00
}
2019-12-19 14:19:41 +01:00
2020-03-06 15:10:58 +01:00
if ( ! output_path . empty ()) {
bool path_on_removable_media = removable_drive_manager . set_and_verify_last_save_path ( output_path . string ());
2020-03-12 12:43:01 +01:00
p -> export_gcode ( output_path , path_on_removable_media , PrintHostJob ());
2020-03-06 15:10:58 +01:00
// Storing a path to AppConfig either as path to removable media or a path to internal media.
// is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives
// while the dialog was open.
appconfig . update_last_output_dir ( output_path . parent_path (). string (), path_on_removable_media );
p -> writing_to_removable_device = path_on_removable_media ;
2019-12-12 10:48:33 +01:00
}
2018-09-17 12:15:11 +02:00
}
2019-05-02 13:46:39 +02:00
void Plater :: export_stl ( bool extended , bool selection_only )
2018-10-04 11:12:55 +02:00
{
2018-10-17 09:30:07 +02:00
if ( p -> model . objects . empty ()) { return ; }
2018-10-04 11:12:55 +02:00
2019-04-04 11:31:26 +02:00
wxString path = p -> get_export_file ( FT_STL );
if ( path . empty ()) { return ; }
2019-01-02 15:11:05 +01:00
const std :: string path_u8 = into_u8 ( path );
2018-12-05 15:22:03 +01:00
2019-02-22 10:11:57 +01:00
wxBusyCursor wait ;
2020-02-11 00:58:19 +01:00
const auto & selection = p -> get_selection ();
const auto obj_idx = selection . get_object_idx ();
if ( selection_only && ( obj_idx == - 1 || selection . is_wipe_tower ()))
return ;
2018-12-05 15:22:03 +01:00
TriangleMesh mesh ;
2020-02-11 00:58:19 +01:00
if ( p -> printer_technology == ptFFF ) {
if ( selection_only ) {
const ModelObject * model_object = p -> model . objects [ obj_idx ];
if ( selection . get_mode () == Selection :: Instance )
{
if ( selection . is_single_full_object ())
mesh = model_object -> mesh ();
else
mesh = model_object -> full_raw_mesh ();
}
2019-04-05 11:30:49 +02:00
else
2020-02-11 00:58:19 +01:00
{
const GLVolume * volume = selection . get_volume ( * selection . get_volume_idxs (). begin ());
mesh = model_object -> volumes [ volume -> volume_idx ()] -> mesh ();
mesh . transform ( volume -> get_volume_transformation (). get_matrix ());
mesh . translate ( - model_object -> origin_translation . cast < float > ());
}
2019-04-05 11:30:49 +02:00
}
2020-02-11 00:58:19 +01:00
else {
mesh = p -> model . mesh ();
2019-04-04 15:13:43 +02:00
}
}
2019-04-05 10:08:34 +02:00
else
2019-05-02 13:46:39 +02:00
{
2020-02-11 00:58:19 +01:00
// This is SLA mode, all objects have only one volume.
// However, we must have a look at the backend to load
// hollowed mesh and/or supports
2018-12-05 15:22:03 +01:00
2020-02-11 00:58:19 +01:00
const PrintObjects & objects = p -> sla_print . objects ();
for ( const SLAPrintObject * object : objects )
2019-05-02 13:46:39 +02:00
{
2020-02-11 00:58:19 +01:00
const ModelObject * model_object = object -> model_object ();
if ( selection_only ) {
if ( model_object -> id () != p -> model . objects [ obj_idx ] -> id ())
continue ;
}
Transform3d mesh_trafo_inv = object -> trafo (). inverse ();
bool is_left_handed = object -> is_left_handed ();
TriangleMesh pad_mesh ;
bool has_pad_mesh = extended && object -> has_mesh ( slaposPad );
if ( has_pad_mesh )
2019-05-02 13:46:39 +02:00
{
2020-02-11 00:58:19 +01:00
pad_mesh = object -> get_mesh ( slaposPad );
pad_mesh . transform ( mesh_trafo_inv );
}
2019-05-02 13:46:39 +02:00
2020-02-11 00:58:19 +01:00
TriangleMesh supports_mesh ;
bool has_supports_mesh = extended && object -> has_mesh ( slaposSupportTree );
if ( has_supports_mesh )
{
supports_mesh = object -> get_mesh ( slaposSupportTree );
supports_mesh . transform ( mesh_trafo_inv );
}
const std :: vector < SLAPrintObject :: Instance >& obj_instances = object -> instances ();
for ( const SLAPrintObject :: Instance & obj_instance : obj_instances )
{
auto it = std :: find_if ( model_object -> instances . begin (), model_object -> instances . end (),
[ & obj_instance ]( const ModelInstance * mi ) { return mi -> id () == obj_instance . instance_id ; });
assert ( it != model_object -> instances . end ());
if ( it != model_object -> instances . end ())
2019-05-02 13:46:39 +02:00
{
2020-02-11 00:58:19 +01:00
bool one_inst_only = selection_only && ! selection . is_single_full_object ();
2019-05-02 13:46:39 +02:00
2020-02-11 00:58:19 +01:00
int instance_idx = it - model_object -> instances . begin ();
const Transform3d & inst_transform = one_inst_only
? Transform3d :: Identity ()
: object -> model_object () -> instances [ instance_idx ] -> get_transformation (). get_matrix ();
2019-05-02 13:46:39 +02:00
2020-02-11 00:58:19 +01:00
if ( has_pad_mesh )
2019-05-02 13:46:39 +02:00
{
2020-02-11 00:58:19 +01:00
TriangleMesh inst_pad_mesh = pad_mesh ;
inst_pad_mesh . transform ( inst_transform , is_left_handed );
mesh . merge ( inst_pad_mesh );
2019-05-02 13:46:39 +02:00
}
2020-02-11 00:58:19 +01:00
if ( has_supports_mesh )
{
TriangleMesh inst_supports_mesh = supports_mesh ;
inst_supports_mesh . transform ( inst_transform , is_left_handed );
mesh . merge ( inst_supports_mesh );
}
TriangleMesh inst_object_mesh = object -> get_mesh_to_print ();
2020-02-13 13:49:13 +01:00
inst_object_mesh . transform ( mesh_trafo_inv );
2020-02-11 00:58:19 +01:00
inst_object_mesh . transform ( inst_transform , is_left_handed );
mesh . merge ( inst_object_mesh );
if ( one_inst_only )
break ;
2019-05-02 13:46:39 +02:00
}
}
}
}
2019-01-02 15:11:05 +01:00
Slic3r :: store_stl ( path_u8 . c_str (), & mesh , true );
2020-04-01 09:48:56 +02:00
p -> statusbar () -> set_status_text ( format_wxstr ( _L ( "STL file exported to %s" ), path ));
2018-10-04 11:12:55 +02:00
}
void Plater :: export_amf ()
{
2018-10-17 09:30:07 +02:00
if ( p -> model . objects . empty ()) { return ; }
2018-10-08 19:55:30 +02:00
2019-04-04 11:31:26 +02:00
wxString path = p -> get_export_file ( FT_AMF );
if ( path . empty ()) { return ; }
2019-01-02 15:11:05 +01:00
const std :: string path_u8 = into_u8 ( path );
2018-10-08 19:55:30 +02:00
2019-02-05 20:23:24 +01:00
wxBusyCursor wait ;
2019-04-04 11:31:26 +02:00
bool export_config = true ;
DynamicPrintConfig cfg = wxGetApp (). preset_bundle -> full_config_secure ();
2020-01-08 11:11:38 +01:00
bool full_pathnames = wxGetApp (). app_config -> get ( "export_sources_full_pathnames" ) == "1" ;
if ( Slic3r :: store_amf ( path_u8 . c_str (), & p -> model , export_config ? & cfg : nullptr , full_pathnames )) {
2018-10-08 19:55:30 +02:00
// Success
2020-04-01 09:48:56 +02:00
p -> statusbar () -> set_status_text ( format_wxstr ( _L ( "AMF file exported to %s" ), path ));
2018-10-08 19:55:30 +02:00
} else {
// Failure
2020-04-01 09:48:56 +02:00
p -> statusbar () -> set_status_text ( format_wxstr ( _L ( "Error exporting AMF file %s" ), path ));
2018-10-08 19:55:30 +02:00
}
2018-10-04 11:12:55 +02:00
}
2018-11-15 15:27:39 +01:00
void Plater :: export_3mf ( const boost :: filesystem :: path & output_path )
2018-10-04 11:12:55 +02:00
{
2018-10-17 09:30:07 +02:00
if ( p -> model . objects . empty ()) { return ; }
2018-10-08 19:55:30 +02:00
2018-11-15 15:27:39 +01:00
wxString path ;
bool export_config = true ;
if ( output_path . empty ())
{
2019-04-04 11:31:26 +02:00
path = p -> get_export_file ( FT_3MF );
if ( path . empty ()) { return ; }
2018-11-15 15:27:39 +01:00
}
else
2019-01-02 15:11:05 +01:00
path = from_path ( output_path );
2018-10-08 19:55:30 +02:00
2018-11-15 15:27:39 +01:00
if ( ! path . Lower (). EndsWith ( ".3mf" ))
return ;
2018-10-08 19:55:30 +02:00
2019-06-18 16:24:30 +02:00
DynamicPrintConfig cfg = wxGetApp (). preset_bundle -> full_config_secure ();
2019-01-02 15:11:05 +01:00
const std :: string path_u8 = into_u8 ( path );
2019-02-05 20:23:24 +01:00
wxBusyCursor wait ;
2020-01-08 11:11:38 +01:00
bool full_pathnames = wxGetApp (). app_config -> get ( "export_sources_full_pathnames" ) == "1" ;
ThumbnailData thumbnail_data ;
p -> generate_thumbnail ( thumbnail_data , THUMBNAIL_SIZE_3MF . first , THUMBNAIL_SIZE_3MF . second , false , true , true , true );
if ( Slic3r :: store_3mf ( path_u8 . c_str (), & p -> model , export_config ? & cfg : nullptr , full_pathnames , & thumbnail_data )) {
2018-10-08 19:55:30 +02:00
// Success
2020-04-01 09:48:56 +02:00
p -> statusbar () -> set_status_text ( format_wxstr ( _L ( "3MF file exported to %s" ), path ));
2019-05-14 15:00:24 +02:00
p -> set_project_filename ( path );
}
else {
2018-10-08 19:55:30 +02:00
// Failure
2020-04-01 09:48:56 +02:00
p -> statusbar () -> set_status_text ( format_wxstr ( _L ( "Error exporting 3MF file %s" ), path ));
2018-10-08 19:55:30 +02:00
}
2018-10-04 11:12:55 +02:00
}
2019-09-19 09:09:11 +02:00
void Plater :: reload_from_disk ()
{
p -> reload_from_disk ();
}
2020-01-31 12:15:04 +01:00
void Plater :: reload_all_from_disk ()
{
p -> reload_all_from_disk ();
}
2019-08-20 11:33:58 +02:00
bool Plater :: has_toolpaths_to_export () const
{
return p -> preview -> get_canvas3d () -> has_toolpaths_to_export ();
}
void Plater :: export_toolpaths_to_obj () const
2019-08-20 09:01:09 +02:00
{
if (( printer_technology () != ptFFF ) || ! is_preview_loaded ())
return ;
wxString path = p -> get_export_file ( FT_OBJ );
if ( path . empty ())
return ;
2019-08-20 11:33:58 +02:00
2019-08-20 09:01:09 +02:00
wxBusyCursor wait ;
p -> preview -> get_canvas3d () -> export_toolpaths_to_obj ( into_u8 ( path ). c_str ());
}
2019-11-07 14:25:03 +01:00
2018-09-17 12:15:11 +02:00
void Plater :: reslice ()
{
2019-05-14 10:34:11 +02:00
// Stop arrange and (or) optimize rotation tasks.
this -> stop_jobs ();
2019-07-31 13:00:35 +02:00
if ( printer_technology () == ptSLA ) {
for ( auto & object : model (). objects )
if ( object -> sla_points_status == sla :: PointsStatus :: NoPoints )
object -> sla_points_status = sla :: PointsStatus :: Generating ;
}
2019-08-06 18:16:02 +02:00
2018-11-13 17:45:44 +01:00
//FIXME Don't reslice if export of G-code or sending to OctoPrint is running.
// bitmask of UpdateBackgroundProcessReturnState
2019-01-10 16:06:24 +01:00
unsigned int state = this -> p -> update_background_process ( true );
2018-11-13 17:45:44 +01:00
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE )
2018-12-04 13:55:25 +01:00
this -> p -> view3D -> reload_scene ( false );
2019-02-25 10:21:12 +01:00
// If the SLA processing of just a single object's supports is running, restart slicing for the whole object.
this -> p -> background_process . set_task ( PrintBase :: TaskParams ());
2018-12-22 10:02:42 +01:00
// Only restarts if the state is valid.
this -> p -> restart_background_process ( state | priv :: UPDATE_BACKGROUND_PROCESS_FORCE_RESTART );
2019-03-01 11:00:34 +01:00
if (( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ) != 0 )
return ;
if ( p -> background_process . running ())
{
if ( wxGetApp (). get_mode () == comSimple )
2020-04-01 09:48:56 +02:00
p -> sidebar -> set_btn_label ( ActionButtonType :: abReslice , _L ( "Slicing" ) + dots );
2019-03-01 11:00:34 +01:00
else
{
2020-04-01 09:48:56 +02:00
p -> sidebar -> set_btn_label ( ActionButtonType :: abReslice , _L ( "Slice now" ));
2019-03-01 11:00:34 +01:00
p -> show_action_buttons ( false );
}
}
else if ( ! p -> background_process . empty () && ! p -> background_process . idle ())
p -> show_action_buttons ( true );
2019-07-02 13:13:17 +02:00
// update type of preview
2019-10-16 09:45:11 +02:00
p -> preview -> update_view_type ( true );
2018-09-17 12:15:11 +02:00
}
2019-08-23 15:53:45 +02:00
void Plater :: reslice_SLA_supports ( const ModelObject & object , bool postpone_error_messages )
2020-01-23 11:22:06 +01:00
{
reslice_SLA_until_step ( slaposPad , object , postpone_error_messages );
}
void Plater :: reslice_SLA_hollowing ( const ModelObject & object , bool postpone_error_messages )
{
2020-02-03 15:42:54 +01:00
reslice_SLA_until_step ( slaposDrillHoles , object , postpone_error_messages );
2020-01-23 11:22:06 +01:00
}
void Plater :: reslice_SLA_until_step ( SLAPrintObjectStep step , const ModelObject & object , bool postpone_error_messages )
2019-02-21 11:40:56 +01:00
{
//FIXME Don't reslice if export of G-code or sending to OctoPrint is running.
// bitmask of UpdateBackgroundProcessReturnState
2019-08-23 15:53:45 +02:00
unsigned int state = this -> p -> update_background_process ( true , postpone_error_messages );
2019-02-21 11:40:56 +01:00
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE )
this -> p -> view3D -> reload_scene ( false );
2019-06-18 16:24:30 +02:00
if ( this -> p -> background_process . empty () || ( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ))
2019-02-21 11:40:56 +01:00
// Nothing to do on empty input or invalid configuration.
return ;
// Limit calculation to the single object only.
PrintBase :: TaskParams task ;
task . single_model_object = object . id ();
// If the background processing is not enabled, calculate supports just for the single instance.
// Otherwise calculate everything, but start with the provided object.
2019-08-06 18:16:02 +02:00
if ( ! this -> p -> background_processing_enabled ()) {
task . single_model_instance_only = true ;
2020-01-23 11:22:06 +01:00
task . to_object_step = step ;
2019-08-06 18:16:02 +02:00
}
2019-02-21 11:40:56 +01:00
this -> p -> background_process . set_task ( task );
// and let the background processing start.
this -> p -> restart_background_process ( state | priv :: UPDATE_BACKGROUND_PROCESS_FORCE_RESTART );
}
2018-10-04 11:12:55 +02:00
void Plater :: send_gcode ()
{
2018-12-11 10:33:11 +01:00
if ( p -> model . objects . empty ()) { return ; }
PrintHostJob upload_job ( p -> config );
if ( upload_job . empty ()) { return ; }
// Obtain default output path
fs :: path default_output_file ;
try {
2019-08-06 18:16:02 +02:00
// Update the background processing, so that the placeholder parser will get the correct values for the ouput file template.
// Also if there is something wrong with the current configuration, a pop-up dialog will be shown and the export will not be performed.
unsigned int state = this -> p -> update_restart_background_process ( false , false );
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID )
return ;
2019-05-17 16:27:00 +02:00
default_output_file = this -> p -> background_process . output_filepath_for_project ( into_path ( get_project_filename ( ".3mf" )));
2019-05-15 14:09:16 +02:00
}
catch ( const std :: exception & ex ) {
2018-12-11 10:33:11 +01:00
show_error ( this , ex . what ());
return ;
}
default_output_file = fs :: path ( Slic3r :: fold_utf8_to_ascii ( default_output_file . string ()));
2019-03-04 16:50:43 +01:00
PrintHostSendDialog dlg ( default_output_file , upload_job . printhost -> can_start_print ());
2018-12-11 10:33:11 +01:00
if ( dlg . ShowModal () == wxID_OK ) {
upload_job . upload_data . upload_path = dlg . filename ();
upload_job . upload_data . start_print = dlg . start_print ();
2020-03-12 12:43:01 +01:00
p -> export_gcode ( fs :: path (), false , std :: move ( upload_job ));
2018-12-11 10:33:11 +01:00
}
2018-10-04 11:12:55 +02:00
}
2020-03-06 15:10:58 +01:00
// Called when the Eject button is pressed.
2019-12-11 12:28:51 +01:00
void Plater :: eject_drive ()
{
2020-03-11 18:25:05 +01:00
wxBusyCursor wait ;
2020-03-06 15:10:58 +01:00
wxGetApp (). removable_drive_manager () -> eject_drive ();
2019-12-11 12:28:51 +01:00
}
2019-07-04 10:45:41 +02:00
void Plater :: take_snapshot ( const std :: string & snapshot_name ) { p -> take_snapshot ( snapshot_name ); }
void Plater :: take_snapshot ( const wxString & snapshot_name ) { p -> take_snapshot ( snapshot_name ); }
2019-07-11 16:00:01 +02:00
void Plater :: suppress_snapshots () { p -> suppress_snapshots (); }
void Plater :: allow_snapshots () { p -> allow_snapshots (); }
2019-07-04 10:45:41 +02:00
void Plater :: undo () { p -> undo (); }
void Plater :: redo () { p -> redo (); }
2019-07-09 20:45:00 +02:00
void Plater :: undo_to ( int selection )
{
if ( selection == 0 ) {
p -> undo ();
return ;
}
2019-08-06 18:16:02 +02:00
2019-07-09 20:45:00 +02:00
const int idx = p -> get_active_snapshot_index () - selection - 1 ;
2019-07-26 13:44:33 +02:00
p -> undo_redo_to ( p -> undo_redo_stack (). snapshots ()[ idx ]. timestamp );
2019-07-09 20:45:00 +02:00
}
void Plater :: redo_to ( int selection )
{
if ( selection == 0 ) {
p -> redo ();
return ;
}
2019-08-06 18:16:02 +02:00
2019-07-10 10:15:07 +02:00
const int idx = p -> get_active_snapshot_index () + selection + 1 ;
2019-07-26 13:44:33 +02:00
p -> undo_redo_to ( p -> undo_redo_stack (). snapshots ()[ idx ]. timestamp );
2019-07-09 20:45:00 +02:00
}
2019-07-09 15:27:28 +02:00
bool Plater :: undo_redo_string_getter ( const bool is_undo , int idx , const char ** out_text )
{
2019-07-26 13:44:33 +02:00
const std :: vector < UndoRedo :: Snapshot >& ss_stack = p -> undo_redo_stack (). snapshots ();
2019-07-09 20:45:00 +02:00
const int idx_in_ss_stack = p -> get_active_snapshot_index () + ( is_undo ? - ( ++ idx ) : idx );
2019-07-09 15:27:28 +02:00
2019-10-29 10:40:34 +01:00
if ( 0 < idx_in_ss_stack && ( size_t ) idx_in_ss_stack < ss_stack . size () - 1 ) {
2019-07-09 15:27:28 +02:00
* out_text = ss_stack [ idx_in_ss_stack ]. name . c_str ();
return true ;
}
return false ;
}
2019-07-02 16:42:23 +02:00
2019-07-20 14:03:34 +02:00
void Plater :: undo_redo_topmost_string_getter ( const bool is_undo , std :: string & out_text )
{
2019-07-26 13:44:33 +02:00
const std :: vector < UndoRedo :: Snapshot >& ss_stack = p -> undo_redo_stack (). snapshots ();
2019-07-20 14:03:34 +02:00
const int idx_in_ss_stack = p -> get_active_snapshot_index () + ( is_undo ? - 1 : 0 );
2019-10-29 10:40:34 +01:00
if ( 0 < idx_in_ss_stack && ( size_t ) idx_in_ss_stack < ss_stack . size () - 1 ) {
2019-07-20 14:03:34 +02:00
out_text = ss_stack [ idx_in_ss_stack ]. name ;
return ;
}
2019-08-06 10:01:10 +02:00
out_text = "" ;
2019-07-20 14:03:34 +02:00
}
2020-04-20 21:46:16 +02:00
bool Plater :: search_string_getter ( int idx , const char ** label , const char ** tooltip )
2020-03-28 19:39:24 +01:00
{
2020-04-13 17:55:38 +02:00
const Search :: OptionsSearcher & search_list = p -> sidebar -> get_searcher ();
2020-03-28 19:39:24 +01:00
if ( 0 <= idx && ( size_t ) idx < search_list . size ()) {
2020-04-20 21:46:16 +02:00
search_list [ idx ]. get_marked_label_and_tooltip ( label , tooltip );
2020-03-28 19:39:24 +01:00
return true ;
}
return false ;
}
2019-09-06 17:46:55 +02:00
void Plater :: on_extruders_change ( size_t num_extruders )
2018-10-10 13:53:45 +02:00
{
auto & choices = sidebar (). combos_filament ();
2019-03-13 13:13:18 +01:00
if ( num_extruders == choices . size ())
return ;
2018-11-15 11:15:24 +01:00
wxWindowUpdateLocker noUpdates_scrolled_panel ( & sidebar () /*.scrolled_panel()*/ );
2018-11-09 18:39:07 +01:00
2019-09-06 17:46:55 +02:00
size_t i = choices . size ();
2018-10-10 13:53:45 +02:00
while ( i < num_extruders )
{
PresetComboBox * choice /*{ nullptr }*/ ;
sidebar (). init_filament_combo ( & choice , i );
choices . push_back ( choice );
// initialize selection
2019-12-02 22:48:01 -05:00
wxGetApp (). preset_bundle -> update_plater_filament_ui ( i , choice );
2018-10-10 13:53:45 +02:00
++ i ;
}
// remove unused choices if any
sidebar (). remove_unused_filament_combos ( num_extruders );
2018-11-15 11:15:24 +01:00
sidebar (). Layout ();
sidebar (). scrolled_panel () -> Refresh ();
2018-10-10 13:53:45 +02:00
}
2018-10-17 11:12:38 +02:00
void Plater :: on_config_change ( const DynamicPrintConfig & config )
2018-10-10 13:53:45 +02:00
{
2018-10-15 10:53:47 +02:00
bool update_scheduled = false ;
2019-02-04 10:06:15 +01:00
bool bed_shape_changed = false ;
2018-10-17 11:12:38 +02:00
for ( auto opt_key : p -> config -> diff ( config )) {
2019-08-19 17:19:21 +02:00
if ( opt_key == "filament_colour" )
{
update_scheduled = true ; // update should be scheduled (for update 3DScene) #2738
/* There is a case, when we use filament_color instead of extruder_color (when extruder_color == "").
* Thus plater config option "filament_colour" should be filled with filament_presets values.
* Otherwise, on 3dScene will be used last edited filament color for all volumes with extruder_color == "".
*/
const std :: vector < std :: string > filament_presets = wxGetApp (). preset_bundle -> filament_presets ;
if ( filament_presets . size () > 1 &&
p -> config -> option < ConfigOptionStrings > ( opt_key ) -> values . size () != config . option < ConfigOptionStrings > ( opt_key ) -> values . size ())
{
const PresetCollection & filaments = wxGetApp (). preset_bundle -> filaments ;
std :: vector < std :: string > filament_colors ;
filament_colors . reserve ( filament_presets . size ());
for ( const std :: string & filament_preset : filament_presets )
filament_colors . push_back ( filaments . find_preset ( filament_preset , true ) -> config . opt_string ( "filament_colour" , ( unsigned ) 0 ));
p -> config -> option < ConfigOptionStrings > ( opt_key ) -> values = filament_colors ;
2019-10-01 18:19:28 +02:00
p -> sidebar -> obj_list () -> update_extruder_colors ();
2019-08-19 17:19:21 +02:00
continue ;
}
}
2018-10-17 11:12:38 +02:00
p -> config -> set_key_value ( opt_key , config . option ( opt_key ) -> clone ());
2020-04-23 21:32:12 +02:00
if ( opt_key == "printer_technology" ) {
2018-12-03 13:14:28 +01:00
this -> set_printer_technology ( config . opt_enum < PrinterTechnology > ( opt_key ));
2020-04-23 21:32:12 +02:00
// print technology is changed, so we should to update a search list
p -> sidebar -> update_searcher ();
}
2019-07-24 14:02:36 +02:00
else if (( opt_key == "bed_shape" ) || ( opt_key == "bed_custom_texture" ) || ( opt_key == "bed_custom_model" )) {
2019-02-04 10:06:15 +01:00
bed_shape_changed = true ;
2018-10-15 10:53:47 +02:00
update_scheduled = true ;
2019-08-06 18:16:02 +02:00
}
2018-12-06 15:40:41 +01:00
else if ( boost :: starts_with ( opt_key , "wipe_tower" ) ||
// opt_key == "filament_minimal_purge_on_wipe_tower" // ? #ys_FIXME
opt_key == "single_extruder_multi_material" ) {
2018-10-15 10:53:47 +02:00
update_scheduled = true ;
2019-08-06 18:16:02 +02:00
}
2018-10-15 10:53:47 +02:00
else if ( opt_key == "variable_layer_height" ) {
if ( p -> config -> opt_bool ( "variable_layer_height" ) != true ) {
2018-12-04 13:55:25 +01:00
p -> view3D -> enable_layers_editing ( false );
p -> view3D -> set_as_dirty ();
2018-10-15 10:53:47 +02:00
}
2019-03-14 13:54:05 +01:00
}
2018-10-15 10:53:47 +02:00
else if ( opt_key == "extruder_colour" ) {
update_scheduled = true ;
p -> preview -> set_number_extruders ( p -> config -> option < ConfigOptionStrings > ( opt_key ) -> values . size ());
2019-10-01 18:19:28 +02:00
p -> sidebar -> obj_list () -> update_extruder_colors ();
2018-10-15 10:53:47 +02:00
} else if ( opt_key == "max_print_height" ) {
update_scheduled = true ;
2019-01-17 13:21:33 +01:00
}
else if ( opt_key == "printer_model" ) {
2018-10-15 10:53:47 +02:00
// update to force bed selection(for texturing)
2019-02-04 10:06:15 +01:00
bed_shape_changed = true ;
2018-10-15 10:53:47 +02:00
update_scheduled = true ;
2019-01-17 13:21:33 +01:00
}
2018-10-15 10:53:47 +02:00
}
2019-02-04 10:06:15 +01:00
if ( bed_shape_changed )
2019-07-18 14:39:19 +02:00
p -> set_bed_shape ( p -> config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ,
2019-07-24 14:02:36 +02:00
p -> config -> option < ConfigOptionString > ( "bed_custom_texture" ) -> value ,
p -> config -> option < ConfigOptionString > ( "bed_custom_model" ) -> value );
2019-02-04 10:06:15 +01:00
2019-08-06 18:16:02 +02:00
if ( update_scheduled )
2018-10-15 10:53:47 +02:00
update ();
2018-10-17 11:12:38 +02:00
if ( p -> main_frame -> is_loaded ())
this -> p -> schedule_background_process ();
2018-10-10 13:53:45 +02:00
}
2020-02-04 15:24:35 +01:00
void Plater :: set_bed_shape () const
{
p -> set_bed_shape ( p -> config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ,
p -> config -> option < ConfigOptionString > ( "bed_custom_texture" ) -> value ,
p -> config -> option < ConfigOptionString > ( "bed_custom_model" ) -> value );
}
2019-09-30 13:59:26 +02:00
void Plater :: force_filament_colors_update ()
{
bool update_scheduled = false ;
DynamicPrintConfig * config = p -> config ;
const std :: vector < std :: string > filament_presets = wxGetApp (). preset_bundle -> filament_presets ;
if ( filament_presets . size () > 1 &&
p -> config -> option < ConfigOptionStrings > ( "filament_colour" ) -> values . size () == filament_presets . size ())
{
const PresetCollection & filaments = wxGetApp (). preset_bundle -> filaments ;
std :: vector < std :: string > filament_colors ;
filament_colors . reserve ( filament_presets . size ());
for ( const std :: string & filament_preset : filament_presets )
filament_colors . push_back ( filaments . find_preset ( filament_preset , true ) -> config . opt_string ( "filament_colour" , ( unsigned ) 0 ));
if ( config -> option < ConfigOptionStrings > ( "filament_colour" ) -> values != filament_colors ) {
config -> option < ConfigOptionStrings > ( "filament_colour" ) -> values = filament_colors ;
update_scheduled = true ;
}
}
2019-10-01 18:19:28 +02:00
if ( update_scheduled ) {
2019-09-30 13:59:26 +02:00
update ();
2019-10-01 18:19:28 +02:00
p -> sidebar -> obj_list () -> update_extruder_colors ();
}
2019-09-30 13:59:26 +02:00
if ( p -> main_frame -> is_loaded ())
this -> p -> schedule_background_process ();
}
2020-01-24 15:16:28 +01:00
void Plater :: force_print_bed_update ()
{
// Fill in the printer model key with something which cannot possibly be valid, so that Plater::on_config_change() will update the print bed
// once a new Printer profile config is loaded.
p -> config -> opt_string ( "printer_model" , true ) = " \x01\x00\x01 " ;
}
2019-02-19 14:57:59 +01:00
void Plater :: on_activate ()
{
2019-02-19 15:54:43 +01:00
#ifdef __linux__
2019-02-19 14:57:59 +01:00
wxWindow * focus_window = wxWindow :: FindFocus ();
2019-02-19 15:54:43 +01:00
// Activating the main frame, and no window has keyboard focus.
// Set the keyboard focus to the visible Canvas3D.
if ( this -> p -> view3D -> IsShown () && ( ! focus_window || focus_window == this -> p -> view3D -> get_wxglcanvas ()))
this -> p -> view3D -> get_wxglcanvas () -> SetFocus ();
else if ( this -> p -> preview -> IsShown () && ( ! focus_window || focus_window == this -> p -> view3D -> get_wxglcanvas ()))
this -> p -> preview -> get_wxglcanvas () -> SetFocus ();
#endif
2019-08-23 15:53:45 +02:00
this -> p -> show_delayed_error_message ();
2019-02-19 14:57:59 +01:00
}
2019-12-17 08:37:50 +01:00
// Get vector of extruder colors considering filament color, if extruder color is undefined.
2019-09-30 14:03:50 +02:00
std :: vector < std :: string > Plater :: get_extruder_colors_from_plater_config () const
{
const Slic3r :: DynamicPrintConfig * config = & wxGetApp (). preset_bundle -> printers . get_edited_preset (). config ;
2019-10-07 12:36:16 +02:00
std :: vector < std :: string > extruder_colors ;
if ( ! config -> has ( "extruder_colour" )) // in case of a SLA print
return extruder_colors ;
extruder_colors = ( config -> option < ConfigOptionStrings > ( "extruder_colour" )) -> values ;
2019-10-18 12:35:35 +02:00
if ( ! wxGetApp (). plater ())
return extruder_colors ;
2019-09-30 14:03:50 +02:00
const std :: vector < std :: string >& filament_colours = ( p -> config -> option < ConfigOptionStrings > ( "filament_colour" )) -> values ;
for ( size_t i = 0 ; i < extruder_colors . size (); ++ i )
if ( extruder_colors [ i ] == "" && i < filament_colours . size ())
extruder_colors [ i ] = filament_colours [ i ];
return extruder_colors ;
}
2019-12-17 08:37:50 +01:00
/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
* It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
*/
2019-11-07 13:51:54 +01:00
std :: vector < std :: string > Plater :: get_colors_for_color_print () const
{
std :: vector < std :: string > colors = get_extruder_colors_from_plater_config ();
2020-01-16 16:01:19 +01:00
colors . reserve ( colors . size () + p -> model . custom_gcode_per_print_z . gcodes . size ());
2019-11-07 13:51:54 +01:00
2020-01-23 12:49:39 +01:00
for ( const CustomGCode :: Item & code : p -> model . custom_gcode_per_print_z . gcodes )
2019-11-11 09:38:45 +01:00
if ( code . gcode == ColorChangeCode )
2019-12-17 08:37:50 +01:00
colors . emplace_back ( code . color );
2019-11-07 13:51:54 +01:00
return colors ;
}
2019-05-15 14:09:16 +02:00
wxString Plater :: get_project_filename ( const wxString & extension ) const
2018-11-15 15:27:39 +01:00
{
2019-05-15 14:09:16 +02:00
return p -> get_project_filename ( extension );
}
void Plater :: set_project_filename ( const wxString & filename )
{
return p -> set_project_filename ( filename );
2018-11-15 15:27:39 +01:00
}
bool Plater :: is_export_gcode_scheduled () const
{
return p -> background_process . is_export_scheduled ();
}
2020-04-23 18:47:51 +02:00
const Selection & Plater :: get_selection () const
{
return p -> get_selection ();
}
2018-11-01 12:33:56 +01:00
int Plater :: get_selected_object_idx ()
{
return p -> get_selected_object_idx ();
}
2018-11-13 15:24:05 +01:00
bool Plater :: is_single_full_object_selection () const
2018-11-09 18:39:07 +01:00
{
return p -> get_selection (). is_single_full_object ();
}
2018-11-23 12:47:32 +01:00
GLCanvas3D * Plater :: canvas3D ()
2018-10-11 15:57:09 +02:00
{
2018-12-04 13:55:25 +01:00
return p -> view3D -> get_canvas3d ();
2018-10-11 15:57:09 +02:00
}
2020-01-21 14:07:13 +01:00
GLCanvas3D * Plater :: get_current_canvas3D ()
{
return p -> get_current_canvas3D ();
}
2019-09-19 12:30:16 +02:00
BoundingBoxf Plater :: bed_shape_bb () const
{
return p -> bed_shape_bb ();
}
2020-04-23 18:47:51 +02:00
void Plater :: arrange ()
{
p -> m_ui_jobs . arrange ();
}
2020-01-03 11:41:29 +01:00
void Plater :: set_current_canvas_as_dirty ()
{
p -> set_current_canvas_as_dirty ();
}
2020-03-04 15:19:48 +01:00
void Plater :: unbind_canvas_event_handlers ()
{
p -> unbind_canvas_event_handlers ();
}
2020-04-01 09:58:31 +02:00
void Plater :: reset_canvas_volumes ()
{
p -> reset_canvas_volumes ();
}
2020-03-04 15:19:48 +01:00
2018-11-12 17:35:57 +01:00
PrinterTechnology Plater :: printer_technology () const
{
2018-11-13 17:45:44 +01:00
return p -> printer_technology ;
2018-11-12 17:35:57 +01:00
}
2020-04-23 18:47:51 +02:00
const DynamicPrintConfig * Plater :: config () const { return p -> config ; }
2018-12-03 13:14:28 +01:00
void Plater :: set_printer_technology ( PrinterTechnology printer_technology )
{
p -> printer_technology = printer_technology ;
if ( p -> background_process . select_technology ( printer_technology )) {
// Update the active presets.
}
2019-08-06 18:16:02 +02:00
//FIXME for SLA synchronize
2018-12-03 13:14:28 +01:00
//p->background_process.apply(Model)!
2019-03-01 11:00:34 +01:00
2019-03-12 09:41:57 +01:00
p -> label_btn_export = printer_technology == ptFFF ? L ( "Export G-code" ) : L ( "Export" );
p -> label_btn_send = printer_technology == ptFFF ? L ( "Send G-code" ) : L ( "Send to printer" );
2019-04-30 13:51:36 +02:00
if ( wxGetApp (). mainframe )
wxGetApp (). mainframe -> update_menubar ();
2020-05-05 17:23:58 +02:00
p -> update_main_toolbar_tooltips ();
2020-05-06 20:45:57 +02:00
p -> sidebar -> get_searcher (). set_printer_technology ( printer_technology );
2018-12-03 13:14:28 +01:00
}
2018-10-31 15:41:27 +01:00
void Plater :: changed_object ( int obj_idx )
2018-10-08 16:27:38 +02:00
{
if ( obj_idx < 0 )
return ;
2019-04-22 01:51:10 +02:00
// recenter and re - align to Z = 0
auto model_object = p -> model . objects [ obj_idx ];
model_object -> ensure_on_bed ();
if ( this -> p -> printer_technology == ptSLA ) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data, update the 3D scene.
this -> p -> update_restart_background_process ( true , false );
2018-10-08 16:27:38 +02:00
}
2019-04-22 01:51:10 +02:00
else
p -> view3D -> reload_scene ( false );
2018-10-08 16:27:38 +02:00
2018-11-07 15:29:13 +01:00
// update print
2018-12-03 13:14:28 +01:00
this -> p -> schedule_background_process ();
2018-10-08 16:27:38 +02:00
}
2019-04-15 15:40:40 +02:00
void Plater :: changed_objects ( const std :: vector < size_t >& object_idxs )
{
if ( object_idxs . empty ())
return ;
2019-09-06 17:46:55 +02:00
for ( size_t obj_idx : object_idxs )
2019-04-22 01:51:10 +02:00
{
if ( obj_idx < p -> model . objects . size ())
// recenter and re - align to Z = 0
p -> model . objects [ obj_idx ] -> ensure_on_bed ();
2019-04-15 15:40:40 +02:00
}
2019-04-22 01:51:10 +02:00
if ( this -> p -> printer_technology == ptSLA ) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data, update the 3D scene.
this -> p -> update_restart_background_process ( true , false );
}
else
p -> view3D -> reload_scene ( false );
2019-04-15 15:40:40 +02:00
// update print
this -> p -> schedule_background_process ();
}
2019-06-26 09:48:52 +02:00
void Plater :: schedule_background_process ( bool schedule /* = true*/ )
2019-04-12 18:29:47 +02:00
{
2019-06-26 09:48:52 +02:00
if ( schedule )
this -> p -> schedule_background_process ();
this -> p -> suppressed_backround_processing_update = false ;
}
2020-03-12 16:11:25 +01:00
bool Plater :: is_background_process_update_scheduled () const
2019-06-26 09:48:52 +02:00
{
return this -> p -> background_process_timer . IsRunning ();
}
void Plater :: suppress_background_process ( const bool stop_background_process )
{
if ( stop_background_process )
this -> p -> background_process_timer . Stop ();
this -> p -> suppressed_backround_processing_update = true ;
2019-04-12 18:29:47 +02:00
}
2019-01-30 16:27:07 +01:00
void Plater :: fix_through_netfabb ( const int obj_idx , const int vol_idx /* = -1*/ ) { p -> fix_through_netfabb ( obj_idx , vol_idx ); }
2018-09-17 12:15:11 +02:00
2018-12-07 17:50:48 +01:00
void Plater :: update_object_menu () { p -> update_object_menu (); }
2020-03-06 15:10:58 +01:00
void Plater :: show_action_buttons ( const bool ready_to_slice ) const { p -> show_action_buttons ( ready_to_slice ); }
2018-12-07 17:50:48 +01:00
2019-04-10 08:40:58 +02:00
void Plater :: copy_selection_to_clipboard ()
{
2019-06-24 13:21:05 +02:00
if ( can_copy_to_clipboard ())
p -> view3D -> get_canvas3d () -> get_selection (). copy_to_clipboard ();
2019-04-10 08:40:58 +02:00
}
void Plater :: paste_from_clipboard ()
{
2019-07-04 17:33:19 +02:00
if ( ! can_paste_from_clipboard ())
return ;
2020-04-01 09:48:56 +02:00
Plater :: TakeSnapshot snapshot ( this , _L ( "Paste From Clipboard" ));
2019-07-04 17:33:19 +02:00
p -> view3D -> get_canvas3d () -> get_selection (). paste_from_clipboard ();
2019-04-10 08:40:58 +02:00
}
2020-04-22 15:46:23 +02:00
void Plater :: search ( bool plater_is_active )
{
if ( plater_is_active ) {
2020-05-11 22:00:26 +02:00
// plater should be focused for correct navigation inside search window
this -> SetFocus ();
2020-04-22 15:46:23 +02:00
wxKeyEvent evt ;
#ifdef __APPLE__
evt . m_keyCode = 'f' ;
#else /* __APPLE__ */
evt . m_keyCode = WXK_CONTROL_F ;
#endif /* __APPLE__ */
evt . SetControlDown ( true );
canvas3D () -> on_char ( evt );
}
else
{
wxPoint pos = this -> ClientToScreen ( wxPoint ( 0 , 0 ));
pos . x += em_unit ( this ) * 40 ;
pos . y += em_unit ( this ) * 4 ;
p -> sidebar -> get_searcher (). search_dialog -> Popup ( pos );
}
}
2019-04-25 01:45:00 +02:00
void Plater :: msw_rescale ()
2019-04-16 10:05:45 +02:00
{
2019-04-25 01:45:00 +02:00
p -> preview -> msw_rescale ();
2019-04-16 12:11:48 +02:00
2019-04-25 01:45:00 +02:00
p -> view3D -> get_canvas3d () -> msw_rescale ();
2019-04-16 10:05:45 +02:00
2019-04-25 01:45:00 +02:00
p -> sidebar -> msw_rescale ();
2019-04-16 10:05:45 +02:00
2019-05-03 09:44:19 +02:00
p -> msw_rescale_object_menu ();
2019-04-16 10:05:45 +02:00
Layout ();
GetParent () -> Layout ();
}
2019-12-10 10:56:21 +01:00
bool Plater :: init_view_toolbar ()
{
return p -> init_view_toolbar ();
}
2019-07-02 15:49:18 +02:00
const Camera & Plater :: get_camera () const
{
return p -> camera ;
}
2020-03-02 10:58:46 +01:00
Camera & Plater :: get_camera ()
{
return p -> camera ;
}
const Bed3D & Plater :: get_bed () const
{
return p -> bed ;
}
Bed3D & Plater :: get_bed ()
{
return p -> bed ;
}
const GLToolbar & Plater :: get_view_toolbar () const
{
return p -> view_toolbar ;
}
GLToolbar & Plater :: get_view_toolbar ()
{
return p -> view_toolbar ;
}
2019-09-27 14:52:19 +02:00
const Mouse3DController & Plater :: get_mouse3d_controller () const
{
return p -> mouse3d_controller ;
}
Mouse3DController & Plater :: get_mouse3d_controller ()
{
return p -> mouse3d_controller ;
}
2019-03-14 13:54:05 +01:00
bool Plater :: can_delete () const { return p -> can_delete (); }
bool Plater :: can_delete_all () const { return p -> can_delete_all (); }
bool Plater :: can_increase_instances () const { return p -> can_increase_instances (); }
bool Plater :: can_decrease_instances () const { return p -> can_decrease_instances (); }
2019-05-13 14:27:51 +02:00
bool Plater :: can_set_instance_to_object () const { return p -> can_set_instance_to_object (); }
bool Plater :: can_fix_through_netfabb () const { return p -> can_fix_through_netfabb (); }
2019-03-14 13:54:05 +01:00
bool Plater :: can_split_to_objects () const { return p -> can_split_to_objects (); }
bool Plater :: can_split_to_volumes () const { return p -> can_split_to_volumes (); }
bool Plater :: can_arrange () const { return p -> can_arrange (); }
bool Plater :: can_layers_editing () const { return p -> can_layers_editing (); }
2019-06-24 13:21:05 +02:00
bool Plater :: can_paste_from_clipboard () const
{
const Selection & selection = p -> view3D -> get_canvas3d () -> get_selection ();
const Selection :: Clipboard & clipboard = selection . get_clipboard ();
if ( clipboard . is_empty ())
return false ;
if (( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptSLA ) && ! clipboard . is_sla_compliant ())
return false ;
Selection :: EMode mode = clipboard . get_mode ();
if (( mode == Selection :: Volume ) && ! selection . is_from_single_instance ())
return false ;
if (( mode == Selection :: Instance ) && ( selection . get_mode () != Selection :: Instance ))
return false ;
return true ;
}
bool Plater :: can_copy_to_clipboard () const
{
if ( is_selection_empty ())
return false ;
const Selection & selection = p -> view3D -> get_canvas3d () -> get_selection ();
if (( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptSLA ) && ! selection . is_sla_compliant ())
return false ;
return true ;
}
2019-03-14 13:54:05 +01:00
2019-07-26 13:44:33 +02:00
bool Plater :: can_undo () const { return p -> undo_redo_stack (). has_undo_snapshot (); }
bool Plater :: can_redo () const { return p -> undo_redo_stack (). has_redo_snapshot (); }
2019-09-19 09:09:11 +02:00
bool Plater :: can_reload_from_disk () const { return p -> can_reload_from_disk (); }
2019-07-26 13:44:33 +02:00
const UndoRedo :: Stack & Plater :: undo_redo_stack_main () const { return p -> undo_redo_stack_main (); }
void Plater :: enter_gizmos_stack () { p -> enter_gizmos_stack (); }
void Plater :: leave_gizmos_stack () { p -> leave_gizmos_stack (); }
2020-03-20 13:41:36 +01:00
bool Plater :: inside_snapshot_capture () { return p -> inside_snapshot_capture (); }
2019-07-08 10:57:35 +02:00
2020-03-12 16:05:39 +01:00
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool Plater :: PopupMenu ( wxMenu * menu , const wxPoint & pos )
{
// Don't want to wake up and trigger reslicing while tracking the pop-up menu.
SuppressBackgroundProcessingUpdate sbpu ;
// When tracking a pop-up menu, postpone error messages from the slicing result.
m_tracking_popup_menu = true ;
bool out = this -> wxPanel :: PopupMenu ( menu , pos );
m_tracking_popup_menu = false ;
if ( ! m_tracking_popup_menu_error_message . empty ()) {
// Don't know whether the CallAfter is necessary, but it should not hurt.
// The menus likely sends out some commands, so we may be safer if the dialog is shown after the menu command is processed.
wxString message = std :: move ( m_tracking_popup_menu_error_message );
wxTheApp -> CallAfter ([ message , this ]() { show_error ( this , message ); });
m_tracking_popup_menu_error_message . clear ();
}
return out ;
}
2019-07-08 10:57:35 +02:00
2019-06-26 09:48:52 +02:00
SuppressBackgroundProcessingUpdate :: SuppressBackgroundProcessingUpdate () :
2020-03-12 16:11:25 +01:00
m_was_scheduled ( wxGetApp (). plater () -> is_background_process_update_scheduled ())
2019-06-26 09:48:52 +02:00
{
2020-03-12 16:11:25 +01:00
wxGetApp (). plater () -> suppress_background_process ( m_was_scheduled );
2019-06-26 09:48:52 +02:00
}
SuppressBackgroundProcessingUpdate ::~ SuppressBackgroundProcessingUpdate ()
{
2020-03-12 16:11:25 +01:00
wxGetApp (). plater () -> schedule_background_process ( m_was_scheduled );
2019-06-26 09:48:52 +02:00
}
2018-09-17 12:15:11 +02:00
}} // namespace Slic3r::GUI