2018-09-17 12:15:11 +02:00
#include "Plater.hpp"
#include <cstddef>
#include <algorithm>
#include <vector>
#include <string>
#include <regex>
2018-12-06 17:32:49 +01:00
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.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>
#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>
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"
#include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/SLA/SLARotfinder.hpp"
#include "libslic3r/Utils.hpp"
2019-04-04 16:03:23 +02:00
#include "libnest2d/optimizers/nlopt/genetic.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"
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"
#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"
2018-09-17 12:15:11 +02:00
#include "Tab.hpp"
#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"
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"
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 ;
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 ) :
wxStaticBoxSizer ( new wxStaticBox ( parent , wxID_ANY , _ ( L ( "Info" ))), wxVERTICAL )
{
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
};
2019-01-21 12:34:28 +01:00
init_info_label ( & info_size , _ ( L ( "Size" )));
2019-02-19 12:14:13 +01:00
label_volume = init_info_label ( & info_volume , _ ( L ( "Volume" )));
2019-01-21 12:34:28 +01:00
init_info_label ( & info_facets , _ ( L ( "Facets" )));
2019-02-19 12:14:13 +01:00
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
2019-02-19 12:14:13 +01: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 ());
2019-04-12 17:10:04 +02:00
manifold_warning_icon = new wxStaticBitmap ( parent , wxID_ANY , create_scaled_bitmap ( parent , "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
{
2019-04-16 10:05:45 +02:00
manifold_warning_icon -> SetBitmap ( create_scaled_bitmap ( nullptr , "exclamation" ));
2019-04-13 23:46:52 +02:00
}
2018-10-31 12:26:57 +01:00
enum SlisedInfoIdx
{
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 );
2018-11-20 10:33:54 +01:00
void SetTextAndShow ( SlisedInfoIdx 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 ) :
wxStaticBoxSizer ( new wxStaticBox ( parent , wxID_ANY , _ ( L ( "Sliced Info" ))), wxVERTICAL )
{
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
};
2018-10-31 12:26:57 +01:00
init_info_label ( _ ( L ( "Used Filament (m)" )));
init_info_label ( _ ( L ( "Used Filament (mm³)" )));
init_info_label ( _ ( L ( "Used Filament (g)" )));
2019-02-12 16:34:42 +01:00
init_info_label ( _ ( L ( "Used Material (unit)" )));
2018-10-31 12:26:57 +01:00
init_info_label ( _ ( L ( "Cost" )));
2018-11-20 10:33:54 +01:00
init_info_label ( _ ( L ( "Estimated printing time" )));
2018-10-31 12:26:57 +01:00
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 );
}
2018-11-20 10:33:54 +01:00
void SlicedInfo :: SetTextAndShow ( SlisedInfoIdx 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 ) :
2019-02-04 10:35:16 +01:00
wxBitmapComboBox ( parent , wxID_ANY , wxEmptyString , wxDefaultPosition , wxSize ( 15 * wxGetApp (). em_unit (), - 1 ), 0 , nullptr , wxCB_READONLY ),
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
{
Bind ( wxEVT_COMBOBOX , [ this ]( wxCommandEvent & evt ) {
auto selected_item = this -> GetSelection ();
auto marker = reinterpret_cast < Marker > ( this -> GetClientData ( selected_item ));
2019-03-22 15:45:51 +01:00
if ( marker == LABEL_ITEM_MARKER || marker == LABEL_ITEM_CONFIG_WIZARD ) {
2018-09-17 12:15:11 +02:00
this -> SetSelection ( this -> last_selected );
evt . StopPropagation ();
2019-03-22 15:45:51 +01:00
if ( marker == LABEL_ITEM_CONFIG_WIZARD )
wxTheApp -> CallAfter ([]() { Slic3r :: GUI :: config_wizard ( Slic3r :: GUI :: ConfigWizard :: RR_USER ); });
2018-12-21 10:58:00 +01:00
} else if ( this -> last_selected != selected_item ||
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 ();
}
});
2018-10-10 13:53:45 +02:00
if ( preset_type == Slic3r :: Preset :: TYPE_FILAMENT )
{
Bind ( wxEVT_LEFT_DOWN , [ this ]( wxMouseEvent & event ) {
if ( extruder_idx < 0 || event . GetLogicalPosition ( wxClientDC ( this )). x > 24 ) {
// Let the combo box process the mouse click.
event . Skip ();
return ;
}
// Swallow the mouse click and open the color picker.
auto data = new wxColourData ();
data -> SetChooseFull ( 1 );
2019-01-02 17:54:54 +01:00
auto dialog = new wxColourDialog ( /* wxGetApp().mainframe */ this , data );
dialog -> CenterOnParent ();
2018-10-10 13:53:45 +02:00
if ( dialog -> ShowModal () == wxID_OK ) {
DynamicPrintConfig cfg = * wxGetApp (). get_tab ( Preset :: TYPE_PRINTER ) -> get_config ();
2018-10-17 11:12:38 +02:00
//FIXME this is too expensive to call full_config to get just the extruder color!
2018-10-10 13:53:45 +02:00
auto colors = static_cast < ConfigOptionStrings *> ( wxGetApp (). preset_bundle -> full_config (). option ( "extruder_colour" ) -> clone ());
colors -> values [ extruder_idx ] = dialog -> GetColourData (). GetColour (). GetAsString ( wxC2S_HTML_SYNTAX );
cfg . set_key_value ( "extruder_colour" , colors );
wxGetApp (). get_tab ( Preset :: TYPE_PRINTER ) -> load_config ( cfg );
2019-04-23 16:33:06 +02:00
wxGetApp (). preset_bundle -> update_platter_filament_ui ( extruder_idx , this );
2018-10-17 11:12:38 +02:00
wxGetApp (). plater () -> on_config_change ( cfg );
2018-10-10 13:53:45 +02:00
}
dialog -> Destroy ();
});
}
2019-03-18 15:10:40 +01:00
2019-04-25 01:45:00 +02:00
edit_btn = new ScalableButton ( parent , wxID_ANY , "cog" );
2019-03-20 10:14:49 +01: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 ;
wxGetApp (). tab_panel () -> ChangeSelection ( page_id );
2019-03-19 14:36:32 +01: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
*/
if ( preset_type == Preset :: TYPE_FILAMENT && wxGetApp (). extruders_cnt () > 1 )
{
const std :: string & selected_preset = GetString ( GetSelection ()). ToUTF8 (). data ();
2019-03-18 15:10:40 +01:00
2019-03-19 14:36:32 +01: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-03-19 14:36:32 +01:00
tab -> select_preset ( selected_preset );
}
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
}
2018-12-03 15:16:33 +01:00
void PresetComboBox :: check_selection ()
{
2019-03-23 10:04:44 +01:00
this -> last_selected = GetSelection ();
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 ;
2018-10-04 16:43:10 +02:00
public :
FreqChangedParams ( wxWindow * parent , const int label_width );
~ 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 );
2018-10-04 16:43:10 +02:00
};
FreqChangedParams :: FreqChangedParams ( wxWindow * parent , const int label_width ) :
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-02-07 14:44:05 +01:00
m_og -> label_width = label_width == 0 ? 1 : label_width ;
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-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 );
m_og -> append_line ( line );
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-02-07 14:44:05 +01:00
option . opt . sidetext = " " ;
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-02-07 14:44:05 +01:00
option . opt . sidetext = " " ;
line . append_option ( option );
2018-10-04 16:43:10 +02:00
2019-02-07 14:44:05 +01:00
auto wiping_dialog_btn = [ config , this ]( wxWindow * parent ) {
2018-10-04 16:43:10 +02:00
m_wiping_dialog_button = new wxButton ( parent , wxID_ANY , _ ( L ( "Purging volumes" )) + dots , wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT );
auto sizer = new wxBoxSizer ( wxHORIZONTAL );
sizer -> Add ( m_wiping_dialog_button );
m_wiping_dialog_button -> Bind ( wxEVT_BUTTON , ([ parent ]( wxCommandEvent & e )
{
auto & config = wxGetApp (). preset_bundle -> project_config ;
const std :: vector < double > & init_matrix = ( config . option < ConfigOptionFloats > ( "wiping_volumes_matrix" )) -> values ;
const std :: vector < double > & init_extruders = ( config . option < ConfigOptionFloats > ( "wiping_volumes_extruders" )) -> values ;
WipingDialog dlg ( parent , cast < float > ( init_matrix ), cast < float > ( init_extruders ));
if ( dlg . ShowModal () == wxID_OK ) {
std :: vector < float > matrix = dlg . get_matrix ();
std :: vector < float > extruders = dlg . get_extruders ();
( config . option < ConfigOptionFloats > ( "wiping_volumes_matrix" )) -> values = std :: vector < double > ( matrix . begin (), matrix . end ());
( 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
}
}));
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 , "" );
DynamicPrintConfig * config_sla = & wxGetApp (). preset_bundle -> sla_prints . get_edited_preset (). config ;
m_og_sla -> set_config ( config_sla );
2019-02-07 14:44:05 +01:00
m_og_sla -> label_width = label_width == 0 ? 1 : label_width ;
2019-01-15 09:31:53 +01:00
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-03-11 16:00:13 +01:00
if ( opt_key == "pad_enable" ) {
tab -> set_value ( opt_key , value );
tab -> update ();
}
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
DynamicPrintConfig new_conf = * config_sla ;
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 ));
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-02-07 14:44:05 +01:00
line . append_option ( option );
2019-03-11 16:00:13 +01:00
m_og_sla -> append_line ( line );
line = Line { "" , "" };
2019-02-07 14:44:05 +01:00
option = m_og_sla -> get_option ( "pad_enable" );
option . opt . sidetext = " " ;
line . append_option ( option );
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
// 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 ;
2018-10-04 16:43:10 +02:00
FreqChangedParams * frequently_changed_parameters ;
ObjectList * object_list ;
ObjectManipulation * object_manipulation ;
2018-11-09 18:39:07 +01:00
ObjectSettings * object_settings ;
2018-09-17 12:15:11 +02:00
ObjectInfo * object_info ;
SlicedInfo * sliced_info ;
wxButton * btn_export_gcode ;
wxButton * btn_reslice ;
wxButton * btn_send_gcode ;
2018-10-04 11:12:55 +02:00
priv ( Plater * plater ) : plater ( plater ) {}
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
};
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-04-13 23:46:52 +02:00
p -> scrolled = new wxScrolledWindow ( this , wxID_ANY /*, wxDefaultPosition, wxSize(40 * wxGetApp().em_unit(), -1)*/ );
2018-12-18 13:22:22 +01:00
p -> scrolled -> SetScrollbars ( 0 , 20 , 1 , 2 );
2018-11-09 18:39:07 +01:00
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-04-25 01:45:00 +02:00
p -> mode_sizer = new ModeSizer ( p -> scrolled , 2 * wxGetApp (). em_unit ());
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 )
combo_and_btn_sizer -> Add (( * combo ) -> edit_btn , 0 , wxLEFT | wxRIGHT , int ( 0.3 * wxGetApp (). em_unit ()));
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 );
2019-05-09 17:18:03 +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;
2019-04-13 23:46:52 +02:00
const int margin_10 = 10 ; //int(1.5*wxGetApp().em_unit());// 15;
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-02-07 14:44:05 +01:00
p -> frequently_changed_parameters = new FreqChangedParams ( p -> scrolled , 0 /*label_width*/ );
p -> sizer_params -> Add ( p -> frequently_changed_parameters -> get_sizer (), 0 , wxEXPAND | wxTOP | wxBOTTOM , margin_10 );
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 );
2018-10-04 16:43:10 +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 );
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 ?
scrolled_sizer -> Add ( p -> presets_panel , 0 , wxEXPAND | wxLEFT , margin_5 ) :
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
auto init_btn = [ this ]( wxButton ** btn , wxString label ) {
2019-04-16 14:06:09 +02:00
* btn = new wxButton ( this , wxID_ANY , label , wxDefaultPosition ,
2019-05-03 13:09:42 +02:00
wxDefaultSize , wxBU_EXACTFIT );
2019-04-13 23:46:52 +02:00
( * btn ) -> SetFont ( wxGetApp (). bold_font ());
};
init_btn ( & p -> btn_send_gcode , _ ( L ( "Send to printer" )));
p -> btn_send_gcode -> Hide ();
init_btn ( & p -> btn_export_gcode , _ ( L ( "Export G-code" )) + dots );
init_btn ( & p -> btn_reslice , _ ( L ( "Slice now" )));
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-02-07 14:44:05 +01:00
btns_sizer -> Add ( p -> btn_reslice , 0 , wxEXPAND | wxTOP , margin_5 );
btns_sizer -> Add ( p -> btn_send_gcode , 0 , wxEXPAND | wxTOP , margin_5 );
btns_sizer -> Add ( p -> btn_export_gcode , 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
p -> btn_export_gcode -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> export_gcode (); });
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-04-09 17:15:14 +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 (); });
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 );
combo_and_btn_sizer -> Add (( * combo ) -> edit_btn , 0 , wxLEFT | wxRIGHT , int ( 0.3 * wxGetApp (). em_unit ()));
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
}
void Sidebar :: remove_unused_filament_combos ( const int current_extruder_count )
{
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 )
preset_bundle . prints . update_platter_ui ( p -> combo_print );
else {
preset_bundle . sla_prints . update_platter_ui ( p -> combo_sla_print );
preset_bundle . sla_materials . update_platter_ui ( p -> combo_sla_material );
}
// Update the printer choosers, update the dirty flags.
preset_bundle . printers . update_platter_ui ( p -> combo_printer );
// 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-04-23 16:33:06 +02:00
preset_bundle . update_platter_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 )
{
2018-10-31 16:22:36 +01: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-01-15 15:54:20 +01:00
case Preset :: TYPE_FILAMENT :
{
2019-02-22 09:38:56 +01:00
const int 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 ();
const int filament_cnt = p -> combos_filament . size () > extruder_cnt ? extruder_cnt : p -> combos_filament . size ();
if ( filament_cnt == 1 ) {
2018-09-17 12:15:11 +02:00
// Single filament printer, synchronize the filament presets.
2019-01-15 15:54:20 +01:00
const std :: string & name = preset_bundle . filaments . get_selected_preset (). name ;
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-04-23 16:33:06 +02:00
preset_bundle . update_platter_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 :
2018-10-31 16:22:36 +01:00
preset_bundle . prints . update_platter_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 :
preset_bundle . sla_prints . update_platter_ui ( p -> combo_sla_print );
break ;
2018-09-17 12:15:11 +02:00
case Preset :: TYPE_SLA_MATERIAL :
2018-10-31 16:22:36 +01:00
preset_bundle . sla_materials . update_platter_ui ( p -> combo_sla_material );
2018-09-17 12:15:11 +02:00
break ;
2018-10-31 16:22:36 +01:00
case Preset :: TYPE_PRINTER :
{
2019-04-10 09:56:32 +02:00
update_all_preset_comboboxes ();
2018-10-31 16:22:36 +01: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 )
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-04-25 01:45:00 +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-04-25 01:45:00 +02:00
p -> frequently_changed_parameters -> get_og ( true ) -> msw_rescale ();
p -> frequently_changed_parameters -> get_og ( false ) -> msw_rescale ();
2019-04-13 23:46:52 +02:00
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-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
p -> scrolled -> Layout ();
}
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 ;
}
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 ();
}
void Sidebar :: update_objects_list_extruder_column ( int extruders_count )
{
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 ;
}
2018-11-01 12:33:56 +01:00
const ModelInstance * model_instance = ! model_object -> instances . empty () ? model_object -> instances . front () : nullptr ;
2018-11-01 14:02:38 +01:00
2018-11-15 11:15:24 +01:00
auto size = model_object -> bounding_box (). size ();
2018-11-01 12:33:56 +01:00
p -> object_info -> info_size -> SetLabel ( wxString :: Format ( "%.2f x %.2f x %.2f" , size ( 0 ), size ( 1 ), size ( 2 )));
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;
2019-03-04 15:29:07 +01:00
p -> object_info -> info_volume -> SetLabel ( wxString :: Format ( "%.2f" , stats . volume ));
2018-11-01 12:33:56 +01:00
p -> object_info -> info_facets -> SetLabel ( wxString :: Format ( _ ( L ( "%d (%d shells)" )), static_cast < int > ( model_object -> facets_count ()), stats . number_of_parts ));
int errors = stats . degenerate_facets + stats . edges_fixed + stats . facets_removed +
stats . facets_added + stats . facets_reversed + stats . backwards_edges ;
if ( errors > 0 ) {
wxString tooltip = wxString :: Format ( _ ( L ( "Auto-repaired (%d errors)" )), errors );
p -> object_info -> info_manifold -> SetLabel ( tooltip );
2019-01-21 12:34:28 +01:00
tooltip += ": \n " + wxString :: Format ( _ ( L ( "%d degenerate facets, %d edges fixed, %d facets removed, "
2018-11-01 12:33:56 +01:00
"%d facets added, %d facets reversed, %d backwards edges" )),
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 );
}
else {
2019-05-04 02:07:07 +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
}
void Sidebar :: show_sliced_info_sizer ( const bool show )
{
2018-11-13 12:19:56 +01:00
wxWindowUpdateLocker freeze_guard ( this );
2018-11-01 12:33:56 +01:00
2018-10-31 12:26:57 +01:00
p -> sliced_info -> Show ( show );
if ( show ) {
2019-02-12 16:34:42 +01:00
if ( p -> plater -> printer_technology () == ptSLA )
{
const SLAPrintStatistics & ps = p -> plater -> sla_print (). print_statistics ();
2019-02-18 12:28:58 +01: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 )
new_label += wxString :: Format ( " \n - %s \n - %s" , _ ( L ( "object(s)" )), _ ( L ( "supports and pad" )));
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 );
p -> sliced_info -> SetTextAndShow ( siCost , "N/A" /*wxString::Format("%.2f", ps.total_cost)*/ );
2019-02-12 16:34:42 +01:00
p -> sliced_info -> SetTextAndShow ( siEstimatedTime , ps . estimated_print_time , _ ( L ( "Estimated printing time" )) + " :" );
// 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-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
2019-02-21 15:46:04 +01:00
wxString new_label = _ ( L ( "Used Filament (m)" ));
if ( is_wipe_tower )
new_label += wxString :: Format ( " : \n - %s \n - %s" , _ ( 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 ?
wxString :: Format ( "%.2f \n %.2f \n %.2f" , ps . total_used_filament / 1000 ,
( ps . total_used_filament - ps . total_wipe_tower_filament ) / 1000 ,
ps . total_wipe_tower_filament / 1000 ) :
wxString :: Format ( "%.2f" , ps . total_used_filament / 1000 );
p -> sliced_info -> SetTextAndShow ( siFilament_m , info_text , new_label );
2018-11-20 10:33:54 +01:00
2019-02-21 15:46:04 +01:00
p -> sliced_info -> SetTextAndShow ( siFilament_mm3 , wxString :: Format ( "%.2f" , ps . total_extruded_volume ));
p -> sliced_info -> SetTextAndShow ( siFilament_g , wxString :: Format ( "%.2f" , ps . total_weight ));
2018-10-31 12:26:57 +01:00
2018-11-20 10:33:54 +01:00
2019-02-21 15:46:04 +01:00
new_label = _ ( L ( "Cost" ));
if ( is_wipe_tower )
new_label += wxString :: Format ( " : \n - %s \n - %s" , _ ( L ( "objects" )), _ ( L ( "wipe tower" )));
info_text = is_wipe_tower ?
wxString :: Format ( "%.2f \n %.2f \n %.2f" , ps . total_cost ,
( ps . total_cost - ps . total_wipe_tower_cost ),
ps . total_wipe_tower_cost ) :
wxString :: Format ( "%.2f" , ps . total_cost );
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 {
new_label = _ ( L ( "Estimated printing time" )) + " :" ;
info_text = "" ;
if ( ps . estimated_normal_print_time != "N/A" ) {
new_label += wxString :: Format ( " \n - %s" , _ ( L ( "normal mode" )));
info_text += wxString :: Format ( " \n %s" , ps . estimated_normal_print_time );
}
if ( ps . estimated_silent_print_time != "N/A" ) {
2019-05-09 15:39:15 +02:00
new_label += wxString :: Format ( " \n - %s" , _ ( L ( "stealth mode" )));
2019-02-21 15:46:04 +01:00
info_text += wxString :: Format ( " \n %s" , ps . estimated_silent_print_time );
}
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:
p -> sliced_info -> SetTextAndShow ( siWTNumbetOfToolchanges , is_wipe_tower ? wxString :: Format ( "%.d" , p -> plater -> fff_print (). wipe_tower_data (). number_of_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-02-12 16:34:42 +01:00
}
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-04-24 19:03:05 +02: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 ); }
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
}
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 ();
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 ();
Layout ();
}
2018-10-10 13:53:45 +02:00
std :: vector < PresetComboBox *>& Sidebar :: combos_filament ()
{
return p -> combos_filament ;
}
2018-09-17 12:15:11 +02:00
// Plater::DropTarget
class PlaterDropTarget : public wxFileDropTarget
{
public :
2019-02-01 08:53:29 +01: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 ;
}
}
plater -> load_files ( paths );
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-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 ;
2018-11-13 17:45:44 +01:00
Slic3r :: SLAPrint sla_print ;
Slic3r :: Model model ;
PrinterTechnology printer_technology = ptFFF ;
Slic3r :: GCodePreviewData gcode_preview_data ;
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 ;
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
wxString project_filename ;
2018-10-17 11:12:38 +02:00
BackgroundSlicingProcess background_process ;
2019-04-17 14:56:00 +02:00
bool arranging ;
bool rotoptimizing ;
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
#if ENABLE_VOLUMES_CENTERING_FIXES
static const std :: regex pattern_prusa ;
#endif // ENABLE_VOLUMES_CENTERING_FIXES
2018-09-17 12:15:11 +02:00
priv ( Plater * q , MainFrame * main_frame );
2018-11-21 13:52:46 +01:00
void update ( bool force_full_scene_refresh = false );
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-03-27 14:16:38 +01:00
void reset_all_gizmos ();
2018-09-17 12:15:11 +02:00
void update_ui_from_settings ();
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 ;
2018-11-16 09:26:41 +01:00
std :: vector < size_t > load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config );
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 ();
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 arrange ();
2018-11-22 16:04:21 +01:00
void sla_optimize_rotation ();
2018-10-09 09:35:19 +02:00
void split_object ();
2018-10-24 12:55:38 +02:00
void split_volume ();
2018-12-03 13:14:28 +01:00
bool background_processing_enabled () const { return this -> get_config ( "background_processing" ) == "1" ; }
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))
2018-11-13 17:45:44 +01:00
UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE = 2 ,
// 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-01-10 16:06:24 +01:00
unsigned int update_background_process ( bool force_validation = 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-02-03 10:41:14 +01:00
// returns bit mask of UpdateBackgroundProcessReturnState
unsigned int update_restart_background_process ( bool force_scene_update , bool force_preview_update );
2018-12-11 10:33:11 +01:00
void export_gcode ( fs :: path output_path , PrintHostJob upload_job );
2018-10-09 09:35:19 +02:00
void reload_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 & );
2018-10-08 19:55:30 +02:00
void on_right_click ( Vec2dEvent & );
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.
void set_bed_shape ( const Pointfs & shape );
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-03-14 13:54:05 +01:00
2019-05-03 09:44:19 +02:00
void msw_rescale_object_menu ();
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-12-06 10:38:19 +01:00
void init_view_toolbar ();
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 ();
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
#if ENABLE_VOLUMES_CENTERING_FIXES
const std :: regex Plater :: priv :: pattern_prusa ( ".*prusa" , std :: regex :: icase );
#endif // ENABLE_VOLUMES_CENTERING_FIXES
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-02-08 12:30:10 +01:00
"bed_shape" , "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" ,
"brim_width" , "perimeters" , "perimeter_extruder" , "fill_density" , "infill_extruder" , "top_solid_layers" , "bottom_solid_layers" , "solid_infill_extruder" ,
"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 ))
2018-11-23 12:47:32 +01:00
, delayed_scene_refresh ( false )
2018-11-15 15:27:39 +01:00
, project_filename ( wxEmptyString )
2019-02-26 12:56:13 +01:00
#if ENABLE_SVG_ICONS
, view_toolbar ( GLToolbar :: Radio , "View" )
#else
2018-12-17 10:55:14 +01:00
, view_toolbar ( GLToolbar :: Radio )
2019-02-26 12:56:13 +01:00
#endif // ENABLE_SVG_ICONS
2018-09-17 12:15:11 +02:00
{
2019-05-06 18:28:23 +02:00
this -> q -> SetFont ( Slic3r :: GUI :: wxGetApp (). normal_font ());
2019-04-17 14:56:00 +02:00
arranging = false ;
rotoptimizing = false ;
2018-12-03 13:14:28 +01:00
background_process . set_fff_print ( & fff_print );
2018-11-08 20:18:40 +01: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 );
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 );
2018-11-08 20:18:40 +01:00
// Default printer technology for default config.
2018-11-13 17:45:44 +01:00
background_process . select_technology ( this -> printer_technology );
2018-10-23 15:27:31 +02:00
// Register progress callback from the Print class to the Platter.
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
2019-03-07 11:49:00 +01:00
view3D = new View3D ( q , bed , camera , view_toolbar , & model , config , & background_process );
2019-04-02 13:47:49 +02:00
preview = new Preview ( q , bed , camera , view_toolbar , & model , config , & background_process , & gcode_preview_data , [ this ](){ schedule_background_process (); });
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 );
2018-12-22 10:02:42 +01:00
this -> q -> Bind ( wxEVT_TIMER , [ this ]( wxTimerEvent & evt ) { 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 ();
// 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 (); });
view3D_canvas -> Bind ( EVT_GLCANVAS_ARRANGE , [ this ]( SimpleEvent & ) { arrange (); });
2019-02-03 14:06:13 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_SELECT_ALL , [ this ]( SimpleEvent & ) { this -> q -> select_all (); });
2018-12-19 14:01:13 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_QUESTION_MARK , [ this ]( SimpleEvent & ) { wxGetApp (). keyboard_shortcuts (); });
2018-12-19 12:07:45 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INCREASE_INSTANCES , [ this ]( Event < int > & evt )
{ if ( evt . data == 1 ) this -> q -> increase_instances (); else if ( this -> can_decrease_instances ()) this -> q -> decrease_instances (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INSTANCE_MOVED , [ this ]( SimpleEvent & ) { update (); });
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-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 (); });
2018-12-04 13:55:25 +01:00
view3D_canvas -> Bind ( EVT_GLTOOLBAR_ARRANGE , [ this ]( SimpleEvent & ) { 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 );
2018-12-06 10:38:19 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_INIT , [ this ]( SimpleEvent & ) { init_view_toolbar (); });
2019-02-19 15:15:27 +01:00
view3D_canvas -> Bind ( EVT_GLCANVAS_UPDATE_BED_SHAPE , [ this ]( SimpleEvent & ) { set_bed_shape ( config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ); });
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-02-19 15:15:27 +01:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_UPDATE_BED_SHAPE , [ this ]( SimpleEvent & ) { set_bed_shape ( config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values ); });
2019-02-21 11:54:18 +01:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_TAB , [ this ]( SimpleEvent & ) { select_next_view_3D (); });
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
update_ui_from_settings ();
q -> Layout ();
2018-12-20 12:51:42 +01:00
set_current_panel ( view3D );
2018-09-17 12:15:11 +02:00
}
2018-11-21 13:52:46 +01:00
void Plater :: priv :: update ( bool force_full_scene_refresh )
2018-09-17 12:15:11 +02:00
{
2018-10-01 16:48:08 +02:00
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 ;
if ( this -> printer_technology == ptSLA )
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-01-10 16:06:24 +01:00
update_status = this -> update_background_process ( false );
2018-12-22 10:02:42 +01:00
this -> view3D -> reload_scene ( false , force_full_scene_refresh );
this -> preview -> reload_print ();
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 ()
{
// TODO: (?)
// my ($self) = @_;
// if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! wxTheApp->{app_config}->get("background_processing"))) {
// $self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing"));
// $self->{buttons_sizer}->Layout;
// }
2019-01-24 13:16:46 +01:00
#if ENABLE_RETINA_GL
view3D -> get_canvas3d () -> update_ui_from_settings ();
preview -> get_canvas3d () -> update_ui_from_settings ();
#endif
2018-09-17 12:15:11 +02:00
}
2018-10-23 15:27:31 +02:00
ProgressStatusBar * Plater :: priv :: statusbar ()
2018-09-17 12:15:11 +02:00
{
return main_frame -> m_statusbar ;
}
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 ();
}
2018-11-16 09:26:41 +01:00
std :: vector < size_t > Plater :: priv :: load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config )
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" );
bool one_by_one = input_files . size () == 1 || nozzle_dmrs -> values . size () <= 1 ;
if ( ! one_by_one ) {
for ( const auto & path : input_files ) {
if ( std :: regex_match ( path . string (), pattern_bundle )) {
one_by_one = true ;
break ;
}
}
}
2019-01-21 12:34:28 +01: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 ();
2019-01-02 15:11:05 +01:00
const auto dlg_info = wxString :: Format ( _ ( L ( "Processing input file %s \n " )), from_path ( filename ));
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
#if ENABLE_VOLUMES_CENTERING_FIXES
const bool type_prusa = std :: regex_match ( path . string (), pattern_prusa );
#endif // ENABLE_VOLUMES_CENTERING_FIXES
2018-09-17 12:15:11 +02:00
Slic3r :: Model model ;
try {
if ( type_3mf || type_zip_amf ) {
DynamicPrintConfig config ;
2018-11-06 19:09:54 +01:00
{
DynamicPrintConfig config_loaded ;
model = Slic3r :: Model :: read_from_archive ( path . string (), & config_loaded , false );
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,
PrinterTechnology printer_technology = Preset :: printer_technology ( config_loaded );
config . apply ( printer_technology == ptFFF ?
2018-11-06 19:09:54 +01:00
static_cast < const ConfigBase &> ( FullPrintConfig :: defaults ()) :
static_cast < const ConfigBase &> ( SLAFullPrintConfig :: defaults ()));
// and place the loaded config over the base.
config += std :: move ( config_loaded );
}
}
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 ();
}
wxGetApp (). app_config -> update_config_dir ( path . parent_path (). string ());
}
}
else {
2018-09-17 12:15:11 +02:00
model = Slic3r :: Model :: read_from_file ( path . string (), nullptr , false );
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
2019-01-18 12:52:09 +01:00
#if ENABLE_VOLUMES_CENTERING_FIXES
2019-01-18 16:01:43 +01:00
if ( ! type_3mf && ! type_any_amf && ! type_prusa ) {
2019-01-18 12:52:09 +01:00
#endif // ENABLE_VOLUMES_CENTERING_FIXES
if ( model . looks_like_multipart_object ()) {
2019-01-21 14:43:57 +01:00
wxMessageDialog dlg ( q , _ ( L (
2019-01-21 12:34:28 +01:00
"This file contains several objects positioned at multiple heights. "
"Instead of considering them as multiple objects, should I consider \n "
"this file as a single object having multiple parts? \n "
2019-01-21 14:43:57 +01:00
)), _ ( L ( "Multi-part object detected" )), wxICON_WARNING | wxYES | wxNO );
if ( dlg . ShowModal () == wxID_YES ) {
model . convert_multipart_object ( nozzle_dmrs -> values . size ());
}
2019-01-18 12:52:09 +01:00
}
#if ENABLE_VOLUMES_CENTERING_FIXES
2018-09-17 12:15:11 +02:00
}
2019-04-04 12:02:13 +02:00
else if (( wxGetApp (). get_mode () == comSimple ) && ( type_3mf || type_any_amf ))
{
bool advanced = false ;
for ( const ModelObject * model_object : model . objects )
{
// is there more than one instance ?
if ( model_object -> instances . size () > 1 )
{
advanced = true ;
break ;
}
2019-04-15 09:22:11 +02:00
// is there any advanced config data ?
auto opt_keys = model_object -> config . keys ();
if ( ! opt_keys . empty () && ! (( opt_keys . size () == 1 ) && ( opt_keys [ 0 ] == "extruder" )))
{
advanced = true ;
break ;
}
2019-04-04 12:02:13 +02:00
// is there any modifier ?
for ( const ModelVolume * model_volume : model_object -> volumes )
{
if ( ! model_volume -> is_model_part ())
{
advanced = true ;
break ;
}
2019-04-15 09:22:11 +02:00
// is there any advanced config data ?
opt_keys = model_volume -> config . keys ();
if ( ! opt_keys . empty () && ! (( opt_keys . size () == 1 ) && ( opt_keys [ 0 ] == "extruder" )))
{
advanced = true ;
break ;
}
2019-04-04 12:02:13 +02:00
}
if ( advanced )
break ;
}
if ( advanced )
{
2019-04-14 08:26:10 +02:00
wxMessageDialog dlg ( q , _ ( L ( "This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode? \n " )),
2019-04-04 12:02:13 +02:00
_ ( L ( "Detected advanced data" )), wxICON_WARNING | wxYES | wxNO );
if ( dlg . ShowModal () == wxID_YES )
{
2019-04-14 08:26:10 +02:00
Slic3r :: GUI :: wxGetApp (). save_mode ( comAdvanced );
2019-04-04 12:02:13 +02:00
view3D -> set_as_dirty ();
}
else
return obj_idxs ;
}
}
2019-01-18 12:52:09 +01:00
#endif // ENABLE_VOLUMES_CENTERING_FIXES
2018-09-17 12:15:11 +02:00
2019-01-18 12:52:09 +01:00
#if !ENABLE_VOLUMES_CENTERING_FIXES
2019-01-03 12:04:14 +01:00
if ( type_3mf || type_any_amf ) {
2019-01-18 12:52:09 +01:00
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
2018-11-16 09:26:41 +01:00
for ( ModelObject * model_object : model . objects ) {
2019-03-22 09:47:40 +01:00
#if ENABLE_VOLUMES_CENTERING_FIXES
model_object -> center_around_origin ( false );
#else
2018-11-16 09:26:41 +01:00
model_object -> center_around_origin ();
2019-03-22 09:47:40 +01:00
#endif // ENABLE_VOLUMES_CENTERING_FIXES
2018-11-16 09:26:41 +01:00
model_object -> ensure_on_bed ();
}
2019-01-18 12:52:09 +01:00
#if !ENABLE_VOLUMES_CENTERING_FIXES
2018-09-17 12:15:11 +02:00
}
2019-01-18 12:52:09 +01:00
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
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 ) {
Slic3r :: GUI :: show_error ( nullptr ,
wxString :: Format ( _ ( L ( "You can't to add the object(s) from %s because of one or some of them is(are) multi-part" )),
2019-01-02 15:11:05 +01:00
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
}
}
}
if ( new_model != nullptr ) {
wxMessageDialog dlg ( q , _ ( L (
"Multiple objects were loaded for a multi-material printer. \n "
"Instead of considering them as multiple objects, should I consider \n "
"these files to represent a single object having multiple parts? \n "
)), _ ( L ( "Multi-part object detected" )), wxICON_WARNING | wxYES | wxNO );
if ( dlg . ShowModal () == wxID_YES ) {
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...
statusbar () -> set_status_text ( _ ( L ( "Loaded" )));
}
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-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 );
}
}
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-01-28 10:56:02 +01: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 ();
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
2018-12-18 18:41:20 +01:00
double inv = 1. / max_ratio ;
2018-12-19 09:54:15 +01:00
object -> scale_mesh ( Vec3d ( inv , inv , inv ));
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-10-18 18:06:40 +02:00
// print.auto_assign_extruders(object);
// print.add_model_object(object);
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-04-29 14:32:02 +02:00
arr :: WipeTowerInfo wti = view3D -> get_canvas3d () -> get_wipe_tower_info ();
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 ,
_ ( L ( "Your object appears to be too large, so it was automatically scaled down to fit your print bed." )),
_ ( L ( "Object too large?" )));
}
for ( const size_t idx : obj_idxs ) {
2018-10-08 16:27:38 +02:00
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 :
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 ;
// 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 ();
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" );
dlg_title = _ ( L ( "Export STL file:" ));
break ;
}
case FT_AMF :
{
// XXX: Problem on OS X with double extension?
output_file . replace_extension ( "zip.amf" );
dlg_title = _ ( L ( "Export AMF file:" ));
break ;
}
case FT_3MF :
{
output_file . replace_extension ( "3mf" );
dlg_title = _ ( L ( "Save file as:" ));
break ;
}
2018-10-04 11:12:55 +02:00
default : break ;
}
2019-04-04 11:31:26 +02:00
wxFileDialog * dlg = new wxFileDialog ( q , dlg_title ,
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
if ( dlg -> ShowModal () != wxID_OK ) {
2019-04-04 11:31:26 +02:00
return wxEmptyString ;
2018-10-04 11:12:55 +02:00
}
2019-04-04 11:31:26 +02:00
wxString out_path = dlg -> GetPath ();
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 ();
if (( 0 > idx ) || ( idx > 1000 ))
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
}
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 );
// Delete object from Sidebar list
2018-10-12 12:00:37 +02:00
sidebar -> obj_list () -> delete_object_from_list ( obj_idx );
2018-10-08 19:14:55 +02:00
update ();
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 )
{
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 ()
{
2018-11-15 15:27:39 +01:00
project_filename . Clear ();
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 ();
// Delete all objects from list on c++ side
sidebar -> obj_list () -> delete_all_objects_from_list ();
update ();
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
auto & config = wxGetApp (). preset_bundle -> project_config ;
config . option < ConfigOptionFloats > ( "colorprint_heights" ) -> values . 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
}
void Plater :: priv :: arrange ()
{
2019-04-17 14:56:00 +02:00
if ( arranging ) { return ; }
arranging = true ;
Slic3r :: ScopeGuard arranging_guard ([ this ]() { arranging = false ; });
2018-11-12 14:52:52 +01:00
2019-02-03 14:06:13 +01:00
wxBusyCursor wait ;
2018-11-12 14:52:52 +01:00
this -> background_process . stop ();
2019-04-17 14:56:00 +02:00
2018-11-12 14:52:52 +01:00
unsigned count = 0 ;
for ( auto obj : model . objects ) count += obj -> instances . size ();
auto prev_range = statusbar () -> get_range ();
statusbar () -> set_range ( count );
auto statusfn = [ this , count ] ( unsigned st , const std :: string & msg ) {
/* // In case we would run the arrange asynchronously
wxCommandEvent event(EVT_PROGRESS_BAR);
event.SetInt(st);
event.SetString(msg);
wxQueueEvent(this->q, event.Clone()); */
statusbar () -> set_progress ( count - st );
2019-05-09 18:18:21 +02:00
statusbar () -> set_status_text ( _ ( msg ));
2018-11-12 14:52:52 +01:00
2019-04-17 14:56:00 +02:00
// ok, this is dangerous, but we are protected by the flag
2018-11-21 11:07:08 +01:00
// 'arranging' and the arrange button is also disabled.
// This call is needed for the cancel button to work.
2018-11-12 14:52:52 +01:00
wxYieldIfNeeded ();
};
statusbar () -> set_cancel_callback ([ this , statusfn ](){
2019-04-17 14:56:00 +02:00
arranging = false ;
2018-11-12 14:52:52 +01:00
statusfn ( 0 , L ( "Arranging canceled" ));
});
static const std :: string arrangestr = L ( "Arranging" );
// FIXME: I don't know how to obtain the minimum distance, it depends
// on printer technology. I guess the following should work but it crashes.
double dist = 6 ; //PrintConfig::min_object_distance(config);
2019-02-08 12:30:10 +01:00
if ( printer_technology == ptFFF ) {
dist = PrintConfig :: min_object_distance ( config );
}
2018-11-12 14:52:52 +01:00
2019-02-08 12:30:10 +01:00
auto min_obj_distance = coord_t ( dist / SCALING_FACTOR );
2018-11-12 14:52:52 +01:00
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 )));
statusfn ( 0 , arrangestr );
2019-04-29 14:32:02 +02:00
arr :: WipeTowerInfo wti = view3D -> get_canvas3d () -> get_wipe_tower_info ();
2018-11-12 14:52:52 +01:00
try {
arr :: BedShapeHint hint ;
// TODO: from Sasha from GUI or
hint . type = arr :: BedShapeType :: WHO_KNOWS ;
arr :: arrange ( model ,
2019-04-29 14:32:02 +02:00
wti ,
2018-11-12 14:52:52 +01:00
min_obj_distance ,
bed ,
hint ,
false , // create many piles not just one pile
[ statusfn ]( unsigned st ) { statusfn ( st , arrangestr ); },
2019-04-17 14:56:00 +02:00
[ this ] () { return ! arranging ; });
2018-11-12 14:52:52 +01:00
} catch ( std :: exception & /*e*/ ) {
GUI :: show_error ( this -> q , L ( "Could not arrange model objects! "
"Some geometries may be invalid." ));
}
2019-04-29 14:32:02 +02:00
// it remains to move the wipe tower:
view3D -> get_canvas3d () -> arrange_wipe_tower ( wti );
2018-11-12 14:52:52 +01:00
statusfn ( 0 , L ( "Arranging done." ));
statusbar () -> set_range ( prev_range );
statusbar () -> set_cancel_callback (); // remove cancel button
2018-11-21 13:52:46 +01:00
// Do a full refresh of scene tree, including regenerating all the GLVolumes.
//FIXME The update function shall just reload the modified matrices.
update ( true );
2018-10-09 09:35:19 +02:00
}
2018-11-22 16:04:21 +01:00
// This method will find an optimal orientation for the currently selected item
// Very similar in nature to the arrange method above...
void Plater :: priv :: sla_optimize_rotation () {
// TODO: we should decide whether to allow arrange when the search is
// running we should probably disable explicit slicing and background
// processing
2019-04-17 14:56:00 +02:00
if ( rotoptimizing ) { return ; }
rotoptimizing = true ;
Slic3r :: ScopeGuard rotoptimizing_guard ([ this ]() { rotoptimizing = false ; });
2018-11-22 16:04:21 +01:00
int obj_idx = get_selected_object_idx ();
2019-04-17 14:56:00 +02:00
if ( obj_idx < 0 ) { return ; }
2019-04-04 16:03:23 +02:00
ModelObject * o = model . objects [ size_t ( obj_idx )];
2018-11-22 16:04:21 +01:00
background_process . stop ();
auto prev_range = statusbar () -> get_range ();
statusbar () -> set_range ( 100 );
auto stfn = [ this ] ( unsigned st , const std :: string & msg ) {
2019-04-04 16:03:23 +02:00
statusbar () -> set_progress ( int ( st ));
2018-11-22 16:04:21 +01:00
statusbar () -> set_status_text ( msg );
// could be problematic, but we need the cancel button.
wxYieldIfNeeded ();
};
statusbar () -> set_cancel_callback ([ this , stfn ](){
2019-04-17 14:56:00 +02:00
rotoptimizing = false ;
2018-11-22 16:04:21 +01:00
stfn ( 0 , L ( "Orientation search canceled" ));
});
auto r = sla :: find_best_rotation (
2018-11-27 11:09:25 +01:00
* o , .005f ,
2018-11-22 16:04:21 +01:00
[ stfn ]( unsigned s ) { stfn ( s , L ( "Searching for optimal orientation" )); },
2019-04-17 14:56:00 +02:00
[ this ](){ return ! rotoptimizing ; }
2018-11-22 16:04:21 +01:00
);
2019-04-04 16:03:23 +02:00
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 )));
double mindist = 6.0 ; // FIXME
double offs = mindist / 2.0 - EPSILON ;
2019-04-17 14:56:00 +02:00
if ( rotoptimizing ) // wasn't canceled
2019-04-04 16:03:23 +02:00
for ( ModelInstance * oi : o -> instances ) {
oi -> set_rotation ({ r [ X ], r [ Y ], r [ Z ]});
auto trchull = o -> convex_hull_2d ( oi -> get_transformation (). get_matrix ());
namespace opt = libnest2d :: opt ;
opt :: StopCriteria stopcr ;
stopcr . relative_score_difference = 0.01 ;
stopcr . max_iterations = 10000 ;
stopcr . stop_score = 0.0 ;
opt :: GeneticOptimizer solver ( stopcr );
Polygon pbed ( bed );
auto bin = pbed . bounding_box ();
double binw = bin . size ()( X ) * SCALING_FACTOR - offs ;
double binh = bin . size ()( Y ) * SCALING_FACTOR - offs ;
auto result = solver . optimize_min ([ & trchull , binw , binh ]( double rot ){
auto chull = trchull ;
chull . rotate ( rot );
auto bb = chull . bounding_box ();
double bbw = bb . size ()( X ) * SCALING_FACTOR ;
double bbh = bb . size ()( Y ) * SCALING_FACTOR ;
auto wdiff = bbw - binw ;
auto hdiff = bbh - binh ;
double diff = 0 ;
if ( wdiff < 0 && hdiff < 0 ) diff = wdiff + hdiff ;
if ( wdiff > 0 ) diff += wdiff ;
if ( hdiff > 0 ) diff += hdiff ;
return diff ;
}, opt :: initvals ( 0.0 ), opt :: bound ( - PI / 2 , PI / 2 ));
double r = std :: get < 0 > ( result . optimum );
Vec3d rt = oi -> get_rotation (); rt ( Z ) += r ;
oi -> set_rotation ( rt );
}
2019-04-29 14:32:02 +02:00
arr :: WipeTowerInfo wti ; // useless in SLA context
arr :: find_new_position ( model , o -> instances , coord_t ( mindist / SCALING_FACTOR ), bed , wti );
2018-11-22 16:04:21 +01:00
2018-11-29 11:45:02 +01:00
// Correct the z offset of the object which was corrupted be the rotation
o -> ensure_on_bed ();
2018-11-22 16:04:21 +01:00
stfn ( 0 , L ( "Orientation found." ));
statusbar () -> set_range ( prev_range );
statusbar () -> set_cancel_callback ();
update ( true );
}
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 ;
// 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 )
{
Slic3r :: GUI :: warning_catcher ( q , _ ( L ( "The selected object can't be split because it contains more than one volume/material." )));
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 )
Slic3r :: GUI :: warning_catcher ( q , _ ( L ( "The selected object couldn't be split because it contains only one part." )));
else
{
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
}
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-01-10 16:06:24 +01:00
unsigned int Plater :: priv :: update_background_process ( bool force_validation )
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 ;
2018-12-22 10:02:42 +01:00
// If the update_background_process() was not called by the timer, kill the timer,
// 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 ();
2019-02-19 14:57:59 +01:00
// The delayed error message is no more valid.
this -> delayed_error_message . clear ();
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-04-09 10:41:42 +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 ()) {
// 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 ()) {
2018-12-22 10:02:42 +01: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 );
if ( top_level_wnd && top_level_wnd -> IsActive ()) {
// The error returned from the Print needs to be translated into the local language.
GUI :: show_error ( this -> q , _ ( err ));
} else {
// Show the error message once the main window gets activated.
this -> delayed_error_message = _ ( err );
}
2018-11-13 17:45:44 +01:00
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID ;
}
}
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 ) {
// 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 ());
}
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.
2019-03-01 11:00:34 +01:00
const wxString invalid_str = _ ( L ( "Invalid data" ));
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 )
2019-05-04 02:07:07 +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-03-01 11:00:34 +01:00
const wxString slice_string = background_process . running () && wxGetApp (). get_mode () == comSimple ?
_ ( L ( "Slicing" )) + dots : _ ( L ( "Slice now" ));
sidebar -> set_btn_label ( ActionButtonType :: abReslice , slice_string );
if ( background_process . finished ())
show_action_buttons ( false );
2019-03-11 10:23:59 +01:00
else if ( ! background_process . empty () &&
! background_process . running ()) /* Do not update buttons if background process is running
* This condition is important for SLA mode especially,
* when this function is called several times during calculations
* */
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-04-17 14:56:00 +02:00
if ( arranging || rotoptimizing ) {
// Avoid a race condition
return false ;
}
2018-12-22 10:02:42 +01:00
if ( ! this -> background_process . empty () &&
( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ) == 0 &&
( (( state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART ) != 0 && ! this -> background_process . finished ()) ||
( 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 ]() {
2019-05-04 02:07:07 +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 ;
}
2018-12-11 10:33:11 +01:00
void Plater :: priv :: export_gcode ( fs :: path output_path , PrintHostJob upload_job )
{
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 ()) {
GUI :: show_error ( q , _ ( L ( "Another export job is currently running." )));
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 ()) {
background_process . schedule_export ( output_path . string ());
} 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-02-03 10:41:14 +01: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:
view3D -> reload_scene ( true );
}
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 ()
{
2018-12-05 15:22:03 +01:00
const auto & selection = get_selection ();
const auto obj_orig_idx = selection . get_object_idx ();
if ( selection . is_wipe_tower () || obj_orig_idx == - 1 ) { return ; }
2018-10-09 09:35:19 +02:00
2018-12-05 15:22:03 +01:00
auto * object_orig = model . objects [ obj_orig_idx ];
std :: vector < fs :: path > input_paths ( 1 , object_orig -> input_file );
const auto new_idxs = load_files ( input_paths , true , false );
for ( const auto idx : new_idxs ) {
ModelObject * object = model . objects [ idx ];
object -> clear_instances ();
for ( const ModelInstance * instance : object_orig -> instances ) {
object -> add_instance ( * instance );
}
if ( object -> volumes . size () == object_orig -> volumes . size ()) {
for ( size_t i = 0 ; i < object -> volumes . size (); i ++ ) {
object -> volumes [ i ] -> config . apply ( object_orig -> volumes [ i ] -> config );
}
}
2019-01-21 10:06:51 +01:00
// XXX: Restore more: layer_height_ranges, layer_height_profile (?)
2018-12-05 15:22:03 +01:00
}
remove ( obj_orig_idx );
2018-10-09 09:35:19 +02:00
}
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-02-03 22:14:34 +01:00
fix_model_by_win10_sdk_gui ( * model . objects [ obj_idx ], vol_idx );
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
}
// 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 )
{
2018-12-11 17:49:31 +01:00
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 );
2018-12-04 13:55:25 +01:00
preview -> set_canvas_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
2018-10-10 13:53:45 +02:00
auto idx = combo -> get_extruder_idx ();
2018-10-09 12:41:05 +02:00
2018-10-15 11:39:48 +02:00
//! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox,
//! but the OSX version derived from wxOwnerDrawnCombo.
//! So, to get selected string we do
//! combo->GetString(combo->GetSelection())
//! instead of
2019-01-03 14:34:53 +01:00
//! combo->GetStringSelection().ToUTF8().data());
2018-10-15 11:39:48 +02:00
2019-03-13 13:13:18 +01:00
const std :: string & selected_string = combo -> GetString ( combo -> GetSelection ()). ToUTF8 (). data ();
2018-10-15 11:39:48 +02:00
2018-09-17 12:15:11 +02:00
if ( preset_type == Preset :: TYPE_FILAMENT ) {
2018-10-15 11:39:48 +02:00
wxGetApp (). preset_bundle -> set_filament_preset ( idx , selected_string );
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 ()) {
2018-10-10 13:53:45 +02:00
// Only update the platter UI for the 2nd and other filaments.
2019-04-23 16:33:06 +02:00
wxGetApp (). preset_bundle -> update_platter_filament_ui ( idx , combo );
2018-10-10 13:53:45 +02:00
}
else {
2019-03-13 13:13:18 +01:00
wxWindowUpdateLocker noUpdates ( sidebar -> presets_panel ());
wxGetApp (). get_tab ( preset_type ) -> select_preset ( selected_string );
2018-09-17 12:15:11 +02:00
}
2018-10-15 11:39:48 +02:00
// update plater with new config
2018-10-17 11:12:38 +02:00
wxGetApp (). plater () -> on_config_change ( wxGetApp (). preset_bundle -> full_config ());
2018-11-29 15:00:23 +01:00
if ( preset_type == Preset :: TYPE_PRINTER )
wxGetApp (). obj_list () -> update_settings_items ();
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-04-17 14:56:00 +02:00
if ( arranging || rotoptimizing ) {
// 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-04-10 13:36:15 +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-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 ;
}
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-03-11 09:48:24 +01:00
const bool canceled = evt . GetInt () < 0 ;
const bool error = evt . GetInt () == 0 ;
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 ())
message = _ ( L ( "Export failed" ));
2019-03-11 09:48:24 +01:00
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
}
2018-10-25 10:07:11 +02:00
if ( canceled )
2019-05-04 02:07:07 +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 ();
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 ;
2018-11-12 17:35:57 +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 );
}
else if ( wxGetApp (). get_mode () == comSimple )
2019-03-01 11:00:34 +01:00
show_action_buttons ( 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
2018-10-17 12:17:25 +02:00
void Plater :: priv :: on_right_click ( Vec2dEvent & evt )
2018-10-08 19:55:30 +02:00
{
2018-10-17 12:17:25 +02:00
int obj_idx = get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
2018-12-07 17:50:48 +01:00
wxMenu * menu = printer_technology == ptSLA ? & sla_object_menu :
2019-01-25 10:34:32 +01:00
get_selection (). is_single_full_instance () ? // show "Object menu" for each FullInstance instead of FullObject
2018-12-13 13:19:11 +01:00
& object_menu : & part_menu ;
2018-12-07 17:50:48 +01:00
sidebar -> obj_list () -> append_menu_item_settings ( menu );
2019-03-28 16:28:34 +01:00
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 ) {
if ( menu -> FindItem ( _ ( L ( "Increase copies" ))) != wxNOT_FOUND )
{
/* 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-19 09:51:50 +01:00
}
2019-03-28 16:28:34 +01:00
else {
if ( menu -> FindItem ( _ ( L ( "Increase copies" ))) == wxNOT_FOUND )
{
// 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-19 09:51:50 +01:00
}
}
2019-02-25 10:35:16 +01:00
if ( q != nullptr ) {
#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
2018-12-07 17:50:48 +01:00
q -> PopupMenu ( menu , ( int ) evt . data . x (), ( int ) evt . data . 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
}
2018-11-23 12:47:32 +01:00
// Update the scene from the background processing,
// 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 ();
return true ;
}
2019-05-03 09:44:19 +02:00
void Plater :: priv :: msw_rescale_object_menu ()
{
for ( MenuWithSeparators * menu : { & object_menu , & sla_object_menu , & part_menu })
msw_rescale_menu ( dynamic_cast < wxMenu *> ( menu ));
}
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 ) {
2019-05-13 14:27:51 +02:00
append_menu_item ( menu , wxID_ANY , _ ( L ( "Delete" )) + " \t Del" , _ ( L ( "Remove the selected object" )),
[ this ]( wxCommandEvent & ) { q -> remove_selected (); }, "delete" , nullptr , [ this ]() { return can_delete (); }, q );
2019-04-04 15:13:43 +02:00
sidebar -> obj_list () -> append_menu_item_export_stl ( menu );
}
else {
2019-01-21 12:34:28 +01:00
wxMenuItem * item_increase = append_menu_item ( menu , wxID_ANY , _ ( L ( "Increase copies" )) + " \t +" , _ ( L ( "Place one more copy 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 );
2019-01-21 12:34:28 +01:00
wxMenuItem * item_decrease = append_menu_item ( menu , wxID_ANY , _ ( L ( "Decrease copies" )) + " \t -" , _ ( L ( "Remove one copy 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 );
2019-01-21 12:34:28 +01:00
wxMenuItem * item_set_number_of_copies = append_menu_item ( menu , wxID_ANY , _ ( L ( "Set number of copies" )) + dots , _ ( L ( "Change the number of copies 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.
2019-05-13 14:27:51 +02:00
append_menu_item ( menu , wxID_ANY , _ ( L ( "Delete" )) + " \t Del" , _ ( L ( "Remove the selected object" )),
[ 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 ();
append_menu_item ( menu , wxID_ANY , _ ( L ( "Reload from Disk" )), _ ( L ( "Reload the selected file from Disk" )),
[ this ]( wxCommandEvent & ) { reload_from_disk (); });
2019-04-05 11:30:49 +02:00
append_menu_item ( menu , wxID_ANY , _ ( L ( "Export as STL" )) + dots , _ ( L ( "Export the selected object as STL file" )),
2019-05-02 13:46:39 +02:00
[ this ]( wxCommandEvent & ) { q -> export_stl ( false , true ); });
2019-04-04 15:13:43 +02:00
menu -> AppendSeparator ();
2018-12-07 17:50:48 +01:00
}
2018-10-17 14:26:13 +02:00
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 ;
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 );
2018-10-18 14:42:21 +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 );
2018-10-18 14:42:21 +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
2019-05-13 14:27:51 +02:00
append_submenu ( menu , mirror_menu , wxID_ANY , _ ( L ( "Mirror" )), _ ( L ( "Mirror the selected object" )), "" ,
[ 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 ;
2019-05-13 14:27:51 +02:00
append_menu_item ( split_menu , wxID_ANY , _ ( L ( "To objects" )), _ ( L ( "Split the selected object into individual objects" )),
[ this ]( wxCommandEvent & ) { split_object (); }, "split_object_SMALL" , & object_menu , [ this ]() { return can_split (); }, q );
append_menu_item ( split_menu , wxID_ANY , _ ( L ( "To parts" )), _ ( L ( "Split the selected object into individual sub-parts" )),
[ this ]( wxCommandEvent & ) { split_volume (); }, "split_parts_SMALL" , & object_menu , [ this ]() { return can_split (); }, q );
2018-10-24 12:55:38 +02:00
2019-05-13 14:27:51 +02:00
append_submenu ( & object_menu , split_menu , wxID_ANY , _ ( L ( "Split" )), _ ( L ( "Split the selected object" )), "" ,
[ 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-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 ()
{
2019-05-13 14:27:51 +02:00
append_menu_item ( & sla_object_menu , wxID_ANY , _ ( L ( "Split" )), _ ( L ( "Split the selected object into individual objects" )),
[ 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
append_menu_item ( & sla_object_menu , wxID_ANY , _ ( L ( "Optimize orientation" )), _ ( L ( "Optimize the rotation of the object for better print results." )),
[ this ]( wxCommandEvent & ) { sla_optimize_rotation (); });
return true ;
}
bool Plater :: priv :: complit_init_part_menu ()
{
2019-05-13 14:27:51 +02:00
append_menu_item ( & part_menu , wxID_ANY , _ ( L ( "Split" )), _ ( L ( "Split the selected object into individual sub-parts" )),
[ 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 ();
obj_list -> append_menu_item_change_type ( & part_menu );
2018-10-17 12:17:25 +02:00
return true ;
}
2018-10-08 19:55:30 +02:00
2018-12-06 10:38:19 +01:00
void Plater :: priv :: init_view_toolbar ()
{
2019-02-26 09:56:23 +01:00
#if !ENABLE_SVG_ICONS
2018-12-17 10:55:14 +01:00
ItemsIconsTexture :: Metadata icons_data ;
icons_data . filename = "view_toolbar.png" ;
icons_data . icon_size = 64 ;
2019-02-26 09:56:23 +01:00
#endif // !ENABLE_SVG_ICONS
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 ENABLE_SVG_ICONS
if ( ! view_toolbar . init ( background_data ))
#else
2018-12-17 10:55:14 +01:00
if ( ! view_toolbar . init ( icons_data , background_data ))
2019-02-26 09:56:23 +01:00
#endif // ENABLE_SVG_ICONS
2018-12-06 10:38:19 +01:00
return ;
2018-12-17 10:55:14 +01:00
view_toolbar . set_layout_orientation ( GLToolbar :: Layout :: Bottom );
view_toolbar . set_border ( 5.0f );
view_toolbar . set_gap_size ( 1.0f );
GLToolbarItem :: Data item ;
item . name = "3D" ;
2019-02-26 09:56:23 +01:00
#if ENABLE_SVG_ICONS
2019-03-04 14:21:52 +01:00
item . icon_filename = "editor.svg" ;
2019-02-26 09:56:23 +01:00
#endif // ENABLE_SVG_ICONS
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-03-14 13:54:05 +01:00
item . action_callback = [ this ]() { if ( this -> q != nullptr ) wxPostEvent ( this -> q , SimpleEvent ( EVT_GLVIEWTOOLBAR_3D )); };
2018-12-17 10:55:14 +01:00
item . is_toggable = false ;
if ( ! view_toolbar . add_item ( item ))
return ;
item . name = "Preview" ;
2019-02-26 09:56:23 +01:00
#if ENABLE_SVG_ICONS
2019-03-04 14:21:52 +01:00
item . icon_filename = "preview.svg" ;
2019-02-26 09:56:23 +01:00
#endif // ENABLE_SVG_ICONS
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-03-14 13:54:05 +01:00
item . action_callback = [ this ]() { if ( this -> q != nullptr ) wxPostEvent ( this -> q , SimpleEvent ( EVT_GLVIEWTOOLBAR_PREVIEW )); };
2018-12-17 10:55:14 +01:00
item . is_toggable = false ;
if ( ! view_toolbar . add_item ( item ))
return ;
view_toolbar . select_item ( "3D" );
view_toolbar . set_enabled ( 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-02-19 15:15:27 +01:00
void Plater :: priv :: set_bed_shape ( const Pointfs & shape )
{
bool new_shape = bed . set_shape ( shape );
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-04-10 12:16:17 +02:00
return ! get_selection (). is_empty () && ! get_selection (). is_wipe_tower ();
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-04-17 14:56:00 +02:00
if ( arranging || rotoptimizing ) {
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-04-17 14:56:00 +02:00
if ( arranging || rotoptimizing ) {
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-04-17 14:56:00 +02:00
return ! model . objects . empty () && ! arranging ;
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
}
2019-03-01 11:00:34 +01:00
void Plater :: priv :: show_action_buttons ( const bool is_ready_to_slice ) const
{
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 ();
// when a background processing is ON, export_btn and/or send_btn are showing
if ( wxGetApp (). app_config -> get ( "background_processing" ) == "1" )
{
2019-04-24 19:03:05 +02:00
if ( sidebar -> show_reslice ( false ) |
sidebar -> show_export ( true ) |
sidebar -> show_send ( send_gcode_shown ))
sidebar -> Layout ();
}
2019-03-01 11:00:34 +01:00
else
{
2019-04-24 19:03:05 +02:00
if ( sidebar -> show_reslice ( is_ready_to_slice ) |
sidebar -> show_export ( ! is_ready_to_slice ) |
sidebar -> show_send ( send_gcode_shown && ! is_ready_to_slice ))
sidebar -> Layout ();
}
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 ;
case ActionButtonType :: abSendGCode : p -> btn_send_gcode -> SetLabelText ( label ); break ;
}
}
2018-09-17 12:15:11 +02:00
// Plater / Public
Plater :: Plater ( wxWindow * parent , MainFrame * main_frame )
: wxPanel ( parent ), p ( new priv ( this , main_frame ))
{
// 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
2018-11-15 15:27:39 +01:00
void Plater :: load_project ()
{
wxString input_file ;
wxGetApp (). load_project ( this , input_file );
if ( input_file . empty ())
return ;
p -> reset ();
p -> project_filename = input_file ;
std :: vector < fs :: path > input_paths ;
2019-01-02 15:11:05 +01:00
input_paths . push_back ( into_path ( input_file ));
2018-11-15 15:27:39 +01:00
load_files ( input_paths );
}
void Plater :: add_model ()
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
std :: vector < fs :: path > input_paths ;
for ( const auto & file : input_files ) {
2019-01-02 15:11:05 +01:00
input_paths . push_back ( into_path ( file ));
2018-10-17 12:17:25 +02:00
}
2018-11-16 09:26:41 +01:00
load_files ( input_paths , true , false );
2018-10-17 12:17:25 +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 );
}
void Plater :: load_files ( const std :: vector < fs :: path >& input_files , bool load_model , bool load_config ) { p -> load_files ( input_files , load_model , load_config ); }
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.
void Plater :: load_files ( const std :: vector < std :: string >& input_files , bool load_model , bool load_config )
{
std :: vector < fs :: path > paths ;
paths . reserve ( input_files . size ());
for ( const std :: string & path : input_files )
paths . emplace_back ( path );
p -> load_files ( paths , load_model , load_config );
}
2018-11-21 13:32:24 +01:00
void Plater :: update () { p -> update (); }
2018-10-17 12:59:58 +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 ); }
2018-11-21 15:28:35 +01:00
void Plater :: select_all () { p -> select_all (); }
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 ()
{
2019-05-10 17:10:12 +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 ()
{
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 ; }
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-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
}
2018-11-07 13:40:24 +01:00
sidebar (). obj_list () -> increase_object_instances ( obj_idx , was_one_instance ? num + 1 : num );
2018-10-11 10:03:38 +02:00
if ( p -> get_config ( "autocenter" ) == "1" ) {
p -> arrange ();
} else {
p -> update ();
}
2018-10-23 09:19:41 +02:00
p -> get_selection (). add_instance ( obj_idx , ( int ) model_object -> instances . size () - 1 );
2018-10-11 10:03:38 +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 ; }
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 ();
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
}
p -> update ();
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
2018-10-25 21:13:45 +02:00
const auto num = wxGetNumberFromUser ( " " , _ ( "Enter the number of copies:" ),
_ ( "Copies of the selected object" ), model_object -> instances . size (), 0 , 1000 , this );
if ( num < 0 )
return ;
2018-10-17 09:30:07 +02:00
int diff = ( int ) 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
}
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 ;
}
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-02-03 10:41:14 +01:00
void Plater :: export_gcode ()
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 {
// 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 ;
default_output_file = this -> p -> background_process . current_print () -> output_filepath ( "" );
} catch ( const std :: exception & ex ) {
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 ()));
auto start_dir = wxGetApp (). app_config -> get_last_output_dir ( default_output_file . parent_path (). string ());
2018-09-17 12:15:11 +02:00
2019-03-01 10:09:20 +01:00
wxFileDialog dlg ( this , ( printer_technology () == ptFFF ) ? _ ( L ( "Save G-code file as:" )) : _ ( L ( "Save SL1 file as:" )),
2019-02-03 10:41:14 +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
);
fs :: path output_path ;
if ( dlg . ShowModal () == wxID_OK ) {
fs :: path path = into_path ( dlg . GetPath ());
wxGetApp (). app_config -> update_last_output_dir ( path . parent_path (). string ());
output_path = std :: move ( path );
}
if ( ! output_path . empty ())
2018-12-11 10:33:11 +01:00
p -> export_gcode ( std :: move ( output_path ), PrintHostJob ());
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 ;
2018-12-05 15:22:03 +01:00
TriangleMesh mesh ;
if ( selection_only ) {
const auto & selection = p -> get_selection ();
if ( selection . is_wipe_tower ()) { return ; }
const auto obj_idx = selection . get_object_idx ();
if ( obj_idx == - 1 ) { return ; }
2019-04-04 15:13:43 +02:00
2019-04-05 10:08:34 +02:00
const ModelObject * model_object = p -> model . objects [ obj_idx ];
2019-04-04 15:13:43 +02:00
if ( selection . get_mode () == Selection :: Instance )
2019-04-05 11:30:49 +02:00
{
if ( selection . is_single_full_object ())
mesh = model_object -> mesh ();
else
mesh = model_object -> full_raw_mesh ();
}
2019-04-04 15:13:43 +02:00
else
{
const GLVolume * volume = selection . get_volume ( * selection . get_volume_idxs (). begin ());
2019-04-05 10:08:34 +02:00
mesh = model_object -> volumes [ volume -> volume_idx ()] -> mesh ;
2019-04-04 15:13:43 +02:00
mesh . transform ( volume -> get_volume_transformation (). get_matrix ());
2019-04-05 10:08:34 +02:00
mesh . translate ( - model_object -> origin_translation . cast < float > ());
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
{
2019-01-02 18:36:48 +01:00
mesh = p -> model . mesh ();
2018-12-05 15:22:03 +01:00
2019-05-02 13:46:39 +02:00
if ( extended && ( p -> printer_technology == ptSLA ))
{
const PrintObjects & objects = p -> sla_print . objects ();
for ( const SLAPrintObject * object : objects )
{
const ModelObject * model_object = object -> model_object ();
Transform3d mesh_trafo_inv = object -> trafo (). inverse ();
bool is_left_handed = object -> is_left_handed ();
TriangleMesh pad_mesh ;
bool has_pad_mesh = object -> has_mesh ( slaposBasePool );
if ( has_pad_mesh )
{
pad_mesh = object -> get_mesh ( slaposBasePool );
pad_mesh . transform ( mesh_trafo_inv );
}
TriangleMesh supports_mesh ;
bool has_supports_mesh = 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 ())
{
int instance_idx = it - model_object -> instances . begin ();
const Transform3d & inst_transform = object -> model_object () -> instances [ instance_idx ] -> get_transformation (). get_matrix ();
if ( has_pad_mesh )
{
TriangleMesh inst_pad_mesh = pad_mesh ;
inst_pad_mesh . transform ( inst_transform , is_left_handed );
mesh . merge ( inst_pad_mesh );
}
if ( has_supports_mesh )
{
TriangleMesh inst_supports_mesh = supports_mesh ;
inst_supports_mesh . transform ( inst_transform , is_left_handed );
mesh . merge ( inst_supports_mesh );
}
}
}
}
}
}
2019-01-02 15:11:05 +01:00
Slic3r :: store_stl ( path_u8 . c_str (), & mesh , true );
2018-10-04 11:12:55 +02:00
p -> statusbar () -> set_status_text ( wxString :: Format ( _ ( L ( "STL file exported to %s" )), path ));
}
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 ();
if ( Slic3r :: store_amf ( path_u8 . c_str (), & p -> model , export_config ? & cfg : nullptr )) {
2018-10-08 19:55:30 +02:00
// Success
p -> statusbar () -> set_status_text ( wxString :: Format ( _ ( L ( "AMF file exported to %s" )), path ));
} else {
// Failure
p -> statusbar () -> set_status_text ( wxString :: Format ( _ ( L ( "Error exporting AMF file %s" )), path ));
}
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
2018-12-06 14:47:53 +01: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 ;
2019-01-02 15:11:05 +01:00
if ( Slic3r :: store_3mf ( path_u8 . c_str (), & p -> model , export_config ? & cfg : nullptr )) {
2018-10-08 19:55:30 +02:00
// Success
p -> statusbar () -> set_status_text ( wxString :: Format ( _ ( L ( "3MF file exported to %s" )), path ));
} else {
// Failure
p -> statusbar () -> set_status_text ( wxString :: Format ( _ ( L ( "Error exporting 3MF file %s" )), path ));
}
2018-10-04 11:12:55 +02:00
}
2018-09-17 12:15:11 +02:00
void Plater :: reslice ()
{
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 )
p -> sidebar -> set_btn_label ( ActionButtonType :: abReslice , _ ( L ( "Slicing" )) + dots );
else
{
p -> sidebar -> set_btn_label ( ActionButtonType :: abReslice , _ ( L ( "Slice now" )));
p -> show_action_buttons ( false );
}
}
else if ( ! p -> background_process . empty () && ! p -> background_process . idle ())
p -> show_action_buttons ( true );
2018-09-17 12:15:11 +02:00
}
2019-02-21 11:40:56 +01:00
void Plater :: reslice_SLA_supports ( const ModelObject & object )
{
//FIXME Don't reslice if export of G-code or sending to OctoPrint is running.
// bitmask of UpdateBackgroundProcessReturnState
unsigned int state = this -> p -> update_background_process ( true );
if ( state & priv :: UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE )
this -> p -> view3D -> reload_scene ( false );
if ( this -> p -> background_process . empty () || ( state & priv :: UPDATE_BACKGROUND_PROCESS_INVALID ))
// 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.
if ( ! this -> p -> background_processing_enabled ()) {
task . single_model_instance_only = true ;
task . to_object_step = slaposBasePool ;
}
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-02-03 10:41:14 +01: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 ;
default_output_file = this -> p -> background_process . current_print () -> output_filepath ( "" );
2018-12-11 10:33:11 +01:00
} catch ( const std :: exception & ex ) {
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 ();
p -> export_gcode ( fs :: path (), std :: move ( upload_job ));
}
2018-10-04 11:12:55 +02:00
}
2018-10-10 13:53:45 +02:00
void Plater :: on_extruders_change ( int num_extruders )
{
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
2018-10-10 13:53:45 +02:00
int i = choices . size ();
while ( i < num_extruders )
{
PresetComboBox * choice /*{ nullptr }*/ ;
sidebar (). init_filament_combo ( & choice , i );
choices . push_back ( choice );
// initialize selection
2019-04-23 16:33:06 +02:00
wxGetApp (). preset_bundle -> update_platter_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 )) {
p -> config -> set_key_value ( opt_key , config . option ( opt_key ) -> clone ());
2018-12-03 13:14:28 +01:00
if ( opt_key == "printer_technology" )
this -> set_printer_technology ( config . opt_enum < PrinterTechnology > ( opt_key ));
2019-01-17 13:21:33 +01:00
else if ( opt_key == "bed_shape" ) {
2019-02-04 10:06:15 +01:00
bed_shape_changed = true ;
2018-10-15 10:53:47 +02:00
update_scheduled = true ;
}
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 ;
}
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 ());
} 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
}
2018-12-11 10:33:11 +01:00
{
const auto prin_host_opt = p -> config -> option < ConfigOptionString > ( "print_host" );
p -> sidebar -> show_send ( prin_host_opt != nullptr && ! prin_host_opt -> value . empty ());
}
2019-02-04 10:06:15 +01:00
if ( bed_shape_changed )
2019-02-19 15:15:27 +01:00
p -> set_bed_shape ( p -> config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values );
2019-02-04 10:06:15 +01:00
2018-10-15 10:53:47 +02:00
if ( update_scheduled )
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
}
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-02-19 14:57:59 +01:00
if ( ! this -> p -> delayed_error_message . empty ()) {
std :: string msg = std :: move ( this -> p -> delayed_error_message );
this -> p -> delayed_error_message . clear ();
GUI :: show_error ( this , msg );
}
}
2018-11-15 15:27:39 +01:00
const wxString & Plater :: get_project_filename () const
{
return p -> project_filename ;
}
bool Plater :: is_export_gcode_scheduled () const
{
return p -> background_process . is_export_scheduled ();
}
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
}
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
}
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.
}
//FIXME for SLA synchronize
//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 ();
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-04-22 01:51:10 +02:00
for ( int obj_idx : object_idxs )
{
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-04-12 18:29:47 +02:00
void Plater :: schedule_background_process ()
{
this -> p -> schedule_background_process ();
}
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 (); }
2019-04-10 08:40:58 +02:00
void Plater :: copy_selection_to_clipboard ()
{
p -> view3D -> get_canvas3d () -> get_selection (). copy_to_clipboard ();
}
void Plater :: paste_from_clipboard ()
{
p -> view3D -> get_canvas3d () -> get_selection (). paste_from_clipboard ();
}
2019-04-12 08:41:59 +02:00
2019-04-11 14:21:08 +02:00
bool Plater :: can_paste_from_clipboard () const
2019-04-10 08:40:58 +02:00
{
2019-04-11 14:21:08 +02:00
const Selection & selection = p -> view3D -> get_canvas3d () -> get_selection ();
const Selection :: Clipboard & clipboard = selection . get_clipboard ();
Selection :: EMode mode = clipboard . get_mode ();
2019-04-12 08:49:24 +02:00
if ( clipboard . is_empty ())
return false ;
if (( mode == Selection :: Volume ) && ! selection . is_from_single_instance ())
return false ;
if (( mode == Selection :: Instance ) && ( selection . get_mode () != Selection :: Instance ))
return false ;
return true ;
2019-04-10 08:40:58 +02:00
}
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-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-04-11 13:20:34 +02:00
bool Plater :: can_copy () const { return ! is_selection_empty (); }
2019-04-11 14:21:08 +02:00
bool Plater :: can_paste () const { return can_paste_from_clipboard (); }
2019-03-14 13:54:05 +01:00
2018-09-17 12:15:11 +02:00
}} // namespace Slic3r::GUI