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"
#include "libslic3r/Print.hpp"
#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-10-11 10:03:38 +02: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-10-10 13:53:45 +02:00
#include "PrintConfig.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
private :
wxStaticText * info_size ;
wxStaticText * info_volume ;
wxStaticText * info_facets ;
wxStaticText * info_materials ;
wxStaticText * info_manifold ;
};
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 );
};
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" )));
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 );
grid_sizer -> Add ( sizer_manifold , 0 , wxEXPAND | wxTOP , 4 );
Add ( grid_sizer , 0 , wxEXPAND );
}
class SlicedInfo : public wxStaticBoxSizer
{
public :
SlicedInfo ( wxWindow * parent );
private :
wxStaticText * info_filament_m ;
wxStaticText * info_filament_mm3 ;
wxStaticText * info_filament_g ;
wxStaticText * info_cost ;
wxStaticText * info_time_normal ;
wxStaticText * info_time_silent ;
};
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 );
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 , "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 );
};
init_info_label ( info_filament_m , _ ( L ( "Used Filament (m)" )));
init_info_label ( info_filament_mm3 , _ ( L ( "Used Filament (mm³)" )));
init_info_label ( info_filament_g , _ ( L ( "Used Filament (g)" )));
init_info_label ( info_cost , _ ( L ( "Cost" )));
init_info_label ( info_time_normal , _ ( L ( "Estimated printing time (normal mode)" )));
init_info_label ( info_time_silent , _ ( L ( "Estimated printing time (silent mode)" )));
Add ( grid_sizer , 0 , wxEXPAND );
}
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 ;
m_og -> m_on_change = [ config , this ]( t_config_option_key opt_key , boost :: any value ){
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 ;
if ( tab -> name () == "print" ){
tab_print = static_cast < TabPrint *> ( tab );
break ;
}
}
if ( tab_print == nullptr )
return ;
if ( opt_key == "fill_density" ){
value = m_og -> get_config_value ( * config , opt_key );
tab_print -> set_value ( opt_key , value );
tab_print -> update ();
}
else {
DynamicPrintConfig new_conf = * config ;
if ( opt_key == "brim" ){
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 = { "" , "" };
line . widget = [ config , this ]( wxWindow * parent ){
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-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-08 16:27:38 +02:00
bool show_manifold_warning_icon = false ;
bool show_print_info = false ;
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 ()
{
const bool showSLA = wxGetApp (). preset_bundle -> printers . get_selected_preset (). printer_technology () == ptSLA ;
wxWindowUpdateLocker noUpdates ( wxGetApp (). mainframe );
for ( size_t i = 0 ; i < 4 ; ++ i )
sizer_presets -> Show ( i , ! showSLA );
sizer_presets -> Show ( 4 , showSLA );
sizer_presets -> Show ( 5 , showSLA );
2018-10-24 12:57:23 +02:00
frequently_changed_parameters -> Show ( ! showSLA );
2018-10-10 13:53:45 +02:00
wxGetApp (). plater () -> Layout ();
wxGetApp (). mainframe -> Layout ();
}
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
{
p -> scrolled = new wxScrolledWindow ( this );
// 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-10-03 15:14:52 +02:00
// Frequently Object Settings
2018-10-04 16:43:10 +02:00
p -> object_manipulation = new ObjectManipulation ( p -> scrolled );
p -> sizer_params -> Add ( p -> object_manipulation -> 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
auto * scrolled_sizer = new wxBoxSizer ( wxVERTICAL );
2018-10-01 15:09:31 +02:00
scrolled_sizer -> SetMinSize ( 320 , - 1 );
p -> scrolled -> SetSizer ( scrolled_sizer );
p -> scrolled -> SetScrollbars ( 0 , 1 , 1 , 1 );
2018-09-17 12:15:11 +02:00
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 )
{
switch ( preset_type ) {
case Preset :: TYPE_FILAMENT :
if ( p -> combos_filament . size () == 1 ) {
// Single filament printer, synchronize the filament presets.
2018-10-03 16:27:02 +02:00
const std :: string & name = wxGetApp (). preset_bundle -> filaments . get_selected_preset (). name ;
wxGetApp (). 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-03 16:27:02 +02:00
wxGetApp (). 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-03 16:27:02 +02:00
wxGetApp (). 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-03 16:27:02 +02:00
wxGetApp (). preset_bundle -> sla_materials . update_platter_ui ( p -> combo_sla_material );
2018-09-17 12:15:11 +02:00
break ;
case Preset :: TYPE_PRINTER :
// Update the print choosers to only contain the compatible presets, update the dirty flags.
2018-10-03 16:27:02 +02:00
wxGetApp (). preset_bundle -> prints . update_platter_ui ( p -> combo_print );
2018-09-17 12:15:11 +02:00
// Update the printer choosers, update the dirty flags.
2018-10-03 16:27:02 +02:00
wxGetApp (). preset_bundle -> printers . update_platter_ui ( p -> combo_printer );
2018-09-17 12:15:11 +02:00
// Update the filament choosers to only contain the compatible presets, update the color preview,
// update the dirty flags.
for ( size_t i = 0 ; i < p -> combos_filament . size (); i ++ ) {
2018-10-03 16:27:02 +02:00
wxGetApp (). preset_bundle -> update_platter_filament_ui ( i , p -> combos_filament [ i ]);
2018-09-17 12:15:11 +02:00
}
2018-10-10 13:53:45 +02:00
p -> show_preset_comboboxes ();
2018-09-17 12:15:11 +02:00
break ;
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-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-10-05 23:29:15 +02:00
void Sidebar :: show_info_sizers ( const bool show )
{
p -> object_info -> Show ( show );
2018-10-08 16:27:38 +02:00
p -> object_info -> manifold_warning_icon -> Show ( show && p -> show_manifold_warning_icon ); // where is g_show_manifold_warning_icon updating? #ys_FIXME
p -> sliced_info -> Show ( show && p -> show_print_info ); // where is g_show_print_info updating? #ys_FIXME
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 ()
{
return p -> combos_filament . size () > 0 ;
}
2018-10-10 13:53:45 +02:00
std :: vector < PresetComboBox *>& Sidebar :: combos_filament ()
{
return p -> combos_filament ;
}
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
// Plater::Object
struct PlaterObject
{
std :: string name ;
bool selected ;
PlaterObject ( std :: string name ) : name ( std :: move ( name )), selected ( false ) {}
};
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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 ;
Slic3r :: Model model ;
Slic3r :: GCodePreviewData gcode_preview_data ;
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
std :: vector < PlaterObject > objects ;
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
// 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 ;
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 );
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
std :: vector < int > collect_selections ();
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
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
#if ENABLE_EXTENDED_SELECTION
const GLCanvas3D :: Selection & get_selection () const ;
GLCanvas3D :: Selection & get_selection ();
int get_selected_object_idx () const ;
#else
2018-10-04 11:12:55 +02:00
void select_object ( optional < size_t > obj_idx );
2018-10-09 09:35:19 +02:00
void select_object_from_cpp ();
2018-10-04 11:12:55 +02:00
optional < size_t > selected_object () const ;
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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-18 15:09:41 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void select_view ();
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void rotate ();
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
void mirror ( Axis axis );
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void scale ();
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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 ();
void fix_through_netfabb ();
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 & );
2018-10-17 12:17:25 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void on_action_settings ( SimpleEvent & );
2018-10-17 12:17:25 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void on_action_layersediting ( SimpleEvent & );
2018-10-09 09:07:49 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void on_action_selectbyparts ( SimpleEvent & );
2018-10-09 09:07:49 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-03 14:25:35 +02:00
2018-10-16 16:04:19 +02:00
#if ENABLE_EXTENDED_SELECTION
void on_object_select ( SimpleEvent & );
#else
2018-10-09 17:14:59 +02:00
void on_object_select ( ObjectSelectEvent & );
2018-10-16 16:04:19 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
void on_viewport_changed ( SimpleEvent & );
void on_right_click ( Vec2dEvent & );
void on_model_update ( SimpleEvent & );
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
void on_scale_uniformly ( SimpleEvent & );
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
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
#if ENABLE_EXTENDED_SELECTION
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 ;
2018-10-22 11:45:03 +02:00
#if ENABLE_MIRROR
2018-10-18 15:09:41 +02:00
bool can_mirror () const ;
2018-10-22 11:45:03 +02:00
#endif // ENABLE_MIRROR
2018-10-18 09:27:37 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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" ,
"extruder_colour" , "filament_colour" , "max_print_height" , "printer_model"
})),
notebook ( new wxNotebook ( q , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxNB_BOTTOM )),
sidebar ( new Sidebar ( q )),
2018-10-01 16:48:08 +02:00
canvas3D ( GLCanvas3DManager :: create_wxglcanvas ( notebook ))
2018-09-17 12:15:11 +02:00
{
background_process . set_print ( & print );
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-10-23 15:27:31 +02:00
// Register progress callback from the Print class to the Platter.
print . set_status_callback ([ this ]( int percent , const std :: string & message ){
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-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-01 16:48:08 +02:00
_3DScene :: set_select_by ( canvas3D , "object" );
_3DScene :: set_drag_by ( canvas3D , "instance" );
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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 );
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 );
// 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 );
2018-10-17 12:17:25 +02:00
#if !ENABLE_EXTENDED_SELECTION
canvas3D -> Bind ( EVT_GLCANVAS_DOUBLE_CLICK , []( SimpleEvent & ) {}); // XXX: remove?
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
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
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_ROTATE_OBJECT , [ this ]( Event < int > & evt ) { /*TODO: call rotate */ });
2018-10-23 15:27:31 +02:00
canvas3D -> Bind ( EVT_GLCANVAS_SCALE_UNIFORMLY , [ this ]( SimpleEvent & ) { this -> scale (); });
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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 );
2018-10-17 12:17:25 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_SETTINGS , & priv :: on_action_settings , this );
2018-10-17 12:17:25 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_LAYERSEDITING , & priv :: on_action_layersediting , this );
2018-10-09 09:07:49 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
canvas3D -> Bind ( EVT_GLTOOLBAR_SELECTBYPARTS , & priv :: on_action_selectbyparts , this );
2018-10-09 09:07:49 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
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 ();
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
std :: vector < int > Plater :: priv :: collect_selections ()
{
std :: vector < int > res ;
for ( const auto & obj : objects ) {
res . push_back ( obj . selected );
}
return res ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
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 );
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-01 16:48:08 +02:00
const auto selections = collect_selections ();
_3DScene :: set_objects_selections ( canvas3D , selections );
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-01 16:48:08 +02:00
_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 ;
config . apply ( FullPrintConfig :: defaults ());
model = Slic3r :: Model :: read_from_archive ( path . string (), & config , false );
Preset :: normalize ( config );
wxGetApp (). preset_bundle -> load_config_model ( filename . string (), std :: move ( config ));
for ( const auto & kv : main_frame -> options_tabs ()) { kv . second -> load_current_preset (); }
wxGetApp (). app_config -> update_config_dir ( path . parent_path (). string ());
// forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
// and if the config contains a "layer_height" different from the current defined one
// TODO:
// $self->async_apply_config;
} 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 ();
}
}
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-10-04 11:12:55 +02:00
const Vec3d bed_center = Slic3r :: to_3d ( bed_shape . center (). cast < double > (), 0.0 );
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-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-19 11:02:50 +02:00
unsigned int obj_count = model . objects . size ();
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
obj_idxs . push_back ( obj_count ++ );
#else
2018-09-17 12:15:11 +02:00
objects . emplace_back ( std :: move ( object_name ));
2018-10-01 16:48:08 +02:00
obj_idxs . push_back ( objects . size () - 1 );
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
auto * instance = object -> add_instance ();
instance -> set_offset ( bed_center );
}
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-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 user turned autocentering off, automatic arranging would disappoint them
if ( get_config ( "autocenter" ) != "1" ) {
need_arrange = false ;
}
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
}
if ( need_arrange ) {
// arrange(); // TODO
}
update ();
_3DScene :: zoom_to_volumes ( canvas3D );
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
#if ENABLE_EXTENDED_SELECTION
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 ;
}
#else
2018-10-04 11:12:55 +02:00
void Plater :: priv :: select_object ( optional < size_t > obj_idx )
{
for ( auto & obj : objects ) {
obj . selected = false ;
}
if ( obj_idx ) {
objects [ * obj_idx ]. selected = true ;
}
2018-10-08 19:14:55 +02:00
selection_changed ();
}
2018-10-09 09:35:19 +02:00
void Plater :: priv :: select_object_from_cpp ()
{
// TODO
}
2018-10-08 19:14:55 +02:00
optional < size_t > Plater :: priv :: selected_object () const
{
for ( size_t i = 0 ; i < objects . size (); i ++ ) {
if ( objects [ i ]. selected ) { return i ; }
}
return boost :: none ;
2018-10-04 11:12:55 +02:00
}
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void Plater :: priv :: selection_changed ()
{
2018-10-18 09:27:37 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
const auto obj_idx = selected_object ();
const bool have_sel = !! obj_idx ;
2018-10-18 09:27:37 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
2018-10-18 09:27:37 +02:00
#if ENABLE_EXTENDED_SELECTION
_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-18 09:27:37 +02:00
#else
2018-10-04 11:12:55 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "fewer" , have_sel );
2018-10-24 12:55:38 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "splitobjects" , have_sel );
_3DScene :: enable_toolbar_item ( canvas3D , "splitvolumes" , have_sel );
2018-10-04 11:12:55 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "cut" , have_sel );
_3DScene :: enable_toolbar_item ( canvas3D , "settings" , have_sel );
2018-10-22 15:18:56 +02:00
_3DScene :: enable_toolbar_item ( canvas3D , "layersediting" , have_sel && config -> opt_bool ( "variable_layer_height" ) && _3DScene :: is_layers_editing_allowed ( canvas3D ));
2018-10-18 09:27:37 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
2018-10-16 16:04:19 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-17 09:30:07 +02:00
int obj_idx = get_selected_object_idx ();
bool have_sel = ( obj_idx != - 1 );
2018-10-16 16:04:19 +02:00
#else
2018-10-04 11:12:55 +02:00
bool can_select_by_parts = false ;
if ( have_sel ) {
const auto * model_object = model . objects [ * obj_idx ];
// XXX: ?
can_select_by_parts = * obj_idx < 1000 && model_object -> volumes . size () > 1 ;
_3DScene :: enable_toolbar_item ( canvas3D , "fewer" , model_object -> instances . size () > 1 );
}
if ( can_select_by_parts ) {
// first disable to let the item in the toolbar to switch to the unpressed state // XXX: ?
_3DScene :: enable_toolbar_item ( canvas3D , "selectbyparts" , false );
_3DScene :: enable_toolbar_item ( canvas3D , "selectbyparts" , true );
} else {
_3DScene :: enable_toolbar_item ( canvas3D , "selectbyparts" , false );
_3DScene :: set_select_by ( canvas3D , "object" );
}
2018-10-18 09:27:37 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
2018-10-18 09:27:37 +02:00
wxWindowUpdateLocker freeze_guard ( sidebar );
2018-10-04 11:12:55 +02:00
if ( have_sel ) {
2018-10-16 16:04:19 +02:00
#if ENABLE_EXTENDED_SELECTION
const ModelObject * model_object = model . objects [ obj_idx ];
#else
2018-10-04 11:12:55 +02:00
const auto * model_object = model . objects [ * obj_idx ];
2018-10-16 16:04:19 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
// FIXME print_info runs model fixing in two rounds, it is very slow, it should not be performed here!
// # $model_object->print_info;
2018-10-16 16:04:19 +02:00
const ModelInstance * model_instance = ! model_object -> instances . empty () ? model_object -> instances . front () : nullptr ;
2018-10-04 11:12:55 +02:00
// TODO
// $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
// $self->{object_info_materials}->SetLabel($model_object->materials_count);
// if (my $stats = $model_object->mesh_stats) {
// $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
// $self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $stats->{number_of_parts}));
// if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
// $self->{object_info_manifold}->SetLabel(sprintf(L("Auto-repaired (%d errors)"), $errors));
// #$self->{object_info_manifold_warning_icon}->Show;
// $self->{"object_info_manifold_warning_icon_show"}->(1);
// # we don't show normals_fixed because we never provide normals
// # to admesh, so it generates normals for all facets
// my $message = sprintf L('%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges'),
// @$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)};
// $self->{object_info_manifold}->SetToolTipString($message);
// $self->{object_info_manifold_warning_icon}->SetToolTipString($message);
// } else {
// $self->{object_info_manifold}->SetLabel(L("Yes"));
// #$self->{object_info_manifold_warning_icon}->Hide;
// $self->{"object_info_manifold_warning_icon_show"}->(0);
// $self->{object_info_manifold}->SetToolTipString("");
// $self->{object_info_manifold_warning_icon}->SetToolTipString("");
// }
// } else {
// $self->{object_info_facets}->SetLabel($object->facets);
// }
} else {
// $self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold);
// $self->{"object_info_manifold_warning_icon_show"}->(0);
// $self->{object_info_manifold}->SetToolTipString("");
// $self->{object_info_manifold_warning_icon}->SetToolTipString("");
}
q -> Layout ();
}
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-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
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-17 09:30:07 +02:00
#else
2018-10-08 19:14:55 +02:00
const bool have_objects = ! objects . empty ();
_3DScene :: enable_toolbar_item ( canvas3D , "deleteall" , have_objects );
_3DScene :: enable_toolbar_item ( canvas3D , "arrange" , have_objects );
2018-10-18 15:09:41 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
sidebar -> enable_buttons ( ! model . objects . empty () && ! export_in_progress && model_fits );
#else
2018-10-08 19:14:55 +02:00
sidebar -> enable_buttons ( have_objects && ! export_in_progress && model_fits );
2018-10-18 15:09:41 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-08 19:14:55 +02:00
}
2018-10-18 15:09:41 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void Plater :: priv :: select_view ()
{
// TODO
}
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
2018-10-08 19:14:55 +02:00
void Plater :: priv :: remove ( size_t obj_idx )
{
// Prevent toolpaths preview from rendering while we modify the Print object
preview -> set_enabled ( false );
2018-10-22 15:18:56 +02:00
if ( _3DScene :: is_layers_editing_enabled ( canvas3D ))
_3DScene :: enable_layers_editing ( canvas3D , false );
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:14:55 +02:00
objects . erase ( objects . begin () + obj_idx );
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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 ();
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:14:55 +02:00
select_object ( boost :: none );
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-08 19:14:55 +02:00
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-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
objects . clear ();
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
model . clear_objects ();
2018-10-23 15:27:31 +02:00
// print.clear_objects();
2018-10-04 11:12:55 +02:00
// 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
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:14:55 +02:00
select_object ( boost :: none );
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
update ();
}
2018-10-18 15:09:41 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void Plater :: priv :: rotate ()
{
// TODO
}
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
2018-10-18 15:09:41 +02:00
void Plater :: priv :: mirror ( Axis axis )
2018-10-09 09:35:19 +02:00
{
2018-10-18 15:50:51 +02:00
#if ENABLE_MIRROR
#if ENABLE_EXTENDED_SELECTION
_3DScene :: mirror_selection ( canvas3D , axis );
#endif // ENABLE_EXTENDED_SELECTION
#else
2018-10-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
int obj_idx = get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
ModelObject * model_object = model . objects [ obj_idx ];
ModelInstance * model_instance = model_object -> instances . front ();
#else
2018-10-09 09:35:19 +02:00
const auto obj_idx = selected_object ();
if ( ! obj_idx ) { return ; }
auto * model_object = model . objects [ * obj_idx ];
auto * model_instance = model_object -> instances [ 0 ];
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
// XXX: ?
// # apply Z rotation before mirroring
// if ($model_instance->rotation != 0) {
// $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
// $_->set_rotation(0) for @{ $model_object->instances };
// }
model_object -> mirror ( axis );
selection_changed ();
update ();
2018-10-18 15:50:51 +02:00
#endif // ENABLE_MIRROR
2018-10-09 09:35:19 +02:00
}
2018-10-18 15:09:41 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void Plater :: priv :: scale ()
{
// TODO
}
2018-10-18 15:09:41 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-09 09:35:19 +02:00
void Plater :: priv :: arrange ()
{
2018-10-11 10:03:38 +02:00
main_frame -> app_controller () -> arrange_model ();
// 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
update ();
2018-10-09 09:35:19 +02:00
}
void Plater :: priv :: split_object ()
{
2018-10-19 15:27:19 +02:00
#if ENABLE_EXTENDED_SELECTION
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
m -> center_around_origin ();
}
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
load_model_objects ( new_objects );
}
#endif // ENABLE_EXTENDED_SELECTION
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 ();
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-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.
this -> sidebar -> show_info_sizers ( false );
// 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 ();
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" )) {
2018-10-18 18:06:40 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-23 22:53:43 +02:00
std :: vector < int > selections = this -> collect_selections ();
Slic3r :: _3DScene :: set_objects_selections ( this -> canvas3D , selections );
Slic3r :: _3DScene :: reload_scene ( this -> canvas3D , 1 );
2018-10-18 18:06:40 +02:00
#endif /* !ENABLE_EXTENDED_SELECTION */
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-10-25 10:07:11 +02:00
this -> print . num_object_instances () > 0 && this -> background_process . start ())
this -> statusbar () -> set_cancel_callback ([ this ](){
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 ;
2018-10-18 18:06:40 +02:00
// return if ! @{$self->{objects}} || $self->{background_slicing_process}->running;
2018-10-23 15:27:31 +02:00
// Don't start process thread if Print is not valid.
std :: string err = this -> q -> print (). 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.
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
}
void Plater :: priv :: fix_through_netfabb ()
{
2018-10-18 18:06:40 +02:00
/*
my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_fixed = Slic3r::Model->new;
Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed);
my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects});
return if !@new_obj_idx;
foreach my $new_obj_idx (@new_obj_idx) {
my $o = $self->{model}->objects->[$new_obj_idx];
$o->clear_instances;
$o->add_instance($_) for @{$model_object->instances};
#$o->invalidate_bounding_box;
if ($o->volumes_count == $model_object->volumes_count) {
for my $i (0..($o->volumes_count-1)) {
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
}
}
#FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
}
$self->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 )) {
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
_3DScene :: set_objects_selections ( canvas3D , collect_selections ());
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-17 12:15:11 +02:00
_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:
#if !ENABLE_EXTENDED_SELECTION
2018-10-25 10:36:47 +02:00
auto selections = collect_selections ();
_3DScene :: set_objects_selections ( canvas3D , selections );
if ( canvas3D )
_3DScene :: reload_scene ( canvas3D , true );
2018-10-18 18:06:40 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
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-25 10:07:11 +02:00
this -> sidebar -> show_info_sizers ( 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
}
2018-10-17 12:17:25 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void Plater :: priv :: on_action_settings ( SimpleEvent & )
{
// TODO
}
2018-10-17 12:17:25 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
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-09 09:07:49 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
void Plater :: priv :: on_action_selectbyparts ( SimpleEvent & )
{
// TODO
}
2018-10-09 09:07:49 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-04 11:12:55 +02:00
2018-10-16 16:04:19 +02:00
#if ENABLE_EXTENDED_SELECTION
void Plater :: priv :: on_object_select ( SimpleEvent & evt )
{
selection_changed ();
wxGetApp (). obj_list () -> update_selections ();
}
#else
2018-10-09 17:14:59 +02:00
void Plater :: priv :: on_object_select ( ObjectSelectEvent & evt )
{
const auto obj_idx = evt . object_id ();
const auto vol_idx = evt . volume_id ();
2018-10-10 11:21:20 +02:00
if ( obj_idx >= 0 && obj_idx < 1000 && vol_idx == - 1 ) {
// Ignore the special objects (the wipe tower proxy and such).
select_object ( obj_idx );
2018-10-25 15:32:35 +02:00
}
2018-10-09 17:14:59 +02:00
}
2018-10-16 16:04:19 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
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-17 12:17:25 +02:00
#else
2018-10-08 19:55:30 +02:00
// TODO
2018-10-17 12:17:25 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
}
void Plater :: priv :: on_model_update ( SimpleEvent & )
{
// TODO
}
2018-10-17 09:30:07 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
void Plater :: priv :: on_scale_uniformly ( SimpleEvent & )
{
2018-10-09 17:14:59 +02:00
// my ($scale) = @_;
// my ($obj_idx, $object) = $self->selected_object;
const auto obj_idx = selected_object ();
if ( ! obj_idx ) { return ; }
// return if !defined $obj_idx;
// my $model_object = $self->{model}->objects->[$obj_idx];
// my $model_instance = $model_object->instances->[0];
// my $variation = $scale / $model_instance->scaling_factor;
// #FIXME Scale the layer height profile?
// foreach my $range (@{ $model_object->layer_height_ranges }) {
// $range->[0] *= $variation;
// $range->[1] *= $variation;
// }
// $_->set_scaling_factor($scale) for @{ $model_object->instances };
// # Set object scale on c++ side
// # Slic3r::GUI::set_object_scale($obj_idx, $model_object->instances->[0]->scaling_factor * 100);
// # $object->transform_thumbnail($self->{model}, $obj_idx);
// #update print and start background processing
// $self->{print}->add_model_object($model_object, $obj_idx);
// $self->selection_changed(1); # refresh info (size, volume etc.)
// $self->update;
2018-10-17 11:12:38 +02:00
this -> schedule_background_process ();
2018-10-08 19:55:30 +02:00
}
2018-10-17 09:30:07 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-08 19:55:30 +02:00
2018-10-09 17:14:59 +02:00
void Plater :: priv :: on_wipetower_moved ( Vec3dEvent & evt )
2018-10-08 19:55:30 +02:00
{
2018-10-09 17:14:59 +02:00
DynamicPrintConfig cfg ;
cfg . opt < ConfigOptionFloat > ( "wipe_tower_x" , true ) -> value = evt . data ( 0 );
cfg . opt < ConfigOptionFloat > ( "wipe_tower_y" , true ) -> value = evt . data ( 1 );
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-17 12:17:25 +02: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-18 15:09:41 +02: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-18 15:09:41 +02: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" )),
[ 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 ();
2018-10-22 11:45:03 +02:00
#if ENABLE_MIRROR
2018-10-18 14:42:21 +02:00
wxMenu * mirror_menu = new wxMenu ();
if ( mirror_menu == nullptr )
return false ;
append_menu_item ( mirror_menu , wxID_ANY , _ ( L ( "Along X axis" )), _ ( L ( "Mirror the selected object along the X axis" )),
[ this ]( wxCommandEvent & ){ mirror ( X ); }, "bullet_red.png" , & object_menu );
append_menu_item ( mirror_menu , wxID_ANY , _ ( L ( "Along Y axis" )), _ ( L ( "Mirror the selected object along the Y axis" )),
[ this ]( wxCommandEvent & ){ mirror ( Y ); }, "bullet_green.png" , & object_menu );
append_menu_item ( mirror_menu , wxID_ANY , _ ( L ( "Along Z axis" )), _ ( L ( "Mirror the selected object along the Z axis" )),
[ this ]( wxCommandEvent & ){ mirror ( Z ); }, "bullet_blue.png" , & object_menu );
wxMenuItem * item_mirror = append_submenu ( & object_menu , mirror_menu , wxID_ANY , _ ( L ( "Mirror" )), _ ( L ( "Mirror the selected object" )));
2018-10-22 11:45:03 +02:00
#endif // ENABLE_MIRROR
2018-10-18 14:42:21 +02:00
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" )),
[ this ]( wxCommandEvent & ){ split_object (); }, "shape_ungroup.png" , & object_menu );
wxMenuItem * item_split_volumes = append_menu_item ( split_menu , wxID_ANY , _ ( L ( "To parts" )), _ ( L ( "Split the selected object into individual sub-parts" )),
[ this ]( wxCommandEvent & ){ split_volume (); }, "shape_ungroup.png" , & object_menu );
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
2018-10-18 09:27:37 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-17 14:26:13 +02:00
// ui updates needs to be binded to the parent panel
if ( q != nullptr )
{
2018-10-22 11:45:03 +02:00
#if ENABLE_MIRROR
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-22 11:45:03 +02:00
#endif // ENABLE_MIRROR
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-18 09:27:37 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
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
{
return ! model . objects . empty ();
}
2018-10-22 11:45:03 +02:00
#if ENABLE_MIRROR
2018-10-18 15:09:41 +02:00
bool Plater :: priv :: can_mirror () const
{
return get_selection (). is_from_single_instance ();
}
2018-10-22 11:45:03 +02:00
#endif // ENABLE_MIRROR
2018-10-18 09:27:37 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
int obj_idx = p -> get_selected_object_idx ();
if ( obj_idx != - 1 )
remove (( size_t ) obj_idx );
#else
2018-10-04 11:12:55 +02:00
const auto selected = p -> selected_object ();
if ( selected ) {
remove ( * selected );
}
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
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 ();
#else
2018-10-11 10:03:38 +02:00
const auto obj_idx = p -> selected_object ();
if ( ! obj_idx ) { return ; }
auto * model_object = p -> model . objects [ * obj_idx ];
auto * model_instance = model_object -> instances [ model_object -> instances . size () - 1 ];
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-23 15:27:31 +02:00
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
2018-10-17 09:30:07 +02:00
#else
2018-10-23 15:27:31 +02:00
// p->print.get_object(*obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-11 10:03:38 +02:00
}
2018-10-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-18 11:41:29 +02:00
sidebar (). obj_list () -> increase_object_instances ( obj_idx , num );
2018-10-17 09:30:07 +02:00
#else
2018-10-18 10:40:26 +02:00
sidebar (). obj_list () -> increase_object_instances ( * obj_idx , num );
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
p -> get_selection (). add_instance ( obj_idx , ( int ) model_object -> instances . size () - 1 );
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
int obj_idx = p -> get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
ModelObject * model_object = p -> model . objects [ obj_idx ];
#else
2018-10-11 10:03:38 +02:00
const auto obj_idx = p -> selected_object ();
if ( ! obj_idx ) { return ; }
auto * model_object = p -> model . objects [ * obj_idx ];
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-11 10:03:38 +02:00
if ( model_object -> instances . size () > num ) {
for ( size_t i = 0 ; i < num ; i ++ ) {
model_object -> delete_last_instance ();
2018-10-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-23 15:27:31 +02:00
// p->print.get_object(obj_idx)->delete_last_copy();
2018-10-17 09:30:07 +02:00
#else
2018-10-23 15:27:31 +02:00
// p->print.get_object(*obj_idx)->delete_last_copy();
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-11 10:03:38 +02:00
}
2018-10-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
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
2018-10-18 10:40:26 +02:00
sidebar (). obj_list () -> decrease_object_instances ( * obj_idx , num );
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
}
else {
#if ENABLE_EXTENDED_SELECTION
remove ( obj_idx );
#else
2018-10-11 10:03:38 +02:00
remove ( * obj_idx );
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-11 10:03:38 +02:00
}
p -> update ();
2018-10-23 09:19:41 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( ! model_object -> instances . empty ())
p -> get_selection (). add_instance ( obj_idx , ( int ) model_object -> instances . size () - 1 );
#endif // ENABLE_EXTENDED_SELECTION
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
#if ENABLE_EXTENDED_SELECTION
int obj_idx = p -> get_selected_object_idx ();
if ( obj_idx == - 1 )
return ;
ModelObject * model_object = p -> model . objects [ obj_idx ];
#else
2018-10-11 10:03:38 +02:00
const auto obj_idx = p -> selected_object ();
if ( ! obj_idx ) { return ; }
auto * model_object = p -> model . objects [ * obj_idx ];
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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-17 09:30:07 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-23 15:27:31 +02:00
if ( p -> model . objects . empty ())
return ;
2018-10-17 09:30:07 +02:00
#else
2018-10-23 15:27:31 +02:00
if ( p -> objects . empty ())
return ;
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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-10-04 11:12:55 +02:00
err = p -> print . 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 ENABLE_EXTENDED_SELECTION
if ( p -> model . objects . empty ()) { return ; }
#else
2018-10-04 11:12:55 +02:00
if ( p -> objects . empty ()) { return ; }
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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 ENABLE_EXTENDED_SELECTION
if ( p -> model . objects . empty ()) { return ; }
#else
2018-10-08 19:55:30 +02:00
if ( p -> objects . empty ()) { return ; }
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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 ();
if ( Slic3r :: store_amf ( path_cstr , & p -> model , & p -> print , dialog -> get_checkbox_value ())) {
// 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 ENABLE_EXTENDED_SELECTION
if ( p -> model . objects . empty ()) { return ; }
#else
2018-10-08 19:55:30 +02:00
if ( p -> objects . empty ()) { return ; }
2018-10-17 09:30:07 +02:00
#endif // ENABLE_EXTENDED_SELECTION
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 ();
if ( Slic3r :: store_3mf ( path_cstr , & p -> model , & p -> print , dialog -> get_checkbox_value ())) {
// 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-25 10:07:11 +02:00
this -> p -> statusbar () -> set_cancel_callback ([ this ](){
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 ();
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 );
sidebar (). Layout ();
GetParent () -> Layout ();
}
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-10-15 10:53:47 +02:00
if ( opt_key == "bed_shape" ) {
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-10-11 15:57:09 +02:00
wxGLCanvas * Plater :: canvas3D ()
{
return p -> canvas3D ;
}
2018-10-08 16:27:38 +02:00
void Plater :: changed_object_settings ( int obj_idx )
{
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-10-08 16:27:38 +02:00
model_object -> center_around_origin ();
}
// update print
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-09 09:12:38 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-08 16:27:38 +02:00
if ( p -> canvas3D ) _3DScene :: reload_scene ( p -> canvas3D , true );
auto selections = p -> collect_selections ();
_3DScene :: set_objects_selections ( p -> canvas3D , selections );
2018-10-09 09:12:38 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-11 15:57:09 +02:00
_3DScene :: reload_scene ( p -> canvas3D , false );
2018-10-16 11:08:37 +02:00
_3DScene :: zoom_to_volumes ( p -> canvas3D );
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-09-17 12:15:11 +02:00
}} // namespace Slic3r::GUI