2018-09-17 12:15:11 +02:00
#include "Plater.hpp"
#include <cstddef>
#include <algorithm>
#include <vector>
#include <string>
#include <regex>
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/notebook.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-25 21:13:45 +02:00
#include <wx/numdlg.h>
2018-09-17 12:15:11 +02:00
2018-10-09 09:35:19 +02:00
#include "libslic3r/libslic3r.h"
2018-09-17 12:15:11 +02:00
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Model.hpp"
2018-11-12 14:52:52 +01:00
#include "libslic3r/ModelArrange.hpp"
2018-09-17 12:15:11 +02:00
#include "libslic3r/Print.hpp"
2018-11-08 20:18:40 +01:00
#include "libslic3r/SLAPrint.hpp"
2018-09-17 12:15:11 +02:00
#include "libslic3r/GCode/PreviewData.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Polygon.hpp"
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-12 14:52:52 +01:00
//#include "slic3r/AppController.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"
2018-09-17 12:15:11 +02:00
#include "GLToolbar.hpp"
#include "GUI_Preview.hpp"
#include "Tab.hpp"
#include "PresetBundle.hpp"
#include "BackgroundSlicingProcess.hpp"
#include "ProgressStatusBar.hpp"
#include "slic3r/Utils/ASCIIFolding.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 ;
namespace Slic3r {
namespace GUI {
2018-10-23 15:27:31 +02:00
wxDEFINE_EVENT ( EVT_PROGRESS_BAR , wxCommandEvent );
2018-09-17 12:15:11 +02:00
wxDEFINE_EVENT ( EVT_SLICING_COMPLETED , wxCommandEvent );
wxDEFINE_EVENT ( EVT_PROCESS_COMPLETED , wxCommandEvent );
// 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 ;
2018-11-01 12:33:56 +01:00
bool showing_manifold_warning_icon ;
void show_sizer ( bool show );
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
auto * grid_sizer = new wxFlexGridSizer ( 4 , 5 , 5 );
grid_sizer -> SetFlexibleDirection ( wxHORIZONTAL );
grid_sizer -> AddGrowableCol ( 1 , 1 );
grid_sizer -> AddGrowableCol ( 3 , 1 );
auto init_info_label = [ parent , grid_sizer ]( wxStaticText ** info_label , wxString text_label ) {
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 );
};
2018-11-01 12:33:56 +01:00
init_info_label ( & info_size , _ ( L ( "Size:" )));
init_info_label ( & info_volume , _ ( L ( "Volume:" )));
init_info_label ( & info_facets , _ ( L ( "Facets:" )));
init_info_label ( & info_materials , _ ( L ( "Materials:" )));
Add ( grid_sizer , 0 , wxEXPAND );
2018-09-17 12:15:11 +02:00
2018-11-01 12:33:56 +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 ());
2018-09-17 12:15:11 +02:00
wxBitmap bitmap ( GUI :: from_u8 ( Slic3r :: var ( "error.png" )), wxBITMAP_TYPE_PNG );
manifold_warning_icon = new wxStaticBitmap ( parent , wxID_ANY , bitmap );
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 );
}
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
}
2018-10-31 12:26:57 +01:00
enum SlisedInfoIdx
{
siFilament_m ,
siFilament_mm3 ,
siFilament_g ,
siCost ,
siTimeNormal ,
siTimeSilent ,
siWTNumbetOfToolchanges ,
siCount
};
2018-09-17 12:15:11 +02:00
class SlicedInfo : public wxStaticBoxSizer
{
public :
SlicedInfo ( wxWindow * parent );
2018-10-31 12:26:57 +01:00
void SetTextAndShow ( SlisedInfoIdx idx , const wxString & text );
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
auto * grid_sizer = new wxFlexGridSizer ( 2 , 5 , 5 );
grid_sizer -> SetFlexibleDirection ( wxHORIZONTAL );
grid_sizer -> AddGrowableCol ( 1 , 1 );
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)" )));
init_info_label ( _ ( L ( "Cost" )));
init_info_label ( _ ( L ( "Estimated printing time (normal mode)" )));
init_info_label ( _ ( L ( "Estimated printing time (silent mode)" )));
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 );
}
void SlicedInfo :: SetTextAndShow ( SlisedInfoIdx idx , const wxString & text )
{
const bool show = text != "N/A" ;
if ( show )
info_vec [ idx ]. second -> SetLabelText ( text );
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 ) :
wxBitmapComboBox ( parent , wxID_ANY , wxEmptyString , wxDefaultPosition , wxDefaultSize , 0 , nullptr , wxCB_READONLY ),
preset_type ( preset_type ),
last_selected ( wxNOT_FOUND )
{
Bind ( wxEVT_COMBOBOX , [ this ]( wxCommandEvent & evt ) {
auto selected_item = this -> GetSelection ();
auto marker = reinterpret_cast < Marker > ( this -> GetClientData ( selected_item ));
if ( marker == LABEL_ITEM_MARKER ) {
this -> SetSelection ( this -> last_selected );
evt . StopPropagation ();
} else if ( this -> last_selected != selected_item ) {
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 );
auto dialog = new wxColourDialog ( wxGetApp (). mainframe , data );
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 );
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 ();
});
}
2018-09-17 12:15:11 +02:00
}
PresetComboBox ::~ PresetComboBox () {}
2018-10-09 12:41:05 +02:00
void PresetComboBox :: set_label_marker ( int item )
{
this -> SetClientData ( item , ( void * ) LABEL_ITEM_MARKER );
}
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 };
public :
FreqChangedParams ( wxWindow * parent , const int label_width );
~ FreqChangedParams () {}
wxButton * get_wiping_dialog_button () { return m_wiping_dialog_button ; }
2018-10-24 12:57:23 +02:00
void Show ( const bool show );
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 ;
m_og -> set_config ( config );
m_og -> label_width = label_width ;
2018-10-31 12:56:08 +01:00
m_og -> m_on_change = [ config , this ]( t_config_option_key opt_key , boost :: any value ) {
2018-10-04 16:43:10 +02:00
TabPrint * tab_print = nullptr ;
for ( size_t i = 0 ; i < wxGetApp (). tab_panel () -> GetPageCount (); ++ i ) {
Tab * tab = dynamic_cast < Tab *> ( wxGetApp (). tab_panel () -> GetPage ( i ));
if ( ! tab )
continue ;
2018-10-31 12:56:08 +01:00
if ( tab -> name () == "print" ) {
2018-10-04 16:43:10 +02:00
tab_print = static_cast < TabPrint *> ( tab );
break ;
}
}
if ( tab_print == nullptr )
return ;
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 )
{
new_val = m_brim_width == 0.0 ? 10 :
m_brim_width < 0.0 ? m_brim_width * ( - 1 ) :
m_brim_width ;
}
else {
m_brim_width = brim_width * ( - 1 );
new_val = 0 ;
}
new_conf . set_key_value ( "brim_width" , new ConfigOptionFloat ( new_val ));
}
else { //(opt_key == "support")
const wxString & selection = boost :: any_cast < wxString > ( value );
auto support_material = selection == _ ( "None" ) ? false : true ;
new_conf . set_key_value ( "support_material" , new ConfigOptionBool ( support_material ));
if ( selection == _ ( "Everywhere" ))
new_conf . set_key_value ( "support_material_buildplate_only" , new ConfigOptionBool ( false ));
else if ( selection == _ ( "Support on build plate only" ))
new_conf . set_key_value ( "support_material_buildplate_only" , new ConfigOptionBool ( true ));
}
tab_print -> load_config ( new_conf );
}
tab_print -> update_dirty ();
};
Option option = m_og -> get_option ( "fill_density" );
option . opt . sidetext = "" ;
option . opt . full_width = true ;
m_og -> append_single_option_line ( option );
ConfigOptionDef def ;
def . label = L ( "Support" );
def . type = coStrings ;
def . gui_type = "select_open" ;
def . tooltip = L ( "Select what kind of support do you need" );
def . enum_labels . push_back ( L ( "None" ));
def . enum_labels . push_back ( L ( "Support on build plate only" ));
def . enum_labels . push_back ( L ( "Everywhere" ));
std :: string selection = ! config -> opt_bool ( "support_material" ) ?
"None" :
config -> opt_bool ( "support_material_buildplate_only" ) ?
"Support on build plate only" :
"Everywhere" ;
def . default_value = new ConfigOptionStrings { selection };
option = Option ( def , "support" );
option . opt . full_width = true ;
m_og -> append_single_option_line ( option );
m_brim_width = config -> opt_float ( "brim_width" );
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 = "" ;
def . default_value = new ConfigOptionBool { m_brim_width > 0.0 ? true : false };
option = Option ( def , "brim" );
m_og -> append_single_option_line ( option );
Line line = { "" , "" };
2018-10-31 12:56:08 +01:00
line . widget = [ 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 ());
g_on_request_update_callback . call ();
}
}));
return sizer ;
};
m_og -> append_line ( line );
}
2018-10-24 12:57:23 +02:00
void FreqChangedParams :: Show ( const bool show )
{
bool is_wdb_shown = m_wiping_dialog_button -> IsShown ();
m_og -> sizer -> Show ( show );
// correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden
if ( show && ! is_wdb_shown )
m_wiping_dialog_button -> Hide ();
}
2018-09-17 12:15:11 +02:00
// Sidebar / private
struct Sidebar :: priv
{
2018-10-04 11:12:55 +02:00
Plater * plater ;
2018-09-17 12:15:11 +02:00
wxScrolledWindow * scrolled ;
wxFlexGridSizer * sizer_presets ;
PresetComboBox * combo_print ;
std :: vector < PresetComboBox *> combos_filament ;
wxBoxSizer * sizer_filaments ;
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_print; // XXX: remove
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 ()
{
2018-10-31 16:22:36 +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-09 18:39:07 +01:00
wxWindowUpdateLocker noUpdates_scrolled ( scrolled );
// scrolled->Freeze();
for ( size_t i = 0 ; i < 4 ; ++ i ) {
if ( sizer_presets -> IsShown ( i ) == showSLA )
sizer_presets -> Show ( i , ! showSLA );
}
2018-10-10 13:53:45 +02:00
2018-11-09 18:39:07 +01:00
for ( size_t i = 4 ; i < 6 ; ++ i ) {
if ( sizer_presets -> IsShown ( i ) != showSLA )
sizer_presets -> Show ( i , showSLA );
}
2018-10-10 13:53:45 +02:00
2018-11-09 18:39:07 +01:00
if ( frequently_changed_parameters -> IsShown () == showSLA )
frequently_changed_parameters -> Show ( ! showSLA );
2018-10-10 13:53:45 +02:00
2018-11-09 18:39:07 +01:00
scrolled -> Layout ();
// scrolled->Thaw();
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 )
: wxPanel ( parent ), p ( new priv ( parent ))
2018-09-17 12:15:11 +02:00
{
2018-11-09 18:39:07 +01:00
p -> scrolled = new wxScrolledWindow ( this , wxID_ANY , wxDefaultPosition , wxSize ( 400 , - 1 ));
p -> scrolled -> SetScrollbars ( 0 , 1 , 1 , 1 );
// Sizer in the scrolled area
auto * scrolled_sizer = new wxBoxSizer ( wxVERTICAL );
p -> scrolled -> SetSizer ( scrolled_sizer );
2018-09-17 12:15:11 +02:00
// The preset chooser
p -> sizer_presets = new wxFlexGridSizer ( 4 , 2 , 1 , 2 );
p -> sizer_presets -> AddGrowableCol ( 1 , 1 );
2018-10-10 13:53:45 +02:00
p -> sizer_presets -> SetFlexibleDirection ( wxBOTH );
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 ) {
2018-10-01 15:09:31 +02:00
auto * text = new wxStaticText ( p -> scrolled , wxID_ANY , label );
text -> SetFont ( wxGetApp (). small_font ());
* combo = new PresetComboBox ( p -> scrolled , preset_type );
2018-09-17 12:15:11 +02:00
auto * sizer_presets = this -> p -> sizer_presets ;
auto * sizer_filaments = this -> p -> sizer_filaments ;
sizer_presets -> Add ( text , 0 , wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT , 4 );
if ( ! filament ) {
sizer_presets -> Add ( * combo , 1 , wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM , 1 );
} else {
sizer_filaments -> Add ( * combo , 1 , 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 );
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_material , _ ( L ( "SLA material" )), Preset :: TYPE_SLA_MATERIAL , false );
init_combo ( & p -> combo_printer , _ ( L ( "Printer" )), Preset :: TYPE_PRINTER , false );
2018-10-04 16:43:10 +02:00
// calculate width of the preset labels
2018-10-01 15:09:31 +02:00
p -> sizer_presets -> Layout ();
2018-10-04 16:43:10 +02:00
const wxArrayInt & ar = p -> sizer_presets -> GetColWidths ();
int label_width = ar . IsEmpty () ? 100 : ar . front () - 4 ;
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
2018-10-04 16:43:10 +02:00
p -> frequently_changed_parameters = new FreqChangedParams ( p -> scrolled , label_width );
p -> sizer_params -> Add ( p -> frequently_changed_parameters -> get_sizer (), 0 , wxEXPAND | wxBOTTOM | wxLEFT , 2 );
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 );
p -> sizer_params -> Add ( p -> object_list -> get_sizer (), 1 , wxEXPAND | wxTOP , 20 );
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 ();
2018-10-04 16:43:10 +02:00
p -> sizer_params -> Add ( p -> object_manipulation -> get_sizer (), 0 , wxEXPAND | wxLEFT | wxTOP , 20 );
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 ();
p -> sizer_params -> Add ( p -> object_settings -> get_sizer (), 0 , wxEXPAND | wxLEFT | wxTOP , 20 );
2018-09-17 12:15:11 +02:00
// Buttons in the scrolled area
wxBitmap arrow_up ( GUI :: from_u8 ( Slic3r :: var ( "brick_go.png" )), wxBITMAP_TYPE_PNG );
2018-10-01 15:09:31 +02:00
p -> btn_send_gcode = new wxButton ( p -> scrolled , wxID_ANY , _ ( L ( "Send to printer" )));
2018-09-17 12:15:11 +02:00
p -> btn_send_gcode -> SetBitmap ( arrow_up );
p -> btn_send_gcode -> Hide ();
auto * btns_sizer_scrolled = new wxBoxSizer ( wxHORIZONTAL );
btns_sizer_scrolled -> Add ( p -> btn_send_gcode );
// 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
scrolled_sizer -> Add ( p -> sizer_presets , 0 , wxEXPAND | wxLEFT , 2 );
2018-10-01 15:09:31 +02:00
scrolled_sizer -> Add ( p -> sizer_params , 1 , wxEXPAND );
scrolled_sizer -> Add ( p -> object_info , 0 , wxEXPAND | wxTOP | wxLEFT , 20 );
2018-09-17 12:15:11 +02:00
scrolled_sizer -> Add ( btns_sizer_scrolled , 0 , wxEXPAND , 0 );
2018-10-01 15:09:31 +02:00
scrolled_sizer -> Add ( p -> sliced_info , 0 , wxEXPAND | wxTOP | wxLEFT , 20 );
2018-09-17 12:15:11 +02:00
// Buttons underneath the scrolled area
p -> btn_export_gcode = new wxButton ( this , wxID_ANY , _ ( L ( "Export G-code…" )));
2018-10-01 15:09:31 +02:00
p -> btn_export_gcode -> SetFont ( wxGetApp (). bold_font ());
2018-09-17 12:15:11 +02:00
p -> btn_reslice = new wxButton ( this , wxID_ANY , _ ( L ( "Slice now" )));
2018-10-01 15:09:31 +02:00
p -> btn_reslice -> SetFont ( wxGetApp (). bold_font ());
2018-09-17 12:15:11 +02:00
auto * btns_sizer = new wxBoxSizer ( wxVERTICAL );
2018-10-01 15:09:31 +02:00
btns_sizer -> Add ( p -> btn_reslice , 0 , wxEXPAND | wxTOP , 5 );
btns_sizer -> Add ( p -> btn_export_gcode , 0 , wxEXPAND | wxTOP , 5 );
2018-09-17 12:15:11 +02:00
auto * sizer = new wxBoxSizer ( wxVERTICAL );
2018-10-01 15:09:31 +02:00
sizer -> Add ( p -> scrolled , 1 , wxEXPAND | wxTOP , 5 );
sizer -> Add ( btns_sizer , 0 , wxEXPAND | wxLEFT , 20 );
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 (); });
p -> btn_reslice -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { p -> plater -> reslice (); });
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 ) {
* combo = new PresetComboBox ( p -> scrolled , Slic3r :: Preset :: TYPE_FILAMENT );
// # copy icons from first choice
// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
( * combo ) -> set_extruder_idx ( extr_idx );
auto /***/ sizer_filaments = this -> p -> sizer_filaments ;
sizer_filaments -> Add ( * combo , 1 , wxEXPAND | wxBOTTOM , 1 );
}
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 ();
}
}
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 ;
2018-09-17 12:15:11 +02:00
switch ( preset_type ) {
case Preset :: TYPE_FILAMENT :
if ( p -> combos_filament . size () == 1 ) {
// Single filament printer, synchronize the filament presets.
2018-10-31 16:22:36 +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
}
for ( size_t i = 0 ; i < p -> combos_filament . size (); i ++ ) {
2018-10-31 16:22:36 +01: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 ;
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 ;
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 :
{
PrinterTechnology printer_technology = preset_bundle . printers . get_edited_preset (). printer_technology ();
// Update the print choosers to only contain the compatible presets, update the dirty flags.
if ( printer_technology == ptFFF )
preset_bundle . prints . update_platter_ui ( p -> combo_print );
else
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 ( printer_technology == ptFFF ) {
for ( size_t i = 0 ; i < p -> combos_filament . size (); ++ i )
preset_bundle . update_platter_filament_ui ( i , p -> combos_filament [ i ]);
}
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
}
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 ;
}
2018-10-05 23:29:15 +02:00
ConfigOptionsGroup * Sidebar :: og_freq_chng_params ()
{
return p -> frequently_changed_parameters -> get_og ();
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-09 18:39:07 +01:00
void Sidebar :: show_info_sizer ( const bool show )
2018-10-05 23:29:15 +02:00
{
2018-11-01 12:33:56 +01:00
p -> object_info -> show_sizer ( show );
2018-11-09 18:39:07 +01:00
p -> scrolled -> Layout ();
2018-11-01 12:33:56 +01:00
}
2018-11-09 18:39:07 +01:00
void Sidebar :: update_info_sizer ()
2018-11-01 12:33:56 +01:00
{
2018-11-09 18:39:07 +01:00
wxWindowUpdateLocker freeze_guard ( p -> scrolled );
2018-11-01 12:33:56 +01:00
2018-11-09 18:39:07 +01:00
if ( /*obj_idx < 0 || */ ! p -> plater -> is_single_full_object_selection ()) {
2018-11-01 12:33:56 +01:00
p -> object_info -> Show ( false );
2018-11-09 18:39:07 +01:00
p -> scrolled -> Layout ();
2018-11-01 12:33:56 +01:00
return ;
}
2018-11-09 18:39:07 +01:00
int obj_idx = p -> plater -> get_selected_object_idx ();
2018-11-01 12:33:56 +01:00
const ModelObject * model_object = ( * wxGetApp (). model_objects ())[ obj_idx ];
const ModelInstance * model_instance = ! model_object -> instances . empty () ? model_object -> instances . front () : nullptr ;
2018-11-01 14:02:38 +01:00
auto size = model_object -> instance_bounding_box ( 0 ). 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 ())));
auto & stats = model_object -> volumes [ 0 ] -> mesh . stl . stats ;
auto sf = model_instance -> get_scaling_factor ();
2018-11-01 14:02:38 +01:00
p -> object_info -> info_volume -> SetLabel ( wxString :: Format ( "%.2f" , stats . volume * sf ( 0 ) * sf ( 1 ) * sf ( 2 )));
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 );
tooltip += wxString :: Format ( _ ( L ( ": \n %d degenerate facets, %d edges fixed, %d facets removed, "
"%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 {
p -> object_info -> info_manifold -> SetLabel ( L ( "Yes" ));
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 );
p -> scrolled -> Layout ();
2018-10-31 12:26:57 +01:00
}
void Sidebar :: show_sliced_info_sizer ( const bool show )
{
2018-11-09 18:39:07 +01:00
wxWindowUpdateLocker freeze_guard ( p -> scrolled );
2018-11-01 12:33:56 +01:00
2018-10-31 12:26:57 +01:00
p -> sliced_info -> Show ( show );
if ( show ) {
const PrintStatistics & ps = p -> plater -> print (). print_statistics ();
const bool is_wipe_tower = ps . total_wipe_tower_filament > 0 ;
wxString info_text = is_wipe_tower ?
wxString :: Format ( "%.2f (%.2f %s + %.2f %s)" , ps . total_used_filament / 1000 ,
( ps . total_used_filament - ps . total_wipe_tower_filament ) / 1000 , _ ( L ( "objects" )),
ps . total_wipe_tower_filament / 1000 , _ ( L ( "wipe tower" ))) :
wxString :: Format ( "%.2f" , ps . total_used_filament / 1000 );
p -> sliced_info -> SetTextAndShow ( siFilament_m , info_text );
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 ));
info_text = is_wipe_tower ?
wxString :: Format ( "%.2f (%.2f %s + %.2f %s)" , ps . total_cost ,
( ps . total_cost - ps . total_wipe_tower_cost ), _ ( L ( "objects" )),
ps . total_wipe_tower_cost , _ ( L ( "wipe tower" ))) :
wxString :: Format ( "%.2f" , ps . total_cost );
p -> sliced_info -> SetTextAndShow ( siCost , info_text );
p -> sliced_info -> SetTextAndShow ( siTimeNormal , ps . estimated_normal_print_time );
p -> sliced_info -> SetTextAndShow ( siTimeSilent , ps . estimated_silent_print_time );
// 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 -> print (). wipe_tower_data (). number_of_toolchanges ) : "N/A" );
}
p -> scrolled -> Layout ();
2018-10-05 23:29:15 +02:00
}
void Sidebar :: show_buttons ( const bool show )
{
p -> btn_reslice -> Show ( show );
for ( size_t i = 0 ; i < wxGetApp (). tab_panel () -> GetPageCount (); ++ i ) {
TabPrinter * tab = dynamic_cast < TabPrinter *> ( wxGetApp (). tab_panel () -> GetPage ( i ));
if ( ! tab )
continue ;
if ( wxGetApp (). preset_bundle -> printers . get_selected_preset (). printer_technology () == ptFFF ) {
p -> btn_send_gcode -> Show ( show && ! tab -> m_config -> opt_string ( "print_host" ). empty ());
}
break ;
}
}
2018-09-17 12:15:11 +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 );
}
2018-10-15 10:53:47 +02:00
void Sidebar :: show_button ( ButtonAction but_action , bool show )
{
switch ( but_action )
{
case baReslice :
p -> btn_reslice -> Show ( show );
break ;
case baExportGcode :
p -> btn_export_gcode -> Show ( show );
break ;
case baSendGcode :
p -> btn_send_gcode -> Show ( show );
break ;
default :
break ;
}
}
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
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 :
PlaterDropTarget ( Plater * plater ) : plater ( plater ) {}
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 ) {
fs :: path path ( filename );
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
2018-10-17 14:26:13 +02:00
wxMenu object_menu ;
2018-10-17 12:17:25 +02:00
2018-09-17 12:15:11 +02:00
// Data
Slic3r :: DynamicPrintConfig * config ;
Slic3r :: Print print ;
2018-11-08 20:18:40 +01:00
Slic3r :: SLAPrint sla_print ;
2018-09-17 12:15:11 +02:00
Slic3r :: Model model ;
Slic3r :: GCodePreviewData gcode_preview_data ;
// GUI elements
wxNotebook * notebook ;
Sidebar * sidebar ;
wxGLCanvas * canvas3D ; // TODO: Use GLCanvas3D when we can
2018-10-04 16:43:10 +02:00
Preview * preview ;
2018-10-17 11:12:38 +02:00
BackgroundSlicingProcess background_process ;
2018-11-12 14:52:52 +01:00
std :: atomic < bool > arranging ;
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
static const std :: regex pattern_bundle ;
static const std :: regex pattern_3mf ;
static const std :: regex pattern_zip_amf ;
priv ( Plater * q , MainFrame * main_frame );
void update ( bool force_autocenter = false );
2018-10-17 12:59:58 +02:00
void select_view ( const std :: string & direction );
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-09-17 12:15:11 +02:00
std :: vector < size_t > load_files ( const std :: vector < fs :: path > & input_files );
2018-10-01 16:48:08 +02:00
std :: vector < size_t > load_model_objects ( const ModelObjectPtrs & model_objects );
2018-10-04 11:12:55 +02:00
std :: unique_ptr < CheckboxFileDialog > get_export_file ( GUI :: FileType file_type );
2018-10-17 09:30:07 +02:00
const GLCanvas3D :: Selection & get_selection () const ;
GLCanvas3D :: Selection & get_selection ();
int get_selected_object_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-10-08 19:14:55 +02:00
void remove ( 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 ();
void split_object ();
2018-10-24 12:55:38 +02:00
void split_volume ();
2018-10-09 09:35:19 +02:00
void schedule_background_process ();
void async_apply_config ();
void start_background_process ();
void reload_from_disk ();
void export_object_stl ();
2018-11-07 13:40:24 +01:00
void fix_through_netfabb ( const int obj_idx );
2018-09-17 12:15:11 +02:00
2018-10-08 19:55:30 +02:00
void on_notebook_changed ( wxBookCtrlEvent & );
void on_select_preset ( wxCommandEvent & );
2018-10-23 15:27:31 +02:00
void on_progress_event ( wxCommandEvent & );
2018-10-08 19:55:30 +02:00
void on_update_print_preview ( wxCommandEvent & );
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-25 12:10:35 +02:00
void on_schedule_background_process ( SimpleEvent & );
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_cut ( SimpleEvent & );
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_viewport_changed ( SimpleEvent & );
void on_right_click ( Vec2dEvent & );
void on_model_update ( SimpleEvent & );
void on_wipetower_moved ( Vec3dEvent & );
void on_enable_action_buttons ( Event < bool >& );
void on_update_geometry ( Vec3dsEvent < 2 >& );
2018-10-17 12:17:25 +02:00
private :
bool init_object_menu ();
2018-10-18 09:27:37 +02:00
bool can_delete_object () const ;
bool can_increase_instances () const ;
bool can_decrease_instances () const ;
2018-10-24 12:55:38 +02:00
bool can_split_to_objects () const ;
bool can_split_to_volumes () const ;
2018-10-18 09:27:37 +02:00
bool can_cut_object () const ;
bool layers_height_allowed () const ;
2018-10-18 15:09:41 +02:00
bool can_delete_all () const ;
bool can_arrange () const ;
bool can_mirror () const ;
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 );
2018-09-17 12:15:11 +02:00
Plater :: priv :: priv ( Plater * q , MainFrame * main_frame ) :
q ( q ),
main_frame ( main_frame ),
config ( Slic3r :: DynamicPrintConfig :: new_from_defaults_keys ({
"bed_shape" , "complete_objects" , "extruder_clearance_radius" , "skirts" , "skirt_distance" ,
"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" ,
2018-11-08 20:18:40 +01:00
"extruder_colour" , "filament_colour" , "max_print_height" , "printer_model" , "printer_technology"
2018-09-17 12:15:11 +02:00
})),
notebook ( new wxNotebook ( q , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxNB_BOTTOM )),
sidebar ( new Sidebar ( q )),
2018-11-08 16:18:05 +01:00
canvas3D ( GLCanvas3DManager :: create_wxglcanvas ( notebook ))
2018-09-17 12:15:11 +02:00
{
2018-11-08 20:18:40 +01:00
background_process . set_fff_print ( & print );
background_process . set_sla_print ( & sla_print );
2018-09-17 12:15:11 +02:00
background_process . set_gcode_preview_data ( & gcode_preview_data );
background_process . set_sliced_event ( EVT_SLICING_COMPLETED );
background_process . set_finished_event ( EVT_PROCESS_COMPLETED );
2018-11-08 20:18:40 +01:00
// Default printer technology for default config.
background_process . select_technology ( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ());
2018-10-23 15:27:31 +02:00
// Register progress callback from the Print class to the Platter.
2018-10-31 12:56:08 +01:00
print . set_status_callback ([ this ]( int percent , const std :: string & message ) {
2018-10-23 15:27:31 +02:00
wxCommandEvent event ( EVT_PROGRESS_BAR );
event . SetInt ( percent );
event . SetString ( message );
wxQueueEvent ( this -> q , event . Clone ());
});
this -> q -> Bind ( EVT_PROGRESS_BAR , & priv :: on_progress_event , this );
2018-09-17 12:15:11 +02:00
_3DScene :: add_canvas ( canvas3D );
2018-10-01 16:48:08 +02:00
_3DScene :: allow_multisample ( canvas3D , GLCanvas3DManager :: can_multisample ());
2018-09-17 12:15:11 +02:00
notebook -> AddPage ( canvas3D , _ ( L ( "3D" )));
preview = new GUI :: Preview ( notebook , config , & print , & gcode_preview_data );
// XXX: If have OpenGL
_3DScene :: enable_picking ( canvas3D , true );
_3DScene :: enable_moving ( canvas3D , true );
// XXX: more config from 3D.pm
2018-10-01 16:48:08 +02:00
_3DScene :: set_model ( canvas3D , & model );
_3DScene :: set_print ( canvas3D , & print );
_3DScene :: set_config ( canvas3D , config );
2018-09-17 12:15:11 +02:00
_3DScene :: enable_gizmos ( canvas3D , true );
_3DScene :: enable_toolbar ( canvas3D , true );
_3DScene :: enable_shader ( canvas3D , true );
_3DScene :: enable_force_zoom_to_bed ( canvas3D , true );
2018-10-23 15:27:31 +02:00
this -> background_process_timer . SetOwner ( this -> q , 0 );
2018-10-31 12:56:08 +01:00
this -> q -> Bind ( wxEVT_TIMER , [ this ]( wxTimerEvent & evt ) { this -> async_apply_config (); });
2018-09-17 12:15:11 +02:00
auto * bed_shape = config -> opt < ConfigOptionPoints > ( "bed_shape" );
_3DScene :: set_bed_shape ( canvas3D , bed_shape -> values );
_3DScene :: zoom_to_bed ( canvas3D );
preview -> set_bed_shape ( bed_shape -> values );
update ();
auto * hsizer = new wxBoxSizer ( wxHORIZONTAL );
hsizer -> Add ( notebook , 1 , wxEXPAND | wxTOP , 1 );
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:
// Notebook page change event
notebook -> Bind ( wxEVT_NOTEBOOK_PAGE_CHANGED , & priv :: on_notebook_changed , this );
// 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-09-17 12:15:11 +02:00
// 3DScene events:
2018-10-25 12:10:35 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS , & priv :: on_schedule_background_process , this );
2018-10-09 17:14:59 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_OBJECT_SELECT , & priv :: on_object_select , this );
2018-10-08 19:55:30 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_VIEWPORT_CHANGED , & priv :: on_viewport_changed , this );
canvas3D -> Bind ( EVT_GLCANVAS_RIGHT_CLICK , & priv :: on_right_click , this );
canvas3D -> Bind ( EVT_GLCANVAS_MODEL_UPDATE , & priv :: on_model_update , this );
2018-10-09 17:14:59 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_REMOVE_OBJECT , [ q ]( SimpleEvent & ) { q -> remove_selected (); });
canvas3D -> Bind ( EVT_GLCANVAS_ARRANGE , [ this ]( SimpleEvent & ) { arrange (); });
2018-10-18 15:09:41 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_INCREASE_INSTANCES , [ q ]( Event < int > & evt ) { evt . data == 1 ? q -> increase_instances () : q -> decrease_instances (); });
2018-10-09 17:14:59 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_INSTANCE_MOVED , [ this ]( SimpleEvent & ) { update (); });
2018-10-08 19:55:30 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_WIPETOWER_MOVED , & priv :: on_wipetower_moved , this );
canvas3D -> Bind ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , & priv :: on_enable_action_buttons , this );
canvas3D -> Bind ( EVT_GLCANVAS_UPDATE_GEOMETRY , & priv :: on_update_geometry , this );
// 3DScene/Toolbar:
2018-09-17 12:15:11 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_ADD , & priv :: on_action_add , this );
2018-10-04 11:12:55 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_DELETE , [ q ]( SimpleEvent & ) { q -> remove_selected (); } );
canvas3D -> Bind ( EVT_GLTOOLBAR_DELETE_ALL , [ this ]( SimpleEvent & ) { reset (); });
2018-10-11 10:03:38 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_ARRANGE , [ this ]( SimpleEvent & ) { arrange (); });
2018-10-18 15:09:41 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_MORE , [ q ]( SimpleEvent & ) { q -> increase_instances (); });
canvas3D -> Bind ( EVT_GLTOOLBAR_FEWER , [ q ]( SimpleEvent & ) { q -> decrease_instances (); });
2018-10-24 12:55:38 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_SPLIT_OBJECTS , & priv :: on_action_split_objects , this );
canvas3D -> Bind ( EVT_GLTOOLBAR_SPLIT_VOLUMES , & priv :: on_action_split_volumes , this );
2018-10-04 11:12:55 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_CUT , & priv :: on_action_cut , this );
canvas3D -> Bind ( EVT_GLTOOLBAR_LAYERSEDITING , & priv :: on_action_layersediting , this );
2018-10-08 19:55:30 +02:00
// Preview events:
2018-10-04 10:41:11 +02:00
preview -> get_wxglcanvas () -> Bind ( EVT_GLCANVAS_VIEWPORT_CHANGED , & priv :: on_viewport_changed , this );
2018-09-17 12:15:11 +02:00
q -> Bind ( EVT_SLICING_COMPLETED , & priv :: on_update_print_preview , this );
q -> Bind ( EVT_PROCESS_COMPLETED , & priv :: on_process_completed , this );
// Drop target:
q -> SetDropTarget ( new PlaterDropTarget ( q )); // if my understanding is right, wxWindow takes the owenership
update_ui_from_settings ();
q -> Layout ();
}
void Plater :: priv :: update ( bool force_autocenter )
{
2018-10-01 16:48:08 +02:00
wxWindowUpdateLocker freeze_guard ( q );
if ( get_config ( "autocenter" ) == "1" || force_autocenter ) {
// 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 );
}
_3DScene :: reload_scene ( canvas3D , false );
preview -> reset_gcode_preview_data ();
preview -> reload_print ();
2018-10-23 15:27:31 +02:00
this -> schedule_background_process ();
2018-09-17 12:15:11 +02:00
}
2018-10-17 12:59:58 +02:00
void Plater :: priv :: select_view ( const std :: string & direction )
{
int page_id = notebook -> GetSelection ();
if ( page_id != wxNOT_FOUND )
{
const wxString & page_text = notebook -> GetPageText ( page_id );
if ( page_text == _ ( L ( "3D" )))
_3DScene :: select_view ( canvas3D , direction );
else if ( page_text == _ ( L ( "Preview" )))
preview -> select_view ( direction );
}
}
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;
// }
}
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-09-17 12:15:11 +02:00
std :: vector < size_t > Plater :: priv :: load_files ( const std :: vector < fs :: path > & input_files )
{
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 ;
}
}
}
const auto loading = _ ( L ( "Loading…" ));
wxProgressDialog dlg ( loading , loading );
dlg . Pulse ();
auto * new_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 ();
const auto dlg_info = wxString :: Format ( _ ( L ( "Processing input file %s \n " )), filename . string ());
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 );
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 );
if ( ! config_loaded . empty ()) {
2018-11-07 14:57:50 +01:00
// 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 );
}
}
if ( ! config . empty ()) {
Preset :: normalize ( config );
wxGetApp (). preset_bundle -> load_config_model ( filename . string (), std :: move ( config ));
2018-11-07 14:57:50 +01:00
wxGetApp (). load_current_presets ();
2018-11-06 19:09:54 +01:00
}
2018-09-17 12:15:11 +02:00
wxGetApp (). app_config -> update_config_dir ( path . parent_path (). string ());
} else {
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
}
}
catch ( const std :: runtime_error & e ) {
GUI :: show_error ( q , e . what ());
continue ;
}
// The model should now be initialized
if ( model . looks_like_multipart_object ()) {
wxMessageDialog dlg ( q , _ ( L (
"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 "
)), _ ( L ( "Multi-part object detected" )), wxICON_WARNING | wxYES | wxNO );
if ( dlg . ShowModal () == wxID_YES ) {
model . convert_multipart_object ( nozzle_dmrs -> values . size ());
}
}
if ( type_3mf ) {
for ( ModelObject * model_object : model . objects ) {
model_object -> center_around_origin ();
2018-10-30 16:03:03 +01:00
model_object -> ensure_on_bed ();
2018-09-17 12:15:11 +02:00
}
}
if ( one_by_one ) {
2018-10-01 16:48:08 +02:00
auto loaded_idxs = load_model_objects ( model . objects );
obj_idxs . insert ( obj_idxs . end (), loaded_idxs . begin (), loaded_idxs . end ());
2018-09-17 12:15:11 +02:00
} 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 );
}
}
}
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
}
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-10-01 16:48:08 +02:00
return obj_idxs ;
2018-09-17 12:15:11 +02:00
}
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-11-02 12:11:28 +01:00
#if !ENABLE_MODELVOLUME_TRANSFORM
2018-10-04 11:12:55 +02:00
const Vec3d bed_center = Slic3r :: to_3d ( bed_shape . center (). cast < double > (), 0.0 );
2018-11-02 12:11:28 +01:00
#endif // !ENABLE_MODELVOLUME_TRANSFORM
2018-10-04 11:12:55 +02:00
const Vec3d bed_size = Slic3r :: to_3d ( bed_shape . size (). cast < double > (), 1.0 );
2018-09-17 12:15:11 +02:00
bool need_arrange = false ;
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
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 ()) {
2018-09-17 12:15:11 +02:00
// if object has no defined position(s) we need to rearrange everything after loading
need_arrange = true ;
// add a default instance and center object around origin
object -> center_around_origin (); // also aligns object to Z = 0
2018-11-02 12:11:28 +01:00
ModelInstance * instance = object -> add_instance ();
#if ENABLE_MODELVOLUME_TRANSFORM
instance -> set_offset ( Slic3r :: to_3d ( bed_shape . center (). cast < double > (), - object -> origin_translation ( 2 )));
#else
2018-09-17 12:15:11 +02:00
instance -> set_offset ( bed_center );
2018-11-02 12:11:28 +01:00
#endif // ENABLE_MODELVOLUME_TRANSFORM
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
// const Vec3d inverse = ratio.cwiseInverse();
// object->scale(inverse);
object -> scale ( ratio . cwiseInverse ());
scaled_down = true ;
} else if ( max_ratio > 5 ) {
const Vec3d inverse = ratio . cwiseInverse ();
for ( ModelInstance * instance : model_object -> instances ) {
instance -> set_scaling_factor ( inverse );
}
}
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
}
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-26 09:50:28 +02:00
#if !ENABLE_MODIFIED_CAMERA_TARGET
2018-10-01 16:48:08 +02:00
_3DScene :: zoom_to_volumes ( canvas3D );
2018-10-26 09:50:28 +02:00
#endif // !ENABLE_MODIFIED_CAMERA_TARGET
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
}
2018-10-04 11:12:55 +02:00
std :: unique_ptr < CheckboxFileDialog > Plater :: priv :: get_export_file ( GUI :: FileType file_type )
{
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 :
wildcard = file_wildcards [ file_type ];
2018-10-04 11:12:55 +02:00
break ;
2018-10-23 15:27:31 +02:00
// FT_GCODE
2018-10-04 11:12:55 +02:00
default :
wildcard = file_wildcards [ FT_MODEL ];
break ;
}
fs :: path output_file ( print . output_filepath ( std :: string ()));
switch ( file_type ) {
case FT_STL : output_file . replace_extension ( "stl" ); break ;
case FT_AMF : output_file . replace_extension ( "zip.amf" ); break ; // XXX: Problem on OS X with double extension?
case FT_3MF : output_file . replace_extension ( "3mf" ); break ;
default : break ;
}
wxGetApp (). preset_bundle -> export_selections ( print . placeholder_parser ());
auto dlg = Slic3r :: make_unique < CheckboxFileDialog > ( q ,
_ ( L ( "Export print config" )),
true ,
_ ( L ( "Save file as:" )),
output_file . parent_path (). string (),
output_file . filename (). string (),
wildcard ,
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if ( dlg -> ShowModal () != wxID_OK ) {
return nullptr ;
}
fs :: path path ( dlg -> GetPath ());
wxGetApp (). app_config -> update_last_output_dir ( path . parent_path (). string ());
return dlg ;
}
2018-10-17 09:30:07 +02:00
const GLCanvas3D :: Selection & Plater :: priv :: get_selection () const
{
return _3DScene :: get_canvas ( canvas3D ) -> get_selection ();
}
GLCanvas3D :: Selection & Plater :: priv :: get_selection ()
{
return _3DScene :: get_canvas ( canvas3D ) -> get_selection ();
}
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
void Plater :: priv :: selection_changed ()
{
2018-10-18 09:27:37 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "delete" , can_delete_object ());
_3DScene :: enable_toolbar_item ( canvas3D , "more" , can_increase_instances ());
_3DScene :: enable_toolbar_item ( canvas3D , "fewer" , can_decrease_instances ());
2018-10-24 12:55:38 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "splitobjects" , can_split_to_objects ());
_3DScene :: enable_toolbar_item ( canvas3D , "splitvolumes" , can_split_to_volumes ());
2018-10-18 09:27:37 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "cut" , can_cut_object ());
_3DScene :: enable_toolbar_item ( canvas3D , "layersediting" , layers_height_allowed ());
2018-10-25 12:36:10 +02:00
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
_3DScene :: render ( canvas3D );
2018-10-04 11:12:55 +02:00
2018-11-09 18:39:07 +01:00
sidebar -> update_info_sizer ();
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-08 19:14:55 +02:00
// Enable/disable buttons depending on whether there are any objects on the platter.
2018-10-18 15:09:41 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "deleteall" , can_delete_all ());
_3DScene :: enable_toolbar_item ( canvas3D , "arrange" , can_arrange ());
2018-10-08 19:14: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?
const bool model_fits = _3DScene :: check_volumes_outside_state ( canvas3D , config ) == ModelInstance :: PVS_Inside ;
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
}
void Plater :: priv :: remove ( size_t obj_idx )
{
// Prevent toolpaths preview from rendering while we modify the Print object
preview -> set_enabled ( false );
2018-10-22 15:18:56 +02:00
if ( _3DScene :: is_layers_editing_enabled ( canvas3D ))
_3DScene :: enable_layers_editing ( canvas3D , false );
2018-10-08 19:14:55 +02:00
model . delete_object ( obj_idx );
2018-10-23 15:27:31 +02:00
// print.delete_object(obj_idx);
2018-10-08 19:14:55 +02:00
// 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
object_list_changed ();
update ();
2018-10-04 11:12:55 +02:00
}
void Plater :: priv :: reset ()
{
// Prevent toolpaths preview from rendering while we modify the Print object
preview -> set_enabled ( false );
2018-10-22 15:18:56 +02:00
if ( _3DScene :: is_layers_editing_enabled ( canvas3D ))
_3DScene :: enable_layers_editing ( canvas3D , false );
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 ();
2018-10-08 19:14:55 +02:00
object_list_changed ();
2018-10-04 11:12:55 +02:00
update ();
}
2018-10-18 15:09:41 +02:00
void Plater :: priv :: mirror ( Axis axis )
2018-10-09 09:35:19 +02:00
{
2018-10-18 15:50:51 +02:00
_3DScene :: mirror_selection ( canvas3D , axis );
2018-10-09 09:35:19 +02:00
}
void Plater :: priv :: arrange ()
{
2018-11-12 14:52:52 +01:00
// don't do anything if currently arranging. Then this is a re-entrance
if ( arranging . load ()) return ;
// Guard the arrange process
arranging . store ( true );
_3DScene :: enable_toolbar_item ( canvas3D , "arrange" , can_arrange ());
this -> background_process . stop ();
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 );
statusbar () -> set_status_text ( msg );
// ok, this is dangerous, but we are protected by the atomic flag
// 'arranging'. This call is needed for the cancel button to work.
wxYieldIfNeeded ();
};
statusbar () -> set_cancel_callback ([ this , statusfn ](){
arranging . store ( false );
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);
auto min_obj_distance = static_cast < coord_t > ( dist / 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 )));
statusfn ( 0 , arrangestr );
try {
arr :: BedShapeHint hint ;
// TODO: from Sasha from GUI or
hint . type = arr :: BedShapeType :: WHO_KNOWS ;
arr :: arrange ( model ,
min_obj_distance ,
bed ,
hint ,
false , // create many piles not just one pile
[ statusfn ]( unsigned st ) { statusfn ( st , arrangestr ); },
[ this ] () { return ! arranging . load (); });
} catch ( std :: exception & /*e*/ ) {
GUI :: show_error ( this -> q , L ( "Could not arrange model objects! "
"Some geometries may be invalid." ));
}
statusfn ( 0 , L ( "Arranging done." ));
statusbar () -> set_range ( prev_range );
statusbar () -> set_cancel_callback (); // remove cancel button
arranging . store ( false );
2018-11-02 19:49:40 +01:00
this -> schedule_background_process ();
2018-10-11 10:03:38 +02:00
2018-11-12 14:52:52 +01:00
// ignore arrange failures on purpose: user has visual feedback and we
// don't need to warn him when parts don't fit in print bed
2018-10-11 10:03:38 +02:00
2018-11-12 14:52:52 +01:00
_3DScene :: enable_toolbar_item ( canvas3D , "arrange" , can_arrange ());
2018-10-11 10:03:38 +02:00
update ();
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 ;
}
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 ()
{
wxGetApp (). obj_list () -> split ( false );
}
2018-10-09 09:35:19 +02:00
void Plater :: priv :: schedule_background_process ()
{
2018-10-17 11:12:38 +02:00
// Trigger the timer event after 0.5s
this -> background_process_timer . Start ( 500 , wxTIMER_ONE_SHOT );
2018-10-09 09:35:19 +02:00
}
void Plater :: priv :: async_apply_config ()
{
2018-10-23 15:27:31 +02:00
DynamicPrintConfig config = wxGetApp (). preset_bundle -> full_config ();
2018-11-08 20:18:40 +01:00
auto printer_technology = config . opt_enum < PrinterTechnology > ( "printer_technology" );
2018-10-23 15:27:31 +02:00
BoundingBox bed_box_2D = get_extents ( Polygon :: new_scale ( 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_ ( 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-10-17 11:12:38 +02:00
// Apply new config to the possibly running background task.
2018-10-23 22:53:43 +02:00
Print :: ApplyStatus invalidated = this -> background_process . apply ( this -> q -> model (), std :: move ( 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.
if ( Slic3r :: _3DScene :: is_layers_editing_enabled ( this -> canvas3D ))
this -> canvas3D -> 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.
this -> gcode_preview_data . reset ();
2018-11-08 20:18:40 +01:00
if ( printer_technology == ptFFF ) {
if ( this -> preview != nullptr )
this -> preview -> reload_print ();
// We also need to reload 3D scene because of the wipe tower preview box
if ( this -> config -> opt_bool ( "wipe_tower" )) {
// std::vector<int> selections = this->collect_selections();
// Slic3r::_3DScene::set_objects_selections(this->canvas3D, selections);
// Slic3r::_3DScene::reload_scene(this->canvas3D, 1);
}
2018-10-17 11:12:38 +02:00
}
}
2018-10-23 22:53:43 +02:00
if ( invalidated != Print :: APPLY_STATUS_UNCHANGED && this -> get_config ( "background_processing" ) == "1" &&
2018-11-08 20:18:40 +01:00
this -> background_process . start ())
2018-10-31 12:56:08 +01:00
this -> statusbar () -> set_cancel_callback ([ this ]() {
2018-10-25 10:07:11 +02:00
this -> statusbar () -> set_status_text ( L ( "Cancelling" ));
this -> background_process . stop ();
});
2018-10-09 09:35:19 +02:00
}
void Plater :: priv :: start_background_process ()
{
2018-10-23 15:27:31 +02:00
if ( this -> background_process . running ())
return ;
// Don't start process thread if Print is not valid.
2018-11-08 20:18:40 +01:00
std :: string err = this -> background_process . validate ();
2018-10-18 18:06:40 +02:00
if ( ! err . empty ()) {
2018-10-23 15:27:31 +02:00
this -> statusbar () -> set_status_text ( err );
} else {
// Copy the names of active presets into the placeholder parser.
2018-11-08 20:18:40 +01:00
//FIXME how to generate a file name for the SLA printers?
2018-10-23 15:27:31 +02:00
wxGetApp (). preset_bundle -> export_selections ( this -> q -> print (). placeholder_parser ());
// Start the background process.
this -> background_process . start ();
}
2018-10-09 09:35:19 +02:00
}
void Plater :: priv :: reload_from_disk ()
{
// TODO
}
void Plater :: priv :: export_object_stl ()
{
// TODO
}
2018-11-07 13:40:24 +01:00
void Plater :: priv :: fix_through_netfabb ( const int obj_idx )
2018-10-09 09:35:19 +02:00
{
2018-11-07 13:40:24 +01:00
if ( obj_idx < 0 )
return ;
2018-10-18 18:06:40 +02:00
2018-11-07 13:40:24 +01:00
const auto model_object = model . objects [ obj_idx ];
Model model_fixed ; // = new Model();
fix_model_by_win10_sdk_gui ( * model_object , print , model_fixed );
auto new_obj_idxs = load_model_objects ( model_fixed . objects );
if ( new_obj_idxs . empty ())
return ;
2018-10-18 18:06:40 +02:00
2018-11-07 13:40:24 +01:00
for ( auto new_obj_idx : new_obj_idxs ) {
auto o = model . objects [ new_obj_idx ];
o -> clear_instances ();
for ( auto instance : model_object -> instances )
o -> add_instance ( * instance );
// o->invalidate_bounding_box();
2018-10-18 18:06:40 +02:00
2018-11-07 13:40:24 +01:00
if ( o -> volumes . size () == model_object -> volumes . size ()) {
for ( int i = 0 ; i < o -> volumes . size (); i ++ ) {
o -> volumes [ i ] -> config . apply ( model_object -> volumes [ i ] -> config );
2018-10-18 18:06:40 +02:00
}
}
2018-11-07 13:40:24 +01:00
// FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
2018-10-18 18:06:40 +02:00
}
2018-11-07 13:40:24 +01:00
remove ( obj_idx );
2018-10-09 09:35:19 +02:00
}
2018-09-17 12:15:11 +02:00
void Plater :: priv :: on_notebook_changed ( wxBookCtrlEvent & )
{
const auto current_id = notebook -> GetCurrentPage () -> GetId ();
if ( current_id == canvas3D -> GetId ()) {
if ( _3DScene :: is_reload_delayed ( canvas3D )) {
_3DScene :: reload_scene ( canvas3D , true );
}
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
_3DScene :: set_as_dirty ( canvas3D );
} else if ( current_id == preview -> GetId ()) {
preview -> reload_print ();
preview -> set_canvas_as_dirty ();
}
}
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
//! combo->GetStringSelection().ToStdString());
std :: string selected_string = combo -> GetString ( combo -> GetSelection ()). ToUTF8 (). data ();
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.
2018-10-09 12:41:05 +02:00
wxGetApp (). preset_bundle -> update_platter_filament_ui ( idx , combo );
2018-10-10 13:53:45 +02:00
}
else {
2018-10-09 12:41:05 +02:00
for ( Tab * tab : wxGetApp (). tabs_list ) {
if ( tab -> type () == preset_type ) {
2018-10-15 11:39:48 +02:00
tab -> select_preset ( selected_string );
2018-10-09 12:41:05 +02:00
break ;
}
}
2018-09-17 12:15:11 +02:00
}
2018-10-09 12:41:05 +02:00
// Synchronize config.ini with the current selections.
wxGetApp (). preset_bundle -> export_selections ( * wxGetApp (). app_config );
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-09-17 12:15:11 +02:00
}
2018-10-23 15:27:31 +02:00
void Plater :: priv :: on_progress_event ( wxCommandEvent & evt )
2018-10-09 09:35:19 +02:00
{
2018-10-23 15:27:31 +02:00
this -> statusbar () -> set_progress ( evt . GetInt ());
this -> statusbar () -> set_status_text ( evt . GetString () + wxString :: FromUTF8 ( "…" ));
2018-10-09 09:35:19 +02:00
}
2018-09-17 12:15:11 +02:00
void Plater :: priv :: on_update_print_preview ( wxCommandEvent & )
{
2018-10-18 18:06:40 +02:00
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:
2018-11-01 10:06:55 +01:00
// auto selections = collect_selections();
// _3DScene::set_objects_selections(canvas3D, selections);
// if (canvas3D)
// _3DScene::reload_scene(canvas3D, true);
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 ();
2018-10-25 10:07:11 +02:00
bool canceled = evt . GetInt () < 0 ;
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 ();
if ( ! success ) {
wxString message = evt . GetString ();
if ( message . IsEmpty ())
message = _ ( L ( "Export failed" ));
this -> statusbar () -> set_status_text ( message );
2018-10-18 18:06:40 +02:00
}
2018-10-25 10:07:11 +02:00
if ( canceled )
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
// this updates buttons status
//$self->object_list_changed;
// refresh preview
if ( this -> preview != nullptr )
this -> preview -> reload_print ();
2018-09-17 12:15:11 +02:00
}
void Plater :: priv :: on_layer_editing_toggled ( bool enable )
{
_3DScene :: enable_layers_editing ( canvas3D , enable );
if ( enable && ! _3DScene :: is_layers_editing_enabled ( canvas3D )) {
// Initialization of the OpenGL shaders failed. Disable the tool.
_3DScene :: enable_toolbar_item ( canvas3D , "layersediting" , false );
}
canvas3D -> Refresh ();
canvas3D -> Update ();
}
2018-10-25 12:10:35 +02:00
void Plater :: priv :: on_schedule_background_process ( SimpleEvent & )
{
schedule_background_process ();
}
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 )
q -> add ();
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_cut ( SimpleEvent & )
{
// TODO
}
void Plater :: priv :: on_action_layersediting ( SimpleEvent & )
{
2018-10-22 15:18:56 +02:00
bool enable = ! _3DScene :: is_layers_editing_enabled ( canvas3D );
_3DScene :: enable_layers_editing ( canvas3D , enable );
if ( enable && ! _3DScene :: is_layers_editing_enabled ( canvas3D ))
_3DScene :: enable_toolbar_item ( canvas3D , "layersediting" , false );
2018-10-04 11:12:55 +02:00
}
2018-10-16 16:04:19 +02:00
void Plater :: priv :: on_object_select ( SimpleEvent & evt )
{
selection_changed ();
wxGetApp (). obj_list () -> update_selections ();
}
2018-10-09 17:14:59 +02:00
2018-10-03 14:25:35 +02:00
void Plater :: priv :: on_viewport_changed ( SimpleEvent & evt )
{
wxObject * o = evt . GetEventObject ();
2018-10-04 10:41:11 +02:00
if ( o == preview -> get_wxglcanvas ())
2018-10-03 14:25:35 +02:00
preview -> set_viewport_into_scene ( canvas3D );
else if ( o == canvas3D )
preview -> set_viewport_from_scene ( canvas3D );
}
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-10-17 14:26:13 +02:00
if ( q != nullptr )
q -> PopupMenu ( & object_menu , ( int ) evt . data . x (), ( int ) evt . data . y ());
2018-10-08 19:55:30 +02:00
}
void Plater :: priv :: on_model_update ( SimpleEvent & )
{
// TODO
}
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 );
main_frame -> get_preset_tab ( "print" ) -> load_config ( cfg );
2018-10-08 19:55:30 +02:00
}
void Plater :: priv :: on_enable_action_buttons ( Event < bool >& )
{
// TODO
}
void Plater :: priv :: on_update_geometry ( Vec3dsEvent < 2 >& )
{
// TODO
}
2018-10-17 12:17:25 +02:00
bool Plater :: priv :: init_object_menu ()
{
2018-10-18 09:27:37 +02:00
wxMenuItem * item_delete = append_menu_item ( & object_menu , wxID_ANY , _ ( L ( "Delete \t Del" )), _ ( L ( "Remove the selected object" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { q -> remove_selected (); }, "brick_delete.png" );
2018-10-18 09:27:37 +02:00
wxMenuItem * item_increase = append_menu_item ( & object_menu , wxID_ANY , _ ( L ( "Increase copies \t +" )), _ ( L ( "Place one more copy of the selected object" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { q -> increase_instances (); }, "add.png" );
2018-10-17 14:26:13 +02:00
wxMenuItem * item_decrease = append_menu_item ( & object_menu , wxID_ANY , _ ( L ( "Decrease copies \t -" )), _ ( L ( "Remove one copy of the selected object" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { q -> decrease_instances (); }, "delete.png" );
2018-10-25 21:13:45 +02:00
wxMenuItem * item_set_number_of_copies = append_menu_item ( & object_menu , wxID_ANY , _ ( L ( "Set number of copies…" )), _ ( L ( "Change the number of copies of the selected object" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { q -> set_number_of_copies (); }, "textfield.png" );
2018-10-17 14:26:13 +02:00
2018-10-18 14:42:21 +02:00
object_menu . AppendSeparator ();
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" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { mirror ( X ); }, "bullet_red.png" , & object_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" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { mirror ( Y ); }, "bullet_green.png" , & object_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" )),
2018-10-31 12:56:08 +01:00
[ this ]( wxCommandEvent & ) { mirror ( Z ); }, "bullet_blue.png" , & object_menu );
2018-10-18 14:42:21 +02:00
wxMenuItem * item_mirror = append_submenu ( & object_menu , mirror_menu , wxID_ANY , _ ( L ( "Mirror" )), _ ( L ( "Mirror the selected object" )));
2018-10-24 12:55:38 +02:00
wxMenu * split_menu = new wxMenu ();
if ( split_menu == nullptr )
return false ;
wxMenuItem * item_split_objects = append_menu_item ( split_menu , wxID_ANY , _ ( L ( "To objects" )), _ ( L ( "Split the selected object into individual objects" )),
2018-11-08 16:43:25 +01:00
[ this ]( wxCommandEvent & ) { split_object (); }, "shape_ungroup_o.png" , & object_menu );
2018-10-24 12:55:38 +02:00
wxMenuItem * item_split_volumes = append_menu_item ( split_menu , wxID_ANY , _ ( L ( "To parts" )), _ ( L ( "Split the selected object into individual sub-parts" )),
2018-11-08 16:43:25 +01:00
[ this ]( wxCommandEvent & ) { split_volume (); }, "shape_ungroup_p.png" , & object_menu );
2018-10-24 12:55:38 +02:00
wxMenuItem * item_split = append_submenu ( & object_menu , split_menu , wxID_ANY , _ ( L ( "Split" )), _ ( L ( "Split the selected object" )), "shape_ungroup.png" );
2018-10-17 14:26:13 +02:00
// ui updates needs to be binded to the parent panel
if ( q != nullptr )
{
2018-10-18 15:09:41 +02:00
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_mirror ()); }, item_mirror -> GetId ());
2018-10-18 09:27:37 +02:00
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_delete_object ()); }, item_delete -> GetId ());
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_increase_instances ()); }, item_increase -> GetId ());
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_decrease_instances ()); }, item_decrease -> GetId ());
2018-10-25 21:13:45 +02:00
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_increase_instances ()); }, item_set_number_of_copies -> GetId ());
2018-10-24 12:55:38 +02:00
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_split_to_objects () || can_split_to_volumes ()); }, item_split -> GetId ());
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_split_to_objects ()); }, item_split_objects -> GetId ());
q -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( can_split_to_volumes ()); }, item_split_volumes -> GetId ());
2018-10-17 14:26:13 +02:00
}
2018-10-17 12:17:25 +02:00
return true ;
}
2018-10-08 19:55:30 +02:00
2018-10-18 09:27:37 +02:00
bool Plater :: priv :: can_delete_object () const
{
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ());
}
bool Plater :: priv :: can_increase_instances () const
{
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ());
}
bool Plater :: priv :: can_decrease_instances () 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-10-24 12:55:38 +02:00
bool Plater :: priv :: can_split_to_objects () const
{
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && ! model . objects [ obj_idx ] -> is_multiparts ();
}
bool Plater :: priv :: can_split_to_volumes () const
2018-10-18 09:27:37 +02:00
{
int obj_idx = get_selected_object_idx ();
2018-10-19 15:27:19 +02:00
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && ! model . objects [ obj_idx ] -> is_multiparts ();
2018-10-18 09:27:37 +02:00
}
bool Plater :: priv :: can_cut_object () const
{
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ());
}
bool Plater :: priv :: layers_height_allowed () const
{
2018-10-22 15:18:56 +02:00
int obj_idx = get_selected_object_idx ();
return ( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()) && config -> opt_bool ( "variable_layer_height" ) && _3DScene :: is_layers_editing_allowed ( canvas3D );
2018-10-18 09:27:37 +02:00
}
2018-10-18 15:09:41 +02:00
bool Plater :: priv :: can_delete_all () const
{
return ! model . objects . empty ();
}
bool Plater :: priv :: can_arrange () const
{
2018-11-12 14:52:52 +01:00
return ! model . objects . empty () && ! arranging . load ();
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
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 ()
{
_3DScene :: remove_canvas ( p -> canvas3D );
}
Sidebar & Plater :: sidebar () { return * p -> sidebar ; }
2018-10-11 13:22:36 +02:00
Model & Plater :: model () { return p -> model ; }
Print & Plater :: print () { return p -> print ; }
2018-09-17 12:15:11 +02:00
2018-10-17 12:17:25 +02:00
void Plater :: add ()
{
wxArrayString input_files ;
wxGetApp (). open_model ( this , input_files );
std :: vector < fs :: path > input_paths ;
for ( const auto & file : input_files ) {
input_paths . push_back ( file . wx_str ());
}
load_files ( input_paths );
}
2018-10-11 10:03:38 +02:00
void Plater :: load_files ( const std :: vector < fs :: path > & input_files ) { p -> load_files ( input_files ); }
2018-09-17 12:15:11 +02:00
2018-10-08 19:14:55 +02:00
void Plater :: update ( bool force_autocenter ) { p -> update ( force_autocenter ); }
2018-10-17 12:59:58 +02:00
void Plater :: select_view ( const std :: string & direction ) { p -> select_view ( direction ); }
2018-10-08 19:14:55 +02:00
void Plater :: remove ( size_t obj_idx ) { p -> remove ( obj_idx ); }
2018-10-04 11:12:55 +02:00
void Plater :: remove_selected ()
{
2018-10-17 09:30:07 +02:00
int obj_idx = p -> get_selected_object_idx ();
if ( obj_idx != - 1 )
remove (( size_t ) obj_idx );
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
{
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 ];
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 ;
2018-10-11 10:03:38 +02:00
float offset = 10.0 ;
for ( size_t i = 0 ; i < num ; i ++ , offset += 10.0 ) {
Vec3d offset_vec = model_instance -> get_offset () + Vec3d ( offset , offset , 0.0 );
2018-10-18 11:28:31 +02:00
model_object -> add_instance ( offset_vec , model_instance -> get_scaling_factor (), model_instance -> get_rotation ());
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
{
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
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-10-23 15:27:31 +02:00
void Plater :: export_gcode ( fs :: path output_path )
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
2018-10-23 15:27:31 +02:00
if ( this -> p -> background_process . is_export_scheduled ()) {
2018-09-17 12:15:11 +02:00
GUI :: show_error ( this , _ ( L ( "Another export job is currently running." )));
2018-10-23 15:27:31 +02:00
return ;
2018-09-17 12:15:11 +02:00
}
2018-10-04 11:12:55 +02:00
std :: string err = wxGetApp (). preset_bundle -> full_config (). validate ();
2018-10-23 15:27:31 +02:00
if ( err . empty ())
2018-11-08 20:18:40 +01:00
err = p -> background_process . validate ();
2018-09-17 12:15:11 +02:00
if ( ! err . empty ()) {
// The config is not valid
GUI :: show_error ( this , _ ( err ));
2018-10-23 15:27:31 +02:00
return ;
2018-09-17 12:15:11 +02:00
}
// Copy the names of active presets into the placeholder parser.
2018-10-04 11:12:55 +02:00
wxGetApp (). preset_bundle -> export_selections ( p -> print . placeholder_parser ());
2018-09-17 12:15:11 +02:00
// select output file
2018-10-23 15:27:31 +02:00
if ( output_path . empty ()) {
2018-10-04 11:12:55 +02:00
// XXX: take output path from CLI opts? Ancient Slic3r versions used to do that...
2018-09-17 12:15:11 +02:00
// If possible, remove accents from accented latin characters.
// This function is useful for generating file names to be processed by legacy firmwares.
2018-10-04 11:12:55 +02:00
auto default_output_file = fs :: path ( Slic3r :: fold_utf8_to_ascii (
p -> print . output_filepath ( output_path . string ())
// FIXME: ^ errors to handle?
));
auto start_dir = wxGetApp (). app_config -> get_last_output_dir ( default_output_file . parent_path (). string ());
2018-09-17 12:15:11 +02:00
wxFileDialog dlg ( this , _ ( L ( "Save G-code file as:" )),
2018-10-04 11:12:55 +02:00
start_dir ,
default_output_file . filename (). string (),
GUI :: file_wildcards [ FT_GCODE ],
2018-09-17 12:15:11 +02:00
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
2018-10-23 15:27:31 +02:00
if ( dlg . ShowModal () == wxID_OK ) {
fs :: path path ( dlg . GetPath ());
wxGetApp (). app_config -> update_last_output_dir ( path . parent_path (). string ());
output_path = path ;
}
2018-09-17 12:15:11 +02:00
}
2018-10-23 15:27:31 +02:00
if ( ! output_path . empty ()) {
this -> p -> background_process . schedule_export ( p -> print . output_filepath ( output_path . string ()));
this -> p -> background_process . start ();
}
2018-09-17 12:15:11 +02:00
}
2018-10-04 11:12:55 +02:00
void Plater :: export_stl ()
{
2018-10-17 09:30:07 +02:00
if ( p -> model . objects . empty ()) { return ; }
2018-10-04 11:12:55 +02:00
auto dialog = p -> get_export_file ( FT_STL );
if ( ! dialog ) { return ; }
// Store a binary STL
wxString path = dialog -> GetPath ();
auto path_cstr = path . c_str ();
auto mesh = p -> model . mesh ();
Slic3r :: store_stl ( path_cstr , & mesh , true );
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
auto dialog = p -> get_export_file ( FT_AMF );
if ( ! dialog ) { return ; }
wxString path = dialog -> GetPath ();
auto path_cstr = path . c_str ();
2018-11-07 14:57:50 +01:00
DynamicPrintConfig cfg = wxGetApp (). preset_bundle -> full_config ();
if ( Slic3r :: store_amf ( path_cstr , & p -> model , dialog -> get_checkbox_value () ? & 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
}
void Plater :: export_3mf ()
{
2018-10-17 09:30:07 +02:00
if ( p -> model . objects . empty ()) { return ; }
2018-10-08 19:55:30 +02:00
auto dialog = p -> get_export_file ( FT_3MF );
if ( ! dialog ) { return ; }
wxString path = dialog -> GetPath ();
auto path_cstr = path . c_str ();
2018-11-07 14:57:50 +01:00
DynamicPrintConfig cfg = wxGetApp (). preset_bundle -> full_config ();
if ( Slic3r :: store_3mf ( path_cstr , & p -> model , dialog -> get_checkbox_value () ? & 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-10-18 18:06:40 +02:00
// explicitly cancel a previous thread and start a new one.
// Don't reslice if export of G-code or sending to OctoPrint is running.
// if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) {
// Stop the background processing threads, stop the async update timer.
2018-10-24 11:48:39 +02:00
// this->p->stop_background_process();
2018-10-18 18:06:40 +02:00
// Rather perform one additional unnecessary update of the print object instead of skipping a pending async update.
this -> p -> async_apply_config ();
2018-10-31 12:56:08 +01:00
this -> p -> statusbar () -> set_cancel_callback ([ this ]() {
2018-10-25 10:07:11 +02:00
this -> p -> statusbar () -> set_status_text ( L ( "Cancelling" ));
this -> p -> background_process . stop ();
2018-10-18 18:06:40 +02:00
});
this -> p -> start_background_process ();
2018-09-17 12:15:11 +02:00
}
2018-10-04 11:12:55 +02:00
void Plater :: send_gcode ()
{
2018-10-23 15:27:31 +02:00
// p->send_gcode_file = export_gcode();
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 ();
2018-11-09 18:39:07 +01:00
wxWindowUpdateLocker noUpdates_scrolled_panel ( sidebar (). scrolled_panel ());
// sidebar().scrolled_panel()->Freeze();
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
wxGetApp (). preset_bundle -> update_platter_filament_ui ( i , choice );
++ i ;
}
// remove unused choices if any
sidebar (). remove_unused_filament_combos ( num_extruders );
2018-11-09 18:39:07 +01:00
sidebar (). scrolled_panel () -> Layout ();
// sidebar().scrolled_panel()->Thaw();
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 ;
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-11-08 20:18:40 +01:00
if ( opt_key == "printer_technology" )
p -> background_process . select_technology ( config . opt_enum < PrinterTechnology > ( opt_key ));
else if ( opt_key == "bed_shape" ) {
2018-10-15 10:53:47 +02:00
if ( p -> canvas3D ) _3DScene :: set_bed_shape ( p -> canvas3D , p -> config -> option < ConfigOptionPoints > ( opt_key ) -> values );
if ( p -> preview ) p -> preview -> set_bed_shape ( p -> config -> option < ConfigOptionPoints > ( opt_key ) -> values );
update_scheduled = true ;
}
else if ( opt_key == "wipe_tower" /*|| opt_key == "filament_minimal_purge_on_wipe_tower"*/ || // ? #ys_FIXME
opt_key == "single_extruder_multi_material" ) {
update_scheduled = true ;
}
// else if(opt_key == "serial_port") {
2018-10-17 11:12:38 +02:00
// sidebar()->p->btn_print->Show(config.get("serial_port")); // ???: btn_print is removed
2018-10-15 10:53:47 +02:00
// Layout();
// }
else if ( opt_key == "print_host" ) {
sidebar (). show_button ( baReslice , ! p -> config -> option < ConfigOptionString > ( opt_key ) -> value . empty ());
Layout ();
}
else if ( opt_key == "variable_layer_height" ) {
if ( p -> config -> opt_bool ( "variable_layer_height" ) != true ) {
_3DScene :: enable_toolbar_item ( p -> canvas3D , "layersediting" , false );
_3DScene :: enable_layers_editing ( p -> canvas3D , 0 );
p -> canvas3D -> Refresh ();
p -> canvas3D -> Update ();
}
else if ( _3DScene :: is_layers_editing_allowed ( p -> canvas3D )) {
_3DScene :: enable_toolbar_item ( p -> canvas3D , "layersediting" , true );
}
}
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 ;
} else if ( opt_key == "printer_model" ) {
// update to force bed selection(for texturing)
if ( p -> canvas3D ) _3DScene :: set_bed_shape ( p -> canvas3D , p -> config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values );
if ( p -> preview ) p -> preview -> set_bed_shape ( p -> config -> option < ConfigOptionPoints > ( "bed_shape" ) -> values );
update_scheduled = true ;
}
}
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
}
2018-11-01 12:33:56 +01:00
int Plater :: get_selected_object_idx ()
{
return p -> get_selected_object_idx ();
}
2018-11-09 18:39:07 +01:00
bool Plater :: is_single_full_object_selection ()
{
return p -> get_selection (). is_single_full_object ();
}
2018-10-11 15:57:09 +02:00
wxGLCanvas * Plater :: canvas3D ()
{
return p -> canvas3D ;
}
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 ;
auto list = wxGetApp (). obj_list ();
wxASSERT ( list != nullptr );
if ( list == nullptr )
return ;
if ( list -> is_parts_changed ()) {
// recenter and re - align to Z = 0
2018-10-12 12:00:37 +02:00
auto model_object = p -> model . objects [ obj_idx ];
2018-11-06 15:51:33 +01:00
#if !ENABLE_MODELVOLUME_TRANSFORM
2018-10-08 16:27:38 +02:00
model_object -> center_around_origin ();
2018-11-06 15:51:33 +01:00
#endif // !ENABLE_MODELVOLUME_TRANSFORM
2018-10-30 16:03:03 +01:00
model_object -> ensure_on_bed ();
2018-10-31 15:41:27 +01:00
_3DScene :: reload_scene ( p -> canvas3D , false );
2018-10-08 16:27:38 +02:00
}
2018-11-07 15:29:13 +01:00
// update print
2018-10-08 16:27:38 +02:00
if ( list -> is_parts_changed () || list -> is_part_settings_changed ()) {
2018-10-17 11:12:38 +02:00
this -> p -> schedule_background_process ();
2018-10-26 09:50:28 +02:00
#if !ENABLE_MODIFIED_CAMERA_TARGET
2018-10-16 11:08:37 +02:00
_3DScene :: zoom_to_volumes ( p -> canvas3D );
2018-10-26 09:50:28 +02:00
#endif // !ENABLE_MODIFIED_CAMERA_TARGET
2018-10-08 16:27:38 +02:00
}
else {
2018-10-17 11:12:38 +02:00
this -> p -> schedule_background_process ();
2018-10-08 16:27:38 +02:00
}
}
2018-11-07 13:40:24 +01:00
void Plater :: fix_through_netfabb ( const int obj_idx ) { p -> fix_through_netfabb ( obj_idx ); }
2018-09-17 12:15:11 +02:00
}} // namespace Slic3r::GUI