2016-09-14 09:22:41 +00:00
# The "Plater" tab. It contains the "3D", "2D", "Preview" and "Layers" subtabs.
2012-05-04 08:15:33 +00:00
package Slic3r::GUI::Plater ;
2012-04-30 12:56:01 +00:00
use strict ;
use warnings ;
use utf8 ;
use File::Basename qw( basename dirname ) ;
2015-05-25 17:51:47 +00:00
use List::Util qw( sum first max ) ;
2016-12-16 23:26:57 +00:00
use Slic3r::Geometry qw( X Y Z MIN MAX scale unscale deg2rad rad2deg ) ;
2016-12-12 17:02:24 +00:00
use LWP::UserAgent ;
2012-05-19 19:13:10 +00:00
use threads::shared qw( shared_clone ) ;
2017-05-24 13:20:20 +00:00
use Wx qw( :button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
2016-12-14 14:36:18 +00:00
: panel : sizer : toolbar : window wxTheApp : notebook : combobox wxNullBitmap ) ;
2016-12-12 17:02:24 +00:00
use Wx::Event qw( EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
2017-05-24 13:20:20 +00:00
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_LEFT_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
2015-05-26 00:01:43 +00:00
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED ) ;
2012-04-30 12:56:01 +00:00
use base 'Wx::Panel' ;
2013-12-12 19:19:33 +00:00
use constant TB_ADD = > & Wx:: NewId ;
2013-08-25 10:22:05 +00:00
use constant TB_REMOVE = > & Wx:: NewId ;
use constant TB_RESET = > & Wx:: NewId ;
use constant TB_ARRANGE = > & Wx:: NewId ;
use constant TB_EXPORT_GCODE = > & Wx:: NewId ;
use constant TB_EXPORT_STL = > & Wx:: NewId ;
2012-08-02 19:11:36 +00:00
use constant TB_MORE = > & Wx:: NewId ;
2013-08-25 13:45:22 +00:00
use constant TB_FEWER = > & Wx:: NewId ;
2012-08-02 19:11:36 +00:00
use constant TB_45CW = > & Wx:: NewId ;
use constant TB_45CCW = > & Wx:: NewId ;
use constant TB_SCALE = > & Wx:: NewId ;
use constant TB_SPLIT = > & Wx:: NewId ;
2014-12-10 16:34:59 +00:00
use constant TB_CUT = > & Wx:: NewId ;
2013-08-25 16:01:59 +00:00
use constant TB_SETTINGS = > & Wx:: NewId ;
2016-12-12 17:02:24 +00:00
use constant TB_LAYER_EDITING = > & Wx:: NewId ;
2012-05-04 09:22:56 +00:00
2013-10-16 13:13:39 +00:00
# package variables to avoid passing lexicals to threads
our $ THUMBNAIL_DONE_EVENT : shared = Wx:: NewEventType ;
our $ PROGRESS_BAR_EVENT : shared = Wx:: NewEventType ;
2014-06-13 12:27:55 +00:00
our $ ERROR_EVENT : shared = Wx:: NewEventType ;
2016-10-25 11:24:42 +00:00
# Emitted from the worker thread when the G-code export is finished.
2013-10-16 13:13:39 +00:00
our $ EXPORT_COMPLETED_EVENT : shared = Wx:: NewEventType ;
2014-06-13 09:19:53 +00:00
our $ PROCESS_COMPLETED_EVENT : shared = Wx:: NewEventType ;
2012-04-30 18:59:14 +00:00
2015-05-26 00:01:43 +00:00
use constant FILAMENT_CHOOSERS_SPACING = > 0 ;
2014-06-13 21:27:52 +00:00
use constant PROCESS_DELAY = > 0.5 * 1000 ; # milliseconds
2012-07-27 19:13:03 +00:00
2014-03-25 14:30:56 +00:00
my $ PreventListEvents = 0 ;
2012-04-30 12:56:01 +00:00
sub new {
my $ class = shift ;
my ( $ parent ) = @ _ ;
2012-07-24 12:28:21 +00:00
my $ self = $ class - > SUPER:: new ( $ parent , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL ) ;
2012-07-27 19:13:03 +00:00
$ self - > { config } = Slic3r::Config - > new_from_defaults ( qw(
2017-02-10 09:21:50 +00:00
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
2015-01-03 22:25:55 +00:00
serial_port serial_speed octoprint_host octoprint_apikey
2017-05-17 14:53:40 +00:00
nozzle_diameter single_extruder_multi_material
2017-05-24 13:20:20 +00:00
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
2012-07-27 19:13:03 +00:00
) ) ;
2017-02-07 17:28:53 +00:00
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
2013-12-12 19:19:33 +00:00
$ self - > { model } = Slic3r::Model - > new ;
2017-02-07 17:28:53 +00:00
# C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
2013-12-13 13:02:01 +00:00
$ self - > { print } = Slic3r::Print - > new ;
2016-09-13 09:24:55 +00:00
# List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter.
2012-09-12 14:30:44 +00:00
$ self - > { objects } = [] ;
2012-04-30 12:56:01 +00:00
2014-06-13 12:27:55 +00:00
$ self - > { print } - > set_status_cb ( sub {
my ( $ percent , $ message ) = @ _ ;
if ( $ Slic3r:: have_threads ) {
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ PROGRESS_BAR_EVENT , shared_clone ( [ $ percent , $ message ] ) ) ) ;
} else {
$ self - > on_progress_event ( $ percent , $ message ) ;
}
} ) ;
2014-07-13 10:10:34 +00:00
# Initialize preview notebook
$ self - > { preview_notebook } = Wx::Notebook - > new ( $ self , - 1 , wxDefaultPosition , [ 335 , 335 ] , wxNB_BOTTOM ) ;
2014-12-13 19:41:03 +00:00
# Initialize handlers for canvases
my $ on_select_object = sub {
2014-05-28 10:29:43 +00:00
my ( $ obj_idx ) = @ _ ;
2017-05-17 14:53:40 +00:00
# Ignore the special objects (the wipe tower proxy and such).
$ self - > select_object ( ( defined ( $ obj_idx ) && $ obj_idx < 1000 ) ? $ obj_idx : undef ) ;
2014-12-13 19:41:03 +00:00
} ;
my $ on_double_click = sub {
2014-11-30 19:19:04 +00:00
$ self - > object_settings_dialog if $ self - > selected_object ;
2014-12-13 19:41:03 +00:00
} ;
my $ on_right_click = sub {
my ( $ canvas , $ click_pos ) = @ _ ;
2014-06-14 19:36:28 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
return if ! defined $ obj_idx ;
my $ menu = $ self - > object_menu ;
2014-12-13 19:41:03 +00:00
$ canvas - > PopupMenu ( $ menu , $ click_pos ) ;
2014-06-14 19:36:28 +00:00
$ menu - > Destroy ;
2014-12-13 19:41:03 +00:00
} ;
2015-01-13 17:41:23 +00:00
my $ on_instances_moved = sub {
2014-05-28 10:29:43 +00:00
$ self - > update ;
2014-12-13 19:41:03 +00:00
} ;
2015-01-18 18:36:47 +00:00
# Initialize 3D plater
2015-01-17 09:53:01 +00:00
if ( $ Slic3r:: GUI:: have_OpenGL ) {
2016-12-12 17:02:24 +00:00
$ self - > { canvas3D } = Slic3r::GUI::Plater:: 3 D - > new ( $ self - > { preview_notebook } , $ self - > { objects } , $ self - > { model } , $ self - > { print } , $ self - > { config } ) ;
2015-01-17 09:53:01 +00:00
$ self - > { preview_notebook } - > AddPage ( $ self - > { canvas3D } , '3D' ) ;
$ self - > { canvas3D } - > set_on_select_object ( $ on_select_object ) ;
$ self - > { canvas3D } - > set_on_double_click ( $ on_double_click ) ;
$ self - > { canvas3D } - > set_on_right_click ( sub { $ on_right_click - > ( $ self - > { canvas3D } , @ _ ) ; } ) ;
$ self - > { canvas3D } - > set_on_instances_moved ( $ on_instances_moved ) ;
2017-05-19 19:48:32 +00:00
$ self - > { canvas3D } - > set_on_wipe_tower_moved ( sub {
my ( $ new_pos_3f ) = @ _ ;
my $ cfg = Slic3r::Config - > new ;
$ cfg - > set ( 'wipe_tower_x' , $ new_pos_3f - > x ) ;
$ cfg - > set ( 'wipe_tower_y' , $ new_pos_3f - > y ) ;
$ self - > GetFrame - > { options_tabs } { print } - > load_config ( $ cfg ) ;
} ) ;
2017-02-09 13:56:13 +00:00
$ self - > { canvas3D } - > set_on_model_update ( sub {
if ( $ Slic3r:: GUI:: Settings - > { _ } { background_processing } ) {
$ self - > { apply_config_timer } - > Stop if defined $ self - > { apply_config_timer } ;
$ self - > async_apply_config ( ) ;
2017-02-15 15:02:54 +00:00
} else {
# Hide the print info box, it is no more valid.
$ self - > { "print_info_box_show" } - > ( 0 ) ;
2017-02-09 13:56:13 +00:00
}
} ) ;
2015-02-15 23:37:36 +00:00
$ self - > { canvas3D } - > on_viewport_changed ( sub {
$ self - > { preview3D } - > canvas - > set_viewport_from_scene ( $ self - > { canvas3D } ) ;
} ) ;
2015-01-17 09:53:01 +00:00
}
2014-12-13 19:41:03 +00:00
# Initialize 2D preview canvas
$ self - > { canvas } = Slic3r::GUI::Plater:: 2 D - > new ( $ self - > { preview_notebook } , wxDefaultSize , $ self - > { objects } , $ self - > { model } , $ self - > { config } ) ;
$ self - > { preview_notebook } - > AddPage ( $ self - > { canvas } , '2D' ) ;
$ self - > { canvas } - > on_select_object ( $ on_select_object ) ;
$ self - > { canvas } - > on_double_click ( $ on_double_click ) ;
$ self - > { canvas } - > on_right_click ( sub { $ on_right_click - > ( $ self - > { canvas } , @ _ ) ; } ) ;
2015-01-13 17:41:23 +00:00
$ self - > { canvas } - > on_instances_moved ( $ on_instances_moved ) ;
2012-07-01 17:24:06 +00:00
2015-01-18 19:55:44 +00:00
# Initialize 3D toolpaths preview
2014-07-13 10:10:34 +00:00
if ( $ Slic3r:: GUI:: have_OpenGL ) {
2017-05-24 13:20:20 +00:00
$ self - > { preview3D } = Slic3r::GUI::Plater:: 3 DPreview - > new ( $ self - > { preview_notebook } , $ self - > { print } , $ self - > { config } ) ;
2015-02-15 23:37:36 +00:00
$ self - > { preview3D } - > canvas - > on_viewport_changed ( sub {
$ self - > { canvas3D } - > set_viewport_from_scene ( $ self - > { preview3D } - > canvas ) ;
} ) ;
2015-01-18 19:55:44 +00:00
$ self - > { preview_notebook } - > AddPage ( $ self - > { preview3D } , 'Preview' ) ;
$ self - > { preview3D_page_idx } = $ self - > { preview_notebook } - > GetPageCount - 1 ;
2014-07-13 10:10:34 +00:00
}
2015-01-18 19:55:44 +00:00
# Initialize toolpaths preview
2014-07-13 10:10:34 +00:00
if ( $ Slic3r:: GUI:: have_OpenGL ) {
2014-11-30 19:18:09 +00:00
$ self - > { toolpaths2D } = Slic3r::GUI::Plater:: 2 DToolpaths - > new ( $ self - > { preview_notebook } , $ self - > { print } ) ;
2015-01-18 19:55:44 +00:00
$ self - > { preview_notebook } - > AddPage ( $ self - > { toolpaths2D } , 'Layers' ) ;
2014-07-13 10:10:34 +00:00
}
2015-01-18 18:36:47 +00:00
EVT_NOTEBOOK_PAGE_CHANGED ( $ self , $ self - > { preview_notebook } , sub {
if ( $ self - > { preview_notebook } - > GetSelection == $ self - > { preview3D_page_idx } ) {
2015-01-25 09:59:39 +00:00
$ self - > { preview3D } - > load_print ;
2015-01-18 18:36:47 +00:00
}
} ) ;
2012-05-04 09:22:56 +00:00
# toolbar for object manipulation
2012-05-20 14:24:10 +00:00
if ( ! & Wx:: wxMSW ) {
2012-05-04 10:56:15 +00:00
Wx::ToolTip:: Enable ( 1 ) ;
2012-07-24 12:28:21 +00:00
$ self - > { htoolbar } = Wx::ToolBar - > new ( $ self , - 1 , wxDefaultPosition , wxDefaultSize , wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL ) ;
2016-04-09 17:10:57 +00:00
$ self - > { htoolbar } - > AddTool ( TB_ADD , "Add…" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "brick_add.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_REMOVE , "Delete" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "brick_delete.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_RESET , "Delete All" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "cross.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_ARRANGE , "Arrange" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "bricks.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 10:22:05 +00:00
$ self - > { htoolbar } - > AddSeparator ;
2016-04-09 17:10:57 +00:00
$ self - > { htoolbar } - > AddTool ( TB_MORE , "More" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "add.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_FEWER , "Fewer" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "delete.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
2012-05-04 09:22:56 +00:00
$ self - > { htoolbar } - > AddSeparator ;
2016-04-09 17:10:57 +00:00
$ self - > { htoolbar } - > AddTool ( TB_45CCW , "45° ccw" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "arrow_rotate_anticlockwise.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_45CW , "45° cw" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "arrow_rotate_clockwise.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_SCALE , "Scale…" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "arrow_out.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_SPLIT , "Split" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "shape_ungroup.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_CUT , "Cut…" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "package.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 16:01:59 +00:00
$ self - > { htoolbar } - > AddSeparator ;
2016-04-09 17:10:57 +00:00
$ self - > { htoolbar } - > AddTool ( TB_SETTINGS , "Settings…" , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "cog.png" ) , wxBITMAP_TYPE_PNG ) , '' ) ;
2017-02-19 18:44:05 +00:00
$ self - > { htoolbar } - > AddTool ( TB_LAYER_EDITING , 'Layer Editing' , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "variable_layer_height.png" ) , wxBITMAP_TYPE_PNG ) , wxNullBitmap , 1 , 0 , 'Layer Editing' ) ;
2012-05-20 14:24:10 +00:00
} else {
2013-08-25 14:10:53 +00:00
my % tbar_buttons = (
2014-03-25 14:07:21 +00:00
add = > "Add…" ,
2013-08-25 14:10:53 +00:00
remove = > "Delete" ,
reset = > "Delete All" ,
arrange = > "Arrange" ,
increase = > "" ,
decrease = > "" ,
rotate45ccw = > "" ,
rotate45cw = > "" ,
changescale = > "Scale…" ,
split = > "Split" ,
2014-12-10 16:34:59 +00:00
cut = > "Cut…" ,
2013-08-25 16:01:59 +00:00
settings = > "Settings…" ,
2016-12-12 17:02:24 +00:00
layer_editing = > "Layer editing" ,
2013-08-25 14:10:53 +00:00
) ;
2012-05-20 14:24:10 +00:00
$ self - > { btoolbar } = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
2014-12-10 16:34:59 +00:00
for ( qw( add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings ) ) {
2012-07-02 23:20:30 +00:00
$ self - > { "btn_$_" } = Wx::Button - > new ( $ self , - 1 , $ tbar_buttons { $ _ } , wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT ) ;
2012-05-20 14:24:10 +00:00
$ self - > { btoolbar } - > Add ( $ self - > { "btn_$_" } ) ;
}
2017-02-03 14:53:31 +00:00
$ self - > { "btn_layer_editing" } = Wx::ToggleButton - > new ( $ self , - 1 , $ tbar_buttons { 'layer_editing' } , wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT ) ;
$ self - > { btoolbar } - > Add ( $ self - > { "btn_layer_editing" } ) ;
2012-05-04 09:22:56 +00:00
}
2012-07-24 12:42:38 +00:00
2014-12-25 17:50:02 +00:00
$ self - > { list } = Wx::ListView - > new ( $ self , - 1 , wxDefaultPosition , wxDefaultSize ,
wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS ) ;
2013-08-25 10:22:05 +00:00
$ self - > { list } - > InsertColumn ( 0 , "Name" , wxLIST_FORMAT_LEFT , 145 ) ;
$ self - > { list } - > InsertColumn ( 1 , "Copies" , wxLIST_FORMAT_CENTER , 45 ) ;
2012-07-24 12:42:38 +00:00
$ self - > { list } - > InsertColumn ( 2 , "Scale" , wxLIST_FORMAT_CENTER , wxLIST_AUTOSIZE_USEHEADER ) ;
EVT_LIST_ITEM_SELECTED ( $ self , $ self - > { list } , \ & list_item_selected ) ;
EVT_LIST_ITEM_DESELECTED ( $ self , $ self - > { list } , \ & list_item_deselected ) ;
2012-10-24 20:44:08 +00:00
EVT_LIST_ITEM_ACTIVATED ( $ self , $ self - > { list } , \ & list_item_activated ) ;
2012-07-24 12:42:38 +00:00
EVT_KEY_DOWN ( $ self - > { list } , sub {
my ( $ list , $ event ) = @ _ ;
if ( $ event - > GetKeyCode == WXK_TAB ) {
$ list - > Navigate ( $ event - > ShiftDown ? & Wx:: wxNavigateBackward : & Wx:: wxNavigateForward ) ;
} else {
$ event - > Skip ;
}
} ) ;
2012-05-04 09:22:56 +00:00
2013-08-25 10:22:05 +00:00
# right pane buttons
2014-07-04 08:32:32 +00:00
$ self - > { btn_export_gcode } = Wx::Button - > new ( $ self , - 1 , "Export G-code…" , wxDefaultPosition , [ - 1 , 30 ] , wxBU_LEFT ) ;
2016-10-25 11:24:42 +00:00
$ self - > { btn_reslice } = Wx::Button - > new ( $ self , - 1 , "Slice now" , wxDefaultPosition , [ - 1 , 30 ] , wxBU_LEFT ) ;
2015-01-03 22:25:55 +00:00
$ self - > { btn_print } = Wx::Button - > new ( $ self , - 1 , "Print…" , wxDefaultPosition , [ - 1 , 30 ] , wxBU_LEFT ) ;
2014-12-28 00:30:05 +00:00
$ self - > { btn_send_gcode } = Wx::Button - > new ( $ self , - 1 , "Send to printer" , wxDefaultPosition , [ - 1 , 30 ] , wxBU_LEFT ) ;
2014-07-04 08:32:32 +00:00
$ self - > { btn_export_stl } = Wx::Button - > new ( $ self , - 1 , "Export STL…" , wxDefaultPosition , [ - 1 , 30 ] , wxBU_LEFT ) ;
2014-12-25 17:50:02 +00:00
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
2015-01-03 22:25:55 +00:00
$ self - > { btn_print } - > Hide ;
2014-12-28 00:30:05 +00:00
$ self - > { btn_send_gcode } - > Hide ;
2012-05-04 09:22:56 +00:00
2013-08-25 16:01:59 +00:00
if ( $ Slic3r:: GUI:: have_button_icons ) {
2012-05-04 09:22:56 +00:00
my % icons = qw(
2014-03-25 14:07:21 +00:00
add brick_add . png
2012-05-04 09:22:56 +00:00
remove brick_delete . png
reset cross . png
arrange bricks . png
2012-05-19 13:02:23 +00:00
export_gcode cog_go . png
2015-01-03 22:25:55 +00:00
print arrow_up . png
2014-12-29 10:45:09 +00:00
send_gcode arrow_up . png
2016-10-25 11:24:42 +00:00
reslice reslice . png
2012-05-04 09:22:56 +00:00
export_stl brick_go . png
increase add . png
decrease delete . png
rotate45cw arrow_rotate_clockwise . png
rotate45ccw arrow_rotate_anticlockwise . png
changescale arrow_out . png
split shape_ungroup . png
2014-12-10 16:34:59 +00:00
cut package . png
2013-08-25 16:08:56 +00:00
settings cog . png
2012-05-04 09:22:56 +00:00
) ;
2012-05-20 14:24:10 +00:00
for ( grep $ self - > { "btn_$_" } , keys % icons ) {
2016-04-09 17:10:57 +00:00
$ self - > { "btn_$_" } - > SetBitmap ( Wx::Bitmap - > new ( $ Slic3r:: var - > ( $ icons { $ _ } ) , wxBITMAP_TYPE_PNG ) ) ;
2012-05-20 14:24:10 +00:00
}
2012-05-01 14:49:34 +00:00
}
2012-04-30 12:56:01 +00:00
$ self - > selection_changed ( 0 ) ;
$ self - > object_list_changed ;
2014-06-13 17:23:51 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_export_gcode } , sub {
$ self - > export_gcode ;
} ) ;
2015-01-03 22:25:55 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_print } , sub {
$ self - > { print_file } = $ self - > export_gcode ( Wx::StandardPaths::Get - > GetTempDir ( ) ) ;
} ) ;
2014-12-28 00:30:05 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_send_gcode } , sub {
$ self - > { send_gcode_file } = $ self - > export_gcode ( Wx::StandardPaths::Get - > GetTempDir ( ) ) ;
} ) ;
2016-10-25 11:24:42 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_reslice } , \ & reslice ) ;
2014-12-28 13:58:24 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_export_stl } , \ & export_stl ) ;
2012-04-30 12:56:01 +00:00
2012-05-04 09:22:56 +00:00
if ( $ self - > { htoolbar } ) {
2014-06-17 14:27:38 +00:00
EVT_TOOL ( $ self , TB_ADD , sub { $ self - > add ; } ) ;
2013-08-25 10:22:05 +00:00
EVT_TOOL ( $ self , TB_REMOVE , sub { $ self - > remove ( ) } ) ; # explicitly pass no argument to remove
2014-06-17 14:27:38 +00:00
EVT_TOOL ( $ self , TB_RESET , sub { $ self - > reset ; } ) ;
EVT_TOOL ( $ self , TB_ARRANGE , sub { $ self - > arrange ; } ) ;
EVT_TOOL ( $ self , TB_MORE , sub { $ self - > increase ; } ) ;
EVT_TOOL ( $ self , TB_FEWER , sub { $ self - > decrease ; } ) ;
2017-03-23 10:53:59 +00:00
EVT_TOOL ( $ self , TB_45CW , sub { $ _ [ 0 ] - > rotate ( - 45 , Z , 'relative' ) } ) ;
EVT_TOOL ( $ self , TB_45CCW , sub { $ _ [ 0 ] - > rotate ( 45 , Z , 'relative' ) } ) ;
2014-06-17 14:27:38 +00:00
EVT_TOOL ( $ self , TB_SCALE , sub { $ self - > changescale ( undef ) ; } ) ;
EVT_TOOL ( $ self , TB_SPLIT , sub { $ self - > split_object ; } ) ;
2014-12-10 16:34:59 +00:00
EVT_TOOL ( $ self , TB_CUT , sub { $ _ [ 0 ] - > object_cut_dialog } ) ;
2013-08-25 16:01:59 +00:00
EVT_TOOL ( $ self , TB_SETTINGS , sub { $ _ [ 0 ] - > object_settings_dialog } ) ;
2016-12-14 14:09:12 +00:00
EVT_TOOL ( $ self , TB_LAYER_EDITING , sub {
my $ state = $ self - > { canvas3D } - > layer_editing_enabled ;
$ self - > { htoolbar } - > ToggleTool ( TB_LAYER_EDITING , ! $ state ) ;
$ self - > on_layer_editing_toggled ( ! $ state ) ;
} ) ;
2012-05-04 09:22:56 +00:00
} else {
2014-06-17 14:27:38 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_add } , sub { $ self - > add ; } ) ;
2013-08-25 14:10:53 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_remove } , sub { $ self - > remove ( ) } ) ; # explicitly pass no argument to remove
2014-06-17 14:27:38 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_reset } , sub { $ self - > reset ; } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_arrange } , sub { $ self - > arrange ; } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_increase } , sub { $ self - > increase ; } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_decrease } , sub { $ self - > decrease ; } ) ;
2017-03-23 10:53:59 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_rotate45cw } , sub { $ _ [ 0 ] - > rotate ( - 45 , Z , 'relative' ) } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_rotate45ccw } , sub { $ _ [ 0 ] - > rotate ( 45 , Z , 'relative' ) } ) ;
2014-06-17 14:27:38 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_changescale } , sub { $ self - > changescale ( undef ) ; } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_split } , sub { $ self - > split_object ; } ) ;
2014-12-10 16:34:59 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_cut } , sub { $ _ [ 0 ] - > object_cut_dialog } ) ;
2013-08-25 16:01:59 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_settings } , sub { $ _ [ 0 ] - > object_settings_dialog } ) ;
2016-12-12 17:02:24 +00:00
EVT_TOGGLEBUTTON ( $ self , $ self - > { btn_layer_editing } , sub { $ self - > on_layer_editing_toggled ( $ self - > { btn_layer_editing } - > GetValue ) ; } ) ;
2012-05-04 09:22:56 +00:00
}
2012-05-04 08:15:33 +00:00
$ _ - > SetDropTarget ( Slic3r::GUI::Plater::DropTarget - > new ( $ self ) )
2015-01-18 18:36:47 +00:00
for grep defined ( $ _ ) ,
$ self , $ self - > { canvas } , $ self - > { canvas3D } , $ self - > { preview3D } , $ self - > { list } ;
2012-04-30 12:56:01 +00:00
2012-04-30 18:59:14 +00:00
EVT_COMMAND ( $ self , - 1 , $ THUMBNAIL_DONE_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
2013-11-12 13:30:13 +00:00
my ( $ obj_idx ) = @ { $ event - > GetData } ;
2013-06-07 11:54:40 +00:00
return if ! $ self - > { objects } [ $ obj_idx ] ; # object was deleted before thumbnail generation completed
2013-07-13 22:38:01 +00:00
2012-10-01 16:12:14 +00:00
$ self - > on_thumbnail_made ( $ obj_idx ) ;
2012-04-30 18:59:14 +00:00
} ) ;
2012-05-05 19:08:15 +00:00
EVT_COMMAND ( $ self , - 1 , $ PROGRESS_BAR_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
my ( $ percent , $ message ) = @ { $ event - > GetData } ;
2014-06-13 12:27:55 +00:00
$ self - > on_progress_event ( $ percent , $ message ) ;
2012-05-05 19:08:15 +00:00
} ) ;
2014-06-13 12:27:55 +00:00
EVT_COMMAND ( $ self , - 1 , $ ERROR_EVENT , sub {
2012-05-05 19:08:15 +00:00
my ( $ self , $ event ) = @ _ ;
2014-06-13 12:27:55 +00:00
Slic3r::GUI:: show_error ( $ self , @ { $ event - > GetData } ) ;
2012-05-05 19:08:15 +00:00
} ) ;
EVT_COMMAND ( $ self , - 1 , $ EXPORT_COMPLETED_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
2014-06-13 12:27:55 +00:00
$ self - > on_export_completed ( $ event - > GetData ) ;
2012-05-19 19:13:10 +00:00
} ) ;
2014-06-13 09:19:53 +00:00
EVT_COMMAND ( $ self , - 1 , $ PROCESS_COMPLETED_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
2014-06-13 12:27:55 +00:00
$ self - > on_process_completed ( $ event - > GetData ) ;
2014-06-13 09:19:53 +00:00
} ) ;
2015-01-18 20:31:09 +00:00
if ( $ Slic3r:: have_threads ) {
my $ timer_id = Wx:: NewId ( ) ;
$ self - > { apply_config_timer } = Wx::Timer - > new ( $ self , $ timer_id ) ;
EVT_TIMER ( $ self , $ timer_id , sub {
my ( $ self , $ event ) = @ _ ;
$ self - > async_apply_config ;
} ) ;
}
2014-06-13 13:54:13 +00:00
2014-05-28 10:29:43 +00:00
$ self - > { canvas } - > update_bed_size ;
2014-12-16 00:12:37 +00:00
if ( $ self - > { canvas3D } ) {
$ self - > { canvas3D } - > update_bed_size ;
$ self - > { canvas3D } - > zoom_to_bed ;
}
2015-01-18 18:36:47 +00:00
if ( $ self - > { preview3D } ) {
$ self - > { preview3D } - > set_bed_shape ( $ self - > { config } - > bed_shape ) ;
}
2013-12-18 17:54:11 +00:00
$ self - > update ;
2012-04-30 12:56:01 +00:00
{
2013-08-25 10:22:05 +00:00
my $ presets ;
2014-06-14 17:54:18 +00:00
if ( $ self - > GetFrame - > { mode } eq 'expert' ) {
2014-12-25 18:14:18 +00:00
$ presets = $ self - > { presets_sizer } = Wx::FlexGridSizer - > new ( 3 , 2 , 1 , 2 ) ;
2014-12-25 17:50:02 +00:00
$ presets - > AddGrowableCol ( 1 , 1 ) ;
$ presets - > SetFlexibleDirection ( wxHORIZONTAL ) ;
2013-01-03 14:49:20 +00:00
my % group_labels = (
print = > 'Print settings' ,
filament = > 'Filament' ,
printer = > 'Printer' ,
) ;
2016-10-24 14:07:36 +00:00
# UI Combo boxes for a print, multiple filaments, and a printer.
# Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase,
# once a printer preset with multiple extruders is activated.
# $self->{preset_choosers}{$group}[$idx]
2013-01-03 14:49:20 +00:00
$ self - > { preset_choosers } = { } ;
2016-10-24 14:07:36 +00:00
# Boolean indicating whether the '- default -' preset is shown by the combo box.
$ self - > { preset_choosers_default_suppressed } = { } ;
2013-01-03 14:49:20 +00:00
for my $ group ( qw( print filament printer ) ) {
my $ text = Wx::StaticText - > new ( $ self , - 1 , "$group_labels{$group}:" , wxDefaultPosition , wxDefaultSize , wxALIGN_RIGHT ) ;
2013-08-25 10:22:05 +00:00
$ text - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2015-05-26 00:01:43 +00:00
my $ choice = Wx::BitmapComboBox - > new ( $ self , - 1 , "" , wxDefaultPosition , wxDefaultSize , [] , wxCB_READONLY ) ;
2017-05-24 13:20:20 +00:00
EVT_LEFT_DOWN ( $ choice , sub { $ self - > filament_color_box_lmouse_down ( 0 , @ _ ) ; } ) ;
2013-01-03 14:49:20 +00:00
$ self - > { preset_choosers } { $ group } = [ $ choice ] ;
2016-10-24 14:07:36 +00:00
$ self - > { preset_choosers_default_suppressed } { $ group } = 0 ;
2015-06-02 09:19:11 +00:00
# setup the listener
EVT_COMBOBOX ( $ choice , $ choice , sub {
my ( $ choice ) = @ _ ;
wxTheApp - > CallAfter ( sub {
$ self - > _on_select_preset ( $ group , $ choice ) ;
} ) ;
} ) ;
2014-12-25 18:42:24 +00:00
$ presets - > Add ( $ text , 0 , wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT , 4 ) ;
2015-05-26 00:01:43 +00:00
$ presets - > Add ( $ choice , 1 , wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM , 0 ) ;
2013-08-25 10:22:05 +00:00
}
}
my $ object_info_sizer ;
{
my $ box = Wx::StaticBox - > new ( $ self , - 1 , "Info" ) ;
$ object_info_sizer = Wx::StaticBoxSizer - > new ( $ box , wxVERTICAL ) ;
2014-06-16 13:18:39 +00:00
$ object_info_sizer - > SetMinSize ( [ 350 , - 1 ] ) ;
2013-08-25 15:26:55 +00:00
my $ grid_sizer = Wx::FlexGridSizer - > new ( 3 , 4 , 5 , 5 ) ;
$ grid_sizer - > SetFlexibleDirection ( wxHORIZONTAL ) ;
$ grid_sizer - > AddGrowableCol ( 1 , 1 ) ;
$ grid_sizer - > AddGrowableCol ( 3 , 1 ) ;
$ object_info_sizer - > Add ( $ grid_sizer , 0 , wxEXPAND ) ;
2013-08-25 10:22:05 +00:00
my @ info = (
2013-08-25 15:26:55 +00:00
size = > "Size" ,
volume = > "Volume" ,
facets = > "Facets" ,
materials = > "Materials" ,
manifold = > "Manifold" ,
2013-08-25 10:22:05 +00:00
) ;
while ( my $ field = shift @ info ) {
my $ label = shift @ info ;
2014-07-03 15:12:47 +00:00
my $ text = Wx::StaticText - > new ( $ self , - 1 , "$label:" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT ) ;
2013-08-25 10:22:05 +00:00
$ text - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
$ grid_sizer - > Add ( $ text , 0 ) ;
2014-07-03 15:12:47 +00:00
$ self - > { "object_info_$field" } = Wx::StaticText - > new ( $ self , - 1 , "" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT ) ;
2013-08-25 10:22:05 +00:00
$ self - > { "object_info_$field" } - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2013-08-25 15:26:55 +00:00
if ( $ field eq 'manifold' ) {
2016-04-09 17:10:57 +00:00
$ self - > { object_info_manifold_warning_icon } = Wx::StaticBitmap - > new ( $ self , - 1 , Wx::Bitmap - > new ( $ Slic3r:: var - > ( "error.png" ) , wxBITMAP_TYPE_PNG ) ) ;
2013-08-25 15:26:55 +00:00
$ self - > { object_info_manifold_warning_icon } - > Hide ;
my $ h_sizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
$ h_sizer - > Add ( $ self - > { object_info_manifold_warning_icon } , 0 ) ;
$ h_sizer - > Add ( $ self - > { "object_info_$field" } , 0 ) ;
$ grid_sizer - > Add ( $ h_sizer , 0 , wxEXPAND ) ;
} else {
$ grid_sizer - > Add ( $ self - > { "object_info_$field" } , 0 ) ;
}
2013-01-03 14:49:20 +00:00
}
}
2017-01-21 03:21:57 +00:00
my $ print_info_sizer ;
{
my $ box = Wx::StaticBox - > new ( $ self , - 1 , "Sliced Info" ) ;
$ print_info_sizer = Wx::StaticBoxSizer - > new ( $ box , wxVERTICAL ) ;
$ print_info_sizer - > SetMinSize ( [ 350 , - 1 ] ) ;
my $ grid_sizer = Wx::FlexGridSizer - > new ( 2 , 2 , 5 , 5 ) ;
$ grid_sizer - > SetFlexibleDirection ( wxHORIZONTAL ) ;
$ grid_sizer - > AddGrowableCol ( 1 , 1 ) ;
$ grid_sizer - > AddGrowableCol ( 3 , 1 ) ;
$ print_info_sizer - > Add ( $ grid_sizer , 0 , wxEXPAND ) ;
my @ info = (
fil_mm3 = > "Used Filament (mm^3)" ,
fil_g = > "Used Filament (g)" ,
cost = > "Cost" ,
) ;
while ( my $ field = shift @ info ) {
my $ label = shift @ info ;
my $ text = Wx::StaticText - > new ( $ self , - 1 , "$label:" , wxDefaultPosition , wxDefaultSize , wxALIGN_RIGHT ) ;
$ text - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
$ grid_sizer - > Add ( $ text , 0 ) ;
$ self - > { "print_info_$field" } = Wx::StaticText - > new ( $ self , - 1 , "" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT ) ;
$ self - > { "print_info_$field" } - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
$ grid_sizer - > Add ( $ self - > { "print_info_$field" } , 0 ) ;
}
}
2012-06-19 15:23:10 +00:00
2014-12-25 17:50:02 +00:00
my $ buttons_sizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
2016-10-25 11:24:42 +00:00
$ self - > { buttons_sizer } = $ buttons_sizer ;
2014-12-25 17:50:02 +00:00
$ buttons_sizer - > AddStretchSpacer ( 1 ) ;
$ buttons_sizer - > Add ( $ self - > { btn_export_stl } , 0 , wxALIGN_RIGHT , 0 ) ;
2016-10-25 11:24:42 +00:00
$ buttons_sizer - > Add ( $ self - > { btn_reslice } , 0 , wxALIGN_RIGHT , 0 ) ;
2015-01-03 22:25:55 +00:00
$ buttons_sizer - > Add ( $ self - > { btn_print } , 0 , wxALIGN_RIGHT , 0 ) ;
2014-12-28 00:30:05 +00:00
$ buttons_sizer - > Add ( $ self - > { btn_send_gcode } , 0 , wxALIGN_RIGHT , 0 ) ;
2014-12-25 17:50:02 +00:00
$ buttons_sizer - > Add ( $ self - > { btn_export_gcode } , 0 , wxALIGN_RIGHT , 0 ) ;
2013-08-25 10:22:05 +00:00
my $ right_sizer = Wx::BoxSizer - > new ( wxVERTICAL ) ;
2014-12-25 17:50:02 +00:00
$ right_sizer - > Add ( $ presets , 0 , wxEXPAND | wxTOP , 10 ) if defined $ presets ;
$ right_sizer - > Add ( $ buttons_sizer , 0 , wxEXPAND | wxBOTTOM , 5 ) ;
$ right_sizer - > Add ( $ self - > { list } , 1 , wxEXPAND , 5 ) ;
$ right_sizer - > Add ( $ object_info_sizer , 0 , wxEXPAND , 0 ) ;
2017-01-21 03:21:57 +00:00
$ right_sizer - > Add ( $ print_info_sizer , 0 , wxEXPAND , 0 ) ;
2017-02-15 15:02:54 +00:00
# Callback for showing / hiding the print info box.
$ self - > { "print_info_box_show" } = sub {
if ( $ right_sizer - > IsShown ( 4 ) != $ _ [ 0 ] ) {
$ right_sizer - > Show ( 4 , $ _ [ 0 ] ) ;
$ self - > Layout
}
} ;
# Show the box initially, let it be shown after the slicing is finished.
$ self - > { "print_info_box_show" } - > ( 0 ) ;
2013-08-25 10:22:05 +00:00
my $ hsizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
2014-07-13 10:10:34 +00:00
$ hsizer - > Add ( $ self - > { preview_notebook } , 1 , wxEXPAND | wxTOP , 1 ) ;
2014-12-25 17:50:02 +00:00
$ hsizer - > Add ( $ right_sizer , 0 , wxEXPAND | wxLEFT | wxRIGHT , 3 ) ;
2013-08-25 10:22:05 +00:00
my $ sizer = Wx::BoxSizer - > new ( wxVERTICAL ) ;
$ sizer - > Add ( $ self - > { htoolbar } , 0 , wxEXPAND , 0 ) if $ self - > { htoolbar } ;
$ sizer - > Add ( $ self - > { btoolbar } , 0 , wxEXPAND , 0 ) if $ self - > { btoolbar } ;
$ sizer - > Add ( $ hsizer , 1 , wxEXPAND , 0 ) ;
2012-04-30 12:56:01 +00:00
$ sizer - > SetSizeHints ( $ self ) ;
$ self - > SetSizer ( $ sizer ) ;
}
2016-10-25 11:24:42 +00:00
$ self - > update_ui_from_settings ( ) ;
2014-07-01 14:40:56 +00:00
2012-04-30 12:56:01 +00:00
return $ self ;
}
2014-07-01 14:40:56 +00:00
# sets the callback
2012-10-24 18:24:11 +00:00
sub on_select_preset {
2014-07-01 14:40:56 +00:00
my ( $ self , $ cb ) = @ _ ;
$ self - > { on_select_preset } = $ cb ;
}
sub _on_select_preset {
2012-10-24 18:24:11 +00:00
my $ self = shift ;
my ( $ group , $ choice ) = @ _ ;
2016-10-24 14:07:36 +00:00
# If user changed a filament preset and the selected machine is equipped with multiple extruders,
# there are multiple filament selection combo boxes shown at the platter. In that case
# don't propagate the filament selection changes to the tab.
my $ default_suppressed = $ self - > { preset_choosers_default_suppressed } { $ group } ;
2012-10-24 18:24:11 +00:00
if ( $ group eq 'filament' && @ { $ self - > { preset_choosers } { filament } } > 1 ) {
2016-10-24 14:07:36 +00:00
# Indices of the filaments selected.
2012-10-24 18:24:11 +00:00
my @ filament_presets = $ self - > filament_presets ;
2016-10-24 14:07:36 +00:00
$ Slic3r:: GUI:: Settings - > { presets } { filament } = $ choice - > GetString ( $ filament_presets [ 0 ] - $ default_suppressed ) . ".ini" ;
$ Slic3r:: GUI:: Settings - > { presets } { "filament_${_}" } = $ choice - > GetString ( $ filament_presets [ $ _ ] - $ default_suppressed )
2012-10-24 18:24:11 +00:00
for 1 .. $# filament_presets ;
2014-06-14 17:59:59 +00:00
wxTheApp - > save_settings ;
2017-05-24 13:20:20 +00:00
$ self - > update_filament_colors_preview ( $ choice ) ;
} else {
# call GetSelection() in scalar context as it's context-aware
$ self - > { on_select_preset } - > ( $ group , scalar ( $ choice - > GetSelection ) + $ default_suppressed )
if $ self - > { on_select_preset } ;
}
2014-07-01 14:40:56 +00:00
# get new config and generate on_config_change() event for updating plater and other things
$ self - > on_config_change ( $ self - > GetFrame - > config ) ;
2012-10-24 18:24:11 +00:00
}
2016-12-12 17:02:24 +00:00
sub on_layer_editing_toggled {
my ( $ self , $ new_state ) = @ _ ;
$ self - > { canvas3D } - > layer_editing_enabled ( $ new_state ) ;
2017-02-19 17:01:03 +00:00
if ( $ new_state && ! $ self - > { canvas3D } - > layer_editing_enabled ) {
# Initialization of the OpenGL shaders failed. Disable the tool.
if ( $ self - > { htoolbar } ) {
$ self - > { htoolbar } - > EnableTool ( TB_LAYER_EDITING , 0 ) ;
$ self - > { htoolbar } - > ToggleTool ( TB_LAYER_EDITING , 0 ) ;
} else {
$ self - > { "btn_layer_editing" } - > Disable ;
$ self - > { "btn_layer_editing" } - > SetValue ( 0 ) ;
}
}
2017-05-17 14:53:40 +00:00
$ self - > { canvas3D } - > Refresh ;
$ self - > { canvas3D } - > Update ;
2016-12-12 17:02:24 +00:00
}
2014-06-14 16:58:56 +00:00
sub GetFrame {
my ( $ self ) = @ _ ;
2014-06-14 17:54:18 +00:00
return & Wx:: GetTopLevelParent ( $ self ) ;
2012-07-27 19:13:03 +00:00
}
2016-10-25 11:24:42 +00:00
# Called after the Preferences dialog is closed and the program settings are saved.
# Update the UI based on the current preferences.
sub update_ui_from_settings
{
my ( $ self ) = @ _ ;
if ( defined ( $ self - > { btn_reslice } ) && $ self - > { buttons_sizer } - > IsShown ( $ self - > { btn_reslice } ) != ( ! $ Slic3r:: GUI:: Settings - > { _ } { background_processing } ) ) {
$ self - > { buttons_sizer } - > Show ( $ self - > { btn_reslice } , ! $ Slic3r:: GUI:: Settings - > { _ } { background_processing } ) ;
$ self - > { buttons_sizer } - > Layout ;
}
}
2017-02-07 17:28:53 +00:00
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
2016-10-24 14:07:36 +00:00
# Called by
# Slic3r::GUI::Tab::Print::_on_presets_changed
# Slic3r::GUI::Tab::Filament::_on_presets_changed
# Slic3r::GUI::Tab::Printer::_on_presets_changed
# when the presets are loaded or the user selects another preset.
# For Print settings and Printer, synchronize the selection index with their tabs.
# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection.
2012-08-07 16:44:47 +00:00
sub update_presets {
my $ self = shift ;
2016-10-24 14:07:36 +00:00
# $presets: one of qw(print filament printer)
# $selected: index of the selected preset in the array. This may not correspond
# with the index of selection in the UI element, where not all items are displayed.
my ( $ group , $ presets , $ default_suppressed , $ selected , $ is_dirty ) = @ _ ;
2012-08-07 16:44:47 +00:00
2015-06-01 21:52:15 +00:00
my @ choosers = @ { $ self - > { preset_choosers } { $ group } } ;
2017-05-24 13:20:20 +00:00
my $ choice_idx = 0 ;
2015-06-01 21:52:15 +00:00
foreach my $ choice ( @ choosers ) {
if ( $ group eq 'filament' && @ choosers > 1 ) {
# if we have more than one filament chooser, keep our selection
# instead of importing the one from the tab
2016-10-24 14:07:36 +00:00
$ selected = $ choice - > GetSelection + $ self - > { preset_choosers_default_suppressed } { $ group } ;
2015-06-01 21:52:15 +00:00
$ is_dirty = 0 ;
}
2012-08-07 16:44:47 +00:00
$ choice - > Clear ;
2015-05-26 00:01:43 +00:00
foreach my $ preset ( @$ presets ) {
2016-10-24 14:07:36 +00:00
next if ( $ preset - > default && $ default_suppressed ) ;
2015-05-26 00:01:43 +00:00
my $ bitmap ;
if ( $ group eq 'filament' ) {
2017-05-24 13:20:20 +00:00
$ bitmap = Wx::Bitmap - > new ( $ Slic3r:: var - > ( "spool.png" ) , wxBITMAP_TYPE_PNG ) ;
2015-05-26 00:01:43 +00:00
} elsif ( $ group eq 'print' ) {
2016-04-09 17:10:57 +00:00
$ bitmap = Wx::Bitmap - > new ( $ Slic3r:: var - > ( "cog.png" ) , wxBITMAP_TYPE_PNG ) ;
2015-05-26 00:01:43 +00:00
} elsif ( $ group eq 'printer' ) {
2016-04-09 17:10:57 +00:00
$ bitmap = Wx::Bitmap - > new ( $ Slic3r:: var - > ( "printer_empty.png" ) , wxBITMAP_TYPE_PNG ) ;
2015-05-26 00:01:43 +00:00
}
$ choice - > AppendString ( $ preset - > name , $ bitmap ) ;
}
2015-06-01 21:34:04 +00:00
2015-06-01 21:52:15 +00:00
if ( $ selected <= $#$ presets ) {
2016-10-24 14:07:36 +00:00
my $ idx = $ selected - $ default_suppressed ;
if ( $ idx >= 0 ) {
if ( $ is_dirty ) {
$ choice - > SetString ( $ idx , $ choice - > GetString ( $ idx ) . " (modified)" ) ;
}
# call SetSelection() only after SetString() otherwise the new string
# won't be picked up as the visible string
$ choice - > SetSelection ( $ idx ) ;
2015-06-01 21:34:04 +00:00
}
}
2017-05-24 13:20:20 +00:00
$ choice_idx += 1 ;
2012-08-07 16:44:47 +00:00
}
2016-10-24 14:07:36 +00:00
$ self - > { preset_choosers_default_suppressed } { $ group } = $ default_suppressed ;
2017-05-24 13:20:20 +00:00
$ self - > update_filament_colors_preview ;
}
# Update the color icon in front of each filament selection on the platter.
# If the extruder has a preview color assigned, apply the extruder color to the active selection.
# Always apply the filament color to the non-active selections.
sub update_filament_colors_preview {
my ( $ self , $ extruder_idx ) = shift ;
2017-05-31 15:05:11 +00:00
my @ choosers = @ { $ self - > { preset_choosers } { filament } } ;
2017-05-24 13:20:20 +00:00
if ( ref $ extruder_idx ) {
# $extruder_idx is the chooser.
foreach my $ chooser ( @ choosers ) {
if ( $ extruder_idx == $ chooser ) {
$ extruder_idx = $ chooser ;
last ;
}
}
}
my @ extruder_colors = @ { $ self - > { config } - > extruder_colour } ;
my @ extruder_list ;
if ( defined $ extruder_idx ) {
@ extruder_list = ( $ extruder_idx ) ;
} else {
# Collect extruder indices.
@ extruder_list = ( 0 .. $# extruder_colors ) ;
}
my $ filament_tab = $ self - > GetFrame - > { options_tabs } { filament } ;
my $ presets = $ filament_tab - > { presets } ;
my $ default_suppressed = $ filament_tab - > { default_suppressed } ;
foreach my $ extruder_idx ( @ extruder_list ) {
my $ chooser = $ choosers [ $ extruder_idx ] ;
my $ extruder_color = $ self - > { config } - > extruder_colour - > [ $ extruder_idx ] ;
my $ preset_idx = 0 ;
my $ selection_idx = $ chooser - > GetSelection ;
foreach my $ preset ( @$ presets ) {
my $ bitmap ;
if ( $ preset - > default ) {
next if $ default_suppressed ;
} else {
# Assign an extruder color to the selected item if the extruder color is defined.
my $ filament_rgb = $ preset - > config ( [ 'filament_colour' ] ) - > filament_colour - > [ 0 ] ;
my $ extruder_rgb = ( $ preset_idx == $ selection_idx && $ extruder_color =~ m/^#[[:xdigit:]]{6}/ ) ? $ extruder_color : $ filament_rgb ;
$ filament_rgb =~ s/^#// ;
$ extruder_rgb =~ s/^#// ;
my $ image = Wx::Image - > new ( 24 , 16 ) ;
if ( $ filament_rgb ne $ extruder_rgb ) {
my @ rgb = unpack 'C*' , pack 'H*' , $ extruder_rgb ;
$ image - > SetRGB ( Wx::Rect - > new ( 0 , 0 , 16 , 16 ) , @ rgb ) ;
@ rgb = unpack 'C*' , pack 'H*' , $ filament_rgb ;
$ image - > SetRGB ( Wx::Rect - > new ( 16 , 0 , 8 , 16 ) , @ rgb ) ;
} else {
my @ rgb = unpack 'C*' , pack 'H*' , $ filament_rgb ;
$ image - > SetRGB ( Wx::Rect - > new ( 0 , 0 , 24 , 16 ) , @ rgb ) ;
}
$ bitmap = Wx::Bitmap - > new ( $ image ) ;
}
$ chooser - > SetItemBitmap ( $ preset_idx , $ bitmap ) if $ bitmap ;
$ preset_idx += 1 ;
}
}
2012-08-07 16:44:47 +00:00
}
2016-10-24 14:07:36 +00:00
# Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes.
2012-08-07 16:44:47 +00:00
sub filament_presets {
my $ self = shift ;
2015-06-01 12:32:31 +00:00
# force scalar context for GetSelection() as it's context-aware
2016-10-24 14:07:36 +00:00
return map scalar ( $ _ - > GetSelection ) + $ self - > { preset_choosers_default_suppressed } { filament } , @ { $ self - > { preset_choosers } { filament } } ;
2012-08-07 16:44:47 +00:00
}
2013-12-12 19:19:33 +00:00
sub add {
2012-04-30 12:56:01 +00:00
my $ self = shift ;
2014-06-14 17:59:59 +00:00
my @ input_files = wxTheApp - > open_model ( $ self ) ;
2012-05-31 07:26:30 +00:00
$ self - > load_file ( $ _ ) for @ input_files ;
2012-04-30 12:56:01 +00:00
}
sub load_file {
my $ self = shift ;
my ( $ input_file ) = @ _ ;
2012-07-30 10:08:28 +00:00
$ Slic3r:: GUI:: Settings - > { recent } { skein_directory } = dirname ( $ input_file ) ;
2014-06-14 17:59:59 +00:00
wxTheApp - > save_settings ;
2012-04-30 12:56:01 +00:00
2012-07-01 21:29:56 +00:00
my $ process_dialog = Wx::ProgressDialog - > new ( 'Loading…' , "Processing input file…" , 100 , $ self , 0 ) ;
2012-04-30 19:49:44 +00:00
$ process_dialog - > Pulse ;
2012-08-29 17:37:27 +00:00
2012-04-30 12:56:01 +00:00
local $ SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $ self ) ;
2013-12-17 15:12:44 +00:00
my $ model = eval { Slic3r::Model - > read_from_file ( $ input_file ) } ;
Slic3r::GUI:: show_error ( $ self , $@ ) if $@ ;
2016-12-17 20:47:45 +00:00
my @ obj_idx = ( ) ;
2014-03-25 18:06:51 +00:00
if ( defined $ model ) {
2016-03-19 21:22:11 +00:00
if ( $ model - > looks_like_multipart_object ) {
my $ dialog = Wx::MessageDialog - > new ( $ self ,
"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" ,
'Multi-part object detected' , wxICON_WARNING | wxYES | wxNO ) ;
if ( $ dialog - > ShowModal ( ) == wxID_YES ) {
$ model - > convert_multipart_object ;
}
}
2016-12-17 20:47:45 +00:00
@ obj_idx = $ self - > load_model_objects ( @ { $ model - > objects } ) ;
2014-03-25 18:06:51 +00:00
$ self - > statusbar - > SetStatusText ( "Loaded " . basename ( $ input_file ) ) ;
}
2013-12-12 19:19:33 +00:00
$ process_dialog - > Destroy ;
2016-12-17 20:47:45 +00:00
return @ obj_idx ;
2013-12-12 19:19:33 +00:00
}
2014-01-05 13:04:32 +00:00
sub load_model_objects {
my ( $ self , @ model_objects ) = @ _ ;
2013-12-12 19:19:33 +00:00
2014-06-16 21:58:45 +00:00
my $ bed_centerf = $ self - > bed_centerf ;
2015-05-25 17:51:47 +00:00
my $ bed_shape = Slic3r::Polygon - > new_scale ( @ { $ self - > { config } - > bed_shape } ) ;
my $ bed_size = $ bed_shape - > bounding_box - > size ;
2014-06-16 21:58:45 +00:00
2014-01-05 13:04:32 +00:00
my $ need_arrange = 0 ;
2015-05-25 17:51:47 +00:00
my $ scaled_down = 0 ;
2014-01-05 13:04:32 +00:00
my @ obj_idx = ( ) ;
foreach my $ model_object ( @ model_objects ) {
my $ o = $ self - > { model } - > add_object ( $ model_object ) ;
2017-02-09 13:56:13 +00:00
my $ object_name = $ model_object - > name ;
$ object_name = basename ( $ model_object - > input_file ) if ( $ object_name eq '' ) ;
push @ { $ self - > { objects } } , Slic3r::GUI::Plater::Object - > new ( name = > $ object_name ) ;
2014-01-05 13:04:32 +00:00
push @ obj_idx , $# { $ self - > { objects } } ;
2013-12-12 19:19:33 +00:00
2014-05-10 14:59:17 +00:00
if ( $ model_object - > instances_count == 0 ) {
2014-01-05 13:04:32 +00:00
# if object has no defined position(s) we need to rearrange everything after loading
$ need_arrange = 1 ;
2012-09-12 14:30:44 +00:00
2014-01-05 13:04:32 +00:00
# add a default instance and center object around origin
2014-12-12 21:43:04 +00:00
$ o - > center_around_origin ; # also aligns object to Z = 0
2014-06-16 21:58:45 +00:00
$ o - > add_instance ( offset = > $ bed_centerf ) ;
2014-01-05 13:04:32 +00:00
}
2015-05-25 17:51:47 +00:00
{
# if the object is too large (more than 5 times the bed), scale it down
my $ size = $ o - > bounding_box - > size ;
my $ ratio = max ( @$ size [ X , Y ] ) / unscale ( max ( @$ bed_size [ X , Y ] ) ) ;
if ( $ ratio > 5 ) {
$ _ - > set_scaling_factor ( 1 / $ ratio ) for @ { $ o - > instances } ;
$ scaled_down = 1 ;
}
}
2012-04-30 12:56:01 +00:00
2014-03-22 19:12:54 +00:00
$ self - > { print } - > auto_assign_extruders ( $ o ) ;
2014-05-06 23:11:49 +00:00
$ self - > { print } - > add_model_object ( $ o ) ;
2014-01-05 13:04:32 +00:00
}
2013-12-13 13:02:01 +00:00
2013-12-18 18:11:20 +00:00
# if user turned autocentering off, automatic arranging would disappoint them
if ( ! $ Slic3r:: GUI:: Settings - > { _ } { autocenter } ) {
$ need_arrange = 0 ;
}
2015-05-25 17:51:47 +00:00
if ( $ scaled_down ) {
Slic3r::GUI:: show_info (
$ self ,
'Your object appears to be too large, so it was automatically scaled down to fit your print bed.' ,
'Object too large?' ,
) ;
}
2012-04-30 22:30:46 +00:00
2015-05-25 17:51:47 +00:00
foreach my $ obj_idx ( @ obj_idx ) {
2014-01-05 13:04:32 +00:00
my $ object = $ self - > { objects } [ $ obj_idx ] ;
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
$ self - > { list } - > InsertStringItem ( $ obj_idx , $ object - > name ) ;
$ self - > { list } - > SetItemFont ( $ obj_idx , Wx::Font - > new ( 10 , wxDEFAULT , wxNORMAL , wxNORMAL ) )
if $ self - > { list } - > can ( 'SetItemFont' ) ; # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
2013-08-25 10:22:05 +00:00
2014-01-05 13:04:32 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ model_object - > instances_count ) ;
$ self - > { list } - > SetItem ( $ obj_idx , 2 , ( $ model_object - > instances - > [ 0 ] - > scaling_factor * 100 ) . "%" ) ;
2012-04-30 12:56:01 +00:00
2014-01-05 13:04:32 +00:00
$ self - > make_thumbnail ( $ obj_idx ) ;
}
2015-05-25 17:51:47 +00:00
$ self - > arrange if $ need_arrange ;
2013-12-18 17:54:11 +00:00
$ self - > update ;
2014-12-16 00:12:37 +00:00
# zoom to objects
$ self - > { canvas3D } - > zoom_to_volumes
if $ self - > { canvas3D } ;
2012-04-30 12:56:01 +00:00
$ self - > { list } - > Update ;
2015-05-25 17:51:47 +00:00
$ self - > { list } - > Select ( $ obj_idx [ - 1 ] , 1 ) ;
2012-04-30 12:56:01 +00:00
$ self - > object_list_changed ;
2014-06-13 09:19:53 +00:00
2014-06-13 18:36:45 +00:00
$ self - > schedule_background_process ;
2016-12-17 20:47:45 +00:00
return @ obj_idx ;
2012-04-30 12:56:01 +00:00
}
2014-06-16 21:58:45 +00:00
sub bed_centerf {
my ( $ self ) = @ _ ;
my $ bed_shape = Slic3r::Polygon - > new_scale ( @ { $ self - > { config } - > bed_shape } ) ;
my $ bed_center = $ bed_shape - > bounding_box - > center ;
return Slic3r::Pointf - > new ( unscale ( $ bed_center - > x ) , unscale ( $ bed_center - > y ) ) ; #)
}
2012-04-30 12:56:01 +00:00
sub remove {
my $ self = shift ;
2012-08-11 14:00:41 +00:00
my ( $ obj_idx ) = @ _ ;
2012-04-30 12:56:01 +00:00
2014-06-14 18:26:53 +00:00
$ self - > stop_background_process ;
2014-12-07 19:23:00 +00:00
# Prevent toolpaths preview from rendering while we modify the Print object
$ self - > { toolpaths2D } - > enabled ( 0 ) if $ self - > { toolpaths2D } ;
2015-01-18 18:36:47 +00:00
$ self - > { preview3D } - > enabled ( 0 ) if $ self - > { preview3D } ;
2014-12-07 19:23:00 +00:00
2012-08-29 17:37:27 +00:00
# if no object index is supplied, remove the selected one
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
2012-04-30 12:56:01 +00:00
}
2012-08-29 17:37:27 +00:00
splice @ { $ self - > { objects } } , $ obj_idx , 1 ;
2013-12-12 19:19:33 +00:00
$ self - > { model } - > delete_object ( $ obj_idx ) ;
2013-12-15 15:17:12 +00:00
$ self - > { print } - > delete_object ( $ obj_idx ) ;
2012-08-29 17:37:27 +00:00
$ self - > { list } - > DeleteItem ( $ obj_idx ) ;
2012-04-30 12:56:01 +00:00
$ self - > object_list_changed ;
2013-12-12 19:19:33 +00:00
$ self - > select_object ( undef ) ;
2013-12-18 17:54:11 +00:00
$ self - > update ;
2014-06-14 18:26:53 +00:00
$ self - > schedule_background_process ;
2012-04-30 12:56:01 +00:00
}
sub reset {
my $ self = shift ;
2014-06-14 18:26:53 +00:00
$ self - > stop_background_process ;
2014-12-07 19:23:00 +00:00
# Prevent toolpaths preview from rendering while we modify the Print object
$ self - > { toolpaths2D } - > enabled ( 0 ) if $ self - > { toolpaths2D } ;
2015-01-18 18:36:47 +00:00
$ self - > { preview3D } - > enabled ( 0 ) if $ self - > { preview3D } ;
2014-12-07 19:23:00 +00:00
2012-08-29 17:37:27 +00:00
@ { $ self - > { objects } } = ( ) ;
2014-05-06 22:58:29 +00:00
$ self - > { model } - > clear_objects ;
$ self - > { print } - > clear_objects ;
2012-04-30 12:56:01 +00:00
$ self - > { list } - > DeleteAllItems ;
$ self - > object_list_changed ;
2013-12-12 19:19:33 +00:00
$ self - > select_object ( undef ) ;
2014-07-14 09:58:00 +00:00
$ self - > update ;
2012-04-30 12:56:01 +00:00
}
sub increase {
2015-01-14 22:19:13 +00:00
my ( $ self , $ copies ) = @ _ ;
2012-04-30 12:56:01 +00:00
2015-01-14 22:19:13 +00:00
$ copies // = 1 ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2013-12-12 19:19:33 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
2015-01-14 22:19:13 +00:00
my $ instance = $ model_object - > instances - > [ - 1 ] ;
for my $ i ( 1 .. $ copies ) {
$ instance = $ model_object - > add_instance (
offset = > Slic3r::Pointf - > new ( map 10 + $ _ , @ { $ instance - > offset } ) ,
scaling_factor = > $ instance - > scaling_factor ,
rotation = > $ instance - > rotation ,
) ;
$ self - > { print } - > objects - > [ $ obj_idx ] - > add_copy ( $ instance - > offset ) ;
}
2013-12-12 19:19:33 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ model_object - > instances_count ) ;
2013-12-18 18:11:20 +00:00
# only autoarrange if user has autocentering enabled
2014-09-21 08:56:51 +00:00
$ self - > stop_background_process ;
2013-12-18 18:11:20 +00:00
if ( $ Slic3r:: GUI:: Settings - > { _ } { autocenter } ) {
$ self - > arrange ;
} else {
2014-07-13 10:10:34 +00:00
$ self - > update ;
2013-12-18 18:11:20 +00:00
}
2014-09-21 08:56:51 +00:00
$ self - > schedule_background_process ;
2012-04-30 12:56:01 +00:00
}
sub decrease {
2015-01-14 22:19:13 +00:00
my ( $ self , $ copies ) = @ _ ;
2012-04-30 12:56:01 +00:00
2015-01-14 22:19:13 +00:00
$ copies // = 1 ;
2014-09-21 08:56:51 +00:00
$ self - > stop_background_process ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2013-12-12 19:19:33 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
2015-01-14 22:19:13 +00:00
if ( $ model_object - > instances_count > $ copies ) {
for my $ i ( 1 .. $ copies ) {
$ model_object - > delete_last_instance ;
$ self - > { print } - > objects - > [ $ obj_idx ] - > delete_last_copy ;
}
2013-12-12 19:19:33 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ model_object - > instances_count ) ;
2012-09-12 14:30:44 +00:00
} else {
$ self - > remove ;
}
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
if ( $ self - > { objects } [ $ obj_idx ] ) {
2012-04-30 12:56:01 +00:00
$ self - > { list } - > Select ( $ obj_idx , 0 ) ;
$ self - > { list } - > Select ( $ obj_idx , 1 ) ;
}
2013-12-18 17:54:11 +00:00
$ self - > update ;
2014-09-21 08:56:51 +00:00
$ self - > schedule_background_process ;
2012-04-30 12:56:01 +00:00
}
2015-01-14 22:19:13 +00:00
sub set_number_of_copies {
my ( $ self ) = @ _ ;
$ self - > pause_background_process ;
# get current number of copies
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
# prompt user
my $ copies = Wx:: GetNumberFromUser ( "" , "Enter the number of copies of the selected object:" , "Copies" , $ model_object - > instances_count , 0 , 1000 , $ self ) ;
my $ diff = $ copies - $ model_object - > instances_count ;
if ( $ diff == 0 ) {
# no variation
$ self - > resume_background_process ;
} elsif ( $ diff > 0 ) {
$ self - > increase ( $ diff ) ;
} elsif ( $ diff < 0 ) {
$ self - > decrease ( - $ diff ) ;
}
}
2017-03-23 13:32:19 +00:00
sub _get_number_from_user {
my ( $ self , $ title , $ prompt_message , $ error_message , $ default , $ only_positive ) = @ _ ;
for ( ; ; ) {
my $ value = Wx:: GetTextFromUser ( $ prompt_message , $ title , $ default , $ self ) ;
# Accept both dashes and dots as a decimal separator.
$ value =~ s/,/./ ;
# If scaling value is being entered, remove the trailing percent sign.
$ value =~ s/%$// if $ only_positive ;
# User canceled the selection, return undef.
return if $ value eq '' ;
# Validate a numeric value.
return $ value if ( $ value =~ /^-?\d*(?:\.\d*)?$/ ) && ( ! $ only_positive || $ value > 0 ) ;
Wx:: MessageBox (
$ error_message .
( ( $ only_positive && $ value <= 0 ) ?
": $value\nNon-positive value." :
": $value\nNot a numeric value." ) ,
"Slic3r Error" , wxOK | wxICON_EXCLAMATION , $ self ) ;
$ default = $ value ;
}
}
2012-04-30 12:56:01 +00:00
sub rotate {
2017-03-23 13:32:19 +00:00
my ( $ self , $ angle , $ axis , $ relative_key ) = @ _ ;
2017-03-23 10:53:59 +00:00
$ relative_key // = 'absolute' ; # relative or absolute coordinates
$ axis // = Z ; # angle is in degrees
my $ relative = $ relative_key eq 'relative' ;
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2014-06-14 18:52:21 +00:00
return if ! defined $ obj_idx ;
2013-12-12 19:19:33 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
my $ model_instance = $ model_object - > instances - > [ 0 ] ;
2012-04-30 12:56:01 +00:00
2012-11-19 16:39:16 +00:00
# we need thumbnail to be computed before allowing rotation
return if ! $ object - > thumbnail ;
2012-04-30 20:14:27 +00:00
if ( ! defined $ angle ) {
2014-06-14 18:52:21 +00:00
my $ axis_name = $ axis == X ? 'X' : $ axis == Y ? 'Y' : 'Z' ;
2016-12-16 23:26:57 +00:00
my $ default = $ axis == Z ? rad2deg ( $ model_instance - > rotation ) : 0 ;
2017-03-23 13:32:19 +00:00
$ angle = $ self - > _get_number_from_user ( "Enter the rotation angle:" , "Rotate around $axis_name axis" , "Invalid rotation angle entered" , $ default ) ;
return if $ angle eq '' ;
2012-04-30 20:14:27 +00:00
}
2014-07-01 14:40:56 +00:00
$ self - > stop_background_process ;
if ( $ axis == Z ) {
2016-12-16 23:26:57 +00:00
my $ new_angle = deg2rad ( $ angle ) ;
2017-03-23 10:53:59 +00:00
$ _ - > set_rotation ( ( $ relative ? $ _ - > rotation : 0 . ) + $ new_angle ) for @ { $ model_object - > instances } ;
2014-07-01 14:40:56 +00:00
$ object - > transform_thumbnail ( $ self - > { model } , $ obj_idx ) ;
} else {
# rotation around X and Y needs to be performed on mesh
# so we first apply any Z rotation
if ( $ model_instance - > rotation != 0 ) {
2015-05-03 16:40:00 +00:00
$ model_object - > rotate ( $ model_instance - > rotation , Z ) ;
2014-07-01 14:40:56 +00:00
$ _ - > set_rotation ( 0 ) for @ { $ model_object - > instances } ;
2014-06-14 18:52:21 +00:00
}
2014-07-01 14:40:56 +00:00
$ model_object - > rotate ( deg2rad ( $ angle ) , $ axis ) ;
2015-01-08 23:47:40 +00:00
# realign object to Z = 0
$ model_object - > center_around_origin ;
2014-07-01 14:40:56 +00:00
$ self - > make_thumbnail ( $ obj_idx ) ;
2013-12-12 19:19:33 +00:00
}
2014-07-01 14:40:56 +00:00
$ model_object - > update_bounding_box ;
# update print and start background processing
$ self - > { print } - > add_model_object ( $ model_object , $ obj_idx ) ;
2013-12-12 19:19:33 +00:00
$ self - > selection_changed ; # refresh info (size etc.)
2013-12-18 17:54:11 +00:00
$ self - > update ;
2014-12-29 21:29:24 +00:00
$ self - > schedule_background_process ;
2012-04-30 12:56:01 +00:00
}
2015-11-04 22:11:30 +00:00
sub mirror {
2014-06-14 19:14:33 +00:00
my ( $ self , $ axis ) = @ _ ;
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
return if ! defined $ obj_idx ;
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
my $ model_instance = $ model_object - > instances - > [ 0 ] ;
2015-11-04 22:11:30 +00:00
# apply Z rotation before mirroring
2014-06-14 19:14:33 +00:00
if ( $ model_instance - > rotation != 0 ) {
2015-05-03 16:40:00 +00:00
$ model_object - > rotate ( $ model_instance - > rotation , Z ) ;
2014-06-14 19:14:33 +00:00
$ _ - > set_rotation ( 0 ) for @ { $ model_object - > instances } ;
}
2015-11-04 22:11:30 +00:00
$ model_object - > mirror ( $ axis ) ;
2014-06-14 19:14:33 +00:00
$ model_object - > update_bounding_box ;
2015-01-08 23:47:40 +00:00
# realign object to Z = 0
$ model_object - > center_around_origin ;
2014-06-14 19:14:33 +00:00
$ self - > make_thumbnail ( $ obj_idx ) ;
# update print and start background processing
$ self - > stop_background_process ;
$ self - > { print } - > add_model_object ( $ model_object , $ obj_idx ) ;
$ self - > selection_changed ; # refresh info (size etc.)
$ self - > update ;
2014-12-29 21:29:24 +00:00
$ self - > schedule_background_process ;
2014-06-14 19:14:33 +00:00
}
2012-04-30 12:56:01 +00:00
sub changescale {
2015-12-05 18:37:57 +00:00
my ( $ self , $ axis , $ tosize ) = @ _ ;
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2014-06-16 22:50:44 +00:00
return if ! defined $ obj_idx ;
2013-12-12 19:19:33 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
my $ model_instance = $ model_object - > instances - > [ 0 ] ;
2012-08-29 17:37:27 +00:00
2013-06-12 14:53:19 +00:00
# we need thumbnail to be computed before allowing scaling
return if ! $ object - > thumbnail ;
2015-12-05 18:37:57 +00:00
my $ object_size = $ model_object - > bounding_box - > size ;
my $ bed_size = Slic3r::Polygon - > new_scale ( @ { $ self - > { config } - > bed_shape } ) - > bounding_box - > size ;
2014-06-16 22:50:44 +00:00
if ( defined $ axis ) {
my $ axis_name = $ axis == X ? 'X' : $ axis == Y ? 'Y' : 'Z' ;
2015-12-05 18:37:57 +00:00
my $ scale ;
if ( $ tosize ) {
my $ cursize = $ object_size - > [ $ axis ] ;
2017-03-23 13:32:19 +00:00
my $ newsize = $ self - > _get_number_from_user (
sprintf ( 'Enter the new size for the selected object (print bed: %smm):' , $ bed_size - > [ $ axis ] ) ,
"Scale along $axis_name" , 'Invalid scaling value entered' , $ cursize , 1 ) ;
return if $ newsize eq '' ;
2015-12-05 18:37:57 +00:00
$ scale = $ newsize / $ cursize * 100 ;
} else {
2017-03-23 13:32:19 +00:00
$ scale = $ self - > _get_number_from_user ( 'Enter the scale % for the selected object:' , "Scale along $axis_name" , 'Invalid scaling value entered' , 100 , 1 ) ;
return if $ scale eq '' ;
2015-12-05 18:37:57 +00:00
}
2014-06-16 22:50:44 +00:00
# apply Z rotation before scaling
if ( $ model_instance - > rotation != 0 ) {
2015-05-03 16:40:00 +00:00
$ model_object - > rotate ( $ model_instance - > rotation , Z ) ;
2014-06-16 22:50:44 +00:00
$ _ - > set_rotation ( 0 ) for @ { $ model_object - > instances } ;
}
my $ versor = [ 1 , 1 , 1 ] ;
$ versor - > [ $ axis ] = $ scale / 100 ;
2014-09-21 08:51:36 +00:00
$ model_object - > scale_xyz ( Slic3r::Pointf3 - > new ( @$ versor ) ) ;
2017-02-28 09:29:52 +00:00
#FIXME Scale the layer height profile when $axis == Z?
#FIXME Scale the layer height ranges $axis == Z?
2015-01-08 23:47:40 +00:00
# object was already aligned to Z = 0, so no need to realign it
2014-06-16 22:50:44 +00:00
$ self - > make_thumbnail ( $ obj_idx ) ;
} else {
2015-12-05 18:37:57 +00:00
my $ scale ;
if ( $ tosize ) {
my $ cursize = max ( @$ object_size ) ;
2017-03-23 13:32:19 +00:00
my $ newsize = $ self - > _get_number_from_user ( 'Enter the new max size for the selected object:' , 'Scale' , 'Invalid scaling value entered' , $ cursize , 1 ) ;
return if $ newsize eq '' ;
$ scale = $ model_instance - > scaling_factor * $ newsize / $ cursize * 100 ;
2015-12-05 18:37:57 +00:00
} else {
# max scale factor should be above 2540 to allow importing files exported in inches
2017-03-23 13:32:19 +00:00
$ scale = $ self - > _get_number_from_user ( 'Enter the scale % for the selected object:' , 'Scale' , 'Invalid scaling value entered' , $ model_instance - > scaling_factor * 100 , 1 ) ;
return if $ scale eq '' ;
2015-12-05 18:37:57 +00:00
}
2012-04-30 12:56:01 +00:00
2014-06-16 22:50:44 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 2 , "$scale%" ) ;
$ scale /= 100 ; # turn percent into factor
2013-12-12 19:19:33 +00:00
my $ variation = $ scale / $ model_instance - > scaling_factor ;
2017-02-28 09:29:52 +00:00
#FIXME Scale the layer height profile?
2013-12-12 19:19:33 +00:00
foreach my $ range ( @ { $ model_object - > layer_height_ranges } ) {
$ range - > [ 0 ] *= $ variation ;
$ range - > [ 1 ] *= $ variation ;
}
2014-04-29 23:04:49 +00:00
$ _ - > set_scaling_factor ( $ scale ) for @ { $ model_object - > instances } ;
2013-12-12 19:19:33 +00:00
$ object - > transform_thumbnail ( $ self - > { model } , $ obj_idx ) ;
}
2014-06-16 22:50:44 +00:00
$ model_object - > update_bounding_box ;
# update print and start background processing
$ self - > stop_background_process ;
$ self - > { print } - > add_model_object ( $ model_object , $ obj_idx ) ;
2013-08-25 10:22:05 +00:00
$ self - > selection_changed ( 1 ) ; # refresh info (size, volume etc.)
2013-12-18 18:11:20 +00:00
$ self - > update ;
2014-12-29 21:29:24 +00:00
$ self - > schedule_background_process ;
2012-08-29 17:37:27 +00:00
}
sub arrange {
my $ self = shift ;
2012-05-04 10:56:15 +00:00
2014-11-12 23:23:31 +00:00
$ self - > pause_background_process ;
2014-06-16 13:18:39 +00:00
my $ bb = Slic3r::Geometry::BoundingBoxf - > new_from_points ( $ self - > { config } - > bed_shape ) ;
2016-11-04 14:03:51 +00:00
my $ success = $ self - > { model } - > arrange_objects ( $ self - > GetFrame - > config - > min_object_distance , $ bb ) ;
2013-12-23 23:30:51 +00:00
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed
2012-04-30 12:56:01 +00:00
2013-12-18 18:11:20 +00:00
$ self - > update ( 1 ) ;
2012-04-30 12:56:01 +00:00
}
2012-04-30 22:30:46 +00:00
sub split_object {
my $ self = shift ;
2013-12-12 19:19:33 +00:00
my ( $ obj_idx , $ current_object ) = $ self - > selected_object ;
2014-11-30 19:53:53 +00:00
# we clone model object because split_object() adds the split volumes
2014-12-30 12:16:28 +00:00
# into the same model object, thus causing duplicates when we call load_model_objects()
my $ new_model = $ self - > { model } - > clone ; # store this before calling get_object()
my $ current_model_object = $ new_model - > get_object ( $ obj_idx ) ;
2012-10-21 18:56:19 +00:00
2014-12-30 12:16:28 +00:00
if ( $ current_model_object - > volumes_count > 1 ) {
2014-11-12 21:36:03 +00:00
Slic3r::GUI:: warning_catcher ( $ self ) - > ( "The selected object can't be split because it contains more than one volume/material." ) ;
2012-10-21 18:56:19 +00:00
return ;
}
2014-12-30 12:16:28 +00:00
$ self - > pause_background_process ;
2014-11-12 21:36:03 +00:00
my @ model_objects = @ { $ current_model_object - > split_object } ;
if ( @ model_objects == 1 ) {
2014-12-30 12:16:28 +00:00
$ self - > resume_background_process ;
2014-12-21 22:40:05 +00:00
Slic3r::GUI:: warning_catcher ( $ self ) - > ( "The selected object couldn't be split because it contains only one part." ) ;
2014-12-30 21:07:21 +00:00
$ self - > resume_background_process ;
2012-06-06 17:00:34 +00:00
return ;
}
2012-08-10 14:05:16 +00:00
2014-11-12 21:36:03 +00:00
foreach my $ object ( @ model_objects ) {
$ object - > instances - > [ $ _ ] - > offset - > translate ( $ _ * 10 , $ _ * 10 )
for 1 .. $# { $ object - > instances } ;
2014-01-14 21:06:28 +00:00
2013-12-12 19:19:33 +00:00
# we need to center this single object around origin
2014-11-12 21:36:03 +00:00
$ object - > center_around_origin ;
2012-04-30 22:30:46 +00:00
}
2014-05-26 11:10:58 +00:00
# remove the original object before spawning the object_loaded event, otherwise
# we'll pass the wrong $obj_idx to it (which won't be recognized after the
# thumbnail thread returns)
$ self - > remove ( $ obj_idx ) ;
$ current_object = $ obj_idx = undef ;
2014-03-24 20:42:38 +00:00
# load all model objects at once, otherwise the plate would be rearranged after each one
# causing original positions not to be kept
$ self - > load_model_objects ( @ model_objects ) ;
2012-04-30 22:30:46 +00:00
}
2014-06-13 18:36:45 +00:00
sub schedule_background_process {
my ( $ self ) = @ _ ;
2014-07-04 08:32:32 +00:00
if ( defined $ self - > { apply_config_timer } ) {
$ self - > { apply_config_timer } - > Start ( PROCESS_DELAY , 1 ) ; # 1 = one shot
}
2014-06-13 18:36:45 +00:00
}
2016-09-13 09:24:55 +00:00
# Executed asynchronously by a timer every PROCESS_DELAY (0.5 second).
# The timer is started by schedule_background_process(),
2014-06-13 17:23:51 +00:00
sub async_apply_config {
2014-06-13 13:54:13 +00:00
my ( $ self ) = @ _ ;
2017-05-30 15:24:50 +00:00
2014-06-13 21:27:52 +00:00
# pause process thread before applying new config
# since we don't want to touch data that is being used by the threads
2014-07-12 09:52:19 +00:00
$ self - > pause_background_process ;
2014-06-13 17:23:51 +00:00
# apply new config
2014-06-14 17:54:18 +00:00
my $ invalidated = $ self - > { print } - > apply_config ( $ self - > GetFrame - > config ) ;
2017-02-09 13:56:13 +00:00
2017-05-31 15:05:11 +00:00
# Just redraw the 3D canvas without reloading the scene.
2017-02-09 13:56:13 +00:00
# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled);
$ self - > { canvas3D } - > Refresh if ( $ self - > { canvas3D } - > layer_editing_enabled ) ;
2017-02-15 15:02:54 +00:00
# Hide the slicing results if the current slicing status is no more valid.
$ self - > { "print_info_box_show" } - > ( 0 ) if $ invalidated ;
2017-05-31 15:05:11 +00:00
if ( $ Slic3r:: GUI:: Settings - > { _ } { background_processing } ) {
if ( $ invalidated ) {
# kill current thread if any
$ self - > stop_background_process ;
} else {
$ self - > resume_background_process ;
}
# schedule a new process thread in case it wasn't running
$ self - > start_background_process ;
2014-06-13 17:23:51 +00:00
}
2017-05-30 15:24:50 +00:00
# Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
# Otherwise they will be just refreshed.
2017-05-31 15:05:11 +00:00
if ( $ invalidated ) {
$ self - > { toolpaths2D } - > reload_print if $ self - > { toolpaths2D } ;
$ self - > { preview3D } - > reload_print if $ self - > { preview3D } ;
}
2014-06-13 13:54:13 +00:00
}
2014-06-13 09:19:53 +00:00
sub start_background_process {
my ( $ self ) = @ _ ;
return if ! $ Slic3r:: have_threads ;
return if ! @ { $ self - > { objects } } ;
2014-06-13 18:36:45 +00:00
return if $ self - > { process_thread } ;
2014-06-13 09:19:53 +00:00
# It looks like declaring a local $SIG{__WARN__} prevents the ugly
# "Attempt to free unreferenced scalar" warning...
local $ SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $ self ) ;
# don't start process thread if config is not valid
eval {
# this will throw errors if config is not valid
2014-06-14 17:54:18 +00:00
$ self - > GetFrame - > config - > validate ;
2014-06-13 09:19:53 +00:00
$ self - > { print } - > validate ;
} ;
2014-11-30 23:15:45 +00:00
if ( $@ ) {
$ self - > statusbar - > SetStatusText ( $@ ) ;
return ;
}
2014-06-13 09:19:53 +00:00
# apply extra variables
{
2014-06-14 17:54:18 +00:00
my $ extra = $ self - > GetFrame - > extra_variables ;
2014-06-13 09:19:53 +00:00
$ self - > { print } - > placeholder_parser - > set ( $ _ , $ extra - > { $ _ } ) for keys %$ extra ;
}
# start thread
2014-06-13 12:27:55 +00:00
@ _ = ( ) ;
2014-07-01 17:00:23 +00:00
$ self - > { process_thread } = Slic3r:: spawn_thread ( sub {
2014-06-13 12:27:55 +00:00
eval {
$ self - > { print } - > process ;
} ;
if ( $@ ) {
2015-11-04 18:27:58 +00:00
Slic3r:: debugf "Background process error: $@\n" ;
2014-11-30 23:15:45 +00:00
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ PROCESS_COMPLETED_EVENT , $@ ) ) ;
2014-06-13 12:27:55 +00:00
} else {
2014-11-30 23:15:45 +00:00
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ PROCESS_COMPLETED_EVENT , undef ) ) ;
2014-06-13 12:27:55 +00:00
}
2014-06-13 09:19:53 +00:00
Slic3r:: thread_cleanup ( ) ;
} ) ;
Slic3r:: debugf "Background processing started.\n" ;
}
sub stop_background_process {
my ( $ self ) = @ _ ;
2014-06-16 13:18:39 +00:00
$ self - > { apply_config_timer } - > Stop if defined $ self - > { apply_config_timer } ;
2014-06-13 12:27:55 +00:00
$ self - > statusbar - > SetCancelCallback ( undef ) ;
$ self - > statusbar - > StopBusy ;
$ self - > statusbar - > SetStatusText ( "" ) ;
2015-01-19 17:53:04 +00:00
$ self - > { toolpaths2D } - > reload_print if $ self - > { toolpaths2D } ;
$ self - > { preview3D } - > reload_print if $ self - > { preview3D } ;
2014-06-13 12:27:55 +00:00
2014-06-13 09:19:53 +00:00
if ( $ self - > { process_thread } ) {
Slic3r:: debugf "Killing background process.\n" ;
2014-07-01 17:00:23 +00:00
Slic3r:: kill_all_threads ( ) ;
2014-06-13 09:19:53 +00:00
$ self - > { process_thread } = undef ;
} else {
Slic3r:: debugf "No background process running.\n" ;
}
2014-06-13 12:27:55 +00:00
# if there's an export process, kill that one as well
if ( $ self - > { export_thread } ) {
Slic3r:: debugf "Killing background export process.\n" ;
2014-07-01 17:00:23 +00:00
Slic3r:: kill_all_threads ( ) ;
2014-06-13 12:27:55 +00:00
$ self - > { export_thread } = undef ;
}
2014-06-13 09:19:53 +00:00
}
2014-07-12 09:52:19 +00:00
sub pause_background_process {
my ( $ self ) = @ _ ;
if ( $ self - > { process_thread } || $ self - > { export_thread } ) {
2014-12-22 18:47:39 +00:00
Slic3r:: pause_all_threads ( ) ;
2015-02-15 23:05:39 +00:00
return 1 ;
2014-07-12 09:52:19 +00:00
} elsif ( defined $ self - > { apply_config_timer } && $ self - > { apply_config_timer } - > IsRunning ) {
$ self - > { apply_config_timer } - > Stop ;
2015-02-15 23:05:39 +00:00
return 1 ;
2014-07-12 09:52:19 +00:00
}
2015-02-15 23:05:39 +00:00
return 0 ;
2014-07-12 09:52:19 +00:00
}
sub resume_background_process {
my ( $ self ) = @ _ ;
if ( $ self - > { process_thread } || $ self - > { export_thread } ) {
2014-12-22 18:47:39 +00:00
Slic3r:: resume_all_threads ( ) ;
2014-07-12 09:52:19 +00:00
}
}
2016-10-25 11:24:42 +00:00
sub reslice {
# explicitly cancel a previous thread and start a new one.
my ( $ self ) = @ _ ;
# Don't reslice if export of G-code or sending to OctoPrint is running.
if ( $ Slic3r:: have_threads && ! defined ( $ self - > { export_gcode_output_file } ) && ! defined ( $ self - > { send_gcode_file } ) ) {
2017-02-09 13:56:13 +00:00
# Stop the background processing threads, stop the async update timer.
2016-10-25 11:24:42 +00:00
$ self - > stop_background_process ;
2017-02-09 13:56:13 +00:00
# Rather perform one additional unnecessary update of the print object instead of skipping a pending async update.
$ self - > async_apply_config ;
2016-10-25 11:24:42 +00:00
$ self - > statusbar - > SetCancelCallback ( sub {
$ self - > stop_background_process ;
$ self - > statusbar - > SetStatusText ( "Slicing cancelled" ) ;
# this updates buttons status
$ self - > object_list_changed ;
} ) ;
$ self - > start_background_process ;
}
}
2012-04-30 12:56:01 +00:00
sub export_gcode {
2014-12-28 00:30:05 +00:00
my ( $ self , $ output_file ) = @ _ ;
2012-04-30 12:56:01 +00:00
2014-06-14 20:40:37 +00:00
return if ! @ { $ self - > { objects } } ;
2014-06-13 09:19:53 +00:00
if ( $ self - > { export_gcode_output_file } ) {
2014-06-13 12:27:55 +00:00
Wx::MessageDialog - > new ( $ self , "Another export job is currently running." , 'Error' , wxOK | wxICON_ERROR ) - > ShowModal ;
2012-05-05 19:08:15 +00:00
return ;
}
2014-06-13 12:27:55 +00:00
# if process is not running, validate config
# (we assume that if it is running, config is valid)
eval {
# this will throw errors if config is not valid
2014-06-14 17:54:18 +00:00
$ self - > GetFrame - > config - > validate ;
2014-06-13 12:27:55 +00:00
$ self - > { print } - > validate ;
} ;
Slic3r::GUI:: catch_error ( $ self ) and return ;
2014-03-16 23:39:07 +00:00
# apply config and validate print
2014-06-14 17:54:18 +00:00
my $ config = $ self - > GetFrame - > config ;
2014-03-16 23:39:07 +00:00
eval {
# this will throw errors if config is not valid
$ config - > validate ;
$ self - > { print } - > apply_config ( $ config ) ;
$ self - > { print } - > validate ;
} ;
2014-06-13 12:27:55 +00:00
if ( ! $ Slic3r:: have_threads ) {
Slic3r::GUI:: catch_error ( $ self ) and return ;
2014-03-16 23:39:07 +00:00
}
2012-08-01 14:06:03 +00:00
2012-05-05 19:08:15 +00:00
# select output file
2014-12-28 00:30:05 +00:00
if ( $ output_file ) {
2016-12-20 18:01:51 +00:00
$ self - > { export_gcode_output_file } = $ self - > { print } - > output_filepath ( $ output_file ) ;
2014-12-28 00:30:05 +00:00
} else {
2016-12-20 18:01:51 +00:00
my $ default_output_file = $ self - > { print } - > output_filepath ( $ main:: opt { output } ) ;
2014-06-14 17:59:59 +00:00
my $ dlg = Wx::FileDialog - > new ( $ self , 'Save G-code file as:' , wxTheApp - > output_path ( dirname ( $ default_output_file ) ) ,
2015-06-03 11:19:43 +00:00
basename ( $ default_output_file ) , & Slic3r::GUI::FILE_WILDCARDS - > { gcode } , wxFD_SAVE | wxFD_OVERWRITE_PROMPT ) ;
2012-05-05 19:08:15 +00:00
if ( $ dlg - > ShowModal != wxID_OK ) {
$ dlg - > Destroy ;
return ;
}
2015-03-09 14:17:50 +00:00
my $ path = Slic3r:: decode_path ( $ dlg - > GetPath ) ;
$ Slic3r:: GUI:: Settings - > { _ } { last_output_path } = dirname ( $ path ) ;
2014-06-14 17:59:59 +00:00
wxTheApp - > save_settings ;
2015-06-02 20:27:11 +00:00
$ self - > { export_gcode_output_file } = $ path ;
2012-05-05 19:08:15 +00:00
$ dlg - > Destroy ;
}
$ self - > statusbar - > StartBusy ;
2013-05-20 08:57:27 +00:00
2012-05-19 19:13:10 +00:00
if ( $ Slic3r:: have_threads ) {
2012-05-05 19:08:15 +00:00
$ self - > statusbar - > SetCancelCallback ( sub {
2014-06-13 12:27:55 +00:00
$ self - > stop_background_process ;
2012-05-05 19:08:15 +00:00
$ self - > statusbar - > SetStatusText ( "Export cancelled" ) ;
2015-03-28 17:53:07 +00:00
$ self - > { export_gcode_output_file } = undef ;
$ self - > { send_gcode_file } = undef ;
# this updates buttons status
$ self - > object_list_changed ;
2012-05-05 19:08:15 +00:00
} ) ;
2014-06-13 12:27:55 +00:00
# start background process, whose completion event handler
# will detect $self->{export_gcode_output_file} and proceed with export
$ self - > start_background_process ;
2012-05-05 19:08:15 +00:00
} else {
2014-06-13 12:27:55 +00:00
eval {
$ self - > { print } - > process ;
$ self - > { print } - > export_gcode ( output_file = > $ self - > { export_gcode_output_file } ) ;
} ;
my $ result = ! Slic3r::GUI:: catch_error ( $ self ) ;
$ self - > on_export_completed ( $ result ) ;
2012-05-05 19:08:15 +00:00
}
2014-12-28 00:30:05 +00:00
2015-01-04 18:29:34 +00:00
# this updates buttons status
$ self - > object_list_changed ;
2014-12-28 00:30:05 +00:00
return $ self - > { export_gcode_output_file } ;
2012-05-05 19:08:15 +00:00
}
2014-06-13 12:27:55 +00:00
# This gets called only if we have threads.
sub on_process_completed {
2014-11-30 23:15:45 +00:00
my ( $ self , $ error ) = @ _ ;
2012-05-05 19:08:15 +00:00
2014-06-13 12:27:55 +00:00
$ self - > statusbar - > SetCancelCallback ( undef ) ;
$ self - > statusbar - > StopBusy ;
2014-11-30 23:15:45 +00:00
$ self - > statusbar - > SetStatusText ( $ error // "" ) ;
2014-01-02 21:06:58 +00:00
2014-06-13 12:27:55 +00:00
Slic3r:: debugf "Background processing completed.\n" ;
$ self - > { process_thread } - > detach if $ self - > { process_thread } ;
$ self - > { process_thread } = undef ;
2015-11-04 18:27:58 +00:00
# if we're supposed to perform an explicit export let's display the error in a dialog
if ( $ error && $ self - > { export_gcode_output_file } ) {
$ self - > { export_gcode_output_file } = undef ;
Slic3r::GUI:: show_error ( $ self , $ error ) ;
}
2014-11-30 23:15:45 +00:00
return if $ error ;
2015-01-19 17:53:04 +00:00
$ self - > { toolpaths2D } - > reload_print if $ self - > { toolpaths2D } ;
$ self - > { preview3D } - > reload_print if $ self - > { preview3D } ;
2014-06-13 12:27:55 +00:00
# if we have an export filename, start a new thread for exporting G-code
if ( $ self - > { export_gcode_output_file } ) {
@ _ = ( ) ;
2014-06-13 17:23:51 +00:00
# workaround for "Attempt to free un referenced scalar..."
our $ _thread_self = $ self ;
2014-07-01 17:00:23 +00:00
$ self - > { export_thread } = Slic3r:: spawn_thread ( sub {
2014-06-13 12:27:55 +00:00
eval {
2014-06-13 17:23:51 +00:00
$ _thread_self - > { print } - > export_gcode ( output_file = > $ _thread_self - > { export_gcode_output_file } ) ;
2014-06-13 12:27:55 +00:00
} ;
if ( $@ ) {
2014-06-13 17:23:51 +00:00
Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ ERROR_EVENT , shared_clone ( [ $@ ] ) ) ) ;
Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ EXPORT_COMPLETED_EVENT , 0 ) ) ;
2012-04-30 12:56:01 +00:00
} else {
2014-06-13 17:23:51 +00:00
Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ EXPORT_COMPLETED_EVENT , 1 ) ) ;
2012-04-30 12:56:01 +00:00
}
2014-06-13 12:27:55 +00:00
Slic3r:: thread_cleanup ( ) ;
} ) ;
Slic3r:: debugf "Background G-code export started.\n" ;
}
}
# This gets called also if we have no threads.
sub on_progress_event {
my ( $ self , $ percent , $ message ) = @ _ ;
$ self - > statusbar - > SetProgress ( $ percent ) ;
$ self - > statusbar - > SetStatusText ( "$message…" ) ;
2012-04-30 12:56:01 +00:00
}
2016-10-25 11:24:42 +00:00
# Called when the G-code export finishes, either successfully or with an error.
2014-06-13 12:27:55 +00:00
# This gets called also if we don't have threads.
2012-05-05 19:08:15 +00:00
sub on_export_completed {
2014-06-13 12:27:55 +00:00
my ( $ self , $ result ) = @ _ ;
2012-05-05 19:08:15 +00:00
2012-05-05 19:26:19 +00:00
$ self - > statusbar - > SetCancelCallback ( undef ) ;
2012-05-05 19:08:15 +00:00
$ self - > statusbar - > StopBusy ;
2014-06-13 12:27:55 +00:00
$ self - > statusbar - > SetStatusText ( "" ) ;
2012-05-19 19:13:10 +00:00
2014-06-13 12:27:55 +00:00
Slic3r:: debugf "Background export process completed.\n" ;
2012-05-20 15:21:31 +00:00
$ self - > { export_thread } - > detach if $ self - > { export_thread } ;
2012-05-19 19:13:10 +00:00
$ self - > { export_thread } = undef ;
2014-06-13 12:27:55 +00:00
my $ message ;
2014-12-28 00:30:05 +00:00
my $ send_gcode = 0 ;
2015-01-03 22:25:55 +00:00
my $ do_print = 0 ;
2014-06-13 12:27:55 +00:00
if ( $ result ) {
2016-10-25 11:24:42 +00:00
# G-code file exported successfully.
2015-01-03 22:25:55 +00:00
if ( $ self - > { print_file } ) {
2015-01-04 22:18:23 +00:00
$ message = "File added to print queue" ;
2015-01-03 22:25:55 +00:00
$ do_print = 1 ;
} elsif ( $ self - > { send_gcode_file } ) {
2014-12-29 11:49:32 +00:00
$ message = "Sending G-code file to the OctoPrint server..." ;
2014-12-28 00:30:05 +00:00
$ send_gcode = 1 ;
} else {
$ message = "G-code file exported to " . $ self - > { export_gcode_output_file } ;
}
2014-06-13 12:27:55 +00:00
} else {
$ message = "Export failed" ;
}
$ self - > { export_gcode_output_file } = undef ;
$ self - > statusbar - > SetStatusText ( $ message ) ;
2014-06-14 17:59:59 +00:00
wxTheApp - > notify ( $ message ) ;
2014-12-28 00:30:05 +00:00
2015-01-03 22:25:55 +00:00
$ self - > do_print if $ do_print ;
2016-10-25 11:24:42 +00:00
# Send $self->{send_gcode_file} to OctoPrint.
2014-12-28 00:30:05 +00:00
$ self - > send_gcode if $ send_gcode ;
2015-01-03 22:25:55 +00:00
$ self - > { print_file } = undef ;
2014-12-28 00:30:05 +00:00
$ self - > { send_gcode_file } = undef ;
2017-01-21 03:21:57 +00:00
$ self - > { "print_info_cost" } - > SetLabel ( sprintf ( "%.2f" , $ self - > { print } - > total_cost ) ) ;
$ self - > { "print_info_fil_g" } - > SetLabel ( sprintf ( "%.2f" , $ self - > { print } - > total_weight ) ) ;
$ self - > { "print_info_fil_mm3" } - > SetLabel ( sprintf ( "%.2f" , $ self - > { print } - > total_extruded_volume ) ) ;
2017-02-15 15:02:54 +00:00
$ self - > { "print_info_box_show" } - > ( 1 ) ;
2015-01-04 18:29:34 +00:00
# this updates buttons status
$ self - > object_list_changed ;
2014-12-28 00:30:05 +00:00
}
2015-01-03 22:25:55 +00:00
sub do_print {
my ( $ self ) = @ _ ;
my $ printer_tab = $ self - > GetFrame - > { options_tabs } { printer } ;
my $ printer_name = $ printer_tab - > get_current_preset - > name ;
2015-01-04 22:18:23 +00:00
my $ controller = $ self - > GetFrame - > { controller } ;
2015-01-03 22:25:55 +00:00
my $ printer_panel = $ controller - > add_printer ( $ printer_name , $ printer_tab - > config ) ;
my $ filament_stats = $ self - > { print } - > filament_stats ;
my @ filament_names = $ self - > GetFrame - > filament_preset_names ;
$ filament_stats = { map { $ filament_names [ $ _ ] = > $ filament_stats - > { $ _ } } keys %$ filament_stats } ;
$ printer_panel - > load_print_job ( $ self - > { print_file } , $ filament_stats ) ;
2015-01-04 22:18:23 +00:00
$ self - > GetFrame - > select_tab ( 1 ) ;
2015-01-03 22:25:55 +00:00
}
2016-10-25 11:24:42 +00:00
# Send $self->{send_gcode_file} to OctoPrint.
#FIXME Currently this call blocks the UI. Make it asynchronous.
2014-12-28 00:30:05 +00:00
sub send_gcode {
my ( $ self ) = @ _ ;
$ self - > statusbar - > StartBusy ;
my $ ua = LWP::UserAgent - > new ;
2015-01-04 14:30:31 +00:00
$ ua - > timeout ( 180 ) ;
2014-12-28 00:30:05 +00:00
2015-06-02 15:10:06 +00:00
my $ path = Slic3r:: encode_path ( $ self - > { send_gcode_file } ) ;
2014-12-28 00:30:05 +00:00
my $ res = $ ua - > post (
"http://" . $ self - > { config } - > octoprint_host . "/api/files/local" ,
Content_Type = > 'form-data' ,
'X-Api-Key' = > $ self - > { config } - > octoprint_apikey ,
Content = > [
2015-06-02 15:10:06 +00:00
# OctoPrint doesn't like Windows paths so we use basename()
# Also, since we need to read from filesystem we process it through encode_path()
file = > [ $ path , basename ( $ path ) ] ,
2014-12-28 00:30:05 +00:00
] ,
) ;
$ self - > statusbar - > StopBusy ;
if ( $ res - > is_success ) {
2014-12-29 11:49:32 +00:00
$ self - > statusbar - > SetStatusText ( "G-code file successfully uploaded to the OctoPrint server" ) ;
2014-12-28 00:30:05 +00:00
} else {
2014-12-29 11:49:32 +00:00
my $ message = "Error while uploading to the OctoPrint server: " . $ res - > status_line ;
2014-12-29 10:45:09 +00:00
Slic3r::GUI:: show_error ( $ self , $ message ) ;
$ self - > statusbar - > SetStatusText ( $ message ) ;
2014-12-28 00:30:05 +00:00
}
2012-05-19 19:13:10 +00:00
}
2012-04-30 15:10:54 +00:00
sub export_stl {
my $ self = shift ;
2014-06-14 20:40:37 +00:00
return if ! @ { $ self - > { objects } } ;
2012-04-30 15:10:54 +00:00
2012-08-29 15:11:56 +00:00
my $ output_file = $ self - > _get_export_file ( 'STL' ) or return ;
2017-02-27 00:03:00 +00:00
$ self - > { model } - > store_stl ( Slic3r:: encode_path ( $ output_file ) , 1 ) ;
2012-08-29 15:11:56 +00:00
$ self - > statusbar - > SetStatusText ( "STL file exported to $output_file" ) ;
}
2016-12-22 11:13:28 +00:00
sub reload_from_disk {
my ( $ self ) = @ _ ;
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
return if ! defined $ obj_idx ;
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
return if ! $ model_object - > input_file
|| ! - e $ model_object - > input_file ;
my @ new_obj_idx = $ self - > load_file ( $ model_object - > input_file ) ;
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 } ;
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 ) ;
}
}
}
$ self - > remove ( $ obj_idx ) ;
# Trigger thumbnail generation again, because the remove() method altered
# object indexes before background thumbnail generation called its completion
# event, so the on_thumbnail_made callback is called with the wrong $obj_idx.
# When porting to C++ we'll probably have cleaner ways to do this.
$ self - > make_thumbnail ( $ _ - 1 ) for @ new_obj_idx ;
}
2015-01-03 14:48:53 +00:00
sub export_object_stl {
my $ self = shift ;
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
return if ! defined $ obj_idx ;
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
my $ output_file = $ self - > _get_export_file ( 'STL' ) or return ;
2017-02-27 00:03:00 +00:00
$ model_object - > mesh - > write_binary ( Slic3r:: encode_path ( $ output_file ) ) ;
2015-01-03 14:48:53 +00:00
$ self - > statusbar - > SetStatusText ( "STL file exported to $output_file" ) ;
}
2012-08-29 15:11:56 +00:00
sub export_amf {
my $ self = shift ;
2014-06-14 20:40:37 +00:00
return if ! @ { $ self - > { objects } } ;
2012-08-29 15:11:56 +00:00
my $ output_file = $ self - > _get_export_file ( 'AMF' ) or return ;
2017-02-27 00:03:00 +00:00
$ self - > { model } - > store_amf ( Slic3r:: encode_path ( $ output_file ) ) ;
2012-08-29 15:11:56 +00:00
$ self - > statusbar - > SetStatusText ( "AMF file exported to $output_file" ) ;
}
sub _get_export_file {
my $ self = shift ;
my ( $ format ) = @ _ ;
my $ suffix = $ format eq 'STL' ? '.stl' : '.amf.xml' ;
2012-04-30 15:10:54 +00:00
my $ output_file = $ main:: opt { output } ;
{
2016-12-20 18:01:51 +00:00
$ output_file = $ self - > { print } - > output_filepath ( $ output_file ) ;
2012-08-29 15:11:56 +00:00
$ output_file =~ s/\.gcode$/$suffix/i ;
my $ dlg = Wx::FileDialog - > new ( $ self , "Save $format file as:" , dirname ( $ output_file ) ,
2014-06-14 17:54:18 +00:00
basename ( $ output_file ) , & Slic3r::GUI:: MODEL_WILDCARD , wxFD_SAVE | wxFD_OVERWRITE_PROMPT ) ;
2012-04-30 15:10:54 +00:00
if ( $ dlg - > ShowModal != wxID_OK ) {
$ dlg - > Destroy ;
2012-08-29 15:11:56 +00:00
return undef ;
2012-04-30 15:10:54 +00:00
}
2015-06-02 20:27:11 +00:00
$ output_file = Slic3r:: decode_path ( $ dlg - > GetPath ) ;
2012-04-30 15:10:54 +00:00
$ dlg - > Destroy ;
}
2012-08-29 15:11:56 +00:00
return $ output_file ;
2012-08-29 14:49:38 +00:00
}
2012-04-30 12:56:01 +00:00
sub make_thumbnail {
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
2013-12-12 19:19:33 +00:00
my $ plater_object = $ self - > { objects } [ $ obj_idx ] ;
$ plater_object - > thumbnail ( Slic3r::ExPolygon::Collection - > new ) ;
2012-04-30 18:59:14 +00:00
my $ cb = sub {
2013-12-12 19:19:33 +00:00
$ plater_object - > make_thumbnail ( $ self - > { model } , $ obj_idx ) ;
2012-04-30 18:59:14 +00:00
2012-05-19 19:13:10 +00:00
if ( $ Slic3r:: have_threads ) {
2013-11-12 13:30:13 +00:00
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ THUMBNAIL_DONE_EVENT , shared_clone ( [ $ obj_idx ] ) ) ) ;
2013-07-11 17:34:37 +00:00
Slic3r:: thread_cleanup ( ) ;
2012-04-30 20:34:41 +00:00
threads - > exit ;
} else {
2012-10-01 16:12:14 +00:00
$ self - > on_thumbnail_made ( $ obj_idx ) ;
2012-04-30 20:34:41 +00:00
}
2012-04-30 18:59:14 +00:00
} ;
2013-05-19 15:34:33 +00:00
@ _ = ( ) ;
2013-12-22 00:27:09 +00:00
$ Slic3r:: have_threads
? threads - > create ( sub { $ cb - > ( ) ; Slic3r:: thread_cleanup ( ) ; } ) - > detach
: $ cb - > ( ) ;
2012-04-30 12:56:01 +00:00
}
2012-08-29 17:37:27 +00:00
sub on_thumbnail_made {
2012-04-30 20:34:41 +00:00
my $ self = shift ;
2012-10-01 16:12:14 +00:00
my ( $ obj_idx ) = @ _ ;
2013-12-12 19:19:33 +00:00
$ self - > { objects } [ $ obj_idx ] - > transform_thumbnail ( $ self - > { model } , $ obj_idx ) ;
2017-05-31 15:05:11 +00:00
$ self - > { canvas } - > Refresh ;
2012-04-30 20:34:41 +00:00
}
2013-12-15 22:50:05 +00:00
# this method gets called whenever print center is changed or the objects' bounding box changes
2013-12-12 19:19:33 +00:00
# (i.e. when an object is added/removed/moved/rotated/scaled)
2013-12-18 17:54:11 +00:00
sub update {
2013-12-18 18:11:20 +00:00
my ( $ self , $ force_autocenter ) = @ _ ;
2016-12-12 17:02:24 +00:00
2013-12-18 18:11:20 +00:00
if ( $ Slic3r:: GUI:: Settings - > { _ } { autocenter } || $ force_autocenter ) {
2014-06-16 21:58:45 +00:00
$ self - > { model } - > center_instances_around_point ( $ self - > bed_centerf ) ;
2013-12-18 18:11:20 +00:00
}
2013-12-18 17:54:11 +00:00
2015-02-15 23:05:39 +00:00
my $ running = $ self - > pause_background_process ;
2014-12-29 21:29:24 +00:00
my $ invalidated = $ self - > { print } - > reload_model_instances ( ) ;
2015-02-15 23:05:39 +00:00
# The mere fact that no steps were invalidated when reloading model instances
# doesn't mean that all steps were done: for example, validation might have
# failed upon previous instance move, so we have no running thread and no steps
# are invalidated on this move, thus we need to schedule a new run.
if ( $ invalidated || ! $ running ) {
2014-12-29 21:29:24 +00:00
$ self - > schedule_background_process ;
} else {
$ self - > resume_background_process ;
}
2017-05-31 15:05:11 +00:00
$ self - > { canvas } - > Refresh ;
$ self - > { canvas3D } - > reload_scene if $ self - > { canvas3D } ;
$ self - > { preview3D } - > reload_print if $ self - > { preview3D } ;
2012-04-30 12:56:01 +00:00
}
2016-10-24 14:07:36 +00:00
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
2014-07-01 14:40:56 +00:00
sub on_extruders_change {
my ( $ self , $ num_extruders ) = @ _ ;
my $ choices = $ self - > { preset_choosers } { filament } ;
while ( @$ choices < $ num_extruders ) {
2015-06-01 21:58:34 +00:00
# copy strings from first choice
2014-07-01 14:40:56 +00:00
my @ presets = $ choices - > [ 0 ] - > GetStrings ;
2015-06-01 21:58:34 +00:00
# initialize new choice
my $ choice = Wx::BitmapComboBox - > new ( $ self , - 1 , "" , wxDefaultPosition , wxDefaultSize , [ @ presets ] , wxCB_READONLY ) ;
2017-05-24 13:20:20 +00:00
my $ extruder_idx = scalar @$ choices ;
EVT_LEFT_DOWN ( $ choice , sub { $ self - > filament_color_box_lmouse_down ( $ extruder_idx , @ _ ) ; } ) ;
2015-06-01 21:58:34 +00:00
push @$ choices , $ choice ;
# copy icons from first choice
$ choice - > SetItemBitmap ( $ _ , $ choices - > [ 0 ] - > GetItemBitmap ( $ _ ) ) for 0 .. $# presets ;
# insert new choice into sizer
2014-12-25 18:14:18 +00:00
$ self - > { presets_sizer } - > Insert ( 4 + ( $#$ choices - 1 ) * 2 , 0 , 0 ) ;
2015-06-01 21:58:34 +00:00
$ self - > { presets_sizer } - > Insert ( 5 + ( $#$ choices - 1 ) * 2 , $ choice , 0 , wxEXPAND | wxBOTTOM , FILAMENT_CHOOSERS_SPACING ) ;
# setup the listener
2015-06-02 09:19:11 +00:00
EVT_COMBOBOX ( $ choice , $ choice , sub {
my ( $ choice ) = @ _ ;
wxTheApp - > CallAfter ( sub {
$ self - > _on_select_preset ( 'filament' , $ choice ) ;
} ) ;
} ) ;
2015-06-01 21:58:34 +00:00
# initialize selection
my $ i = first { $ choice - > GetString ( $ _ ) eq ( $ Slic3r:: GUI:: Settings - > { presets } { "filament_" . $#$ choices } || '' ) } 0 .. $# presets ;
$ choice - > SetSelection ( $ i || 0 ) ;
2014-07-01 14:40:56 +00:00
}
2015-06-01 21:58:34 +00:00
# remove unused choices if any
2014-07-01 14:40:56 +00:00
while ( @$ choices > $ num_extruders ) {
2014-12-25 18:14:18 +00:00
$ self - > { presets_sizer } - > Remove ( 4 + ( $#$ choices - 1 ) * 2 ) ; # label
$ self - > { presets_sizer } - > Remove ( 4 + ( $#$ choices - 1 ) * 2 ) ; # wxChoice
2014-07-01 14:40:56 +00:00
$ choices - > [ - 1 ] - > Destroy ;
pop @$ choices ;
}
$ self - > Layout ;
}
2012-07-27 19:13:03 +00:00
sub on_config_change {
my $ self = shift ;
2014-07-01 14:40:56 +00:00
my ( $ config ) = @ _ ;
2017-05-17 14:53:40 +00:00
my $ update_scheduled ;
2014-07-01 14:40:56 +00:00
foreach my $ opt_key ( @ { $ self - > { config } - > diff ( $ config ) } ) {
$ self - > { config } - > set ( $ opt_key , $ config - > get ( $ opt_key ) ) ;
2014-06-16 13:18:39 +00:00
if ( $ opt_key eq 'bed_shape' ) {
2014-05-28 10:29:43 +00:00
$ self - > { canvas } - > update_bed_size ;
2014-12-16 00:12:37 +00:00
$ self - > { canvas3D } - > update_bed_size if $ self - > { canvas3D } ;
2015-01-24 23:29:51 +00:00
$ self - > { preview3D } - > set_bed_shape ( $ self - > { config } - > bed_shape )
if $ self - > { preview3D } ;
2017-05-17 14:53:40 +00:00
$ update_scheduled = 1 ;
2017-05-19 19:48:32 +00:00
} elsif ( $ opt_key =~ '^wipe_tower' || $ opt_key eq 'single_extruder_multi_material' ) {
2017-05-17 14:53:40 +00:00
$ update_scheduled = 1 ;
2015-01-03 22:25:55 +00:00
} elsif ( $ opt_key eq 'serial_port' ) {
2017-05-31 15:05:11 +00:00
$ self - > { btn_print } - > Show ( $ config - > get ( 'serial_port' ) ) ;
2015-01-03 22:25:55 +00:00
$ self - > Layout ;
2014-12-28 00:30:05 +00:00
} elsif ( $ opt_key eq 'octoprint_host' ) {
2017-05-31 15:05:11 +00:00
$ self - > { btn_send_gcode } - > Show ( $ config - > get ( 'octoprint_host' ) ) ;
2014-12-28 00:30:05 +00:00
$ self - > Layout ;
2017-02-10 09:21:50 +00:00
} elsif ( $ opt_key eq 'variable_layer_height' ) {
if ( $ config - > get ( 'variable_layer_height' ) != 1 ) {
2017-02-13 11:40:12 +00:00
if ( $ self - > { htoolbar } ) {
$ self - > { htoolbar } - > EnableTool ( TB_LAYER_EDITING , 0 ) ;
$ self - > { htoolbar } - > ToggleTool ( TB_LAYER_EDITING , 0 ) ;
} else {
$ self - > { "btn_layer_editing" } - > Disable ;
$ self - > { "btn_layer_editing" } - > SetValue ( 0 ) ;
}
2017-02-10 09:21:50 +00:00
$ self - > { canvas3D } - > layer_editing_enabled ( 0 ) ;
2017-05-17 14:53:40 +00:00
$ self - > { canvas3D } - > Refresh ;
$ self - > { canvas3D } - > Update ;
2017-02-19 17:01:03 +00:00
} elsif ( $ self - > { canvas3D } - > layer_editing_allowed ) {
# Want to allow the layer editing, but do it only if the OpenGL supports it.
2017-02-13 11:40:12 +00:00
if ( $ self - > { htoolbar } ) {
$ self - > { htoolbar } - > EnableTool ( TB_LAYER_EDITING , 1 ) ;
} else {
$ self - > { "btn_layer_editing" } - > Enable ;
}
2017-02-10 09:21:50 +00:00
}
2017-05-30 15:24:50 +00:00
} elsif ( $ opt_key eq 'extruder_colour' ) {
$ update_scheduled = 1 ;
my $ extruder_colors = $ config - > get ( 'extruder_colour' ) ;
$ self - > { preview3D } - > set_number_extruders ( scalar ( @ { $ extruder_colors } ) ) ;
2012-07-24 22:15:32 +00:00
}
2012-04-30 12:56:01 +00:00
}
2017-05-17 14:53:40 +00:00
$ self - > update if $ update_scheduled ;
2014-06-13 09:19:53 +00:00
2014-06-14 17:54:18 +00:00
return if ! $ self - > GetFrame - > is_loaded ;
2014-06-13 13:54:13 +00:00
2014-06-13 17:23:51 +00:00
# (re)start timer
2014-06-13 18:36:45 +00:00
$ self - > schedule_background_process ;
2012-04-30 12:56:01 +00:00
}
sub list_item_deselected {
my ( $ self , $ event ) = @ _ ;
2014-03-25 14:30:56 +00:00
return if $ PreventListEvents ;
2012-04-30 12:56:01 +00:00
if ( $ self - > { list } - > GetFirstSelected == - 1 ) {
2013-12-12 19:19:33 +00:00
$ self - > select_object ( undef ) ;
2017-05-31 15:05:11 +00:00
$ self - > { canvas } - > Refresh ;
#FIXME VBOs are being refreshed just to change a selection color?
$ self - > { canvas3D } - > reload_scene if $ self - > { canvas3D } ;
2012-04-30 12:56:01 +00:00
}
}
sub list_item_selected {
my ( $ self , $ event ) = @ _ ;
2014-03-25 14:30:56 +00:00
return if $ PreventListEvents ;
2012-04-30 12:56:01 +00:00
my $ obj_idx = $ event - > GetIndex ;
2013-12-12 19:19:33 +00:00
$ self - > select_object ( $ obj_idx ) ;
2017-05-31 15:05:11 +00:00
$ self - > { canvas } - > Refresh ;
#FIXME VBOs are being refreshed just to change a selection color?
$ self - > { canvas3D } - > reload_scene if $ self - > { canvas3D } ;
2012-04-30 12:56:01 +00:00
}
2012-10-24 20:44:08 +00:00
sub list_item_activated {
my ( $ self , $ event , $ obj_idx ) = @ _ ;
$ obj_idx // = $ event - > GetIndex ;
2015-01-14 22:21:54 +00:00
$ self - > object_settings_dialog ( $ obj_idx ) ;
2013-08-25 13:45:22 +00:00
}
2017-05-24 13:20:20 +00:00
# Called when clicked on the filament preset combo box.
# When clicked on the icon, show the color picker.
sub filament_color_box_lmouse_down
{
my ( $ self , $ extruder_idx , $ combobox , $ event ) = @ _ ;
my $ pos = $ event - > GetLogicalPosition ( Wx::ClientDC - > new ( $ combobox ) ) ;
my ( $ x , $ y ) = ( $ pos - > x , $ pos - > y ) ;
if ( $ x > 24 ) {
# Let the combo box process the mouse click.
$ event - > Skip ;
} else {
# Swallow the mouse click and open the color picker.
my $ data = Wx::ColourData - > new ;
$ data - > SetChooseFull ( 1 ) ;
my $ dialog = Wx::ColourDialog - > new ( $ self - > GetFrame , $ data ) ;
if ( $ dialog - > ShowModal == wxID_OK ) {
my $ cfg = Slic3r::Config - > new ;
my $ colors = $ self - > GetFrame - > config - > get ( 'extruder_colour' ) ;
$ colors - > [ $ extruder_idx ] = $ dialog - > GetColourData - > GetColour - > GetAsString ( wxC2S_HTML_SYNTAX ) ;
$ cfg - > set ( 'extruder_colour' , $ colors ) ;
$ self - > GetFrame - > { options_tabs } { printer } - > load_config ( $ cfg ) ;
$ self - > update_filament_colors_preview ( $ extruder_idx ) ;
}
$ dialog - > Destroy ( ) ;
}
}
2014-04-25 12:54:08 +00:00
sub object_cut_dialog {
2013-08-25 13:45:22 +00:00
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
}
2013-09-16 08:18:42 +00:00
if ( ! $ Slic3r:: GUI:: have_OpenGL ) {
Slic3r::GUI:: show_error ( $ self , "Please install the OpenGL modules to use this feature (see build instructions)." ) ;
return ;
}
2014-04-25 12:54:08 +00:00
my $ dlg = Slic3r::GUI::Plater::ObjectCutDialog - > new ( $ self ,
object = > $ self - > { objects } [ $ obj_idx ] ,
model_object = > $ self - > { model } - > objects - > [ $ obj_idx ] ,
2013-08-25 16:01:59 +00:00
) ;
2015-12-19 19:41:47 +00:00
return unless $ dlg - > ShowModal == wxID_OK ;
2014-04-25 12:54:08 +00:00
if ( my @ new_objects = $ dlg - > NewModelObjects ) {
$ self - > remove ( $ obj_idx ) ;
2014-06-14 20:36:49 +00:00
$ self - > load_model_objects ( grep defined ( $ _ ) , @ new_objects ) ;
2014-04-25 15:14:39 +00:00
$ self - > arrange ;
2014-04-25 12:54:08 +00:00
}
2013-08-25 16:01:59 +00:00
}
sub object_settings_dialog {
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
}
2014-03-23 15:45:55 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
2013-08-25 16:01:59 +00:00
2013-08-25 20:24:43 +00:00
# validate config before opening the settings dialog because
# that dialog can't be closed if validation fails, but user
# can't fix any error which is outside that dialog
return unless $ self - > validate_config ;
2013-08-25 16:01:59 +00:00
my $ dlg = Slic3r::GUI::Plater::ObjectSettingsDialog - > new ( $ self ,
2013-12-12 19:19:33 +00:00
object = > $ self - > { objects } [ $ obj_idx ] ,
2014-03-23 15:45:55 +00:00
model_object = > $ model_object ,
2017-04-05 11:27:00 +00:00
config = > $ self - > GetFrame - > config ,
2012-10-24 20:44:08 +00:00
) ;
2014-07-12 09:52:19 +00:00
$ self - > pause_background_process ;
2012-10-24 20:44:08 +00:00
$ dlg - > ShowModal ;
2014-03-23 15:45:55 +00:00
2014-12-12 21:43:04 +00:00
# update thumbnail since parts may have changed
if ( $ dlg - > PartsChanged ) {
# recenter and re-align to Z = 0
$ model_object - > center_around_origin ;
$ self - > make_thumbnail ( $ obj_idx ) ;
}
2014-03-23 15:45:55 +00:00
# update print
if ( $ dlg - > PartsChanged || $ dlg - > PartSettingsChanged ) {
2014-07-01 14:40:56 +00:00
$ self - > stop_background_process ;
2014-03-26 18:42:01 +00:00
$ self - > { print } - > reload_object ( $ obj_idx ) ;
2014-06-13 18:36:45 +00:00
$ self - > schedule_background_process ;
2014-07-01 14:40:56 +00:00
} else {
2014-07-12 09:52:19 +00:00
$ self - > resume_background_process ;
2014-03-23 15:45:55 +00:00
}
2014-07-03 07:24:19 +00:00
}
2016-10-25 11:24:42 +00:00
# Called to update various buttons depending on whether there are any objects or
# whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active.
2012-04-30 12:56:01 +00:00
sub object_list_changed {
my $ self = shift ;
2017-02-13 11:40:12 +00:00
2016-10-25 11:24:42 +00:00
# Enable/disable buttons depending on whether there are any objects on the platter.
2013-08-25 16:01:59 +00:00
my $ have_objects = @ { $ self - > { objects } } ? 1 : 0 ;
2017-02-19 17:01:03 +00:00
my $ variable_layer_height_allowed = $ self - > { config } - > variable_layer_height && $ self - > { canvas3D } - > layer_editing_allowed ;
2017-02-13 11:40:12 +00:00
if ( $ self - > { htoolbar } ) {
# On OSX or Linux
$ self - > { htoolbar } - > EnableTool ( $ _ , $ have_objects )
for ( TB_RESET , TB_ARRANGE , TB_LAYER_EDITING ) ;
2017-02-19 17:01:03 +00:00
$ self - > { htoolbar } - > EnableTool ( TB_LAYER_EDITING , 0 ) if ( ! $ variable_layer_height_allowed ) ;
2017-02-13 11:40:12 +00:00
} else {
# On MSW
my $ method = $ have_objects ? 'Enable' : 'Disable' ;
$ self - > { "btn_$_" } - > $ method
for grep $ self - > { "btn_$_" } , qw( reset arrange reslice export_gcode export_stl print send_gcode layer_editing ) ;
2017-02-19 17:01:03 +00:00
$ self - > { "btn_layer_editing" } - > Disable if ( ! $ variable_layer_height_allowed ) ;
2017-02-13 11:40:12 +00:00
}
2017-02-17 14:00:01 +00:00
my $ export_in_progress = $ self - > { export_gcode_output_file } || $ self - > { send_gcode_file } ;
my $ method = ( $ have_objects && ! $ export_in_progress ) ? 'Enable' : 'Disable' ;
$ self - > { "btn_$_" } - > $ method
for grep $ self - > { "btn_$_" } , qw( reslice export_gcode print send_gcode ) ;
2012-04-30 12:56:01 +00:00
}
sub selection_changed {
my $ self = shift ;
2013-12-12 19:19:33 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
my $ have_sel = defined $ obj_idx ;
2012-04-30 12:56:01 +00:00
2012-05-04 09:22:56 +00:00
if ( $ self - > { htoolbar } ) {
2017-02-13 11:40:12 +00:00
# On OSX or Linux
2012-08-02 19:49:26 +00:00
$ self - > { htoolbar } - > EnableTool ( $ _ , $ have_sel )
2014-12-10 16:34:59 +00:00
for ( TB_REMOVE , TB_MORE , TB_FEWER , TB_45CW , TB_45CCW , TB_SCALE , TB_SPLIT , TB_CUT , TB_SETTINGS ) ;
2017-02-13 11:40:12 +00:00
} else {
# On MSW
my $ method = $ have_sel ? 'Enable' : 'Disable' ;
$ self - > { "btn_$_" } - > $ method
for grep $ self - > { "btn_$_" } , qw( remove increase decrease rotate45cw rotate45ccw changescale split cut settings ) ;
2013-08-25 10:22:05 +00:00
}
if ( $ self - > { object_info_size } ) { # have we already loaded the info pane?
if ( $ have_sel ) {
2013-12-12 19:19:33 +00:00
my $ model_object = $ self - > { model } - > objects - > [ $ obj_idx ] ;
2016-12-12 17:02:24 +00:00
$ model_object - > print_info ;
2013-12-12 19:19:33 +00:00
my $ model_instance = $ model_object - > instances - > [ 0 ] ;
$ 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 ) ;
2013-08-25 10:22:05 +00:00
2013-12-12 19:19:33 +00:00
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 ( '%d (%d shells)' , $ model_object - > facets_count , $ stats - > { number_of_parts } ) ) ;
2013-08-25 15:26:55 +00:00
if ( my $ errors = sum ( @$ stats { qw( degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges ) } ) ) {
$ self - > { object_info_manifold } - > SetLabel ( sprintf ( "Auto-repaired (%d errors)" , $ errors ) ) ;
$ self - > { object_info_manifold_warning_icon } - > Show ;
2013-08-25 16:01:59 +00:00
# we don't show normals_fixed because we never provide normals
# to admesh, so it generates normals for all facets
2013-08-25 15:26:55 +00:00
my $ message = sprintf '%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 ( "Yes" ) ;
}
} else {
$ self - > { object_info_facets } - > SetLabel ( $ object - > facets ) ;
2013-08-25 10:22:05 +00:00
}
} else {
2013-08-25 15:26:55 +00:00
$ self - > { "object_info_$_" } - > SetLabel ( "" ) for qw( size volume facets materials manifold ) ;
$ self - > { object_info_manifold_warning_icon } - > Hide ;
$ self - > { object_info_manifold } - > SetToolTipString ( "" ) ;
2013-08-25 10:22:05 +00:00
}
2013-08-25 15:26:55 +00:00
$ self - > Layout ;
2012-05-04 09:22:56 +00:00
}
2014-06-14 16:58:56 +00:00
# prepagate the event to the frame (a custom Wx event would be cleaner)
2014-06-14 17:11:04 +00:00
$ self - > GetFrame - > on_plater_selection_changed ( $ have_sel ) ;
2012-04-30 12:56:01 +00:00
}
2013-12-12 19:19:33 +00:00
sub select_object {
my ( $ self , $ obj_idx ) = @ _ ;
$ _ - > selected ( 0 ) for @ { $ self - > { objects } } ;
if ( defined $ obj_idx ) {
$ self - > { objects } - > [ $ obj_idx ] - > selected ( 1 ) ;
2014-03-25 14:30:56 +00:00
# We use this flag to avoid circular event handling
# Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows,
# whose event handler calls this method again and again and again
$ PreventListEvents = 1 ;
2013-12-12 19:19:33 +00:00
$ self - > { list } - > Select ( $ obj_idx , 1 ) ;
2014-03-25 14:30:56 +00:00
$ PreventListEvents = 0 ;
2013-12-12 19:19:33 +00:00
} else {
# TODO: deselect all in list
}
$ self - > selection_changed ( 1 ) ;
}
2012-08-29 17:37:27 +00:00
sub selected_object {
2012-04-30 12:56:01 +00:00
my $ self = shift ;
2013-12-12 19:19:33 +00:00
my $ obj_idx = first { $ self - > { objects } [ $ _ ] - > selected } 0 .. $# { $ self - > { objects } } ;
return undef if ! defined $ obj_idx ;
2012-08-29 17:37:27 +00:00
return ( $ obj_idx , $ self - > { objects } [ $ obj_idx ] ) ,
2012-04-30 12:56:01 +00:00
}
2013-08-25 20:24:43 +00:00
sub validate_config {
my $ self = shift ;
eval {
2014-06-14 17:54:18 +00:00
$ self - > GetFrame - > config - > validate ;
2013-08-25 20:24:43 +00:00
} ;
return 0 if Slic3r::GUI:: catch_error ( $ self ) ;
return 1 ;
}
2012-05-04 10:56:15 +00:00
sub statusbar {
my $ self = shift ;
2014-06-14 17:54:18 +00:00
return $ self - > GetFrame - > { statusbar } ;
2012-05-04 10:56:15 +00:00
}
2014-06-14 19:36:28 +00:00
sub object_menu {
my ( $ self ) = @ _ ;
my $ frame = $ self - > GetFrame ;
my $ menu = Wx::Menu - > new ;
$ frame - > _append_menu_item ( $ menu , "Delete\tCtrl+Del" , 'Remove the selected object' , sub {
$ self - > remove ;
2015-05-25 20:37:04 +00:00
} , undef , 'brick_delete.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ menu , "Increase copies\tCtrl++" , 'Place one more copy of the selected object' , sub {
$ self - > increase ;
2015-05-25 20:37:04 +00:00
} , undef , 'add.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ menu , "Decrease copies\tCtrl+-" , 'Remove one copy of the selected object' , sub {
$ self - > decrease ;
2015-05-25 20:37:04 +00:00
} , undef , 'delete.png' ) ;
2015-01-14 22:19:13 +00:00
$ frame - > _append_menu_item ( $ menu , "Set number of copies…" , 'Change the number of copies of the selected object' , sub {
$ self - > set_number_of_copies ;
2015-05-25 20:37:04 +00:00
} , undef , 'textfield.png' ) ;
2014-06-14 19:36:28 +00:00
$ menu - > AppendSeparator ( ) ;
$ frame - > _append_menu_item ( $ menu , "Rotate 45° clockwise" , 'Rotate the selected object by 45° clockwise' , sub {
2017-03-23 10:53:59 +00:00
$ self - > rotate ( - 45 , Z , 'relative' ) ;
2015-05-25 20:37:04 +00:00
} , undef , 'arrow_rotate_clockwise.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ menu , "Rotate 45° counter-clockwise" , 'Rotate the selected object by 45° counter-clockwise' , sub {
2017-03-23 10:53:59 +00:00
$ self - > rotate ( + 45 , Z , 'relative' ) ;
2015-05-25 20:37:04 +00:00
} , undef , 'arrow_rotate_anticlockwise.png' ) ;
2014-06-14 19:36:28 +00:00
my $ rotateMenu = Wx::Menu - > new ;
2015-05-25 20:37:04 +00:00
my $ rotateMenuItem = $ menu - > AppendSubMenu ( $ rotateMenu , "Rotate" , 'Rotate the selected object by an arbitrary angle' ) ;
$ frame - > _set_menu_item_icon ( $ rotateMenuItem , 'textfield.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ rotateMenu , "Around X axis…" , 'Rotate the selected object by an arbitrary angle around X axis' , sub {
$ self - > rotate ( undef , X ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_red.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ rotateMenu , "Around Y axis…" , 'Rotate the selected object by an arbitrary angle around Y axis' , sub {
$ self - > rotate ( undef , Y ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_green.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ rotateMenu , "Around Z axis…" , 'Rotate the selected object by an arbitrary angle around Z axis' , sub {
$ self - > rotate ( undef , Z ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_blue.png' ) ;
2014-06-14 19:36:28 +00:00
2015-11-04 22:11:30 +00:00
my $ mirrorMenu = Wx::Menu - > new ;
my $ mirrorMenuItem = $ menu - > AppendSubMenu ( $ mirrorMenu , "Mirror" , 'Mirror the selected object' ) ;
$ frame - > _set_menu_item_icon ( $ mirrorMenuItem , 'shape_flip_horizontal.png' ) ;
$ frame - > _append_menu_item ( $ mirrorMenu , "Along X axis…" , 'Mirror the selected object along the X axis' , sub {
$ self - > mirror ( X ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_red.png' ) ;
2015-11-04 22:11:30 +00:00
$ frame - > _append_menu_item ( $ mirrorMenu , "Along Y axis…" , 'Mirror the selected object along the Y axis' , sub {
$ self - > mirror ( Y ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_green.png' ) ;
2015-11-04 22:11:30 +00:00
$ frame - > _append_menu_item ( $ mirrorMenu , "Along Z axis…" , 'Mirror the selected object along the Z axis' , sub {
$ self - > mirror ( Z ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_blue.png' ) ;
2014-06-14 19:36:28 +00:00
2014-06-16 22:50:44 +00:00
my $ scaleMenu = Wx::Menu - > new ;
2015-05-25 20:37:04 +00:00
my $ scaleMenuItem = $ menu - > AppendSubMenu ( $ scaleMenu , "Scale" , 'Scale the selected object along a single axis' ) ;
$ frame - > _set_menu_item_icon ( $ scaleMenuItem , 'arrow_out.png' ) ;
2014-06-16 22:50:44 +00:00
$ frame - > _append_menu_item ( $ scaleMenu , "Uniformly…" , 'Scale the selected object along the XYZ axes' , sub {
$ self - > changescale ( undef ) ;
} ) ;
$ frame - > _append_menu_item ( $ scaleMenu , "Along X axis…" , 'Scale the selected object along the X axis' , sub {
$ self - > changescale ( X ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_red.png' ) ;
2014-06-16 22:50:44 +00:00
$ frame - > _append_menu_item ( $ scaleMenu , "Along Y axis…" , 'Scale the selected object along the Y axis' , sub {
$ self - > changescale ( Y ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_green.png' ) ;
2014-06-16 22:50:44 +00:00
$ frame - > _append_menu_item ( $ scaleMenu , "Along Z axis…" , 'Scale the selected object along the Z axis' , sub {
$ self - > changescale ( Z ) ;
2015-12-05 18:01:17 +00:00
} , undef , 'bullet_blue.png' ) ;
2014-06-16 22:50:44 +00:00
2015-12-05 18:37:57 +00:00
my $ scaleToSizeMenu = Wx::Menu - > new ;
my $ scaleToSizeMenuItem = $ menu - > AppendSubMenu ( $ scaleToSizeMenu , "Scale to size" , 'Scale the selected object along a single axis' ) ;
$ frame - > _set_menu_item_icon ( $ scaleToSizeMenuItem , 'arrow_out.png' ) ;
$ frame - > _append_menu_item ( $ scaleToSizeMenu , "Uniformly…" , 'Scale the selected object along the XYZ axes' , sub {
$ self - > changescale ( undef , 1 ) ;
} ) ;
$ frame - > _append_menu_item ( $ scaleToSizeMenu , "Along X axis…" , 'Scale the selected object along the X axis' , sub {
$ self - > changescale ( X , 1 ) ;
} , undef , 'bullet_red.png' ) ;
$ frame - > _append_menu_item ( $ scaleToSizeMenu , "Along Y axis…" , 'Scale the selected object along the Y axis' , sub {
$ self - > changescale ( Y , 1 ) ;
} , undef , 'bullet_green.png' ) ;
$ frame - > _append_menu_item ( $ scaleToSizeMenu , "Along Z axis…" , 'Scale the selected object along the Z axis' , sub {
$ self - > changescale ( Z , 1 ) ;
} , undef , 'bullet_blue.png' ) ;
2014-06-14 19:36:28 +00:00
$ frame - > _append_menu_item ( $ menu , "Split" , 'Split the selected object into individual parts' , sub {
$ self - > split_object ;
2015-05-25 20:37:04 +00:00
} , undef , 'shape_ungroup.png' ) ;
2014-07-13 10:10:34 +00:00
$ frame - > _append_menu_item ( $ menu , "Cut…" , 'Open the 3D cutting tool' , sub {
2014-06-14 19:36:28 +00:00
$ self - > object_cut_dialog ;
2015-05-25 20:37:04 +00:00
} , undef , 'package.png' ) ;
2014-06-14 19:36:28 +00:00
$ menu - > AppendSeparator ( ) ;
$ frame - > _append_menu_item ( $ menu , "Settings…" , 'Open the object editor dialog' , sub {
$ self - > object_settings_dialog ;
2015-05-25 20:37:04 +00:00
} , undef , 'cog.png' ) ;
2015-01-03 14:48:53 +00:00
$ menu - > AppendSeparator ( ) ;
2016-12-17 20:47:45 +00:00
$ frame - > _append_menu_item ( $ menu , "Reload from Disk" , 'Reload the selected file from Disk' , sub {
$ self - > reload_from_disk ;
} , undef , 'arrow_refresh.png' ) ;
2015-01-03 14:48:53 +00:00
$ frame - > _append_menu_item ( $ menu , "Export object as STL…" , 'Export this single object as STL file' , sub {
$ self - > export_object_stl ;
2015-05-25 20:37:04 +00:00
} , undef , 'brick_go.png' ) ;
2014-06-14 19:36:28 +00:00
return $ menu ;
}
2016-10-05 12:13:07 +00:00
# Set a camera direction, zoom to all objects.
sub select_view {
my ( $ self , $ direction ) = @ _ ;
my $ idx_page = $ self - > { preview_notebook } - > GetSelection ;
my $ page = ( $ idx_page == & Wx:: wxNOT_FOUND ) ? '3D' : $ self - > { preview_notebook } - > GetPageText ( $ idx_page ) ;
if ( $ page eq 'Preview' ) {
$ self - > { preview3D } - > canvas - > select_view ( $ direction ) ;
$ self - > { canvas3D } - > set_viewport_from_scene ( $ self - > { preview3D } - > canvas ) ;
} else {
$ self - > { canvas3D } - > select_view ( $ direction ) ;
$ self - > { preview3D } - > canvas - > set_viewport_from_scene ( $ self - > { canvas3D } ) ;
}
}
2012-05-04 08:15:33 +00:00
package Slic3r::GUI::Plater::DropTarget ;
2012-04-30 12:56:01 +00:00
use Wx::DND ;
use base 'Wx::FileDropTarget' ;
sub new {
my $ class = shift ;
my ( $ window ) = @ _ ;
my $ self = $ class - > SUPER:: new ;
$ self - > { window } = $ window ;
return $ self ;
}
sub OnDropFiles {
my $ self = shift ;
my ( $ x , $ y , $ filenames ) = @ _ ;
2012-07-21 12:45:45 +00:00
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@ _ = ( ) ;
2013-03-19 11:24:39 +00:00
# only accept STL, OBJ and AMF files
2017-04-05 12:45:43 +00:00
return 0 if grep ! /\.(?:stl|obj|amf(?:\.xml)?|prusa)$/i , @$ filenames ;
2012-04-30 12:56:01 +00:00
$ self - > { window } - > load_file ( $ _ ) for @$ filenames ;
}
2016-09-13 09:24:55 +00:00
# 2D preview of an object. Each object is previewed by its convex hull.
2012-08-29 17:37:27 +00:00
package Slic3r::GUI::Plater::Object ;
use Moo ;
2013-07-13 18:34:57 +00:00
use List::Util qw( first ) ;
2013-06-07 21:16:02 +00:00
use Slic3r::Geometry qw( X Y Z MIN MAX deg2rad ) ;
2012-08-29 17:37:27 +00:00
has 'name' = > ( is = > 'rw' , required = > 1 ) ;
2013-12-12 19:19:33 +00:00
has 'thumbnail' = > ( is = > 'rw' ) ; # ExPolygon::Collection in scaled model units with no transforms
2013-06-12 14:53:19 +00:00
has 'transformed_thumbnail' = > ( is = > 'rw' ) ;
2013-12-12 19:19:33 +00:00
has 'instance_thumbnails' = > ( is = > 'ro' , default = > sub { [] } ) ; # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
has 'selected' = > ( is = > 'rw' , default = > sub { 0 } ) ;
2012-08-29 17:37:27 +00:00
sub make_thumbnail {
2013-12-12 19:19:33 +00:00
my ( $ self , $ model , $ obj_idx ) = @ _ ;
2012-08-29 17:37:27 +00:00
2014-06-14 18:52:21 +00:00
# make method idempotent
$ self - > thumbnail - > clear ;
2013-12-12 19:19:33 +00:00
2014-06-14 18:52:21 +00:00
my $ mesh = $ model - > objects - > [ $ obj_idx ] - > raw_mesh ;
2013-12-12 19:19:33 +00:00
if ( $ mesh - > facets_count <= 5000 ) {
2013-08-25 20:33:50 +00:00
# remove polygons with area <= 1mm
my $ area_threshold = Slic3r::Geometry:: scale 1 ;
2013-11-12 13:30:13 +00:00
$ self - > thumbnail - > append (
2013-08-25 20:33:50 +00:00
grep $ _ - > area >= $ area_threshold ,
2013-12-12 19:19:33 +00:00
@ { $ mesh - > horizontal_projection } , # horizontal_projection returns scaled expolygons
2013-08-25 20:33:50 +00:00
) ;
2013-11-21 19:25:24 +00:00
$ self - > thumbnail - > simplify ( 0.5 ) ;
2013-08-05 18:48:09 +00:00
} else {
2013-12-12 19:19:33 +00:00
my $ convex_hull = Slic3r::ExPolygon - > new ( $ mesh - > convex_hull ) ;
2013-11-12 13:30:13 +00:00
$ self - > thumbnail - > append ( $ convex_hull ) ;
2013-08-05 18:48:09 +00:00
}
2013-08-05 18:21:08 +00:00
2013-11-12 13:30:13 +00:00
return $ self - > thumbnail ;
2012-08-29 17:37:27 +00:00
}
2013-12-12 19:19:33 +00:00
sub transform_thumbnail {
my ( $ self , $ model , $ obj_idx ) = @ _ ;
2012-08-29 17:37:27 +00:00
2013-07-13 22:38:01 +00:00
return unless defined $ self - > thumbnail ;
2012-08-29 17:37:27 +00:00
2013-12-12 19:19:33 +00:00
my $ model_object = $ model - > objects - > [ $ obj_idx ] ;
my $ model_instance = $ model_object - > instances - > [ 0 ] ;
2013-06-07 21:16:02 +00:00
2013-06-13 18:05:32 +00:00
# the order of these transformations MUST be the same everywhere, including
2013-12-12 19:19:33 +00:00
# in Slic3r::Print->add_model_object()
my $ t = $ self - > thumbnail - > clone ;
2015-05-03 16:40:00 +00:00
$ t - > rotate ( $ model_instance - > rotation , Slic3r::Point - > new ( 0 , 0 ) ) ;
2013-12-12 19:19:33 +00:00
$ t - > scale ( $ model_instance - > scaling_factor ) ;
$ self - > transformed_thumbnail ( $ t ) ;
2013-06-07 21:16:02 +00:00
}
2012-04-30 12:56:01 +00:00
1 ;