2016-09-14 11:22:41 +02:00
# The "Plater" tab. It contains the "3D", "2D", "Preview" and "Layers" subtabs.
2012-05-04 10:15:33 +02:00
package Slic3r::GUI::Plater ;
2012-04-30 14:56:01 +02:00
use strict ;
use warnings ;
use utf8 ;
use File::Basename qw(basename dirname) ;
2015-05-25 19:51:47 +02:00
use List::Util qw(sum first max) ;
2017-07-21 16:29:40 +02:00
use Slic3r::Geometry qw(X Y Z scale unscale deg2rad rad2deg) ;
2012-05-19 21:13:10 +02:00
use threads::shared qw(shared_clone) ;
2017-05-24 15:20:20 +02:00
use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
2016-12-14 15:36:18 +01:00
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap) ;
2016-12-12 18:02:24 +01:00
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
2017-05-24 15:20:20 +02:00
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_LEFT_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
2015-05-26 02:01:43 +02:00
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED) ;
2012-04-30 14:56:01 +02:00
use base 'Wx::Panel' ;
2013-12-12 20:19:33 +01:00
use constant TB_ADD => & Wx:: NewId ;
2013-08-25 12:22:05 +02: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 21:11:36 +02:00
use constant TB_MORE => & Wx:: NewId ;
2013-08-25 15:45:22 +02:00
use constant TB_FEWER => & Wx:: NewId ;
2012-08-02 21:11:36 +02: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 17:34:59 +01:00
use constant TB_CUT => & Wx:: NewId ;
2013-08-25 18:01:59 +02:00
use constant TB_SETTINGS => & Wx:: NewId ;
2016-12-12 18:02:24 +01:00
use constant TB_LAYER_EDITING => & Wx:: NewId ;
2012-05-04 11:22:56 +02:00
2018-02-22 15:13:07 +01:00
use Wx::Locale gettext => 'L' ;
2013-10-16 15:13:39 +02:00
# package variables to avoid passing lexicals to threads
our $PROGRESS_BAR_EVENT : shared = Wx:: NewEventType ;
2014-06-13 14:27:55 +02:00
our $ERROR_EVENT : shared = Wx:: NewEventType ;
2016-10-25 13:24:42 +02:00
# Emitted from the worker thread when the G-code export is finished.
2013-10-16 15:13:39 +02:00
our $EXPORT_COMPLETED_EVENT : shared = Wx:: NewEventType ;
2014-06-13 11:19:53 +02:00
our $PROCESS_COMPLETED_EVENT : shared = Wx:: NewEventType ;
2012-04-30 20:59:14 +02:00
2015-05-26 02:01:43 +02:00
use constant FILAMENT_CHOOSERS_SPACING => 0 ;
2014-06-13 23:27:52 +02:00
use constant PROCESS_DELAY => 0.5 * 1000 ; # milliseconds
2012-07-27 21:13:03 +02:00
2014-03-25 15:30:56 +01:00
my $PreventListEvents = 0 ;
2012-04-30 14:56:01 +02:00
sub new {
2017-10-27 22:49:59 +02:00
my ( $class , $parent ) = @_ ;
2012-07-24 14:28:21 +02:00
my $self = $class -> SUPER:: new ( $parent , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
2017-10-27 22:49:59 +02:00
$self -> { config } = Slic3r::Config:: new_from_defaults_keys ([ qw(
2017-02-10 10:21:50 +01:00
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
2018-02-07 11:37:15 +01:00
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
2018-03-16 14:06:23 +01:00
nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
2018-03-22 13:37:01 +01:00
wipe_tower_rotation_angle extruder_colour filament_colour max_print_height
2017-10-27 22:49:59 +02:00
) ]);
2017-02-07 18:28:53 +01:00
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
2013-12-12 20:19:33 +01:00
$self -> { model } = Slic3r::Model -> new ;
2017-02-07 18:28:53 +01:00
# C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
2013-12-13 14:02:01 +01:00
$self -> { print } = Slic3r::Print -> new ;
2016-09-13 11:24:55 +02:00
# List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter.
2012-09-12 16:30:44 +02:00
$self -> { objects } = [] ;
2018-02-14 20:35:59 +01:00
$self -> { gcode_preview_data } = Slic3r::GCode::PreviewData -> new ;
2012-04-30 14:56:01 +02:00
2014-06-13 14:27:55 +02:00
$self -> { print } -> set_status_cb ( sub {
my ( $percent , $message ) = @_ ;
2018-02-13 18:31:34 +01:00
my $event = Wx::CommandEvent -> new ( $PROGRESS_BAR_EVENT );
$event -> SetString ( $message );
$event -> SetInt ( $percent );
Wx:: PostEvent ( $self , $event );
2014-06-13 14:27:55 +02:00
});
2014-07-13 12:10:34 +02:00
# Initialize preview notebook
$self -> { preview_notebook } = Wx::Notebook -> new ( $self , - 1 , wxDefaultPosition , [ 335 , 335 ], wxNB_BOTTOM );
2014-12-13 20:41:03 +01:00
# Initialize handlers for canvases
my $on_select_object = sub {
2014-05-28 12:29:43 +02:00
my ( $obj_idx ) = @_ ;
2017-05-17 16:53:40 +02:00
# Ignore the special objects (the wipe tower proxy and such).
2018-06-08 11:37:07 +02:00
#==============================================================================================================================
$self -> select_object (( defined ( $obj_idx ) && $obj_idx >= 0 && $obj_idx < 1000 ) ? $obj_idx : undef );
# $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef);
#==============================================================================================================================
2014-12-13 20:41:03 +01:00
};
my $on_double_click = sub {
2014-11-30 20:19:04 +01:00
$self -> object_settings_dialog if $self -> selected_object ;
2014-12-13 20:41:03 +01:00
};
my $on_right_click = sub {
2018-05-31 13:51:50 +02:00
#==============================================================================================================================
my ( $canvas , $click_pos_x , $click_pos_y ) = @_ ;
# my ($canvas, $click_pos) = @_;
#==============================================================================================================================
2014-06-14 21:36:28 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
return if ! defined $obj_idx ;
my $menu = $self -> object_menu ;
2018-05-31 13:51:50 +02:00
#==============================================================================================================================
$canvas -> PopupMenu ( $menu , $click_pos_x , $click_pos_y );
# $canvas->PopupMenu($menu, $click_pos);
#==============================================================================================================================
2014-06-14 21:36:28 +02:00
$menu -> Destroy ;
2014-12-13 20:41:03 +01:00
};
2015-01-13 18:41:23 +01:00
my $on_instances_moved = sub {
2014-05-28 12:29:43 +02:00
$self -> update ;
2014-12-13 20:41:03 +01:00
};
2018-03-21 15:21:03 +01:00
# callback to enable/disable action buttons
my $enable_action_buttons = sub {
my ( $enable ) = @_ ;
$self -> { btn_export_gcode } -> Enable ( $enable );
$self -> { btn_reslice } -> Enable ( $enable );
$self -> { btn_print } -> Enable ( $enable );
$self -> { btn_send_gcode } -> Enable ( $enable );
};
2015-01-18 19:36:47 +01:00
# Initialize 3D plater
2015-01-17 10:53:01 +01:00
if ( $ Slic3r::GUI:: have_OpenGL ) {
2016-12-12 18:02:24 +01:00
$self -> { canvas3D } = Slic3r::GUI::Plater:: 3 D -> new ( $self -> { preview_notebook }, $self -> { objects }, $self -> { model }, $self -> { print }, $self -> { config });
2018-02-22 15:13:07 +01:00
$self -> { preview_notebook } -> AddPage ( $self -> { canvas3D }, L ( '3D' ));
2018-05-31 13:51:50 +02:00
#==============================================================================================================================
2018-06-08 11:37:07 +02:00
Slic3r::GUI::_3DScene:: register_on_select_object_callback ( $self -> { canvas3D }, $on_select_object );
2018-05-31 13:51:50 +02:00
Slic3r::GUI::_3DScene:: register_on_double_click_callback ( $self -> { canvas3D }, $on_double_click );
Slic3r::GUI::_3DScene:: register_on_right_click_callback ( $self -> { canvas3D }, sub { $on_right_click -> ( $self -> { canvas3D }, @_ ); });
2018-06-07 09:22:19 +02:00
Slic3r::GUI::_3DScene:: register_on_arrange_callback ( $self -> { canvas3D }, sub { $self -> arrange });
Slic3r::GUI::_3DScene:: register_on_rotate_object_left_callback ( $self -> { canvas3D }, sub { $self -> rotate ( - 45 , Z , 'relative' ) });
Slic3r::GUI::_3DScene:: register_on_rotate_object_right_callback ( $self -> { canvas3D }, sub { $self -> rotate ( 45 , Z , 'relative' ) });
Slic3r::GUI::_3DScene:: register_on_scale_object_uniformly_callback ( $self -> { canvas3D }, sub { $self -> changescale ( undef ) });
Slic3r::GUI::_3DScene:: register_on_increase_objects_callback ( $self -> { canvas3D }, sub { $self -> increase () });
Slic3r::GUI::_3DScene:: register_on_decrease_objects_callback ( $self -> { canvas3D }, sub { $self -> decrease () });
2018-06-07 11:18:28 +02:00
Slic3r::GUI::_3DScene:: register_on_remove_object_callback ( $self -> { canvas3D }, sub { $self -> remove () });
Slic3r::GUI::_3DScene:: register_on_instance_moved_callback ( $self -> { canvas3D }, $on_instances_moved );
2018-06-08 09:40:00 +02:00
Slic3r::GUI::_3DScene:: register_on_enable_action_buttons_callback ( $self -> { canvas3D }, $enable_action_buttons );
2018-05-23 09:57:44 +02:00
Slic3r::GUI::_3DScene:: enable_shader ( $self -> { canvas3D }, 1 );
2018-06-05 10:56:55 +02:00
Slic3r::GUI::_3DScene:: enable_force_zoom_to_bed ( $self -> { canvas3D }, 1 );
2018-06-08 09:40:00 +02:00
2018-06-07 11:18:28 +02:00
Slic3r::GUI::_3DScene:: register_on_wipe_tower_moved_callback ( $self -> { canvas3D }, sub {
my ( $x , $y ) = @_ ;
2017-05-19 21:48:32 +02:00
my $cfg = Slic3r::Config -> new ;
2018-06-07 11:18:28 +02:00
$cfg -> set ( 'wipe_tower_x' , $x );
$cfg -> set ( 'wipe_tower_y' , $y );
2017-05-19 21:48:32 +02:00
$self -> GetFrame -> { options_tabs }{ print } -> load_config ( $cfg );
});
2018-06-07 11:18:28 +02:00
2018-05-31 16:04:59 +02:00
Slic3r::GUI::_3DScene:: register_on_model_update_callback ( $self -> { canvas3D }, sub {
2017-11-02 16:21:34 +01:00
if ( wxTheApp -> { app_config } -> get ( "background_processing" )) {
2017-06-09 13:27:35 +02:00
$self -> schedule_background_process ;
2017-02-15 16:02:54 +01:00
} else {
# Hide the print info box, it is no more valid.
$self -> { "print_info_box_show" } -> ( 0 );
2017-02-09 14:56:13 +01:00
}
});
2018-06-04 15:42:34 +02:00
Slic3r::GUI::_3DScene:: register_on_viewport_changed_callback ( $self -> { canvas3D }, sub { Slic3r::GUI::_3DScene:: set_viewport_from_scene ( $self -> { preview3D } -> canvas , $self -> { canvas3D }); });
2018-05-31 16:04:59 +02:00
2018-06-08 11:37:07 +02:00
# $self->{canvas3D}->set_on_select_object($on_select_object);
2018-06-08 09:40:00 +02:00
# $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_arrange(sub { $self->arrange });
# $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') });
# $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') });
# $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
# $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() });
# $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() });
# $self->{canvas3D}->set_on_remove_object(sub { $self->remove() });
# $self->{canvas3D}->set_on_instances_moved($on_instances_moved);
# $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons);
# $self->{canvas3D}->use_plain_shader(1);
#
# $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);
# });
#
2018-05-31 16:04:59 +02:00
# $self->{canvas3D}->set_on_model_update(sub {
# if (wxTheApp->{app_config}->get("background_processing")) {
# $self->schedule_background_process;
# } else {
# # Hide the print info box, it is no more valid.
# $self->{"print_info_box_show"}->(0);
# }
# });
2018-06-04 15:42:34 +02:00
# $self->{canvas3D}->on_viewport_changed(sub {
2018-05-29 15:36:09 +02:00
# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
2018-06-04 15:42:34 +02:00
# });
2018-05-15 09:50:01 +02:00
#==============================================================================================================================
2015-01-17 10:53:01 +01:00
}
2014-12-13 20:41:03 +01:00
# Initialize 2D preview canvas
$self -> { canvas } = Slic3r::GUI::Plater:: 2 D -> new ( $self -> { preview_notebook }, wxDefaultSize , $self -> { objects }, $self -> { model }, $self -> { config });
2018-02-22 15:13:07 +01:00
$self -> { preview_notebook } -> AddPage ( $self -> { canvas }, L ( '2D' ));
2014-12-13 20:41:03 +01:00
$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 18:41:23 +01:00
$self -> { canvas } -> on_instances_moved ( $on_instances_moved );
2012-04-30 14:56:01 +02:00
2015-01-18 20:55:44 +01:00
# Initialize 3D toolpaths preview
2014-07-13 12:10:34 +02:00
if ( $ Slic3r::GUI:: have_OpenGL ) {
2018-02-14 20:35:59 +01:00
$self -> { preview3D } = Slic3r::GUI::Plater:: 3 DPreview -> new ( $self -> { preview_notebook }, $self -> { print }, $self -> { gcode_preview_data }, $self -> { config });
2018-05-15 09:50:01 +02:00
#==============================================================================================================================
2018-05-29 15:36:09 +02:00
Slic3r::GUI::_3DScene:: register_on_viewport_changed_callback ( $self -> { preview3D } -> canvas , sub { Slic3r::GUI::_3DScene:: set_viewport_from_scene ( $self -> { canvas3D }, $self -> { preview3D } -> canvas ); });
2018-06-04 15:42:34 +02:00
# $self->{preview3D}->canvas->on_viewport_changed(sub {
# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
# });
2018-05-15 09:50:01 +02:00
#==============================================================================================================================
2018-02-22 15:13:07 +01:00
$self -> { preview_notebook } -> AddPage ( $self -> { preview3D }, L ( 'Preview' ));
2015-01-18 20:55:44 +01:00
$self -> { preview3D_page_idx } = $self -> { preview_notebook } -> GetPageCount - 1 ;
2014-07-13 12:10:34 +02:00
}
2015-01-17 10:53:01 +01:00
# Initialize toolpaths preview
2014-07-13 12:10:34 +02:00
if ( $ Slic3r::GUI:: have_OpenGL ) {
2014-11-30 20:18:09 +01:00
$self -> { toolpaths2D } = Slic3r::GUI::Plater:: 2 DToolpaths -> new ( $self -> { preview_notebook }, $self -> { print });
2018-02-22 15:13:07 +01:00
$self -> { preview_notebook } -> AddPage ( $self -> { toolpaths2D }, L ( 'Layers' ));
2015-01-18 19:36:47 +01:00
}
EVT_NOTEBOOK_PAGE_CHANGED ( $self , $self -> { preview_notebook }, sub {
2017-06-01 16:31:29 +02:00
my $preview = $self -> { preview_notebook } -> GetCurrentPage ;
2018-01-16 14:59:06 +01:00
if ( $preview == $self -> { preview3D })
{
2018-05-21 14:57:43 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: enable_legend_texture ( $self -> { preview3D } -> canvas , 1 );
# $self->{preview3D}->canvas->set_legend_enabled(1);
#==============================================================================================================================
2018-01-16 14:59:06 +01:00
$self -> { preview3D } -> load_print ( 1 );
} else {
2018-05-21 14:57:43 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: enable_legend_texture ( $self -> { preview3D } -> canvas , 0 );
# $self->{preview3D}->canvas->set_legend_enabled(0);
#==============================================================================================================================
2018-01-16 14:59:06 +01:00
}
2018-01-17 10:39:05 +01:00
2018-06-08 09:40:00 +02:00
#==============================================================================================================================
if ( $preview == $self -> { canvas3D }) {
if ( Slic3r::GUI::_3DScene:: is_reload_delayed ( $self -> { canvas3D })) {
my $selections = $self -> collect_selections ;
Slic3r::GUI::_3DScene:: set_objects_selections ( $self -> { canvas3D }, \ @$selections );
Slic3r::GUI::_3DScene:: reload_scene ( $self -> { canvas3D }, 1 );
}
}
else {
#==============================================================================================================================
2018-01-16 14:59:06 +01:00
$preview -> OnActivate if $preview -> can ( 'OnActivate' );
2018-06-08 09:40:00 +02:00
#==============================================================================================================================
}
#==============================================================================================================================
2015-01-18 19:36:47 +01:00
});
2012-05-04 11:22:56 +02:00
# toolbar for object manipulation
2012-05-20 16:24:10 +02:00
if ( !& Wx:: wxMSW ) {
2012-05-04 12:56:15 +02:00
Wx::ToolTip:: Enable ( 1 );
2012-07-24 14:28:21 +02:00
$self -> { htoolbar } = Wx::ToolBar -> new ( $self , - 1 , wxDefaultPosition , wxDefaultSize , wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL );
2018-02-22 15:13:07 +01:00
$self -> { htoolbar } -> AddTool ( TB_ADD , L ( "Add…" ), Wx::Bitmap -> new ( Slic3r:: var ( "brick_add.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_REMOVE , L ( "Delete" ), Wx::Bitmap -> new ( Slic3r:: var ( "brick_delete.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_RESET , L ( "Delete All" ), Wx::Bitmap -> new ( Slic3r:: var ( "cross.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_ARRANGE , L ( "Arrange" ), Wx::Bitmap -> new ( Slic3r:: var ( "bricks.png" ), wxBITMAP_TYPE_PNG ), '' );
2013-08-25 12:22:05 +02:00
$self -> { htoolbar } -> AddSeparator ;
2018-02-22 15:13:07 +01:00
$self -> { htoolbar } -> AddTool ( TB_MORE , L ( "More" ), Wx::Bitmap -> new ( Slic3r:: var ( "add.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_FEWER , L ( "Fewer" ), Wx::Bitmap -> new ( Slic3r:: var ( "delete.png" ), wxBITMAP_TYPE_PNG ), '' );
2012-05-04 11:22:56 +02:00
$self -> { htoolbar } -> AddSeparator ;
2018-02-22 15:13:07 +01:00
$self -> { htoolbar } -> AddTool ( TB_45CCW , L ( "45° ccw" ), Wx::Bitmap -> new ( Slic3r:: var ( "arrow_rotate_anticlockwise.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_45CW , L ( "45° cw" ), Wx::Bitmap -> new ( Slic3r:: var ( "arrow_rotate_clockwise.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_SCALE , L ( "Scale…" ), Wx::Bitmap -> new ( Slic3r:: var ( "arrow_out.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_SPLIT , L ( "Split" ), Wx::Bitmap -> new ( Slic3r:: var ( "shape_ungroup.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_CUT , L ( "Cut…" ), Wx::Bitmap -> new ( Slic3r:: var ( "package.png" ), wxBITMAP_TYPE_PNG ), '' );
2013-08-25 18:01:59 +02:00
$self -> { htoolbar } -> AddSeparator ;
2018-02-22 15:13:07 +01:00
$self -> { htoolbar } -> AddTool ( TB_SETTINGS , L ( "Settings…" ), Wx::Bitmap -> new ( Slic3r:: var ( "cog.png" ), wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_LAYER_EDITING , L ( 'Layer Editing' ), Wx::Bitmap -> new ( Slic3r:: var ( "variable_layer_height.png" ), wxBITMAP_TYPE_PNG ), wxNullBitmap , 1 , 0 , 'Layer Editing' );
2012-05-20 16:24:10 +02:00
} else {
2013-08-25 16:10:53 +02:00
my %tbar_buttons = (
2018-02-22 15:13:07 +01:00
add => L ( "Add…" ),
remove => L ( "Delete" ),
reset => L ( "Delete All" ),
arrange => L ( "Arrange" ),
2013-08-25 16:10:53 +02:00
increase => "" ,
decrease => "" ,
rotate45ccw => "" ,
rotate45cw => "" ,
2018-02-22 15:13:07 +01:00
changescale => L ( "Scale…" ),
split => L ( "Split" ),
cut => L ( "Cut…" ),
settings => L ( "Settings…" ),
layer_editing => L ( "Layer editing" ),
2013-08-25 16:10:53 +02:00
);
2012-05-20 16:24:10 +02:00
$self -> { btoolbar } = Wx::BoxSizer -> new ( wxHORIZONTAL );
2014-12-10 17:34:59 +01:00
for ( qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings) ) {
2012-07-03 01:20:30 +02:00
$self -> { "btn_$_" } = Wx::Button -> new ( $self , - 1 , $tbar_buttons { $_ }, wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT );
2012-05-20 16:24:10 +02:00
$self -> { btoolbar } -> Add ( $self -> { "btn_$_" });
}
2017-02-03 15:53:31 +01: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 11:22:56 +02:00
}
2012-07-24 14:42:38 +02:00
2018-05-18 13:56:51 +02:00
### Panel for right column
$self -> { right_panel } = Wx::Panel -> new ( $self , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
2018-05-13 21:00:03 +02:00
### Scrolled Window for info boxes
my $scrolled_window_sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2018-05-21 12:34:31 +02:00
$scrolled_window_sizer -> SetMinSize ([ 310 , - 1 ]);
2018-05-18 13:56:51 +02:00
my $scrolled_window_panel = Wx::ScrolledWindow -> new ( $self -> { right_panel }, - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
2018-05-13 21:00:03 +02:00
$scrolled_window_panel -> SetSizer ( $scrolled_window_sizer );
$scrolled_window_panel -> SetScrollbars ( 1 , 1 , 1 , 1 );
$self -> { list } = Wx::ListView -> new ( $scrolled_window_panel , - 1 , wxDefaultPosition , wxDefaultSize ,
2014-12-25 18:50:02 +01:00
wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
2018-02-22 15:13:07 +01:00
$self -> { list } -> InsertColumn ( 0 , L ( "Name" ), wxLIST_FORMAT_LEFT , 145 );
$self -> { list } -> InsertColumn ( 1 , L ( "Copies" ), wxLIST_FORMAT_CENTER , 45 );
$self -> { list } -> InsertColumn ( 2 , L ( "Scale" ), wxLIST_FORMAT_CENTER , wxLIST_AUTOSIZE_USEHEADER );
2012-07-24 14:42:38 +02:00
EVT_LIST_ITEM_SELECTED ( $self , $self -> { list }, \& list_item_selected );
EVT_LIST_ITEM_DESELECTED ( $self , $self -> { list }, \& list_item_deselected );
2012-10-24 22:44:08 +02:00
EVT_LIST_ITEM_ACTIVATED ( $self , $self -> { list }, \& list_item_activated );
2012-07-24 14:42:38 +02: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 11:22:56 +02:00
2013-08-25 12:22:05 +02:00
# right pane buttons
2018-05-18 13:56:51 +02:00
$self -> { btn_export_gcode } = Wx::Button -> new ( $self -> { right_panel }, - 1 , L ( "Export G-code…" ), wxDefaultPosition , [ - 1 , 30 ], wxBU_LEFT );
$self -> { btn_reslice } = Wx::Button -> new ( $self -> { right_panel }, - 1 , L ( "Slice now" ), wxDefaultPosition , [ - 1 , 30 ], wxBU_LEFT );
$self -> { btn_print } = Wx::Button -> new ( $self -> { right_panel }, - 1 , L ( "Print…" ), wxDefaultPosition , [ - 1 , 30 ], wxBU_LEFT );
$self -> { btn_send_gcode } = Wx::Button -> new ( $self -> { right_panel }, - 1 , L ( "Send to printer" ), wxDefaultPosition , [ - 1 , 30 ], wxBU_LEFT );
$self -> { btn_export_stl } = Wx::Button -> new ( $self -> { right_panel }, - 1 , L ( "Export STL…" ), wxDefaultPosition , [ - 1 , 30 ], wxBU_LEFT );
2014-12-25 18:50:02 +01:00
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
2015-01-03 23:25:55 +01:00
$self -> { btn_print } -> Hide ;
2014-12-28 01:30:05 +01:00
$self -> { btn_send_gcode } -> Hide ;
2012-05-04 11:22:56 +02:00
2017-08-03 17:47:18 +02:00
my %icons = qw(
add brick_add.png
remove brick_delete.png
reset cross.png
arrange bricks.png
export_gcode cog_go.png
print arrow_up.png
send_gcode arrow_up.png
reslice reslice.png
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
cut package.png
settings cog.png
) ;
for ( grep $self -> { "btn_$_" }, keys %icons ) {
2017-10-25 12:53:31 +02:00
$self -> { "btn_$_" } -> SetBitmap ( Wx::Bitmap -> new ( Slic3r:: var ( $icons { $_ }), wxBITMAP_TYPE_PNG ));
2012-05-01 16:49:34 +02:00
}
2012-04-30 14:56:01 +02:00
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
2014-06-13 19:23:51 +02:00
EVT_BUTTON ( $self , $self -> { btn_export_gcode }, sub {
$self -> export_gcode ;
});
2015-01-03 23:25:55 +01:00
EVT_BUTTON ( $self , $self -> { btn_print }, sub {
$self -> { print_file } = $self -> export_gcode ( Wx::StandardPaths::Get -> GetTempDir ());
});
2014-12-28 01:30:05 +01:00
EVT_BUTTON ( $self , $self -> { btn_send_gcode }, sub {
$self -> { send_gcode_file } = $self -> export_gcode ( Wx::StandardPaths::Get -> GetTempDir ());
});
2016-10-25 13:24:42 +02:00
EVT_BUTTON ( $self , $self -> { btn_reslice }, \& reslice );
2014-12-28 14:58:24 +01:00
EVT_BUTTON ( $self , $self -> { btn_export_stl }, \& export_stl );
2012-04-30 14:56:01 +02:00
2012-05-04 11:22:56 +02:00
if ( $self -> { htoolbar }) {
2014-06-17 16:27:38 +02:00
EVT_TOOL ( $self , TB_ADD , sub { $self -> add ; });
2013-08-25 12:22:05 +02:00
EVT_TOOL ( $self , TB_REMOVE , sub { $self -> remove () }); # explicitly pass no argument to remove
2014-06-17 16:27:38 +02: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 11:53:59 +01: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 16:27:38 +02:00
EVT_TOOL ( $self , TB_SCALE , sub { $self -> changescale ( undef ); });
EVT_TOOL ( $self , TB_SPLIT , sub { $self -> split_object ; });
2014-12-10 17:34:59 +01:00
EVT_TOOL ( $self , TB_CUT , sub { $_ [ 0 ] -> object_cut_dialog });
2013-08-25 18:01:59 +02:00
EVT_TOOL ( $self , TB_SETTINGS , sub { $_ [ 0 ] -> object_settings_dialog });
2016-12-14 15:09:12 +01:00
EVT_TOOL ( $self , TB_LAYER_EDITING , sub {
2018-05-25 14:05:08 +02:00
#==============================================================================================================================
my $state = Slic3r::GUI::_3DScene:: is_layers_editing_enabled ( $self -> { canvas3D });
# my $state = $self->{canvas3D}->layer_editing_enabled;
#==============================================================================================================================
2016-12-14 15:09:12 +01:00
$self -> { htoolbar } -> ToggleTool ( TB_LAYER_EDITING , ! $state );
$self -> on_layer_editing_toggled ( ! $state );
});
2012-05-04 11:22:56 +02:00
} else {
2014-06-17 16:27:38 +02:00
EVT_BUTTON ( $self , $self -> { btn_add }, sub { $self -> add ; });
2013-08-25 16:10:53 +02:00
EVT_BUTTON ( $self , $self -> { btn_remove }, sub { $self -> remove () }); # explicitly pass no argument to remove
2014-06-17 16:27:38 +02: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 11:53:59 +01: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 16:27:38 +02:00
EVT_BUTTON ( $self , $self -> { btn_changescale }, sub { $self -> changescale ( undef ); });
EVT_BUTTON ( $self , $self -> { btn_split }, sub { $self -> split_object ; });
2014-12-10 17:34:59 +01:00
EVT_BUTTON ( $self , $self -> { btn_cut }, sub { $_ [ 0 ] -> object_cut_dialog });
2013-08-25 18:01:59 +02:00
EVT_BUTTON ( $self , $self -> { btn_settings }, sub { $_ [ 0 ] -> object_settings_dialog });
2016-12-12 18:02:24 +01:00
EVT_TOGGLEBUTTON ( $self , $self -> { btn_layer_editing }, sub { $self -> on_layer_editing_toggled ( $self -> { btn_layer_editing } -> GetValue ); });
2012-05-04 11:22:56 +02:00
}
2012-05-04 10:15:33 +02:00
$_ -> SetDropTarget ( Slic3r::GUI::Plater::DropTarget -> new ( $self ))
2015-01-18 19:36:47 +01:00
for grep defined ( $_ ),
$self , $self -> { canvas }, $self -> { canvas3D }, $self -> { preview3D }, $self -> { list };
2012-04-30 14:56:01 +02:00
2012-05-05 21:08:15 +02:00
EVT_COMMAND ( $self , - 1 , $PROGRESS_BAR_EVENT , sub {
my ( $self , $event ) = @_ ;
2018-02-13 18:31:34 +01:00
$self -> on_progress_event ( $event -> GetInt , $event -> GetString );
2012-05-05 21:08:15 +02:00
});
2014-06-13 14:27:55 +02:00
EVT_COMMAND ( $self , - 1 , $ERROR_EVENT , sub {
2012-05-05 21:08:15 +02:00
my ( $self , $event ) = @_ ;
2018-02-13 18:31:34 +01:00
Slic3r::GUI:: show_error ( $self , $event -> GetString );
2012-05-05 21:08:15 +02:00
});
EVT_COMMAND ( $self , - 1 , $EXPORT_COMPLETED_EVENT , sub {
my ( $self , $event ) = @_ ;
2018-02-13 18:31:34 +01:00
$self -> on_export_completed ( $event -> GetInt );
2012-05-19 21:13:10 +02:00
});
2014-06-13 11:19:53 +02:00
EVT_COMMAND ( $self , - 1 , $PROCESS_COMPLETED_EVENT , sub {
my ( $self , $event ) = @_ ;
2018-02-13 18:31:34 +01:00
$self -> on_process_completed ( $event -> GetInt ? undef : $event -> GetString );
2014-06-13 11:19:53 +02:00
});
2017-11-02 16:21:34 +01:00
{
2015-01-18 21:31:09 +01:00
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 15:54:13 +02:00
2014-05-28 12:29:43 +02:00
$self -> { canvas } -> update_bed_size ;
2014-12-16 01:12:37 +01:00
if ( $self -> { canvas3D }) {
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
2018-06-06 15:17:10 +02:00
Slic3r::GUI::_3DScene:: set_bed_shape ( $self -> { canvas3D }, $self -> { config } -> bed_shape );
2018-05-24 16:11:34 +02:00
Slic3r::GUI::_3DScene:: zoom_to_bed ( $self -> { canvas3D });
2018-06-06 15:17:10 +02:00
# $self->{canvas3D}->update_bed_size;
2018-05-24 16:11:34 +02:00
# $self->{canvas3D}->zoom_to_bed;
#==============================================================================================================================
2014-12-16 01:12:37 +01:00
}
2015-01-18 19:36:47 +01:00
if ( $self -> { preview3D }) {
2018-06-06 15:17:10 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: set_bed_shape ( $self -> { preview3D } -> canvas , $self -> { config } -> bed_shape );
# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape);
#==============================================================================================================================
2015-01-18 19:36:47 +01:00
}
2013-12-18 18:54:11 +01:00
$self -> update ;
2012-04-30 14:56:01 +02:00
{
2013-08-25 12:22:05 +02:00
my $presets ;
2017-06-14 11:48:08 +02:00
{
2014-12-25 19:14:18 +01:00
$presets = $self -> { presets_sizer } = Wx::FlexGridSizer -> new ( 3 , 2 , 1 , 2 );
2014-12-25 18:50:02 +01:00
$presets -> AddGrowableCol ( 1 , 1 );
$presets -> SetFlexibleDirection ( wxHORIZONTAL );
2013-01-03 15:49:20 +01:00
my %group_labels = (
2018-02-22 15:13:07 +01:00
print => L ( 'Print settings' ),
filament => L ( 'Filament' ),
printer => L ( 'Printer' ),
2013-01-03 15:49:20 +01:00
);
2016-10-24 16:07:36 +02: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 15:49:20 +01:00
$self -> { preset_choosers } = {};
for my $group ( qw(print filament printer) ) {
2018-05-18 13:56:51 +02:00
my $text = Wx::StaticText -> new ( $self -> { right_panel }, - 1 , "$group_labels{$group}:" , wxDefaultPosition , wxDefaultSize , wxALIGN_RIGHT );
2013-08-25 12:22:05 +02:00
$text -> SetFont ( $ Slic3r::GUI:: small_font );
2018-05-18 13:56:51 +02:00
my $choice = Wx::BitmapComboBox -> new ( $self -> { right_panel }, - 1 , "" , wxDefaultPosition , wxDefaultSize , [] , wxCB_READONLY );
2018-02-15 16:41:26 +01:00
if ( $group eq 'filament' ) {
EVT_LEFT_DOWN ( $choice , sub { $self -> filament_color_box_lmouse_down ( 0 , @_ ); } );
}
2013-01-03 15:49:20 +01:00
$self -> { preset_choosers }{ $group } = [ $choice ];
2015-06-02 11:19:11 +02:00
# setup the listener
EVT_COMBOBOX ( $choice , $choice , sub {
my ( $choice ) = @_ ;
wxTheApp -> CallAfter ( sub {
2017-10-26 17:17:39 +02:00
$self -> _on_select_preset ( $group , $choice , 0 );
2015-06-02 11:19:11 +02:00
});
});
2014-12-25 19:42:24 +01:00
$presets -> Add ( $text , 0 , wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT , 4 );
2018-03-09 17:17:51 +01:00
$presets -> Add ( $choice , 1 , wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM , 1 );
2013-01-03 15:49:20 +01:00
}
2018-05-20 23:39:52 +02:00
$presets -> Layout ;
2013-01-03 15:49:20 +01:00
}
2018-03-09 17:17:51 +01:00
my $frequently_changed_parameters_sizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
2018-05-18 13:56:51 +02:00
Slic3r::GUI:: add_frequently_changed_parameters ( $self -> { right_panel }, $frequently_changed_parameters_sizer , $presets );
2018-05-13 21:00:03 +02:00
2013-08-25 12:22:05 +02:00
my $object_info_sizer ;
{
2018-05-13 21:00:03 +02:00
my $box = Wx::StaticBox -> new ( $scrolled_window_panel , - 1 , L ( "Info" ));
2013-08-25 12:22:05 +02:00
$object_info_sizer = Wx::StaticBoxSizer -> new ( $box , wxVERTICAL );
2018-05-13 21:00:03 +02:00
$object_info_sizer -> SetMinSize ([ 300 , - 1 ]);
2013-08-25 17:26:55 +02: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 12:22:05 +02:00
my @info = (
2018-02-22 15:13:07 +01:00
size => L ( "Size" ),
volume => L ( "Volume" ),
facets => L ( "Facets" ),
materials => L ( "Materials" ),
manifold => L ( "Manifold" ),
2013-08-25 12:22:05 +02:00
);
while ( my $field = shift @info ) {
my $label = shift @info ;
2018-05-13 21:00:03 +02:00
my $text = Wx::StaticText -> new ( $scrolled_window_panel , - 1 , "$label:" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
2013-08-25 12:22:05 +02:00
$text -> SetFont ( $ Slic3r::GUI:: small_font );
$grid_sizer -> Add ( $text , 0 );
2018-05-13 21:00:03 +02:00
$self -> { "object_info_$field" } = Wx::StaticText -> new ( $scrolled_window_panel , - 1 , "" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
2013-08-25 12:22:05 +02:00
$self -> { "object_info_$field" } -> SetFont ( $ Slic3r::GUI:: small_font );
2013-08-25 17:26:55 +02:00
if ( $field eq 'manifold' ) {
2018-05-13 21:00:03 +02:00
$self -> { object_info_manifold_warning_icon } = Wx::StaticBitmap -> new ( $scrolled_window_panel , - 1 , Wx::Bitmap -> new ( Slic3r:: var ( "error.png" ), wxBITMAP_TYPE_PNG ));
2013-08-25 17:26:55 +02: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-08-25 12:22:05 +02:00
}
}
2017-01-20 21:21:57 -06:00
my $print_info_sizer ;
{
2018-05-13 21:00:03 +02:00
my $box = Wx::StaticBox -> new ( $scrolled_window_panel , - 1 , L ( "Sliced Info" ));
2017-01-20 21:21:57 -06:00
$print_info_sizer = Wx::StaticBoxSizer -> new ( $box , wxVERTICAL );
2018-05-13 21:00:03 +02:00
$print_info_sizer -> SetMinSize ([ 300 , - 1 ]);
2017-01-20 21:21:57 -06:00
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 = (
2018-02-22 15:13:07 +01:00
fil_m => L ( "Used Filament (m)" ),
fil_mm3 => L ( "Used Filament (mm³)" ),
fil_g => L ( "Used Filament (g)" ),
cost => L ( "Cost" ),
time => L ( "Estimated printing time" ),
2017-01-20 21:21:57 -06:00
);
while ( my $field = shift @info ) {
my $label = shift @info ;
2018-05-13 21:00:03 +02:00
my $text = Wx::StaticText -> new ( $scrolled_window_panel , - 1 , "$label:" , wxDefaultPosition , wxDefaultSize , wxALIGN_RIGHT );
2017-01-20 21:21:57 -06:00
$text -> SetFont ( $ Slic3r::GUI:: small_font );
$grid_sizer -> Add ( $text , 0 );
2018-05-13 21:00:03 +02:00
$self -> { "print_info_$field" } = Wx::StaticText -> new ( $scrolled_window_panel , - 1 , "" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT );
2017-01-20 21:21:57 -06:00
$self -> { "print_info_$field" } -> SetFont ( $ Slic3r::GUI:: small_font );
$grid_sizer -> Add ( $self -> { "print_info_$field" }, 0 );
}
}
2013-08-25 12:22:05 +02:00
2014-12-25 18:50:02 +01:00
my $buttons_sizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
2016-10-25 13:24:42 +02:00
$self -> { buttons_sizer } = $buttons_sizer ;
2014-12-25 18:50:02 +01:00
$buttons_sizer -> AddStretchSpacer ( 1 );
$buttons_sizer -> Add ( $self -> { btn_export_stl }, 0 , wxALIGN_RIGHT , 0 );
2016-10-25 13:24:42 +02:00
$buttons_sizer -> Add ( $self -> { btn_reslice }, 0 , wxALIGN_RIGHT , 0 );
2015-01-03 23:25:55 +01:00
$buttons_sizer -> Add ( $self -> { btn_print }, 0 , wxALIGN_RIGHT , 0 );
2014-12-28 01:30:05 +01:00
$buttons_sizer -> Add ( $self -> { btn_send_gcode }, 0 , wxALIGN_RIGHT , 0 );
2014-12-25 18:50:02 +01:00
$buttons_sizer -> Add ( $self -> { btn_export_gcode }, 0 , wxALIGN_RIGHT , 0 );
2013-08-25 12:22:05 +02:00
2018-05-13 21:00:03 +02:00
$scrolled_window_sizer -> Add ( $self -> { list }, 1 , wxEXPAND , 5 );
$scrolled_window_sizer -> Add ( $object_info_sizer , 0 , wxEXPAND , 0 );
$scrolled_window_sizer -> Add ( $print_info_sizer , 0 , wxEXPAND , 0 );
2013-08-25 12:22:05 +02:00
my $right_sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2018-05-17 08:50:05 +02:00
$right_sizer -> SetMinSize ([ 320 , - 1 ]);
2014-12-25 18:50:02 +01:00
$right_sizer -> Add ( $presets , 0 , wxEXPAND | wxTOP , 10 ) if defined $presets ;
2018-04-25 11:10:34 +02:00
$right_sizer -> Add ( $frequently_changed_parameters_sizer , 0 , wxEXPAND | wxTOP , 0 ) if defined $frequently_changed_parameters_sizer ;
2014-12-25 18:50:02 +01:00
$right_sizer -> Add ( $buttons_sizer , 0 , wxEXPAND | wxBOTTOM , 5 );
2018-05-13 21:00:03 +02:00
$right_sizer -> Add ( $scrolled_window_panel , 1 , wxEXPAND | wxALL , 1 );
2017-02-15 16:02:54 +01:00
# Callback for showing / hiding the print info box.
$self -> { "print_info_box_show" } = sub {
2018-05-13 21:00:03 +02:00
# if ($right_sizer->IsShown(5) != $_[0]) {
# $right_sizer->Show(5, $_[0]);
# $self->Layout
# }
if ( $scrolled_window_sizer -> IsShown ( 2 ) != $_ [ 0 ]) {
$scrolled_window_sizer -> Show ( 2 , $_ [ 0 ]);
$scrolled_window_panel -> Layout
2017-02-15 16:02:54 +01:00
}
};
# Show the box initially, let it be shown after the slicing is finished.
$self -> { "print_info_box_show" } -> ( 0 );
2018-05-18 13:56:51 +02:00
$self -> { right_panel } -> SetSizer ( $right_sizer );
2013-08-25 12:22:05 +02:00
my $hsizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
2014-07-13 12:10:34 +02:00
$hsizer -> Add ( $self -> { preview_notebook }, 1 , wxEXPAND | wxTOP , 1 );
2018-05-18 13:56:51 +02:00
$hsizer -> Add ( $self -> { right_panel }, 0 , wxEXPAND | wxLEFT | wxRIGHT , 3 );
2013-08-25 12:22:05 +02: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 14:56:01 +02:00
$sizer -> SetSizeHints ( $self );
$self -> SetSizer ( $sizer );
}
2016-10-25 13:24:42 +02:00
2018-04-20 17:32:08 +02:00
# Last correct selected item for each preset
{
$self -> { selected_item_print } = 0 ;
$self -> { selected_item_filament } = 0 ;
$self -> { selected_item_printer } = 0 ;
}
2016-10-25 13:24:42 +02:00
$self -> update_ui_from_settings ();
2014-07-01 16:40:56 +02:00
2012-04-30 14:56:01 +02:00
return $self ;
}
2014-07-01 16:40:56 +02:00
# sets the callback
2012-10-24 20:24:11 +02:00
sub on_select_preset {
2014-07-01 16:40:56 +02:00
my ( $self , $cb ) = @_ ;
$self -> { on_select_preset } = $cb ;
}
2017-10-26 17:17:39 +02:00
# Called from the platter combo boxes selecting the active print, filament or printer.
2014-07-01 16:40:56 +02:00
sub _on_select_preset {
2017-10-26 17:17:39 +02:00
my ( $self , $group , $choice , $idx ) = @_ ;
2016-10-24 16:07:36 +02: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.
2017-10-26 17:17:39 +02:00
if ( $group eq 'filament' ) {
wxTheApp -> { preset_bundle } -> set_filament_preset ( $idx , $choice -> GetStringSelection );
}
2012-10-24 20:24:11 +02:00
if ( $group eq 'filament' && @ { $self -> { preset_choosers }{ filament }} > 1 ) {
2017-12-13 14:00:14 +01:00
# Only update the platter UI for the 2nd and other filaments.
2017-11-10 17:27:05 +01:00
wxTheApp -> { preset_bundle } -> update_platter_filament_ui ( $idx , $choice );
2017-05-24 15:20:20 +02:00
} else {
2018-04-20 17:32:08 +02:00
my $selected_item = $choice -> GetSelection ();
2018-05-22 14:00:42 +02:00
return if ( $selected_item == $self -> { "selected_item_$group" } &&
! Slic3r::GUI:: get_preset_tab ( $group ) -> current_preset_is_dirty );
2018-04-20 17:32:08 +02:00
my $selected_string = $choice -> GetString ( $selected_item );
2018-05-02 13:20:36 +02:00
if ( $selected_string eq ( "------- " . L ( "System presets" ) . " -------" ) ||
$selected_string eq ( "------- " . L ( "User presets" ) . " -------" ) ){
2018-04-20 17:32:08 +02:00
$choice -> SetSelection ( $self -> { "selected_item_$group" });
return ;
}
2017-05-24 15:20:20 +02:00
# call GetSelection() in scalar context as it's context-aware
2018-04-20 17:32:08 +02:00
# $self->{on_select_preset}->($group, $choice->GetStringSelection)
$self -> { on_select_preset } -> ( $group , $selected_string )
if $self -> { on_select_preset };
$self -> { "selected_item_$group" } = $selected_item ;
2017-05-24 15:20:20 +02:00
}
2017-11-02 16:21:34 +01:00
# Synchronize config.ini with the current selections.
wxTheApp -> { preset_bundle } -> export_selections ( wxTheApp -> { app_config });
2014-07-01 16:40:56 +02:00
# get new config and generate on_config_change() event for updating plater and other things
2017-10-25 12:53:31 +02:00
$self -> on_config_change ( wxTheApp -> { preset_bundle } -> full_config );
2012-10-24 20:24:11 +02:00
}
2016-12-12 18:02:24 +01:00
sub on_layer_editing_toggled {
my ( $self , $new_state ) = @_ ;
2018-05-25 14:05:08 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: enable_layers_editing ( $self -> { canvas3D }, $new_state );
if ( $new_state && ! Slic3r::GUI::_3DScene:: is_layers_editing_enabled ( $self -> { canvas3D })) {
# $self->{canvas3D}->layer_editing_enabled($new_state);
# if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) {
#==============================================================================================================================
2017-02-19 18:01:03 +01:00
# 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 16:53:40 +02:00
$self -> { canvas3D } -> Refresh ;
$self -> { canvas3D } -> Update ;
2016-12-12 18:02:24 +01:00
}
2014-06-14 18:58:56 +02:00
sub GetFrame {
my ( $self ) = @_ ;
2014-06-14 19:54:18 +02:00
return & Wx:: GetTopLevelParent ( $self );
2012-07-27 21:13:03 +02:00
}
2016-10-25 13:24:42 +02: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 ) = @_ ;
2017-11-02 16:21:34 +01:00
if ( defined ( $self -> { btn_reslice }) && $self -> { buttons_sizer } -> IsShown ( $self -> { btn_reslice }) != ( ! wxTheApp -> { app_config } -> get ( "background_processing" ))) {
$self -> { buttons_sizer } -> Show ( $self -> { btn_reslice }, ! wxTheApp -> { app_config } -> get ( "background_processing" ));
2016-10-25 13:24:42 +02:00
$self -> { buttons_sizer } -> Layout ;
}
}
2017-02-07 18:28:53 +01:00
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
2016-10-24 16:07:36 +02: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 18:44:47 +02:00
sub update_presets {
2017-10-26 17:17:39 +02:00
# $group: one of qw(print filament printer)
# $presets: PresetCollection
my ( $self , $group , $presets ) = @_ ;
my @choosers = @ { $self -> { preset_choosers }{ $group }};
if ( $group eq 'filament' ) {
my $choice_idx = 0 ;
if ( int ( @choosers ) == 1 ) {
# Single filament printer, synchronize the filament presets.
wxTheApp -> { preset_bundle } -> set_filament_preset ( 0 , wxTheApp -> { preset_bundle } -> filament -> get_selected_preset -> name );
}
foreach my $choice ( @choosers ) {
wxTheApp -> { preset_bundle } -> update_platter_filament_ui ( $choice_idx , $choice );
$choice_idx += 1 ;
}
} elsif ( $group eq 'print' ) {
2017-11-10 17:27:05 +01:00
wxTheApp -> { preset_bundle } -> print -> update_platter_ui ( $choosers [ 0 ]);
2017-10-26 17:17:39 +02:00
} elsif ( $group eq 'printer' ) {
2017-11-10 17:27:05 +01:00
# Update the print choosers to only contain the compatible presets, update the dirty flags.
wxTheApp -> { preset_bundle } -> print -> update_platter_ui ( $self -> { preset_choosers }{ print } -> [ 0 ]);
# Update the printer choosers, update the dirty flags.
wxTheApp -> { preset_bundle } -> printer -> update_platter_ui ( $choosers [ 0 ]);
# Update the filament choosers to only contain the compatible presets, update the color preview,
# update the dirty flags.
2017-10-26 17:17:39 +02:00
my $choice_idx = 0 ;
foreach my $choice ( @ { $self -> { preset_choosers }{ filament }}) {
2017-11-10 17:27:05 +01:00
wxTheApp -> { preset_bundle } -> update_platter_filament_ui ( $choice_idx , $choice );
2017-10-26 17:17:39 +02:00
$choice_idx += 1 ;
}
}
2017-11-02 16:21:34 +01:00
# Synchronize config.ini with the current selections.
wxTheApp -> { preset_bundle } -> export_selections ( wxTheApp -> { app_config });
2012-08-07 18:44:47 +02:00
}
2013-12-12 20:19:33 +01:00
sub add {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2014-06-14 19:59:59 +02:00
my @input_files = wxTheApp -> open_model ( $self );
2017-07-11 13:55:55 +02:00
$self -> load_files ( \ @input_files );
2012-04-30 14:56:01 +02:00
}
2017-07-11 13:55:55 +02:00
sub load_files {
my ( $self , $input_files ) = @_ ;
return if ! defined ( $input_files ) || ! scalar ( @$input_files );
my $nozzle_dmrs = $self -> { config } -> get ( 'nozzle_diameter' );
# One of the files is potentionally a bundle of files. Don't bundle them, but load them one by one.
# Only bundle .stls or .objs if the printer has multiple extruders.
my $one_by_one = ( @$nozzle_dmrs <= 1 ) || ( @$input_files == 1 ) ||
2018-02-14 14:30:03 +01:00
defined ( first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.[aA][mM][fF].[xX][mM][lL]$/ || $_ =~ /.[zZ][iI][pP].[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files );
2017-07-11 13:55:55 +02:00
2018-02-22 15:13:07 +01:00
my $process_dialog = Wx::ProgressDialog -> new ( L ( 'Loading…' ), L ( "Processing input file\n" ) . basename ( $input_files -> [ 0 ]), 100 , $self , 0 );
2012-04-30 21:49:44 +02:00
$process_dialog -> Pulse ;
2012-08-29 19:37:27 +02:00
local $SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $self );
2017-07-11 13:55:55 +02:00
# new_model to collect volumes, if all of them come from .stl or .obj and there is a chance, that they will be
# possibly merged into a single multi-part object.
my $new_model = $one_by_one ? undef : Slic3r::Model -> new ;
# Object indices for the UI.
2016-12-17 21:47:45 +01:00
my @obj_idx = ();
2017-07-11 13:55:55 +02:00
# Collected file names to display the final message on the status bar.
my @loaded_files = ();
# For all input files.
for ( my $i = 0 ; $i < @$input_files ; $i += 1 ) {
my $input_file = $input_files -> [ $i ];
2018-02-22 15:13:07 +01:00
$process_dialog -> Update ( 100 . * $i / @$input_files , L ( "Processing input file\n" ) . basename ( $input_file ));
2017-07-11 13:55:55 +02:00
2018-02-13 15:19:55 +01:00
my $model ;
2018-02-14 14:30:03 +01:00
if (( $input_file =~ /.3[mM][fF]$/ ) || ( $input_file =~ /.[zZ][iI][pP].[aA][mM][fF]$/ ))
2018-02-13 15:19:55 +01:00
{
$model = eval { Slic3r::Model -> read_from_archive ( $input_file , wxTheApp -> { preset_bundle }, 0 ) };
Slic3r::GUI:: show_error ( $self , $@ ) if $@ ;
$_ -> load_current_preset for ( values % { $self -> GetFrame -> { options_tabs }});
wxTheApp -> { app_config } -> update_config_dir ( dirname ( $input_file ));
2018-04-25 10:59:06 +02:00
# forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
# and if the config contains a "layer_height" different from the current defined one
$self -> async_apply_config ;
2018-02-13 15:19:55 +01:00
}
else
{
$model = eval { Slic3r::Model -> read_from_file ( $input_file , 0 ) };
Slic3r::GUI:: show_error ( $self , $@ ) if $@ ;
}
2017-07-11 13:55:55 +02:00
next if ! defined $model ;
2016-03-19 22:22:11 +01:00
if ( $model -> looks_like_multipart_object ) {
my $dialog = Wx::MessageDialog -> new ( $self ,
2018-02-22 15:13:07 +01:00
L ( "This file contains several objects positioned at multiple heights. "
2016-03-19 22:22:11 +01:00
. "Instead of considering them as multiple objects, should I consider\n"
2018-02-22 15:13:07 +01:00
. "this file as a single object having multiple parts?\n" ),
L ( 'Multi-part object detected' ), wxICON_WARNING | wxYES | wxNO );
2018-05-10 18:07:22 +02:00
$model -> convert_multipart_object ( scalar ( @$nozzle_dmrs )) if $dialog -> ShowModal () == wxID_YES ;
2017-07-11 13:55:55 +02:00
}
if ( $one_by_one ) {
push @obj_idx , $self -> load_model_objects ( @ { $model -> objects });
} else {
# This must be an .stl or .obj file, which may contain a maximum of one volume.
$new_model -> add_object ( $_ ) for ( @ { $model -> objects });
2016-03-19 22:22:11 +01:00
}
2014-03-25 19:06:51 +01:00
}
2017-07-11 13:55:55 +02:00
if ( $new_model ) {
my $dialog = Wx::MessageDialog -> new ( $self ,
2018-02-22 15:13:07 +01:00
L ( "Multiple objects were loaded for a multi-material printer.\n"
2017-07-11 13:55:55 +02:00
. "Instead of considering them as multiple objects, should I consider\n"
2018-02-22 15:13:07 +01:00
. "these files to represent a single object having multiple parts?\n" ),
L ( 'Multi-part object detected' ), wxICON_WARNING | wxYES | wxNO );
2018-05-10 18:07:22 +02:00
$new_model -> convert_multipart_object ( scalar ( @$nozzle_dmrs )) if $dialog -> ShowModal () == wxID_YES ;
2017-07-11 13:55:55 +02:00
push @obj_idx , $self -> load_model_objects ( @ { $new_model -> objects });
}
# Note the current directory for the file open dialog.
2017-11-02 16:21:34 +01:00
wxTheApp -> { app_config } -> update_skein_dir ( dirname ( $input_files -> [ - 1 ]));
2012-08-29 19:37:27 +02:00
$process_dialog -> Destroy ;
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "Loaded " ) . join ( ',' , @loaded_files ));
2016-12-17 21:47:45 +01:00
return @obj_idx ;
2013-12-12 20:19:33 +01:00
}
2014-01-05 14:04:32 +01:00
sub load_model_objects {
my ( $self , @model_objects ) = @_ ;
2013-12-12 20:19:33 +01:00
2014-06-16 23:58:45 +02:00
my $bed_centerf = $self -> bed_centerf ;
2015-05-25 19:51:47 +02:00
my $bed_shape = Slic3r::Polygon -> new_scale ( @ { $self -> { config } -> bed_shape });
my $bed_size = $bed_shape -> bounding_box -> size ;
2014-06-16 23:58:45 +02:00
2013-12-12 20:19:33 +01:00
my $need_arrange = 0 ;
2015-05-25 19:51:47 +02:00
my $scaled_down = 0 ;
2014-01-05 14:04:32 +01:00
my @obj_idx = ();
foreach my $model_object ( @model_objects ) {
my $o = $self -> { model } -> add_object ( $model_object );
2017-02-09 14:56:13 +01: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 14:04:32 +01:00
push @obj_idx , $# { $self -> { objects } };
2013-12-12 20:19:33 +01:00
2014-05-10 16:59:17 +02:00
if ( $model_object -> instances_count == 0 ) {
2014-01-05 14:04:32 +01:00
# if object has no defined position(s) we need to rearrange everything after loading
$need_arrange = 1 ;
# add a default instance and center object around origin
2014-12-12 22:43:04 +01:00
$o -> center_around_origin ; # also aligns object to Z = 0
2014-06-16 23:58:45 +02:00
$o -> add_instance ( offset => $bed_centerf );
2014-01-05 14:04:32 +01:00
}
2015-05-25 19:51:47 +02:00
{
# if the object is too large (more than 5 times the bed), scale it down
my $size = $o -> bounding_box -> size ;
2018-04-10 13:39:10 +02:00
my $ratio = max ( $size -> x / unscale($bed_size->x), $size->y / unscale ( $bed_size -> y ));
2018-04-13 13:59:36 +02:00
if ( $ratio > 10000 ) {
# the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
# so scale down the mesh
$o -> scale_xyz ( Slic3r::Pointf3 -> new ( 1 /$ratio, 1/ $ratio , 1 / $ratio ));
$scaled_down = 1 ;
}
elsif ( $ratio > 5 ) {
2015-05-25 19:51:47 +02:00
$_ -> set_scaling_factor ( 1 / $ratio ) for @ { $o -> instances };
$scaled_down = 1 ;
}
}
2014-01-05 14:04:32 +01:00
2014-03-22 20:12:54 +01:00
$self -> { print } -> auto_assign_extruders ( $o );
2014-05-07 01:11:49 +02:00
$self -> { print } -> add_model_object ( $o );
2014-01-05 14:04:32 +01:00
}
2013-12-13 14:02:01 +01:00
2013-12-18 19:11:20 +01:00
# if user turned autocentering off, automatic arranging would disappoint them
2017-11-02 16:21:34 +01:00
if ( ! wxTheApp -> { app_config } -> get ( "autocenter" )) {
2013-12-18 19:11:20 +01:00
$need_arrange = 0 ;
}
2015-05-25 19:51:47 +02:00
if ( $scaled_down ) {
Slic3r::GUI:: show_info (
$self ,
2018-02-22 15:13:07 +01:00
L ( 'Your object appears to be too large, so it was automatically scaled down to fit your print bed.' ),
L ( 'Object too large?' ),
2015-05-25 19:51:47 +02:00
);
}
2012-05-01 00:30:46 +02:00
2015-05-25 19:51:47 +02:00
foreach my $obj_idx ( @obj_idx ) {
2014-01-05 14:04:32 +01: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 12:22:05 +02:00
2014-01-05 14:04:32 +01: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 14:56:01 +02:00
2017-06-12 14:25:35 +02:00
$self -> reset_thumbnail ( $obj_idx );
2014-01-05 14:04:32 +01:00
}
2015-05-25 19:51:47 +02:00
$self -> arrange if $need_arrange ;
2013-12-18 18:54:11 +01:00
$self -> update ;
2014-12-16 01:12:37 +01:00
# zoom to objects
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: zoom_to_volumes ( $self -> { canvas3D }) if $self -> { canvas3D };
# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D};
#==============================================================================================================================
2014-12-16 01:12:37 +01:00
2012-04-30 14:56:01 +02:00
$self -> { list } -> Update ;
2015-05-25 19:51:47 +02:00
$self -> { list } -> Select ( $obj_idx [ - 1 ], 1 );
2012-04-30 14:56:01 +02:00
$self -> object_list_changed ;
2014-06-13 11:19:53 +02:00
2014-06-13 20:36:45 +02:00
$self -> schedule_background_process ;
2016-12-17 21:47:45 +01:00
return @obj_idx ;
2012-04-30 14:56:01 +02:00
}
2014-06-16 23:58:45 +02: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 14:56:01 +02:00
sub remove {
my $self = shift ;
2012-08-11 16:00:41 +02:00
my ( $obj_idx ) = @_ ;
2012-04-30 14:56:01 +02:00
2014-06-14 20:26:53 +02:00
$self -> stop_background_process ;
2014-12-07 20:23:00 +01:00
# Prevent toolpaths preview from rendering while we modify the Print object
$self -> { toolpaths2D } -> enabled ( 0 ) if $self -> { toolpaths2D };
2015-01-18 19:36:47 +01:00
$self -> { preview3D } -> enabled ( 0 ) if $self -> { preview3D };
2014-12-07 20:23:00 +01:00
2012-08-29 19:37:27 +02:00
# if no object index is supplied, remove the selected one
2017-07-21 16:29:40 +02:00
if ( ! defined $obj_idx ) {
2012-08-29 19:37:27 +02:00
( $obj_idx , undef ) = $self -> selected_object ;
2017-07-21 16:29:40 +02:00
return if ! defined $obj_idx ;
2012-04-30 14:56:01 +02:00
}
2012-08-29 19:37:27 +02:00
splice @ { $self -> { objects }}, $obj_idx , 1 ;
2013-12-12 20:19:33 +01:00
$self -> { model } -> delete_object ( $obj_idx );
2013-12-15 16:17:12 +01:00
$self -> { print } -> delete_object ( $obj_idx );
2012-08-29 19:37:27 +02:00
$self -> { list } -> DeleteItem ( $obj_idx );
2012-04-30 14:56:01 +02:00
$self -> object_list_changed ;
2013-12-12 20:19:33 +01:00
$self -> select_object ( undef );
2013-12-18 18:54:11 +01:00
$self -> update ;
2014-06-14 20:26:53 +02:00
$self -> schedule_background_process ;
2012-04-30 14:56:01 +02:00
}
sub reset {
my $self = shift ;
2014-06-14 20:26:53 +02:00
$self -> stop_background_process ;
2014-12-07 20:23:00 +01:00
# Prevent toolpaths preview from rendering while we modify the Print object
$self -> { toolpaths2D } -> enabled ( 0 ) if $self -> { toolpaths2D };
2015-01-18 19:36:47 +01:00
$self -> { preview3D } -> enabled ( 0 ) if $self -> { preview3D };
2014-12-07 20:23:00 +01:00
2012-08-29 19:37:27 +02:00
@ { $self -> { objects }} = ();
2014-05-07 00:58:29 +02:00
$self -> { model } -> clear_objects ;
$self -> { print } -> clear_objects ;
2012-04-30 14:56:01 +02:00
$self -> { list } -> DeleteAllItems ;
$self -> object_list_changed ;
2013-12-12 20:19:33 +01:00
$self -> select_object ( undef );
2014-07-14 11:58:00 +02:00
$self -> update ;
2012-04-30 14:56:01 +02:00
}
sub increase {
2015-01-14 23:19:13 +01:00
my ( $self , $copies ) = @_ ;
$copies // = 1 ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2017-08-14 12:50:35 +02:00
return if ! defined $obj_idx ;
2013-12-12 20:19:33 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
2015-01-14 23:19:13 +01: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 20:19:33 +01:00
$self -> { list } -> SetItem ( $obj_idx , 1 , $model_object -> instances_count );
2013-12-18 19:11:20 +01:00
# only autoarrange if user has autocentering enabled
2014-09-21 10:56:51 +02:00
$self -> stop_background_process ;
2017-11-02 16:21:34 +01:00
if ( wxTheApp -> { app_config } -> get ( "autocenter" )) {
2013-12-18 19:11:20 +01:00
$self -> arrange ;
} else {
2014-07-13 12:10:34 +02:00
$self -> update ;
2013-12-18 19:11:20 +01:00
}
2014-09-21 10:56:51 +02:00
$self -> schedule_background_process ;
2012-04-30 14:56:01 +02:00
}
sub decrease {
2017-06-06 15:38:27 +02:00
my ( $self , $copies_asked ) = @_ ;
my $copies = $copies_asked // 1 ;
2017-08-14 12:50:35 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
return if ! defined $obj_idx ;
2014-09-21 10:56:51 +02:00
$self -> stop_background_process ;
2013-12-12 20:19:33 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
2015-01-14 23:19:13 +01: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 20:19:33 +01:00
$self -> { list } -> SetItem ( $obj_idx , 1 , $model_object -> instances_count );
2017-06-06 15:38:27 +02:00
} elsif ( defined $copies_asked ) {
# The "decrease" came from the "set number of copies" dialog.
2012-09-12 16:30:44 +02:00
$self -> remove ;
2017-06-06 15:38:27 +02:00
} else {
# The "decrease" came from the "-" button. Don't allow the object to disappear.
$self -> resume_background_process ;
return ;
2012-09-12 16:30:44 +02:00
}
2012-04-30 14:56:01 +02:00
2012-08-29 19:37:27 +02:00
if ( $self -> { objects }[ $obj_idx ]) {
2012-04-30 14:56:01 +02:00
$self -> { list } -> Select ( $obj_idx , 0 );
$self -> { list } -> Select ( $obj_idx , 1 );
}
2013-12-18 18:54:11 +01:00
$self -> update ;
2014-09-21 10:56:51 +02:00
$self -> schedule_background_process ;
2012-04-30 14:56:01 +02:00
}
2015-01-14 23:19:13 +01: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
2018-02-22 15:13:07 +01:00
my $copies = Wx:: GetNumberFromUser ( "" , L ( "Enter the number of copies of the selected object:" ), L ( "Copies" ), $model_object -> instances_count , 0 , 1000 , $self );
2015-01-14 23:19:13 +01:00
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 14:32:19 +01: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 ) ?
2018-02-22 15:13:07 +01:00
": " . $value . L ( "\nNon-positive value." ) :
": " . $value . L ( "\nNot a numeric value." )),
L ( "Slic3r Error" ), wxOK | wxICON_EXCLAMATION , $self );
2017-03-23 14:32:19 +01:00
$default = $value ;
}
}
2012-04-30 14:56:01 +02:00
sub rotate {
2017-03-23 14:32:19 +01:00
my ( $self , $angle , $axis , $relative_key ) = @_ ;
2017-03-23 11:53:59 +01:00
$relative_key // = 'absolute' ; # relative or absolute coordinates
$axis // = Z ; # angle is in degrees
my $relative = $relative_key eq 'relative' ;
2012-04-30 14:56:01 +02:00
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2014-06-14 20:52:21 +02:00
return if ! defined $obj_idx ;
2013-12-12 20:19:33 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
my $model_instance = $model_object -> instances -> [ 0 ];
2017-06-12 14:25:35 +02:00
2012-04-30 22:14:27 +02:00
if ( ! defined $angle ) {
2014-06-14 20:52:21 +02:00
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z' ;
2016-12-17 00:26:57 +01:00
my $default = $axis == Z ? rad2deg ( $model_instance -> rotation ) : 0 ;
2018-02-23 09:16:35 +01:00
$angle = $self -> _get_number_from_user ( L ( "Enter the rotation angle:" ), L ( "Rotate around " ) . $axis_name . ( " axis" ), L ( "Invalid rotation angle entered" ), $default );
2017-03-23 14:32:19 +01:00
return if $angle eq '' ;
2012-04-30 22:14:27 +02:00
}
2014-07-01 16:40:56 +02:00
$self -> stop_background_process ;
if ( $axis == Z ) {
2016-12-17 00:26:57 +01:00
my $new_angle = deg2rad ( $angle );
2017-03-23 11:53:59 +01:00
$_ -> set_rotation (( $relative ? $_ -> rotation : 0 . ) + $new_angle ) for @ { $model_object -> instances };
2014-07-01 16:40:56 +02: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 18:40:00 +02:00
$model_object -> rotate ( $model_instance -> rotation , Z );
2014-07-01 16:40:56 +02:00
$_ -> set_rotation ( 0 ) for @ { $model_object -> instances };
2014-06-14 20:52:21 +02:00
}
2014-07-01 16:40:56 +02:00
$model_object -> rotate ( deg2rad ( $angle ), $axis );
2015-01-09 00:47:40 +01:00
# realign object to Z = 0
$model_object -> center_around_origin ;
2017-06-12 14:25:35 +02:00
$self -> reset_thumbnail ( $obj_idx );
2013-12-12 20:19:33 +01:00
}
2014-07-01 16:40:56 +02:00
# update print and start background processing
$self -> { print } -> add_model_object ( $model_object , $obj_idx );
2013-12-12 20:19:33 +01:00
$self -> selection_changed ; # refresh info (size etc.)
2013-12-18 18:54:11 +01:00
$self -> update ;
2014-12-29 22:29:24 +01:00
$self -> schedule_background_process ;
2012-04-30 14:56:01 +02:00
}
2015-11-04 23:11:30 +01:00
sub mirror {
2014-06-14 21:14:33 +02: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 23:11:30 +01:00
# apply Z rotation before mirroring
2014-06-14 21:14:33 +02:00
if ( $model_instance -> rotation != 0 ) {
2015-05-03 18:40:00 +02:00
$model_object -> rotate ( $model_instance -> rotation , Z );
2014-06-14 21:14:33 +02:00
$_ -> set_rotation ( 0 ) for @ { $model_object -> instances };
}
2015-11-04 23:11:30 +01:00
$model_object -> mirror ( $axis );
2015-01-09 00:47:40 +01:00
# realign object to Z = 0
$model_object -> center_around_origin ;
2017-06-12 14:25:35 +02:00
$self -> reset_thumbnail ( $obj_idx );
2014-06-14 21:14:33 +02:00
# 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 22:29:24 +01:00
$self -> schedule_background_process ;
2014-06-14 21:14:33 +02:00
}
2012-04-30 14:56:01 +02:00
sub changescale {
2015-12-05 19:37:57 +01:00
my ( $self , $axis , $tosize ) = @_ ;
2012-04-30 14:56:01 +02:00
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2014-06-17 00:50:44 +02:00
return if ! defined $obj_idx ;
2013-12-12 20:19:33 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
my $model_instance = $model_object -> instances -> [ 0 ];
2012-08-29 19:37:27 +02:00
2015-12-05 19:37:57 +01: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-17 00:50:44 +02:00
if ( defined $axis ) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z' ;
2015-12-05 19:37:57 +01:00
my $scale ;
if ( $tosize ) {
my $cursize = $object_size -> [ $axis ];
2017-03-23 14:32:19 +01:00
my $newsize = $self -> _get_number_from_user (
2018-02-22 15:13:07 +01:00
sprintf ( L ( 'Enter the new size for the selected object (print bed: %smm):' ), unscale ( $bed_size -> [ $axis ])),
L ( "Scale along " ) . $axis_name , L ( 'Invalid scaling value entered' ), $cursize , 1 );
2017-03-23 14:32:19 +01:00
return if $newsize eq '' ;
2015-12-05 19:37:57 +01:00
$scale = $newsize / $cursize * 100 ;
} else {
2018-02-23 09:16:35 +01:00
$scale = $self -> _get_number_from_user ( L ( 'Enter the scale % for the selected object:' ), L ( "Scale along " ) . $axis_name , L ( 'Invalid scaling value entered' ), 100 , 1 );
2017-03-23 14:32:19 +01:00
return if $scale eq '' ;
2015-12-05 19:37:57 +01:00
}
2014-06-17 00:50:44 +02:00
# apply Z rotation before scaling
if ( $model_instance -> rotation != 0 ) {
2015-05-03 18:40:00 +02:00
$model_object -> rotate ( $model_instance -> rotation , Z );
2014-06-17 00:50:44 +02:00
$_ -> set_rotation ( 0 ) for @ { $model_object -> instances };
}
my $versor = [ 1 , 1 , 1 ];
$versor -> [ $axis ] = $scale / 100 ;
2014-09-21 10:51:36 +02:00
$model_object -> scale_xyz ( Slic3r::Pointf3 -> new ( @$versor ));
2017-02-28 10:29:52 +01:00
#FIXME Scale the layer height profile when $axis == Z?
#FIXME Scale the layer height ranges $axis == Z?
2015-01-09 00:47:40 +01:00
# object was already aligned to Z = 0, so no need to realign it
2017-06-12 14:25:35 +02:00
$self -> reset_thumbnail ( $obj_idx );
2014-06-17 00:50:44 +02:00
} else {
2015-12-05 19:37:57 +01:00
my $scale ;
if ( $tosize ) {
my $cursize = max ( @$object_size );
2018-02-22 15:13:07 +01:00
my $newsize = $self -> _get_number_from_user ( L ( 'Enter the new max size for the selected object:' ), L ( 'Scale' ), L ( 'Invalid scaling value entered' ), $cursize , 1 );
2017-07-21 16:29:40 +02:00
return if ! defined ( $newsize ) || $newsize eq '' ;
2017-03-23 14:32:19 +01:00
$scale = $model_instance -> scaling_factor * $newsize / $cursize * 100 ;
2015-12-05 19:37:57 +01:00
} else {
# max scale factor should be above 2540 to allow importing files exported in inches
2018-02-22 15:13:07 +01:00
$scale = $self -> _get_number_from_user ( L ( 'Enter the scale % for the selected object:' ), L ( 'Scale' ), L ( 'Invalid scaling value entered' ), $model_instance -> scaling_factor * 100 , 1 );
2017-07-21 16:29:40 +02:00
return if ! defined ( $scale ) || $scale eq '' ;
2015-12-05 19:37:57 +01:00
}
2012-04-30 14:56:01 +02:00
2014-06-17 00:50:44 +02:00
$self -> { list } -> SetItem ( $obj_idx , 2 , "$scale%" );
$scale /= 100 ; # turn percent into factor
2013-12-12 20:19:33 +01:00
my $variation = $scale / $model_instance -> scaling_factor ;
2017-02-28 10:29:52 +01:00
#FIXME Scale the layer height profile?
2013-12-12 20:19:33 +01:00
foreach my $range ( @ { $model_object -> layer_height_ranges }) {
$range -> [ 0 ] *= $variation ;
$range -> [ 1 ] *= $variation ;
}
2014-04-30 02:04:49 +03:00
$_ -> set_scaling_factor ( $scale ) for @ { $model_object -> instances };
2013-12-12 20:19:33 +01:00
$object -> transform_thumbnail ( $self -> { model }, $obj_idx );
}
2014-06-17 00:50:44 +02:00
# update print and start background processing
$self -> stop_background_process ;
$self -> { print } -> add_model_object ( $model_object , $obj_idx );
2013-08-25 12:22:05 +02:00
$self -> selection_changed ( 1 ); # refresh info (size, volume etc.)
2013-12-18 19:11:20 +01:00
$self -> update ;
2014-12-29 22:29:24 +01:00
$self -> schedule_background_process ;
2012-08-29 19:37:27 +02:00
}
sub arrange {
my $self = shift ;
2014-11-13 00:23:31 +01:00
$self -> pause_background_process ;
2014-06-16 15:18:39 +02:00
my $bb = Slic3r::Geometry::BoundingBoxf -> new_from_points ( $self -> { config } -> bed_shape );
2017-10-25 12:53:31 +02:00
my $success = $self -> { model } -> arrange_objects ( wxTheApp -> { preset_bundle } -> full_config -> min_object_distance , $bb );
2013-12-24 00:30:51 +01:00
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed
2012-08-29 19:37:27 +02:00
2017-06-13 11:35:24 +02:00
# Force auto center of the aligned grid of of objects on the print bed.
2013-12-18 19:11:20 +01:00
$self -> update ( 1 );
2012-04-30 14:56:01 +02:00
}
2012-05-01 00:30:46 +02:00
sub split_object {
my $self = shift ;
2013-12-12 20:19:33 +01:00
my ( $obj_idx , $current_object ) = $self -> selected_object ;
2014-11-30 20:53:53 +01:00
# we clone model object because split_object() adds the split volumes
2014-12-30 13:16:28 +01: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 20:56:19 +02:00
2014-12-30 13:16:28 +01:00
if ( $current_model_object -> volumes_count > 1 ) {
2018-02-22 15:13:07 +01:00
Slic3r::GUI:: warning_catcher ( $self ) -> ( L ( "The selected object can't be split because it contains more than one volume/material." ));
2012-06-06 19:00:34 +02:00
return ;
}
2012-08-10 16:05:16 +02:00
2014-12-30 13:16:28 +01:00
$self -> pause_background_process ;
2014-06-13 23:27:52 +02:00
2014-11-12 22:36:03 +01:00
my @model_objects = @ { $current_model_object -> split_object };
if ( @model_objects == 1 ) {
2014-12-30 13:16:28 +01:00
$self -> resume_background_process ;
2018-02-22 15:13:07 +01:00
Slic3r::GUI:: warning_catcher ( $self ) -> ( L ( "The selected object couldn't be split because it contains only one part." ));
2014-12-30 22:07:21 +01:00
$self -> resume_background_process ;
2014-11-12 22:36:03 +01:00
return ;
}
2017-06-19 14:26:19 +02:00
$_ -> center_around_origin for ( @model_objects );
2014-05-26 13:10:58 +02:00
$self -> remove ( $obj_idx );
$current_object = $obj_idx = undef ;
2014-03-24 21:42:38 +01: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-05-01 00:30:46 +02:00
}
2014-06-13 20:36:45 +02:00
sub schedule_background_process {
my ( $self ) = @_ ;
2014-07-04 10:32:32 +02:00
if ( defined $self -> { apply_config_timer }) {
$self -> { apply_config_timer } -> Start ( PROCESS_DELAY , 1 ); # 1 = one shot
}
2014-06-13 20:36:45 +02:00
}
2016-09-13 11:24:55 +02:00
# Executed asynchronously by a timer every PROCESS_DELAY (0.5 second).
# The timer is started by schedule_background_process(),
2014-06-13 19:23:51 +02:00
sub async_apply_config {
2014-06-13 15:54:13 +02:00
my ( $self ) = @_ ;
2017-05-30 17:24:50 +02:00
2014-06-13 23:27:52 +02: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 11:52:19 +02:00
$self -> pause_background_process ;
2014-06-13 19:23:51 +02:00
# apply new config
2017-10-25 12:53:31 +02:00
my $invalidated = $self -> { print } -> apply_config ( wxTheApp -> { preset_bundle } -> full_config );
2017-02-09 14:56:13 +01:00
2017-05-31 17:05:11 +02:00
# Just redraw the 3D canvas without reloading the scene.
2018-05-25 14:05:08 +02:00
#==============================================================================================================================
# $self->{canvas3D}->Refresh if ($invalidated && Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D}));
$self -> { canvas3D } -> Refresh if Slic3r::GUI::_3DScene:: is_layers_editing_enabled ( $self -> { canvas3D });
## $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled);
# $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled);
#==============================================================================================================================
2017-02-15 16:02:54 +01:00
# Hide the slicing results if the current slicing status is no more valid.
$self -> { "print_info_box_show" } -> ( 0 ) if $invalidated ;
2017-11-02 16:21:34 +01:00
if ( wxTheApp -> { app_config } -> get ( "background_processing" )) {
2017-05-31 17:05:11 +02:00
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 19:23:51 +02:00
}
2017-05-30 17:24:50 +02: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 17:05:11 +02:00
if ( $invalidated ) {
2018-02-15 14:37:53 +01:00
$self -> { gcode_preview_data } -> reset ;
2017-05-31 17:05:11 +02:00
$self -> { toolpaths2D } -> reload_print if $self -> { toolpaths2D };
$self -> { preview3D } -> reload_print if $self -> { preview3D };
}
2014-06-13 15:54:13 +02:00
}
2014-06-13 11:19:53 +02:00
sub start_background_process {
my ( $self ) = @_ ;
return if ! @ { $self -> { objects }};
2014-06-13 20:36:45 +02:00
return if $self -> { process_thread };
2014-06-13 11:19:53 +02: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
2017-10-25 12:53:31 +02:00
wxTheApp -> { preset_bundle } -> full_config -> validate ;
2014-06-13 11:19:53 +02:00
$self -> { print } -> validate ;
};
2014-12-01 00:15:45 +01:00
if ( $@ ) {
$self -> statusbar -> SetStatusText ( $@ );
return ;
}
2014-06-13 11:19:53 +02:00
2017-11-02 16:21:34 +01:00
# Copy the names of active presets into the placeholder parser.
wxTheApp -> { preset_bundle } -> export_selections_pp ( $self -> { print } -> placeholder_parser );
2014-06-13 11:19:53 +02:00
# start thread
2014-06-13 14:27:55 +02:00
@_ = ();
2014-07-01 19:00:23 +02:00
$self -> { process_thread } = Slic3r:: spawn_thread ( sub {
2014-06-13 14:27:55 +02:00
eval {
$self -> { print } -> process ;
};
2018-02-13 18:31:34 +01:00
my $event = Wx::CommandEvent -> new ( $PROCESS_COMPLETED_EVENT );
2014-06-13 14:27:55 +02:00
if ( $@ ) {
2015-11-04 19:27:58 +01:00
Slic3r:: debugf "Background process error: $@\n" ;
2018-02-13 18:31:34 +01:00
$event -> SetInt ( 0 );
$event -> SetString ( $@ );
2014-06-13 14:27:55 +02:00
} else {
2018-02-13 18:31:34 +01:00
$event -> SetInt ( 1 );
2014-06-13 14:27:55 +02:00
}
2018-02-13 18:31:34 +01:00
Wx:: PostEvent ( $self , $event );
2014-06-13 11:19:53 +02:00
Slic3r:: thread_cleanup ();
});
Slic3r:: debugf "Background processing started.\n" ;
}
sub stop_background_process {
my ( $self ) = @_ ;
2014-06-16 15:18:39 +02:00
$self -> { apply_config_timer } -> Stop if defined $self -> { apply_config_timer };
2014-06-13 14:27:55 +02:00
$self -> statusbar -> SetCancelCallback ( undef );
$self -> statusbar -> StopBusy ;
$self -> statusbar -> SetStatusText ( "" );
2015-01-19 18:53:04 +01:00
$self -> { toolpaths2D } -> reload_print if $self -> { toolpaths2D };
$self -> { preview3D } -> reload_print if $self -> { preview3D };
2014-06-13 14:27:55 +02:00
2014-06-13 11:19:53 +02:00
if ( $self -> { process_thread }) {
Slic3r:: debugf "Killing background process.\n" ;
2014-07-01 19:00:23 +02:00
Slic3r:: kill_all_threads ();
2014-06-13 11:19:53 +02:00
$self -> { process_thread } = undef ;
} else {
Slic3r:: debugf "No background process running.\n" ;
}
2014-06-13 14:27:55 +02: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 19:00:23 +02:00
Slic3r:: kill_all_threads ();
2014-06-13 14:27:55 +02:00
$self -> { export_thread } = undef ;
}
2014-06-13 11:19:53 +02:00
}
2014-07-12 11:52:19 +02:00
sub pause_background_process {
my ( $self ) = @_ ;
if ( $self -> { process_thread } || $self -> { export_thread }) {
2014-12-22 19:47:39 +01:00
Slic3r:: pause_all_threads ();
2015-02-16 00:05:39 +01:00
return 1 ;
2014-07-12 11:52:19 +02:00
} elsif ( defined $self -> { apply_config_timer } && $self -> { apply_config_timer } -> IsRunning ) {
$self -> { apply_config_timer } -> Stop ;
2015-02-16 00:05:39 +01:00
return 1 ;
2014-07-12 11:52:19 +02:00
}
2015-02-16 00:05:39 +01:00
return 0 ;
2014-07-12 11:52:19 +02:00
}
sub resume_background_process {
my ( $self ) = @_ ;
if ( $self -> { process_thread } || $self -> { export_thread }) {
2014-12-22 19:47:39 +01:00
Slic3r:: resume_all_threads ();
2014-07-12 11:52:19 +02:00
}
}
2016-10-25 13:24:42 +02: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.
2017-11-02 16:21:34 +01:00
if ( ! defined ( $self -> { export_gcode_output_file }) && ! defined ( $self -> { send_gcode_file })) {
2017-02-09 14:56:13 +01:00
# Stop the background processing threads, stop the async update timer.
2016-10-25 13:24:42 +02:00
$self -> stop_background_process ;
2017-02-09 14:56:13 +01: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 13:24:42 +02:00
$self -> statusbar -> SetCancelCallback ( sub {
$self -> stop_background_process ;
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "Slicing cancelled" ));
2016-10-25 13:24:42 +02:00
# this updates buttons status
$self -> object_list_changed ;
});
$self -> start_background_process ;
}
}
2012-04-30 14:56:01 +02:00
sub export_gcode {
2014-12-28 01:30:05 +01:00
my ( $self , $output_file ) = @_ ;
2012-04-30 14:56:01 +02:00
2014-06-14 22:40:37 +02:00
return if ! @ { $self -> { objects }};
2014-06-13 11:19:53 +02:00
if ( $self -> { export_gcode_output_file }) {
2018-02-22 15:13:07 +01:00
Wx::MessageDialog -> new ( $self , L ( "Another export job is currently running." ), L ( 'Error' ), wxOK | wxICON_ERROR ) -> ShowModal ;
2012-05-05 21:08:15 +02:00
return ;
}
2014-06-13 14:27:55 +02: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
2017-10-25 12:53:31 +02:00
wxTheApp -> { preset_bundle } -> full_config -> validate ;
2014-06-13 14:27:55 +02:00
$self -> { print } -> validate ;
};
Slic3r::GUI:: catch_error ( $self ) and return ;
2014-03-17 00:39:07 +01:00
# apply config and validate print
2017-10-25 12:53:31 +02:00
my $config = wxTheApp -> { preset_bundle } -> full_config ;
2014-03-17 00:39:07 +01:00
eval {
# this will throw errors if config is not valid
$config -> validate ;
$self -> { print } -> apply_config ( $config );
$self -> { print } -> validate ;
};
2017-11-02 16:21:34 +01:00
Slic3r::GUI:: catch_error ( $self ) and return ;
2012-08-01 16:06:03 +02:00
2018-05-16 12:16:30 +02:00
# Copy the names of active presets into the placeholder parser.
wxTheApp -> { preset_bundle } -> export_selections_pp ( $self -> { print } -> placeholder_parser );
2012-05-05 21:08:15 +02:00
# select output file
2014-12-28 01:30:05 +01:00
if ( $output_file ) {
2017-12-05 15:54:24 +01:00
$self -> { export_gcode_output_file } = eval { $self -> { print } -> output_filepath ( $output_file ) };
Slic3r::GUI:: catch_error ( $self ) and return ;
2014-12-28 01:30:05 +01:00
} else {
2017-12-05 15:54:24 +01:00
my $default_output_file = eval { $self -> { print } -> output_filepath ( $ main:: opt { output } // '' ) };
Slic3r::GUI:: catch_error ( $self ) and return ;
2018-03-16 18:20:47 +01:00
# If possible, remove accents from accented latin characters.
# This function is useful for generating file names to be processed by legacy firmwares.
$default_output_file = Slic3r::GUI:: fold_utf8_to_ascii ( $default_output_file );
2018-02-22 15:13:07 +01:00
my $dlg = Wx::FileDialog -> new ( $self , L ( 'Save G-code file as:' ),
2017-11-02 16:21:34 +01:00
wxTheApp -> { app_config } -> get_last_output_dir ( dirname ( $default_output_file )),
2015-06-03 13:19:43 +02:00
basename ( $default_output_file ), & Slic3r::GUI::FILE_WILDCARDS -> { gcode }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2012-05-05 21:08:15 +02:00
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return ;
}
2017-08-03 17:31:31 +02:00
my $path = $dlg -> GetPath ;
2017-11-02 16:21:34 +01:00
wxTheApp -> { app_config } -> update_last_output_dir ( dirname ( $path ));
2015-06-02 22:27:11 +02:00
$self -> { export_gcode_output_file } = $path ;
2012-05-05 21:08:15 +02:00
$dlg -> Destroy ;
}
$self -> statusbar -> StartBusy ;
2013-05-20 10:57:27 +02:00
2017-11-02 16:21:34 +01:00
$self -> statusbar -> SetCancelCallback ( sub {
$self -> stop_background_process ;
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "Export cancelled" ));
2017-11-02 16:21:34 +01:00
$self -> { export_gcode_output_file } = undef ;
$self -> { send_gcode_file } = undef ;
2014-06-13 14:27:55 +02:00
2017-11-02 16:21:34 +01:00
# this updates buttons status
$self -> object_list_changed ;
});
# start background process, whose completion event handler
# will detect $self->{export_gcode_output_file} and proceed with export
$self -> start_background_process ;
2014-12-28 01:30:05 +01:00
2015-01-04 19:29:34 +01:00
# this updates buttons status
$self -> object_list_changed ;
2014-12-28 01:30:05 +01:00
return $self -> { export_gcode_output_file };
2012-05-05 21:08:15 +02:00
}
2014-06-13 14:27:55 +02:00
# This gets called only if we have threads.
sub on_process_completed {
2014-12-01 00:15:45 +01:00
my ( $self , $error ) = @_ ;
2012-05-05 21:08:15 +02:00
2012-05-05 21:26:19 +02:00
$self -> statusbar -> SetCancelCallback ( undef );
2012-05-05 21:08:15 +02:00
$self -> statusbar -> StopBusy ;
2014-12-01 00:15:45 +01:00
$self -> statusbar -> SetStatusText ( $error // "" );
2014-06-13 14:27:55 +02:00
Slic3r:: debugf "Background processing completed.\n" ;
$self -> { process_thread } -> detach if $self -> { process_thread };
$self -> { process_thread } = undef ;
2015-11-04 19:27:58 +01: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-12-01 00:15:45 +01:00
return if $error ;
2015-01-19 18:53:04 +01:00
$self -> { toolpaths2D } -> reload_print if $self -> { toolpaths2D };
$self -> { preview3D } -> reload_print if $self -> { preview3D };
2014-06-13 14:27:55 +02:00
# if we have an export filename, start a new thread for exporting G-code
if ( $self -> { export_gcode_output_file }) {
@_ = ();
2014-06-13 19:23:51 +02:00
# workaround for "Attempt to free un referenced scalar..."
our $_thread_self = $self ;
2014-07-01 19:00:23 +02:00
$self -> { export_thread } = Slic3r:: spawn_thread ( sub {
2014-06-13 14:27:55 +02:00
eval {
2018-02-14 20:35:59 +01:00
$_thread_self -> { print } -> export_gcode ( output_file => $_thread_self -> { export_gcode_output_file }, gcode_preview_data => $_thread_self -> { gcode_preview_data });
2014-06-13 14:27:55 +02:00
};
2018-02-13 18:31:34 +01:00
my $export_completed_event = Wx::CommandEvent -> new ( $EXPORT_COMPLETED_EVENT );
2014-06-13 14:27:55 +02:00
if ( $@ ) {
2018-02-13 18:31:34 +01:00
{
my $error_event = Wx::CommandEvent -> new ( $ERROR_EVENT );
$error_event -> SetString ( $@ );
Wx:: PostEvent ( $_thread_self , $error_event );
}
$export_completed_event -> SetInt ( 0 );
$export_completed_event -> SetString ( $@ );
2014-06-13 14:27:55 +02:00
} else {
2018-02-13 18:31:34 +01:00
$export_completed_event -> SetInt ( 1 );
2014-06-13 14:27:55 +02:00
}
2018-02-13 18:31:34 +01:00
Wx:: PostEvent ( $_thread_self , $export_completed_event );
2014-06-13 14:27:55 +02: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…" );
}
2016-10-25 13:24:42 +02:00
# Called when the G-code export finishes, either successfully or with an error.
2014-06-13 14:27:55 +02:00
# This gets called also if we don't have threads.
sub on_export_completed {
my ( $self , $result ) = @_ ;
$self -> statusbar -> SetCancelCallback ( undef );
$self -> statusbar -> StopBusy ;
$self -> statusbar -> SetStatusText ( "" );
Slic3r:: debugf "Background export process completed.\n" ;
$self -> { export_thread } -> detach if $self -> { export_thread };
$self -> { export_thread } = undef ;
my $message ;
2014-12-28 01:30:05 +01:00
my $send_gcode = 0 ;
2015-01-03 23:25:55 +01:00
my $do_print = 0 ;
2014-06-13 14:27:55 +02:00
if ( $result ) {
2016-10-25 13:24:42 +02:00
# G-code file exported successfully.
2015-01-03 23:25:55 +01:00
if ( $self -> { print_file }) {
2018-02-22 15:13:07 +01:00
$message = L ( "File added to print queue" );
2015-01-03 23:25:55 +01:00
$do_print = 1 ;
} elsif ( $self -> { send_gcode_file }) {
2018-02-22 15:13:07 +01:00
$message = L ( "Sending G-code file to the OctoPrint server..." );
2014-12-28 01:30:05 +01:00
$send_gcode = 1 ;
} else {
2018-02-22 15:13:07 +01:00
$message = L ( "G-code file exported to " ) . $self -> { export_gcode_output_file };
2014-12-28 01:30:05 +01:00
}
2014-06-13 14:27:55 +02:00
} else {
2018-02-22 15:13:07 +01:00
$message = L ( "Export failed" );
2014-06-13 14:27:55 +02:00
}
$self -> { export_gcode_output_file } = undef ;
2013-12-16 00:54:59 +01:00
$self -> statusbar -> SetStatusText ( $message );
2014-06-14 19:59:59 +02:00
wxTheApp -> notify ( $message );
2014-12-28 01:30:05 +01:00
2015-01-03 23:25:55 +01:00
$self -> do_print if $do_print ;
2018-02-07 11:37:15 +01:00
2016-10-25 13:24:42 +02:00
# Send $self->{send_gcode_file} to OctoPrint.
2018-02-07 11:37:15 +01:00
if ( $send_gcode ) {
my $op = Slic3r::OctoPrint -> new ( $self -> { config });
2018-04-04 11:18:22 +02:00
if ( $op -> send_gcode ( $self -> { send_gcode_file })) {
$self -> statusbar -> SetStatusText ( L ( "OctoPrint upload finished." ));
} else {
$self -> statusbar -> SetStatusText ( "" );
}
2018-02-07 11:37:15 +01:00
}
2015-01-03 23:25:55 +01:00
$self -> { print_file } = undef ;
2014-12-28 01:30:05 +01:00
$self -> { send_gcode_file } = undef ;
2017-01-20 21:21:57 -06: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-12-14 09:18:28 +01:00
$self -> { "print_info_time" } -> SetLabel ( $self -> { print } -> estimated_print_time );
2017-12-05 19:05:49 +01:00
$self -> { "print_info_fil_m" } -> SetLabel ( sprintf ( "%.2f" , $self -> { print } -> total_used_filament / 1000 ));
2017-02-15 16:02:54 +01:00
$self -> { "print_info_box_show" } -> ( 1 );
2015-01-04 19:29:34 +01:00
# this updates buttons status
$self -> object_list_changed ;
2018-01-08 13:44:10 +01:00
# refresh preview
$self -> { toolpaths2D } -> reload_print if $self -> { toolpaths2D };
$self -> { preview3D } -> reload_print if $self -> { preview3D };
2014-12-28 01:30:05 +01:00
}
2015-01-03 23:25:55 +01:00
sub do_print {
my ( $self ) = @_ ;
2015-01-04 23:18:23 +01:00
my $controller = $self -> GetFrame -> { controller };
2017-10-25 12:53:31 +02:00
my $printer_preset = wxTheApp -> { preset_bundle } -> printer -> get_edited_preset ;
my $printer_panel = $controller -> add_printer ( $printer_preset -> name , $printer_preset -> config );
2015-01-03 23:25:55 +01:00
my $filament_stats = $self -> { print } -> filament_stats ;
2017-12-19 21:12:24 +01:00
my $filament_names = wxTheApp -> { preset_bundle } -> filament_presets ;
$filament_stats = { map { $filament_names -> [ $_ ] => $filament_stats -> { $_ } } keys %$filament_stats };
2015-01-03 23:25:55 +01:00
$printer_panel -> load_print_job ( $self -> { print_file }, $filament_stats );
2012-05-05 21:08:15 +02:00
}
2012-04-30 17:10:54 +02:00
sub export_stl {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2014-06-14 22:40:37 +02:00
return if ! @ { $self -> { objects }};
2017-10-27 22:49:59 +02:00
# Ask user for a file name to write into.
2012-08-29 17:11:56 +02:00
my $output_file = $self -> _get_export_file ( 'STL' ) or return ;
2017-10-27 22:49:59 +02:00
# Store a binary STL.
2017-08-03 17:31:31 +02:00
$self -> { model } -> store_stl ( $output_file , 1 );
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "STL file exported to " ) . $output_file );
2012-08-29 17:11:56 +02:00
}
2016-12-22 12:13:28 +01: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 ];
2017-11-30 20:13:05 +01:00
#FIXME convert to local file encoding
2016-12-22 12:13:28 +01:00
return if ! $model_object -> input_file
|| !- e $model_object -> input_file ;
2017-07-11 13:55:55 +02:00
my @new_obj_idx = $self -> load_files ([ $model_object -> input_file ]);
2016-12-22 12:13:28 +01:00
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 };
2017-11-30 20:13:05 +01:00
#$o->invalidate_bounding_box;
2016-12-22 12:13:28 +01:00
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 );
}
}
2017-11-30 20:13:05 +01:00
#FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
2016-12-22 12:13:28 +01:00
}
$self -> remove ( $obj_idx );
}
2015-01-03 15:48:53 +01:00
sub export_object_stl {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2015-01-03 15:48:53 +01:00
my ( $obj_idx , $object ) = $self -> selected_object ;
return if ! defined $obj_idx ;
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
2017-10-27 22:49:59 +02:00
# Ask user for a file name to write into.
2015-01-03 15:48:53 +01:00
my $output_file = $self -> _get_export_file ( 'STL' ) or return ;
2017-08-03 17:31:31 +02:00
$model_object -> mesh -> write_binary ( $output_file );
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "STL file exported to " ) . $output_file );
2015-01-03 15:48:53 +01:00
}
2012-08-29 17:11:56 +02:00
sub export_amf {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2014-06-14 22:40:37 +02:00
return if ! @ { $self -> { objects }};
2017-10-27 22:49:59 +02:00
# Ask user for a file name to write into.
2012-08-29 17:11:56 +02:00
my $output_file = $self -> _get_export_file ( 'AMF' ) or return ;
2018-03-22 13:49:48 +01:00
my $res = $self -> { model } -> store_amf ( $output_file , $self -> { print }, $self -> { export_option });
2018-02-14 14:30:03 +01:00
if ( $res )
{
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "AMF file exported to " ) . $output_file );
2018-02-14 14:30:03 +01:00
}
else
{
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "Error exporting AMF file " ) . $output_file );
2018-02-14 14:30:03 +01:00
}
2012-08-29 17:11:56 +02:00
}
2018-02-08 13:26:50 +01:00
sub export_3mf {
my ( $self ) = @_ ;
return if ! @ { $self -> { objects }};
# Ask user for a file name to write into.
my $output_file = $self -> _get_export_file ( '3MF' ) or return ;
2018-03-22 13:49:48 +01:00
my $res = $self -> { model } -> store_3mf ( $output_file , $self -> { print }, $self -> { export_option });
2018-02-08 13:26:50 +01:00
if ( $res )
{
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "3MF file exported to " ) . $output_file );
2018-02-08 13:26:50 +01:00
}
else
{
2018-02-22 15:13:07 +01:00
$self -> statusbar -> SetStatusText ( L ( "Error exporting 3MF file " ) . $output_file );
2018-02-08 13:26:50 +01:00
}
2012-08-29 17:11:56 +02:00
}
2017-10-27 22:49:59 +02:00
# Ask user to select an output file for a given file format (STl, AMF, 3MF).
# Propose a default file name based on the 'output_filename_format' configuration value.
2012-08-29 17:11:56 +02:00
sub _get_export_file {
2017-10-27 22:49:59 +02:00
my ( $self , $format ) = @_ ;
2018-02-08 13:26:50 +01:00
my $suffix = '' ;
2018-02-19 15:33:01 +01:00
my $wildcard = 'known' ;
2018-02-08 13:26:50 +01:00
if ( $format eq 'STL' )
{
$suffix = '.stl' ;
2018-02-19 15:33:01 +01:00
$wildcard = 'stl' ;
2018-02-08 13:26:50 +01:00
}
elsif ( $format eq 'AMF' )
{
2018-02-20 11:33:38 +01:00
if ( & Wx:: wxMAC ) {
# It seems that MacOS does not like double extension
$suffix = '.amf' ;
} else {
$suffix = '.zip.amf' ;
}
2018-02-19 15:33:01 +01:00
$wildcard = 'amf' ;
2018-02-08 13:26:50 +01:00
}
elsif ( $format eq '3MF' )
{
$suffix = '.3mf' ;
2018-02-19 15:33:01 +01:00
$wildcard = 'threemf' ;
2018-02-08 13:26:50 +01:00
}
2018-05-16 12:16:30 +02:00
# Copy the names of active presets into the placeholder parser.
wxTheApp -> { preset_bundle } -> export_selections_pp ( $self -> { print } -> placeholder_parser );
2017-12-05 15:54:24 +01:00
my $output_file = eval { $self -> { print } -> output_filepath ( $ main:: opt { output } // '' ) };
Slic3r::GUI:: catch_error ( $self ) and return undef ;
2017-10-27 22:49:59 +02:00
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/ ;
2018-02-22 15:13:07 +01:00
my $dlg = Wx::FileDialog -> new ( $self , L ( "Save " ) . $format . L ( " file as:" ), dirname ( $output_file ),
2018-02-19 15:33:01 +01:00
basename ( $output_file ), & Slic3r::GUI::FILE_WILDCARDS -> { $wildcard }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2018-03-22 13:49:48 +01:00
Slic3r::GUI:: add_export_option ( $dlg , $format );
2017-10-27 22:49:59 +02:00
if ( $dlg -> ShowModal != wxID_OK ) {
2012-04-30 17:10:54 +02:00
$dlg -> Destroy ;
2017-10-27 22:49:59 +02:00
return undef ;
2012-04-30 17:10:54 +02:00
}
2017-10-27 22:49:59 +02:00
$output_file = $dlg -> GetPath ;
2018-03-22 13:49:48 +01:00
$self -> { export_option } = Slic3r::GUI:: get_export_option ( $dlg );
2017-10-27 22:49:59 +02:00
$dlg -> Destroy ;
2012-08-29 17:11:56 +02:00
return $output_file ;
2012-08-29 16:49:38 +02:00
}
2017-06-12 14:25:35 +02:00
sub reset_thumbnail {
my ( $self , $obj_idx ) = @_ ;
$self -> { objects }[ $obj_idx ] -> thumbnail ( undef );
2012-04-30 22:34:41 +02:00
}
2013-12-15 23:50:05 +01:00
# this method gets called whenever print center is changed or the objects' bounding box changes
2013-12-12 20:19:33 +01:00
# (i.e. when an object is added/removed/moved/rotated/scaled)
2013-12-18 18:54:11 +01:00
sub update {
2013-12-18 19:11:20 +01:00
my ( $self , $force_autocenter ) = @_ ;
2016-12-12 18:02:24 +01:00
2017-11-02 16:21:34 +01:00
if ( wxTheApp -> { app_config } -> get ( "autocenter" ) || $force_autocenter ) {
2014-06-16 23:58:45 +02:00
$self -> { model } -> center_instances_around_point ( $self -> bed_centerf );
2013-12-18 19:11:20 +01:00
}
2013-12-18 18:54:11 +01:00
2015-02-16 00:05:39 +01:00
my $running = $self -> pause_background_process ;
2014-12-29 22:29:24 +01:00
my $invalidated = $self -> { print } -> reload_model_instances ();
2015-02-16 00:05:39 +01: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 22:29:24 +01:00
$self -> schedule_background_process ;
} else {
$self -> resume_background_process ;
}
2017-06-12 14:25:35 +02:00
$self -> { canvas } -> reload_scene if $self -> { canvas };
2018-06-08 09:40:00 +02:00
#==============================================================================================================================
my $selections = $self -> collect_selections ;
Slic3r::GUI::_3DScene:: set_objects_selections ( $self -> { canvas3D }, \ @$selections );
Slic3r::GUI::_3DScene:: reload_scene ( $self -> { canvas3D }, 0 );
# $self->{canvas3D}->reload_scene if $self->{canvas3D};
#==============================================================================================================================
2018-04-23 15:30:41 +02:00
$self -> { preview3D } -> reset_gcode_preview_data if $self -> { preview3D };
2017-05-31 17:05:11 +02:00
$self -> { preview3D } -> reload_print if $self -> { preview3D };
2012-04-30 14:56:01 +02:00
}
2016-10-24 16:07:36 +02:00
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
2017-10-26 17:17:39 +02:00
# Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly
# and some reasonable default has to be selected for the additional extruders.
2014-07-01 16:40:56 +02:00
sub on_extruders_change {
my ( $self , $num_extruders ) = @_ ;
my $choices = $self -> { preset_choosers }{ filament };
2017-10-26 17:17:39 +02:00
while ( int ( @$choices ) < $num_extruders ) {
2015-06-01 23:58:34 +02:00
# copy strings from first choice
2014-07-01 16:40:56 +02:00
my @presets = $choices -> [ 0 ] -> GetStrings ;
2015-06-01 23:58:34 +02:00
# initialize new choice
2018-05-20 23:39:52 +02:00
my $choice = Wx::BitmapComboBox -> new ( $self -> { right_panel }, - 1 , "" , wxDefaultPosition , wxDefaultSize , [ @presets ], wxCB_READONLY );
2017-05-24 15:20:20 +02:00
my $extruder_idx = scalar @$choices ;
EVT_LEFT_DOWN ( $choice , sub { $self -> filament_color_box_lmouse_down ( $extruder_idx , @_ ); } );
2015-06-01 23:58:34 +02: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 19:14:18 +01:00
$self -> { presets_sizer } -> Insert ( 4 + ( $#$choices - 1 ) * 2 , 0 , 0 );
2015-06-01 23:58:34 +02:00
$self -> { presets_sizer } -> Insert ( 5 + ( $#$choices - 1 ) * 2 , $choice , 0 , wxEXPAND | wxBOTTOM , FILAMENT_CHOOSERS_SPACING );
# setup the listener
2015-06-02 11:19:11 +02:00
EVT_COMBOBOX ( $choice , $choice , sub {
my ( $choice ) = @_ ;
wxTheApp -> CallAfter ( sub {
2017-10-26 17:17:39 +02:00
$self -> _on_select_preset ( 'filament' , $choice , $extruder_idx );
2015-06-02 11:19:11 +02:00
});
});
2015-06-01 23:58:34 +02:00
# initialize selection
2017-10-26 17:17:39 +02:00
wxTheApp -> { preset_bundle } -> update_platter_filament_ui ( $extruder_idx , $choice );
2014-07-01 16:40:56 +02:00
}
2015-06-01 23:58:34 +02:00
# remove unused choices if any
2014-07-01 16:40:56 +02:00
while ( @$choices > $num_extruders ) {
2014-12-25 19:14:18 +01:00
$self -> { presets_sizer } -> Remove ( 4 + ( $#$choices - 1 ) * 2 ); # label
$self -> { presets_sizer } -> Remove ( 4 + ( $#$choices - 1 ) * 2 ); # wxChoice
2014-07-01 16:40:56 +02:00
$choices -> [ - 1 ] -> Destroy ;
pop @$choices ;
}
$self -> Layout ;
}
2012-07-27 21:13:03 +02:00
sub on_config_change {
2017-10-26 17:17:39 +02:00
my ( $self , $config ) = @_ ;
2014-06-13 11:19:53 +02:00
2017-05-17 16:53:40 +02:00
my $update_scheduled ;
2014-07-01 16:40:56 +02:00
foreach my $opt_key ( @ { $self -> { config } -> diff ( $config )}) {
$self -> { config } -> set ( $opt_key , $config -> get ( $opt_key ));
2014-06-16 15:18:39 +02:00
if ( $opt_key eq 'bed_shape' ) {
2014-05-28 12:29:43 +02:00
$self -> { canvas } -> update_bed_size ;
2018-06-06 15:17:10 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: set_bed_shape ( $self -> { canvas3D }, $self -> { config } -> bed_shape ) if $self -> { canvas3D };
Slic3r::GUI::_3DScene:: set_bed_shape ( $self -> { preview3D } -> canvas , $self -> { config } -> bed_shape ) if $self -> { preview3D };
# $self->{canvas3D}->update_bed_size if $self->{canvas3D};
# $self->{preview3D}->set_bed_shape($self->{config}->bed_shape)
# if $self->{preview3D};
#==============================================================================================================================
2017-05-17 16:53:40 +02:00
$update_scheduled = 1 ;
2017-05-19 21:48:32 +02:00
} elsif ( $opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material' ) {
2017-05-17 16:53:40 +02:00
$update_scheduled = 1 ;
2015-01-03 23:25:55 +01:00
} elsif ( $opt_key eq 'serial_port' ) {
2017-05-31 17:05:11 +02:00
$self -> { btn_print } -> Show ( $config -> get ( 'serial_port' ));
2015-01-03 23:25:55 +01:00
$self -> Layout ;
2014-12-28 01:30:05 +01:00
} elsif ( $opt_key eq 'octoprint_host' ) {
2017-05-31 17:05:11 +02:00
$self -> { btn_send_gcode } -> Show ( $config -> get ( 'octoprint_host' ));
2014-12-28 01:30:05 +01:00
$self -> Layout ;
2017-02-10 10:21:50 +01:00
} elsif ( $opt_key eq 'variable_layer_height' ) {
if ( $config -> get ( 'variable_layer_height' ) != 1 ) {
2017-02-13 12:40:12 +01: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 );
}
2018-05-25 14:05:08 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: enable_layers_editing ( $self -> { canvas3D }, 0 );
# $self->{canvas3D}->layer_editing_enabled(0);
#==============================================================================================================================
2017-05-17 16:53:40 +02:00
$self -> { canvas3D } -> Refresh ;
$self -> { canvas3D } -> Update ;
2018-05-25 16:28:24 +02:00
#==============================================================================================================================
} elsif ( Slic3r::GUI::_3DScene:: is_layers_editing_allowed ( $self -> { canvas3D })) {
# } elsif ($self->{canvas3D}->layer_editing_allowed) {
#==============================================================================================================================
2017-02-19 18:01:03 +01:00
# Want to allow the layer editing, but do it only if the OpenGL supports it.
2017-02-13 12:40:12 +01:00
if ( $self -> { htoolbar }) {
$self -> { htoolbar } -> EnableTool ( TB_LAYER_EDITING , 1 );
} else {
$self -> { "btn_layer_editing" } -> Enable ;
}
2017-02-10 10:21:50 +01:00
}
2017-05-30 17:24:50 +02: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 }));
2018-03-09 10:40:42 +01:00
} elsif ( $opt_key eq 'max_print_height' ) {
$update_scheduled = 1 ;
2014-05-28 12:29:43 +02:00
}
2012-08-06 19:18:31 +02:00
}
2017-05-17 16:53:40 +02:00
$self -> update if $update_scheduled ;
2014-06-13 11:19:53 +02:00
2014-06-14 19:54:18 +02:00
return if ! $self -> GetFrame -> is_loaded ;
2014-06-13 15:54:13 +02:00
2014-06-13 19:23:51 +02:00
# (re)start timer
2014-06-13 20:36:45 +02:00
$self -> schedule_background_process ;
2012-07-27 21:13:03 +02:00
}
2012-04-30 14:56:01 +02:00
sub list_item_deselected {
my ( $self , $event ) = @_ ;
2014-03-25 15:30:56 +01:00
return if $PreventListEvents ;
2018-04-27 15:39:00 +02:00
$self -> { _lecursor } = Wx::BusyCursor -> new ();
2012-04-30 14:56:01 +02:00
if ( $self -> { list } -> GetFirstSelected == - 1 ) {
2013-12-12 20:19:33 +01:00
$self -> select_object ( undef );
2017-05-31 17:05:11 +02:00
$self -> { canvas } -> Refresh ;
2018-05-25 09:03:55 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: deselect_volumes ( $self -> { canvas3D }) if $self -> { canvas3D };
2018-06-04 15:42:34 +02:00
Slic3r::GUI::_3DScene:: render ( $self -> { canvas3D }) if $self -> { canvas3D };
2018-05-25 09:03:55 +02:00
# $self->{canvas3D}->deselect_volumes if $self->{canvas3D};
2018-06-04 15:42:34 +02:00
# $self->{canvas3D}->Render if $self->{canvas3D};
2018-05-25 09:03:55 +02:00
#==============================================================================================================================
2012-04-30 14:56:01 +02:00
}
2018-04-27 15:39:00 +02:00
undef $self -> { _lecursor };
2012-04-30 14:56:01 +02:00
}
sub list_item_selected {
my ( $self , $event ) = @_ ;
2014-03-25 15:30:56 +01:00
return if $PreventListEvents ;
2018-04-27 15:39:00 +02:00
$self -> { _lecursor } = Wx::BusyCursor -> new ();
2012-04-30 14:56:01 +02:00
my $obj_idx = $event -> GetIndex ;
2013-12-12 20:19:33 +01:00
$self -> select_object ( $obj_idx );
2017-05-31 17:05:11 +02:00
$self -> { canvas } -> Refresh ;
2018-06-04 15:42:34 +02:00
#==============================================================================================================================
2018-06-08 09:40:00 +02:00
if ( $self -> { canvas3D }) {
my $selections = $self -> collect_selections ;
Slic3r::GUI::_3DScene:: update_volumes_selection ( $self -> { canvas3D }, \ @$selections );
Slic3r::GUI::_3DScene:: render ( $self -> { canvas3D });
}
# $self->{canvas3D}->update_volumes_selection if $self->{canvas3D};
2018-06-04 15:42:34 +02:00
# $self->{canvas3D}->Render if $self->{canvas3D};
#==============================================================================================================================
2018-04-27 15:39:00 +02:00
undef $self -> { _lecursor };
2012-04-30 14:56:01 +02:00
}
2018-06-08 09:40:00 +02:00
#==============================================================================================================================
sub collect_selections {
my ( $self ) = @_ ;
my $selections = [] ;
foreach my $o ( @ { $self -> { objects }}) {
push ( @$selections , $o -> selected );
}
return $selections ;
}
#==============================================================================================================================
2012-10-24 22:44:08 +02:00
sub list_item_activated {
my ( $self , $event , $obj_idx ) = @_ ;
$obj_idx // = $event -> GetIndex ;
2015-01-14 23:21:54 +01:00
$self -> object_settings_dialog ( $obj_idx );
2013-08-25 15:45:22 +02:00
}
2017-05-24 15:20:20 +02: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 ;
2017-10-25 12:53:31 +02:00
my $colors = wxTheApp -> { preset_bundle } -> full_config -> get ( 'extruder_colour' );
2017-05-24 15:20:20 +02:00
$colors -> [ $extruder_idx ] = $dialog -> GetColourData -> GetColour -> GetAsString ( wxC2S_HTML_SYNTAX );
$cfg -> set ( 'extruder_colour' , $colors );
$self -> GetFrame -> { options_tabs }{ printer } -> load_config ( $cfg );
2017-11-10 17:27:05 +01:00
wxTheApp -> { preset_bundle } -> update_platter_filament_ui ( $extruder_idx , $combobox );
2017-05-24 15:20:20 +02:00
}
$dialog -> Destroy ();
}
}
2014-04-25 14:54:08 +02:00
sub object_cut_dialog {
2017-10-27 22:49:59 +02:00
my ( $self , $obj_idx ) = @_ ;
2013-08-25 15:45:22 +02:00
if ( ! defined $obj_idx ) {
( $obj_idx , undef ) = $self -> selected_object ;
}
2013-09-16 10:18:42 +02:00
if ( ! $ Slic3r::GUI:: have_OpenGL ) {
2018-02-22 15:13:07 +01:00
Slic3r::GUI:: show_error ( $self , L ( "Please install the OpenGL modules to use this feature (see build instructions)." ));
2013-09-16 10:18:42 +02:00
return ;
}
2014-04-25 14:54:08 +02:00
my $dlg = Slic3r::GUI::Plater::ObjectCutDialog -> new ( $self ,
object => $self -> { objects }[ $obj_idx ],
model_object => $self -> { model } -> objects -> [ $obj_idx ],
2013-08-25 18:01:59 +02:00
);
2015-12-19 20:41:47 +01:00
return unless $dlg -> ShowModal == wxID_OK ;
2014-04-25 14:54:08 +02:00
if ( my @new_objects = $dlg -> NewModelObjects ) {
$self -> remove ( $obj_idx );
2014-06-14 22:36:49 +02:00
$self -> load_model_objects ( grep defined ( $_ ), @new_objects );
2014-04-25 17:14:39 +02:00
$self -> arrange ;
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: zoom_to_volumes ( $self -> { canvas3D }) if $self -> { canvas3D };
# $self->{canvas3D}->zoom_to_volumes if $self->{canvas3D};
#==============================================================================================================================
2014-04-25 14:54:08 +02:00
}
2013-08-25 18:01:59 +02:00
}
sub object_settings_dialog {
2017-10-27 22:49:59 +02:00
my ( $self , $obj_idx ) = @_ ;
( $obj_idx , undef ) = $self -> selected_object if ! defined $obj_idx ;
2014-03-23 16:45:55 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
2013-08-25 18:01:59 +02:00
2013-08-25 22:24:43 +02: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
2017-10-27 22:49:59 +02:00
eval { wxTheApp -> { preset_bundle } -> full_config -> validate ; };
return if Slic3r::GUI:: catch_error ( $_ [ 0 ]);
2013-08-25 22:24:43 +02:00
2013-08-25 18:01:59 +02:00
my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog -> new ( $self ,
2013-12-12 20:19:33 +01:00
object => $self -> { objects }[ $obj_idx ],
2014-03-23 16:45:55 +01:00
model_object => $model_object ,
2017-10-25 12:53:31 +02:00
config => wxTheApp -> { preset_bundle } -> full_config ,
2012-10-24 22:44:08 +02:00
);
2014-07-12 11:52:19 +02:00
$self -> pause_background_process ;
2012-10-24 22:44:08 +02:00
$dlg -> ShowModal ;
2014-03-23 16:45:55 +01:00
2014-12-12 22:43:04 +01:00
# update thumbnail since parts may have changed
if ( $dlg -> PartsChanged ) {
# recenter and re-align to Z = 0
$model_object -> center_around_origin ;
2017-06-12 14:25:35 +02:00
$self -> reset_thumbnail ( $obj_idx );
2014-12-12 22:43:04 +01:00
}
2014-03-23 16:45:55 +01:00
# update print
if ( $dlg -> PartsChanged || $dlg -> PartSettingsChanged ) {
2014-07-01 16:40:56 +02:00
$self -> stop_background_process ;
2014-03-26 19:42:01 +01:00
$self -> { print } -> reload_object ( $obj_idx );
2014-06-13 20:36:45 +02:00
$self -> schedule_background_process ;
2017-06-12 14:25:35 +02:00
$self -> { canvas } -> reload_scene if $self -> { canvas };
2018-06-08 09:40:00 +02:00
#==============================================================================================================================
my $selections = $self -> collect_selections ;
Slic3r::GUI::_3DScene:: set_objects_selections ( $self -> { canvas3D }, \ @$selections );
Slic3r::GUI::_3DScene:: reload_scene ( $self -> { canvas3D }, 0 );
# $self->{canvas3D}->reload_scene if $self->{canvas3D};
#==============================================================================================================================
2014-07-01 16:40:56 +02:00
} else {
2014-07-12 11:52:19 +02:00
$self -> resume_background_process ;
2014-03-23 16:45:55 +01:00
}
2014-07-03 09:24:19 +02:00
}
2016-10-25 13:24:42 +02: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 14:56:01 +02:00
sub object_list_changed {
my $self = shift ;
2017-02-13 12:40:12 +01:00
2016-10-25 13:24:42 +02:00
# Enable/disable buttons depending on whether there are any objects on the platter.
2013-08-25 18:01:59 +02:00
my $have_objects = @ { $self -> { objects }} ? 1 : 0 ;
2018-05-25 16:28:24 +02:00
#==============================================================================================================================
my $variable_layer_height_allowed = $self -> { config } -> variable_layer_height && Slic3r::GUI::_3DScene:: is_layers_editing_allowed ( $self -> { canvas3D });
# my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed;
#==============================================================================================================================
2017-02-13 12:40:12 +01:00
if ( $self -> { htoolbar }) {
# On OSX or Linux
$self -> { htoolbar } -> EnableTool ( $_ , $have_objects )
for ( TB_RESET , TB_ARRANGE , TB_LAYER_EDITING );
2017-02-19 18:01:03 +01:00
$self -> { htoolbar } -> EnableTool ( TB_LAYER_EDITING , 0 ) if ( ! $variable_layer_height_allowed );
2017-02-13 12:40:12 +01: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 18:01:03 +01:00
$self -> { "btn_layer_editing" } -> Disable if ( ! $variable_layer_height_allowed );
2017-02-13 12:40:12 +01:00
}
2017-02-17 15:00:01 +01:00
my $export_in_progress = $self -> { export_gcode_output_file } || $self -> { send_gcode_file };
2018-06-11 13:48:02 +02:00
#==============================================================================================================================
my $model_fits = $self -> { canvas3D } ? Slic3r::GUI::_3DScene:: check_volumes_outside_state ( $self -> { canvas3D }, $self -> { config }) : 1 ;
# my $model_fits = $self->{canvas3D} ? $self->{canvas3D}->volumes->check_outside_state($self->{config}) : 1;
#==============================================================================================================================
2018-04-05 09:02:03 +02:00
my $method = ( $have_objects && ! $export_in_progress && $model_fits ) ? 'Enable' : 'Disable' ;
2017-02-17 15:00:01 +01:00
$self -> { "btn_$_" } -> $method
for grep $self -> { "btn_$_" }, qw(reslice export_gcode print send_gcode) ;
2012-04-30 14:56:01 +02:00
}
2017-10-26 17:17:39 +02:00
# Selection of an active 3D object changed.
2012-04-30 14:56:01 +02:00
sub selection_changed {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2013-12-12 20:19:33 +01:00
my ( $obj_idx , $object ) = $self -> selected_object ;
my $have_sel = defined $obj_idx ;
2018-04-27 15:39:00 +02:00
2018-05-18 13:56:51 +02:00
$self -> { right_panel } -> Freeze ;
2012-05-04 11:22:56 +02:00
if ( $self -> { htoolbar }) {
2017-02-13 12:40:12 +01:00
# On OSX or Linux
2012-08-02 21:49:26 +02:00
$self -> { htoolbar } -> EnableTool ( $_ , $have_sel )
2014-12-10 17:34:59 +01:00
for ( TB_REMOVE , TB_MORE , TB_FEWER , TB_45CW , TB_45CCW , TB_SCALE , TB_SPLIT , TB_CUT , TB_SETTINGS );
2017-02-13 12:40:12 +01: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 12:22:05 +02:00
}
if ( $self -> { object_info_size }) { # have we already loaded the info pane?
if ( $have_sel ) {
2013-12-12 20:19:33 +01:00
my $model_object = $self -> { model } -> objects -> [ $obj_idx ];
2017-09-14 09:06:14 +02:00
#FIXME print_info runs model fixing in two rounds, it is very slow, it should not be performed here!
# $model_object->print_info;
2013-12-12 20:19:33 +01: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 12:22:05 +02:00
2013-12-12 20:19:33 +01:00
if ( my $stats = $model_object -> mesh_stats ) {
$self -> { object_info_volume } -> SetLabel ( sprintf ( '%.2f' , $stats -> { volume } * ( $model_instance -> scaling_factor ** 3 )));
2018-02-22 15:13:07 +01:00
$self -> { object_info_facets } -> SetLabel ( sprintf ( L ( '%d (%d shells)' ), $model_object -> facets_count , $stats -> { number_of_parts }));
2013-08-25 17:26:55 +02:00
if ( my $errors = sum ( @$stats { qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges) })) {
2018-02-22 15:13:07 +01:00
$self -> { object_info_manifold } -> SetLabel ( sprintf ( L ( "Auto-repaired (%d errors)" ), $errors ));
2013-08-25 17:26:55 +02:00
$self -> { object_info_manifold_warning_icon } -> Show ;
2013-08-25 18:01:59 +02:00
# we don't show normals_fixed because we never provide normals
# to admesh, so it generates normals for all facets
2018-02-22 15:13:07 +01:00
my $message = sprintf L ( '%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges' ),
2013-08-25 17:26:55 +02:00
@$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 {
2018-02-22 15:13:07 +01:00
$self -> { object_info_manifold } -> SetLabel ( L ( "Yes" ));
2018-03-15 11:59:12 +01:00
$self -> { object_info_manifold_warning_icon } -> Hide ;
2018-05-17 09:26:50 +02:00
$self -> { object_info_manifold } -> SetToolTipString ( "" );
$self -> { object_info_manifold_warning_icon } -> SetToolTipString ( "" );
2013-08-25 17:26:55 +02:00
}
} else {
$self -> { object_info_facets } -> SetLabel ( $object -> facets );
2013-08-25 12:22:05 +02:00
}
} else {
2013-08-25 17:26:55 +02:00
$self -> { "object_info_$_" } -> SetLabel ( "" ) for qw(size volume facets materials manifold) ;
$self -> { object_info_manifold_warning_icon } -> Hide ;
$self -> { object_info_manifold } -> SetToolTipString ( "" );
2018-05-17 09:26:50 +02:00
$self -> { object_info_manifold_warning_icon } -> SetToolTipString ( "" );
2013-08-25 12:22:05 +02:00
}
2013-08-25 17:26:55 +02:00
$self -> Layout ;
2012-05-04 11:22:56 +02:00
}
2014-06-14 18:58:56 +02:00
# prepagate the event to the frame (a custom Wx event would be cleaner)
2014-06-14 19:11:04 +02:00
$self -> GetFrame -> on_plater_selection_changed ( $have_sel );
2018-05-18 13:56:51 +02:00
$self -> { right_panel } -> Thaw ;
2012-04-30 14:56:01 +02:00
}
2013-12-12 20:19:33 +01:00
sub select_object {
my ( $self , $obj_idx ) = @_ ;
2018-05-02 13:55:04 +02:00
# remove current selection
foreach my $o ( 0 .. $# { $self -> { objects }}) {
$PreventListEvents = 1 ;
$self -> { objects } -> [ $o ] -> selected ( 0 );
$self -> { list } -> Select ( $o , 0 );
$PreventListEvents = 0 ;
}
2013-12-12 20:19:33 +01:00
if ( defined $obj_idx ) {
$self -> { objects } -> [ $obj_idx ] -> selected ( 1 );
2014-03-25 15:30:56 +01: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 20:19:33 +01:00
$self -> { list } -> Select ( $obj_idx , 1 );
2014-03-25 15:30:56 +01:00
$PreventListEvents = 0 ;
2013-12-12 20:19:33 +01:00
} else {
# TODO: deselect all in list
}
$self -> selection_changed ( 1 );
}
2012-08-29 19:37:27 +02:00
sub selected_object {
2017-10-27 22:49:59 +02:00
my ( $self ) = @_ ;
2013-12-12 20:19:33 +01:00
my $obj_idx = first { $self -> { objects }[ $_ ] -> selected } 0 .. $# { $self -> { objects } };
2017-10-27 22:49:59 +02:00
return defined $obj_idx ? ( $obj_idx , $self -> { objects }[ $obj_idx ]) : undef ;
2013-08-25 22:24:43 +02:00
}
2012-05-04 12:56:15 +02:00
sub statusbar {
2017-10-25 12:53:31 +02:00
return $_ [ 0 ] -> GetFrame -> { statusbar };
2012-05-04 12:56:15 +02:00
}
2014-06-14 21:36:28 +02:00
sub object_menu {
my ( $self ) = @_ ;
my $frame = $self -> GetFrame ;
my $menu = Wx::Menu -> new ;
2017-11-30 18:26:15 +01:00
my $accel = ( $^O eq 'MSWin32' ) ? sub { $_ [ 0 ] . "\t\xA0" . $_ [ 1 ] } : sub { $_ [ 0 ] };
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , $accel -> ( L ( 'Delete' ), 'Del' ), L ( 'Remove the selected object' ), sub {
2014-06-14 21:36:28 +02:00
$self -> remove ;
2015-05-25 22:37:04 +02:00
}, undef , 'brick_delete.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , $accel -> ( L ( 'Increase copies' ), '+' ), L ( 'Place one more copy of the selected object' ), sub {
2014-06-14 21:36:28 +02:00
$self -> increase ;
2015-05-25 22:37:04 +02:00
}, undef , 'add.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , $accel -> ( L ( 'Decrease copies' ), '-' ), L ( 'Remove one copy of the selected object' ), sub {
2014-06-14 21:36:28 +02:00
$self -> decrease ;
2015-05-25 22:37:04 +02:00
}, undef , 'delete.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Set number of copies…" ), L ( 'Change the number of copies of the selected object' ), sub {
2015-01-14 23:19:13 +01:00
$self -> set_number_of_copies ;
2015-05-25 22:37:04 +02:00
}, undef , 'textfield.png' );
2014-06-14 21:36:28 +02:00
$menu -> AppendSeparator ();
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , $accel -> ( L ( 'Rotate 45° clockwise' ), 'l' ), L ( 'Rotate the selected object by 45° clockwise' ), sub {
2017-03-23 11:53:59 +01:00
$self -> rotate ( - 45 , Z , 'relative' );
2015-05-25 22:37:04 +02:00
}, undef , 'arrow_rotate_clockwise.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , $accel -> ( L ( 'Rotate 45° counter-clockwise' ), 'r' ), L ( 'Rotate the selected object by 45° counter-clockwise' ), sub {
2017-03-23 11:53:59 +01:00
$self -> rotate ( + 45 , Z , 'relative' );
2015-05-25 22:37:04 +02:00
}, undef , 'arrow_rotate_anticlockwise.png' );
2014-06-14 21:36:28 +02:00
my $rotateMenu = Wx::Menu -> new ;
2018-02-22 15:13:07 +01:00
my $rotateMenuItem = $menu -> AppendSubMenu ( $rotateMenu , L ( "Rotate" ), L ( 'Rotate the selected object by an arbitrary angle' ));
2015-05-25 22:37:04 +02:00
$frame -> _set_menu_item_icon ( $rotateMenuItem , 'textfield.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $rotateMenu , L ( "Around X axis…" ), L ( 'Rotate the selected object by an arbitrary angle around X axis' ), sub {
2014-06-14 21:36:28 +02:00
$self -> rotate ( undef , X );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_red.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $rotateMenu , L ( "Around Y axis…" ), L ( 'Rotate the selected object by an arbitrary angle around Y axis' ), sub {
2014-06-14 21:36:28 +02:00
$self -> rotate ( undef , Y );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_green.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $rotateMenu , L ( "Around Z axis…" ), L ( 'Rotate the selected object by an arbitrary angle around Z axis' ), sub {
2014-06-14 21:36:28 +02:00
$self -> rotate ( undef , Z );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_blue.png' );
2014-06-14 21:36:28 +02:00
2015-11-04 23:11:30 +01:00
my $mirrorMenu = Wx::Menu -> new ;
2018-02-22 15:13:07 +01:00
my $mirrorMenuItem = $menu -> AppendSubMenu ( $mirrorMenu , L ( "Mirror" ), L ( 'Mirror the selected object' ));
2015-11-04 23:11:30 +01:00
$frame -> _set_menu_item_icon ( $mirrorMenuItem , 'shape_flip_horizontal.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $mirrorMenu , L ( "Along X axis…" ), L ( 'Mirror the selected object along the X axis' ), sub {
2015-11-04 23:11:30 +01:00
$self -> mirror ( X );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_red.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $mirrorMenu , L ( "Along Y axis…" ), L ( 'Mirror the selected object along the Y axis' ), sub {
2015-11-04 23:11:30 +01:00
$self -> mirror ( Y );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_green.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $mirrorMenu , L ( "Along Z axis…" ), L ( 'Mirror the selected object along the Z axis' ), sub {
2015-11-04 23:11:30 +01:00
$self -> mirror ( Z );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_blue.png' );
2014-06-14 21:36:28 +02:00
2014-06-17 00:50:44 +02:00
my $scaleMenu = Wx::Menu -> new ;
2018-02-22 15:13:07 +01:00
my $scaleMenuItem = $menu -> AppendSubMenu ( $scaleMenu , L ( "Scale" ), L ( 'Scale the selected object along a single axis' ));
2015-05-25 22:37:04 +02:00
$frame -> _set_menu_item_icon ( $scaleMenuItem , 'arrow_out.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleMenu , $accel -> ( L ( 'Uniformly…' ), 's' ), L ( 'Scale the selected object along the XYZ axes' ), sub {
2014-06-17 00:50:44 +02:00
$self -> changescale ( undef );
2014-06-14 21:36:28 +02:00
});
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleMenu , L ( "Along X axis…" ), L ( 'Scale the selected object along the X axis' ), sub {
2014-06-17 00:50:44 +02:00
$self -> changescale ( X );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_red.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleMenu , L ( "Along Y axis…" ), L ( 'Scale the selected object along the Y axis' ), sub {
2014-06-17 00:50:44 +02:00
$self -> changescale ( Y );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_green.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleMenu , L ( "Along Z axis…" ), L ( 'Scale the selected object along the Z axis' ), sub {
2014-06-17 00:50:44 +02:00
$self -> changescale ( Z );
2015-12-05 19:01:17 +01:00
}, undef , 'bullet_blue.png' );
2014-06-17 00:50:44 +02:00
2015-12-05 19:37:57 +01:00
my $scaleToSizeMenu = Wx::Menu -> new ;
2018-02-22 15:13:07 +01:00
my $scaleToSizeMenuItem = $menu -> AppendSubMenu ( $scaleToSizeMenu , L ( "Scale to size" ), L ( 'Scale the selected object along a single axis' ));
2015-12-05 19:37:57 +01:00
$frame -> _set_menu_item_icon ( $scaleToSizeMenuItem , 'arrow_out.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleToSizeMenu , L ( "Uniformly…" ), L ( 'Scale the selected object along the XYZ axes' ), sub {
2015-12-05 19:37:57 +01:00
$self -> changescale ( undef , 1 );
});
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleToSizeMenu , L ( "Along X axis…" ), L ( 'Scale the selected object along the X axis' ), sub {
2015-12-05 19:37:57 +01:00
$self -> changescale ( X , 1 );
}, undef , 'bullet_red.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleToSizeMenu , L ( "Along Y axis…" ), L ( 'Scale the selected object along the Y axis' ), sub {
2015-12-05 19:37:57 +01:00
$self -> changescale ( Y , 1 );
}, undef , 'bullet_green.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $scaleToSizeMenu , L ( "Along Z axis…" ), L ( 'Scale the selected object along the Z axis' ), sub {
2015-12-05 19:37:57 +01:00
$self -> changescale ( Z , 1 );
}, undef , 'bullet_blue.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Split" ), L ( 'Split the selected object into individual parts' ), sub {
2014-06-14 21:36:28 +02:00
$self -> split_object ;
2015-05-25 22:37:04 +02:00
}, undef , 'shape_ungroup.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Cut…" ), L ( 'Open the 3D cutting tool' ), sub {
2014-06-14 21:36:28 +02:00
$self -> object_cut_dialog ;
2015-05-25 22:37:04 +02:00
}, undef , 'package.png' );
2014-06-14 21:36:28 +02:00
$menu -> AppendSeparator ();
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Settings…" ), L ( 'Open the object editor dialog' ), sub {
2014-06-14 21:36:28 +02:00
$self -> object_settings_dialog ;
2015-05-25 22:37:04 +02:00
}, undef , 'cog.png' );
2015-01-03 15:48:53 +01:00
$menu -> AppendSeparator ();
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Reload from Disk" ), L ( 'Reload the selected file from Disk' ), sub {
2016-12-17 21:47:45 +01:00
$self -> reload_from_disk ;
}, undef , 'arrow_refresh.png' );
2018-02-22 15:13:07 +01:00
$frame -> _append_menu_item ( $menu , L ( "Export object as STL…" ), L ( 'Export this single object as STL file' ), sub {
2015-01-03 15:48:53 +01:00
$self -> export_object_stl ;
2015-05-25 22:37:04 +02:00
}, undef , 'brick_go.png' );
2014-06-14 21:36:28 +02:00
return $menu ;
}
2016-10-05 14:13:07 +02:00
# Set a camera direction, zoom to all objects.
sub select_view {
my ( $self , $direction ) = @_ ;
my $idx_page = $self -> { preview_notebook } -> GetSelection ;
2018-02-22 15:13:07 +01:00
my $page = ( $idx_page == & Wx:: wxNOT_FOUND ) ? L ( '3D' ) : $self -> { preview_notebook } -> GetPageText ( $idx_page );
if ( $page eq L ( 'Preview' )) {
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: select_view ( $self -> { preview3D } -> canvas , $direction );
2018-05-29 15:36:09 +02:00
Slic3r::GUI::_3DScene:: set_viewport_from_scene ( $self -> { canvas3D }, $self -> { preview3D } -> canvas );
2018-05-24 16:11:34 +02:00
# $self->{preview3D}->canvas->select_view($direction);
2018-05-29 15:36:09 +02:00
# $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
2016-10-05 14:13:07 +02:00
} else {
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
Slic3r::GUI::_3DScene:: select_view ( $self -> { canvas3D }, $direction );
2018-05-29 15:36:09 +02:00
Slic3r::GUI::_3DScene:: set_viewport_from_scene ( $self -> { preview3D } -> canvas , $self -> { canvas3D });
2018-05-24 16:11:34 +02:00
# $self->{canvas3D}->select_view($direction);
2018-05-29 15:36:09 +02:00
# $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
2018-05-24 16:11:34 +02:00
#==============================================================================================================================
2016-10-05 14:13:07 +02:00
}
}
2012-05-04 10:15:33 +02:00
package Slic3r::GUI::Plater::DropTarget ;
2012-04-30 14:56:01 +02:00
use Wx::DND ;
use base 'Wx::FileDropTarget' ;
sub new {
2017-10-27 22:49:59 +02:00
my ( $class , $window ) = @_ ;
2012-04-30 14:56:01 +02:00
my $self = $class -> SUPER:: new ;
$self -> { window } = $window ;
return $self ;
}
sub OnDropFiles {
2017-10-27 22:49:59 +02:00
my ( $self , $x , $y , $filenames ) = @_ ;
2012-07-21 14:45:45 +02:00
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@_ = ();
2018-02-14 14:30:03 +01:00
# only accept STL, OBJ, AMF, 3MF and PRUSA files
return 0 if grep ! /\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF]|[3][mM][fF]|[aA][mM][fF].[xX][mM][lL]|[zZ][iI][pP].[aA][mM][lL]|[pP][rR][uU][sS][aA])$/ , @$filenames ;
2017-07-11 13:55:55 +02:00
$self -> { window } -> load_files ( $filenames );
2012-04-30 14:56:01 +02:00
}
2016-09-13 11:24:55 +02:00
# 2D preview of an object. Each object is previewed by its convex hull.
2012-08-29 19:37:27 +02:00
package Slic3r::GUI::Plater::Object ;
use Moo ;
has 'name' => ( is => 'rw' , required => 1 );
2013-12-12 20:19:33 +01:00
has 'thumbnail' => ( is => 'rw' ); # ExPolygon::Collection in scaled model units with no transforms
2013-06-12 16:53:19 +02:00
has 'transformed_thumbnail' => ( is => 'rw' );
2013-12-12 20:19:33 +01: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 19:37:27 +02:00
sub make_thumbnail {
2013-12-12 20:19:33 +01:00
my ( $self , $model , $obj_idx ) = @_ ;
2014-06-14 20:52:21 +02:00
# make method idempotent
$self -> thumbnail -> clear ;
2017-06-13 11:35:24 +02:00
# raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
2014-06-14 20:52:21 +02:00
my $mesh = $model -> objects -> [ $obj_idx ] -> raw_mesh ;
2017-06-12 14:25:35 +02:00
#FIXME The "correct" variant could be extremely slow.
# if ($mesh->facets_count <= 5000) {
# # remove polygons with area <= 1mm
# my $area_threshold = Slic3r::Geometry::scale 1;
# $self->thumbnail->append(
# grep $_->area >= $area_threshold,
# @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons
# );
# $self->thumbnail->simplify(0.5);
# } else {
2013-12-12 20:19:33 +01:00
my $convex_hull = Slic3r::ExPolygon -> new ( $mesh -> convex_hull );
2013-11-12 14:30:13 +01:00
$self -> thumbnail -> append ( $convex_hull );
2017-06-12 14:25:35 +02:00
# }
2013-11-12 14:30:13 +01:00
return $self -> thumbnail ;
2012-08-29 19:37:27 +02:00
}
2013-12-12 20:19:33 +01:00
sub transform_thumbnail {
my ( $self , $model , $obj_idx ) = @_ ;
2012-08-29 19:37:27 +02:00
2013-07-14 00:38:01 +02:00
return unless defined $self -> thumbnail ;
2013-12-12 20:19:33 +01:00
my $model_object = $model -> objects -> [ $obj_idx ];
my $model_instance = $model_object -> instances -> [ 0 ];
# the order of these transformations MUST be the same everywhere, including
# in Slic3r::Print->add_model_object()
my $t = $self -> thumbnail -> clone ;
2015-05-03 18:40:00 +02:00
$t -> rotate ( $model_instance -> rotation , Slic3r::Point -> new ( 0 , 0 ));
2013-12-12 20:19:33 +01:00
$t -> scale ( $model_instance -> scaling_factor );
2012-08-29 19:37:27 +02:00
2013-06-12 16:53:19 +02:00
$self -> transformed_thumbnail ( $t );
2012-08-29 19:37:27 +02:00
}
2012-04-30 14:56:01 +02:00
1 ;