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) ;
2012-10-24 20:24:11 +02:00
use List::Util qw(max sum first) ;
2013-03-31 19:40:25 +02:00
use Math::Clipper qw(offset JT_ROUND) ;
2012-10-22 15:29:54 +02:00
use Math::ConvexHull::MonotoneChain qw(convex_hull) ;
2012-09-21 15:35:32 +02:00
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX) ;
2012-05-19 21:13:10 +02:00
use threads::shared qw(shared_clone) ;
2012-07-25 00:15:32 +02:00
use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window) ;
2012-10-24 22:44:08 +02:00
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE) ;
2012-04-30 14:56:01 +02:00
use base 'Wx::Panel' ;
2012-08-02 21:11:36 +02:00
use constant TB_MORE => & Wx:: NewId ;
use constant TB_LESS => & Wx:: NewId ;
use constant TB_45CW => & Wx:: NewId ;
use constant TB_45CCW => & Wx:: NewId ;
use constant TB_ROTATE => & Wx:: NewId ;
use constant TB_SCALE => & Wx:: NewId ;
use constant TB_SPLIT => & Wx:: NewId ;
2012-05-04 11:22:56 +02:00
2012-05-05 21:08:15 +02:00
my $THUMBNAIL_DONE_EVENT : shared = Wx:: NewEventType ;
my $PROGRESS_BAR_EVENT : shared = Wx:: NewEventType ;
my $MESSAGE_DIALOG_EVENT : shared = Wx:: NewEventType ;
my $EXPORT_COMPLETED_EVENT : shared = Wx:: NewEventType ;
2012-05-19 21:13:10 +02:00
my $EXPORT_FAILED_EVENT : shared = Wx:: NewEventType ;
2012-04-30 20:59:14 +02:00
2012-08-06 19:18:31 +02:00
use constant CANVAS_SIZE => [ 300 , 300 ];
2012-07-27 21:13:03 +02:00
use constant CANVAS_TEXT => join ( '-' , + ( localtime )[ 3 , 4 ]) eq '13-8'
? 'What do you want to print today? ™' # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
: 'Drag your objects here' ;
2012-08-07 18:44:47 +02:00
use constant FILAMENT_CHOOSERS_SPACING => 3 ;
2012-07-27 21:13:03 +02:00
2012-04-30 14:56:01 +02:00
sub new {
my $class = shift ;
my ( $parent ) = @_ ;
2012-07-24 14:28:21 +02:00
my $self = $class -> SUPER:: new ( $parent , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
2012-07-27 21:13:03 +02:00
$self -> { config } = Slic3r::Config -> new_from_defaults ( qw(
2012-08-07 20:14:28 +02:00
bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance
2012-07-27 21:13:03 +02:00
) );
2012-09-12 16:30:44 +02:00
$self -> { objects } = [] ;
$self -> { selected_objects } = [] ;
2012-04-30 14:56:01 +02:00
2012-08-06 19:18:31 +02:00
$self -> { canvas } = Wx::Panel -> new ( $self , - 1 , wxDefaultPosition , CANVAS_SIZE , wxTAB_TRAVERSAL );
2012-04-30 14:56:01 +02:00
$self -> { canvas } -> SetBackgroundColour ( Wx:: wxWHITE );
EVT_PAINT ( $self -> { canvas }, \& repaint );
EVT_MOUSE_EVENTS ( $self -> { canvas }, \& mouse_event );
2012-07-01 19:24:06 +02:00
$self -> { objects_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 210 , 210 , 210 ), wxSOLID );
$self -> { selected_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 255 , 128 , 128 ), wxSOLID );
2012-07-25 10:06:45 +02:00
$self -> { dragged_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 128 , 128 , 255 ), wxSOLID );
2012-07-01 19:24:06 +02:00
$self -> { transparent_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 0 , 0 , 0 ), wxTRANSPARENT );
$self -> { grid_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 230 , 230 , 230 ), 1 , wxSOLID );
$self -> { print_center_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 200 , 200 , 200 ), 1 , wxSOLID );
$self -> { clearance_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 0 , 0 , 200 ), 1 , wxSOLID );
$self -> { skirt_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 150 , 150 , 150 ), 1 , wxSOLID );
2012-04-30 14:56:01 +02: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 );
2012-07-01 19:24:06 +02:00
$self -> { htoolbar } -> AddTool ( TB_MORE , "More" , Wx::Bitmap -> new ( "$Slic3r::var/add.png" , wxBITMAP_TYPE_PNG ), '' );
2013-01-23 19:16:57 +01:00
$self -> { htoolbar } -> AddTool ( TB_LESS , "Fewer" , Wx::Bitmap -> new ( "$Slic3r::var/delete.png" , wxBITMAP_TYPE_PNG ), '' );
2012-05-04 11:22:56 +02:00
$self -> { htoolbar } -> AddSeparator ;
2012-07-01 19:24:06 +02:00
$self -> { htoolbar } -> AddTool ( TB_45CCW , "45° ccw" , Wx::Bitmap -> new ( "$Slic3r::var/arrow_rotate_anticlockwise.png" , wxBITMAP_TYPE_PNG ), '' );
$self -> { htoolbar } -> AddTool ( TB_45CW , "45° cw" , Wx::Bitmap -> new ( "$Slic3r::var/arrow_rotate_clockwise.png" , wxBITMAP_TYPE_PNG ), '' );
2012-07-01 23:29:56 +02:00
$self -> { htoolbar } -> AddTool ( TB_ROTATE , "Rotate…" , Wx::Bitmap -> new ( "$Slic3r::var/arrow_rotate_clockwise.png" , wxBITMAP_TYPE_PNG ), '' );
2012-05-04 11:22:56 +02:00
$self -> { htoolbar } -> AddSeparator ;
2012-07-01 23:29:56 +02:00
$self -> { htoolbar } -> AddTool ( TB_SCALE , "Scale…" , Wx::Bitmap -> new ( "$Slic3r::var/arrow_out.png" , wxBITMAP_TYPE_PNG ), '' );
2012-05-04 11:22:56 +02:00
$self -> { htoolbar } -> AddSeparator ;
2012-07-01 19:24:06 +02:00
$self -> { htoolbar } -> AddTool ( TB_SPLIT , "Split" , Wx::Bitmap -> new ( "$Slic3r::var/shape_ungroup.png" , wxBITMAP_TYPE_PNG ), '' );
2012-05-20 16:24:10 +02:00
} else {
my %tbar_buttons = ( increase => "More" , decrease => "Less" , rotate45ccw => "45°" , rotate45cw => "45°" ,
rotate => "Rotate…" , changescale => "Scale…" , split => "Split" );
$self -> { btoolbar } = Wx::BoxSizer -> new ( wxHORIZONTAL );
for ( qw(increase decrease rotate45ccw rotate45cw rotate changescale split) ) {
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_$_" });
}
2012-05-04 11:22:56 +02:00
}
2012-07-24 14:42:38 +02:00
$self -> { list } = Wx::ListView -> new ( $self , - 1 , wxDefaultPosition , [ - 1 , 180 ], wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
$self -> { list } -> InsertColumn ( 0 , "Name" , wxLIST_FORMAT_LEFT , 300 );
$self -> { list } -> InsertColumn ( 1 , "Copies" , wxLIST_FORMAT_CENTER , 50 );
$self -> { list } -> InsertColumn ( 2 , "Scale" , wxLIST_FORMAT_CENTER , wxLIST_AUTOSIZE_USEHEADER );
EVT_LIST_ITEM_SELECTED ( $self , $self -> { list }, \& list_item_selected );
EVT_LIST_ITEM_DESELECTED ( $self , $self -> { list }, \& list_item_deselected );
2012-10-24 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
# general buttons
2012-07-03 01:20:30 +02:00
$self -> { btn_load } = Wx::Button -> new ( $self , - 1 , "Add…" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$self -> { btn_remove } = Wx::Button -> new ( $self , - 1 , "Delete" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$self -> { btn_reset } = Wx::Button -> new ( $self , - 1 , "Delete All" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$self -> { btn_arrange } = Wx::Button -> new ( $self , - 1 , "Autoarrange" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$self -> { btn_export_gcode } = Wx::Button -> new ( $self , - 1 , "Export G-code…" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$self -> { btn_export_stl } = Wx::Button -> new ( $self , - 1 , "Export STL…" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
2012-05-04 11:22:56 +02:00
2012-05-01 16:49:34 +02:00
if ( & Wx:: wxVERSION_STRING =~ / 2\.9\.[1-9]/ ) {
2012-05-04 11:22:56 +02:00
my %icons = qw(
load brick_add.png
remove brick_delete.png
reset cross.png
arrange bricks.png
2012-05-19 15:02:23 +02:00
export_gcode cog_go.png
2012-05-04 11:22:56 +02:00
export_stl brick_go.png
increase add.png
decrease delete.png
rotate45cw arrow_rotate_clockwise.png
rotate45ccw arrow_rotate_anticlockwise.png
rotate arrow_rotate_clockwise.png
changescale arrow_out.png
split shape_ungroup.png
) ;
2012-05-20 16:24:10 +02:00
for ( grep $self -> { "btn_$_" }, keys %icons ) {
2012-07-01 19:24:06 +02:00
$self -> { "btn_$_" } -> SetBitmap ( Wx::Bitmap -> new ( "$Slic3r::var/$icons{$_}" , wxBITMAP_TYPE_PNG ));
2012-05-20 16:24:10 +02:00
}
2012-05-01 16:49:34 +02:00
}
2012-04-30 14:56:01 +02:00
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
EVT_BUTTON ( $self , $self -> { btn_load }, \& load );
2012-08-11 16:05:17 +02:00
EVT_BUTTON ( $self , $self -> { btn_remove }, sub { $self -> remove () }); # explicitly pass no argument to remove
2012-04-30 14:56:01 +02:00
EVT_BUTTON ( $self , $self -> { btn_reset }, \& reset );
EVT_BUTTON ( $self , $self -> { btn_arrange }, \& arrange );
EVT_BUTTON ( $self , $self -> { btn_export_gcode }, \& export_gcode );
2012-04-30 17:10:54 +02: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 }) {
EVT_TOOL ( $self , TB_MORE , \& increase );
EVT_TOOL ( $self , TB_LESS , \& decrease );
EVT_TOOL ( $self , TB_45CW , sub { $_ [ 0 ] -> rotate ( - 45 ) });
EVT_TOOL ( $self , TB_45CCW , sub { $_ [ 0 ] -> rotate ( 45 ) });
2012-05-04 12:56:15 +02:00
EVT_TOOL ( $self , TB_ROTATE , sub { $_ [ 0 ] -> rotate ( undef ) });
EVT_TOOL ( $self , TB_SCALE , \& changescale );
2012-05-04 11:22:56 +02:00
EVT_TOOL ( $self , TB_SPLIT , \& split_object );
} else {
EVT_BUTTON ( $self , $self -> { btn_increase }, \& increase );
EVT_BUTTON ( $self , $self -> { btn_decrease }, \& decrease );
EVT_BUTTON ( $self , $self -> { btn_rotate45cw }, sub { $_ [ 0 ] -> rotate ( - 45 ) });
EVT_BUTTON ( $self , $self -> { btn_rotate45ccw }, sub { $_ [ 0 ] -> rotate ( 45 ) });
EVT_BUTTON ( $self , $self -> { btn_changescale }, \& changescale );
EVT_BUTTON ( $self , $self -> { btn_rotate }, sub { $_ [ 0 ] -> rotate ( undef ) });
EVT_BUTTON ( $self , $self -> { btn_split }, \& split_object );
}
2012-05-04 10:15:33 +02:00
$_ -> SetDropTarget ( Slic3r::GUI::Plater::DropTarget -> new ( $self ))
2012-04-30 17:10:54 +02:00
for $self , $self -> { canvas }, $self -> { list };
2012-04-30 14:56:01 +02:00
2012-04-30 20:59:14 +02:00
EVT_COMMAND ( $self , - 1 , $THUMBNAIL_DONE_EVENT , sub {
my ( $self , $event ) = @_ ;
my ( $obj_idx , $thumbnail ) = @ { $event -> GetData };
2012-09-21 17:45:54 +02:00
$self -> { objects }[ $obj_idx ] -> thumbnail ( $thumbnail -> clone );
2012-10-01 18:12:14 +02:00
$self -> on_thumbnail_made ( $obj_idx );
2012-04-30 20:59:14 +02:00
});
2012-05-05 21:08:15 +02:00
EVT_COMMAND ( $self , - 1 , $PROGRESS_BAR_EVENT , sub {
my ( $self , $event ) = @_ ;
my ( $percent , $message ) = @ { $event -> GetData };
$self -> statusbar -> SetProgress ( $percent );
2012-07-01 23:29:56 +02:00
$self -> statusbar -> SetStatusText ( "$message…" );
2012-05-05 21:08:15 +02:00
});
EVT_COMMAND ( $self , - 1 , $MESSAGE_DIALOG_EVENT , sub {
my ( $self , $event ) = @_ ;
Wx::MessageDialog -> new ( $self , @ { $event -> GetData }) -> ShowModal ;
});
EVT_COMMAND ( $self , - 1 , $EXPORT_COMPLETED_EVENT , sub {
my ( $self , $event ) = @_ ;
$self -> on_export_completed ( @ { $event -> GetData });
});
2012-05-19 21:13:10 +02:00
EVT_COMMAND ( $self , - 1 , $EXPORT_FAILED_EVENT , sub {
my ( $self , $event ) = @_ ;
$self -> on_export_failed ;
});
2012-07-27 21:13:03 +02:00
$self -> _update_bed_size ;
2012-04-30 14:56:01 +02:00
$self -> recenter ;
{
2012-05-04 11:22:56 +02:00
my $buttons = Wx::GridSizer -> new ( 2 , 3 , 5 , 5 );
$buttons -> Add ( $self -> { "btn_load" }, 0 , wxEXPAND | wxALL );
$buttons -> Add ( $self -> { "btn_arrange" }, 0 , wxEXPAND | wxALL );
$buttons -> Add ( $self -> { "btn_export_gcode" }, 0 , wxEXPAND | wxALL );
$buttons -> Add ( $self -> { "btn_remove" }, 0 , wxEXPAND | wxALL );
$buttons -> Add ( $self -> { "btn_reset" }, 0 , wxEXPAND | wxALL );
$buttons -> Add ( $self -> { "btn_export_stl" }, 0 , wxEXPAND | wxALL );
2012-07-24 18:21:02 +02:00
# force sane tab order
my @taborder = qw/btn_load btn_arrange btn_export_gcode btn_remove btn_reset btn_export_stl/ ;
$self -> { $taborder [ $_ ]} -> MoveAfterInTabOrder ( $self -> { $taborder [ $_ - 1 ]}) for ( 1 .. $#taborder );
2012-04-30 14:56:01 +02:00
my $vertical_sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2012-05-04 11:22:56 +02:00
$vertical_sizer -> Add ( $self -> { htoolbar }, 0 , wxEXPAND , 0 ) if $self -> { htoolbar };
2012-05-20 16:24:10 +02:00
$vertical_sizer -> Add ( $self -> { btoolbar }, 0 , wxEXPAND , 0 ) if $self -> { btoolbar };
2012-07-26 16:41:22 +02:00
$vertical_sizer -> Add ( $self -> { list }, 1 , wxEXPAND | wxBOTTOM , 10 );
2012-05-04 11:22:56 +02:00
$vertical_sizer -> Add ( $buttons , 0 , wxEXPAND );
2012-04-30 14:56:01 +02:00
2012-06-19 17:23:10 +02:00
my $hsizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
$hsizer -> Add ( $self -> { canvas }, 0 , wxALL , 10 );
$hsizer -> Add ( $vertical_sizer , 1 , wxEXPAND | wxALL , 10 );
my $sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2012-07-26 16:50:57 +02:00
$sizer -> Add ( $hsizer , 1 , wxEXPAND | wxBOTTOM , 10 );
2013-01-03 15:49:20 +01:00
if ( $self -> skeinpanel -> { mode } eq 'expert' ) {
my $presets = Wx::BoxSizer -> new ( wxHORIZONTAL );
$presets -> AddStretchSpacer ( 1 );
my %group_labels = (
print => 'Print settings' ,
filament => 'Filament' ,
printer => 'Printer' ,
);
$self -> { preset_choosers } = {};
$self -> { preset_choosers_sizers } = {};
for my $group ( qw(print filament printer) ) {
my $text = Wx::StaticText -> new ( $self , - 1 , "$group_labels{$group}:" , wxDefaultPosition , wxDefaultSize , wxALIGN_RIGHT );
my $choice = Wx::Choice -> new ( $self , - 1 , wxDefaultPosition , [ 150 , - 1 ], [] );
$self -> { preset_choosers }{ $group } = [ $choice ];
EVT_CHOICE ( $choice , $choice , sub { $self -> on_select_preset ( $group , @_ ) });
$self -> { preset_choosers_sizers }{ $group } = Wx::BoxSizer -> new ( wxVERTICAL );
$self -> { preset_choosers_sizers }{ $group } -> Add ( $choice , 0 , wxEXPAND | wxBOTTOM , FILAMENT_CHOOSERS_SPACING );
$presets -> Add ( $text , 0 , wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT , 4 );
$presets -> Add ( $self -> { preset_choosers_sizers }{ $group }, 0 , wxALIGN_CENTER_VERTICAL | wxRIGHT , 15 );
}
$presets -> AddStretchSpacer ( 1 );
$sizer -> Add ( $presets , 0 , wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM , 10 );
}
2012-06-19 17:23:10 +02:00
2012-04-30 14:56:01 +02:00
$sizer -> SetSizeHints ( $self );
$self -> SetSizer ( $sizer );
}
return $self ;
}
2012-10-24 20:24:11 +02:00
sub on_select_preset {
my $self = shift ;
my ( $group , $choice ) = @_ ;
if ( $group eq 'filament' && @ { $self -> { preset_choosers }{ filament }} > 1 ) {
my @filament_presets = $self -> filament_presets ;
$ Slic3r::GUI:: Settings -> { presets }{ filament } = $choice -> GetString ( $filament_presets [ 0 ]) . ".ini" ;
$ Slic3r::GUI:: Settings -> { presets }{ "filament_${_}" } = $choice -> GetString ( $filament_presets [ $_ ])
for 1 .. $#filament_presets ;
Slic3r::GUI -> save_settings ;
return ;
}
$self -> skeinpanel -> { options_tabs }{ $group } -> select_preset ( $choice -> GetSelection );
}
2012-07-27 21:13:03 +02:00
sub skeinpanel {
my $self = shift ;
return $self -> GetParent -> GetParent ;
}
2012-08-07 18:44:47 +02:00
sub update_presets {
my $self = shift ;
my ( $group , $items , $selected ) = @_ ;
foreach my $choice ( @ { $self -> { preset_choosers }{ $group } }) {
my $sel = $choice -> GetSelection ;
$choice -> Clear ;
$choice -> Append ( $_ ) for @$items ;
$choice -> SetSelection ( $sel ) if $sel <= $#$items ;
}
$self -> { preset_choosers }{ $group }[ 0 ] -> SetSelection ( $selected );
}
sub filament_presets {
my $self = shift ;
return map $_ -> GetSelection , @ { $self -> { preset_choosers }{ filament } };
}
2012-04-30 14:56:01 +02:00
sub load {
my $self = shift ;
2012-07-30 12:08:28 +02:00
my $dir = $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || '' ;
2012-10-01 16:49:02 +02:00
my $dialog = Wx::FileDialog -> new ( $self , 'Choose one or more files (STL/OBJ/AMF):' , $dir , "" , & Slic3r::GUI::SkeinPanel:: MODEL_WILDCARD , wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST );
2012-04-30 14:56:01 +02:00
if ( $dialog -> ShowModal != wxID_OK ) {
$dialog -> Destroy ;
return ;
}
2012-05-31 09:26:30 +02:00
my @input_files = $dialog -> GetPaths ;
2012-04-30 14:56:01 +02:00
$dialog -> Destroy ;
2012-05-31 09:26:30 +02:00
$self -> load_file ( $_ ) for @input_files ;
2012-04-30 14:56:01 +02:00
}
sub load_file {
my $self = shift ;
my ( $input_file ) = @_ ;
2012-07-30 12:08:28 +02:00
$ Slic3r::GUI:: Settings -> { recent }{ skein_directory } = dirname ( $input_file );
Slic3r::GUI -> save_settings ;
2012-04-30 14:56:01 +02:00
2012-07-01 23:29:56 +02:00
my $process_dialog = Wx::ProgressDialog -> new ( 'Loading…' , "Processing input file…" , 100 , $self , 0 );
2012-04-30 21:49:44 +02:00
$process_dialog -> Pulse ;
2012-04-30 14:56:01 +02:00
2012-08-29 19:37:27 +02:00
local $SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $self );
my $model = Slic3r::Model -> read_from_file ( $input_file );
for my $i ( 0 .. $# { $model -> objects }) {
my $object = Slic3r::GUI::Plater::Object -> new (
2012-09-12 16:30:44 +02:00
name => basename ( $input_file ),
2012-08-29 19:37:27 +02:00
input_file => $input_file ,
input_file_object_id => $i ,
2012-10-22 14:02:58 +02:00
model_object => $model -> objects -> [ $i ],
2012-08-29 19:37:27 +02:00
instances => [
$model -> objects -> [ $i ] -> instances
? ( map $_ -> offset , @ { $model -> objects -> [ $i ] -> instances })
: [ 0 , 0 ],
],
);
2012-10-24 22:44:08 +02:00
$object -> check_manifoldness ;
2012-09-12 16:30:44 +02:00
# we only consider the rotation of the first instance for now
$object -> set_rotation ( $model -> objects -> [ $i ] -> instances -> [ 0 ] -> rotation )
if $model -> objects -> [ $i ] -> instances ;
2012-08-29 19:37:27 +02:00
push @ { $self -> { objects } }, $object ;
2012-09-12 16:30:44 +02:00
$self -> object_loaded ( $# { $self -> { objects } }, no_arrange => ( @ { $object -> instances } > 1 ));
2012-08-29 19:37:27 +02:00
}
$process_dialog -> Destroy ;
2012-05-04 12:56:15 +02:00
$self -> statusbar -> SetStatusText ( "Loaded $input_file" );
2012-05-01 00:30:46 +02:00
}
sub object_loaded {
my $self = shift ;
my ( $obj_idx , %params ) = @_ ;
2012-08-29 19:37:27 +02:00
my $object = $self -> { objects }[ $obj_idx ];
$self -> { list } -> InsertStringItem ( $obj_idx , $object -> name );
$self -> { list } -> SetItem ( $obj_idx , 1 , $object -> instances_count );
$self -> { list } -> SetItem ( $obj_idx , 2 , ( $object -> scale * 100 ) . "%" );
2012-04-30 14:56:01 +02:00
$self -> make_thumbnail ( $obj_idx );
2012-05-01 00:30:46 +02:00
$self -> arrange unless $params { no_arrange };
2012-04-30 14:56:01 +02:00
$self -> { list } -> Update ;
$self -> { list } -> Select ( $obj_idx , 1 );
$self -> object_list_changed ;
}
sub remove {
my $self = shift ;
2012-08-11 16:00:41 +02:00
my ( $obj_idx ) = @_ ;
2012-04-30 14:56:01 +02:00
2012-08-29 19:37:27 +02:00
# if no object index is supplied, remove the selected one
if ( ! defined $obj_idx ) {
( $obj_idx , undef ) = $self -> selected_object ;
2012-04-30 14:56:01 +02:00
}
2012-08-29 19:37:27 +02:00
splice @ { $self -> { objects }}, $obj_idx , 1 ;
$self -> { list } -> DeleteItem ( $obj_idx );
2012-04-30 14:56:01 +02:00
$self -> { selected_objects } = [] ;
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
sub reset {
my $self = shift ;
2012-08-29 19:37:27 +02:00
@ { $self -> { objects }} = ();
2012-04-30 14:56:01 +02:00
$self -> { list } -> DeleteAllItems ;
$self -> { selected_objects } = [] ;
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
$self -> { canvas } -> Refresh ;
}
sub increase {
my $self = shift ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
my $instances = $object -> instances ;
2012-09-11 18:11:46 +02:00
push @$instances , [ $instances -> [ - 1 ] -> [ X ] + 10 , $instances -> [ - 1 ] -> [ Y ] + 10 ];
2012-08-29 19:37:27 +02:00
$self -> { list } -> SetItem ( $obj_idx , 1 , $object -> instances_count );
2012-04-30 14:56:01 +02:00
$self -> arrange ;
}
sub decrease {
my $self = shift ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2012-09-12 16:30:44 +02:00
if ( $object -> instances_count >= 2 ) {
pop @ { $object -> instances };
2012-10-15 10:59:54 +02:00
$self -> { list } -> SetItem ( $obj_idx , 1 , $object -> instances_count );
2012-09-12 16:30:44 +02:00
} else {
$self -> remove ;
}
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 );
}
2012-09-12 16:30:44 +02:00
$self -> recenter ;
$self -> { canvas } -> Refresh ;
2012-04-30 14:56:01 +02:00
}
sub rotate {
my $self = shift ;
my ( $angle ) = @_ ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2012-04-30 14:56:01 +02:00
2012-11-19 17:39:16 +01:00
# we need thumbnail to be computed before allowing rotation
return if ! $object -> thumbnail ;
2012-04-30 22:14:27 +02:00
if ( ! defined $angle ) {
2012-08-29 19:37:27 +02:00
$angle = Wx:: GetNumberFromUser ( "" , "Enter the rotation angle:" , "Rotate" , $object -> rotate , - 364 , 364 , $self );
2012-04-30 22:14:27 +02:00
return if ! $angle || $angle == - 1 ;
}
2012-09-12 16:30:44 +02:00
$object -> set_rotation ( $object -> rotate + $angle );
2012-04-30 14:56:01 +02:00
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
sub changescale {
my $self = shift ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $object ) = $self -> selected_object ;
2012-06-29 20:46:51 +02:00
# max scale factor should be above 2540 to allow importing files exported in inches
2013-03-30 00:47:13 +01:00
my $scale = Wx:: GetNumberFromUser ( "" , "Enter the scale % for the selected object:" , "Scale" , $object -> scale * 100 , 0 , 100000 , $self );
2012-04-30 14:56:01 +02:00
return if ! $scale || $scale == - 1 ;
2012-09-12 16:30:44 +02:00
$self -> { list } -> SetItem ( $obj_idx , 2 , "$scale%" );
$object -> set_scale ( $scale / 100 );
2012-04-30 17:25:08 +02:00
$self -> arrange ;
2012-08-29 19:37:27 +02:00
}
sub arrange {
my $self = shift ;
my $total_parts = sum ( map $_ -> instances_count , @ { $self -> { objects }}) or return ;
my @size = ();
for my $a ( X , Y ) {
2012-11-19 17:39:16 +01:00
$size [ $a ] = max ( map $_ -> size -> [ $a ], @ { $self -> { objects }});
2012-08-29 19:37:27 +02:00
}
eval {
my $config = $self -> skeinpanel -> config ;
my @positions = Slic3r::Geometry:: arrange
2012-09-12 16:30:44 +02:00
( $total_parts , @size , @ { $config -> bed_size }, $config -> min_object_distance , $self -> skeinpanel -> config );
@$_ = @ { shift @positions }
for map @ { $_ -> instances }, @ { $self -> { objects }};
2012-08-29 19:37:27 +02:00
};
# ignore arrange warnings on purpose
$self -> recenter ;
$self -> { canvas } -> Refresh ;
2012-04-30 14:56:01 +02:00
}
2012-05-01 00:30:46 +02:00
sub split_object {
my $self = shift ;
2012-08-29 19:37:27 +02:00
my ( $obj_idx , $current_object ) = $self -> selected_object ;
my $current_copies_num = $current_object -> instances_count ;
2012-10-21 20:56:19 +02:00
my $model_object = $current_object -> get_model_object ;
if ( @ { $model_object -> volumes } > 1 ) {
Slic3r::GUI:: warning_catcher ( $self ) -> ( "The selected object couldn't be splitted because it contains more than one volume/material." );
return ;
}
my $mesh = $model_object -> mesh ;
2012-09-21 15:35:32 +02:00
$mesh -> align_to_origin ;
2012-05-01 00:30:46 +02:00
2012-05-04 10:17:36 +02:00
my @new_meshes = $mesh -> split_mesh ;
2012-06-06 19:00:34 +02:00
if ( @new_meshes == 1 ) {
2012-08-10 16:05:16 +02:00
Slic3r::GUI:: warning_catcher ( $self ) -> ( "The selected object couldn't be splitted because it already contains a single part." );
2012-06-06 19:00:34 +02:00
return ;
}
2012-08-10 16:05:16 +02:00
# remove the original object before spawning the object_loaded event, otherwise
# we'll pass the wrong $obj_idx to it (which won't be recognized after the
# thumbnail thread returns)
$self -> remove ( $obj_idx );
2012-10-27 21:39:57 +02:00
# create a bogus Model object, we only need to instantiate the new Model::Object objects
my $new_model = Slic3r::Model -> new ;
2012-05-04 10:17:36 +02:00
foreach my $mesh ( @new_meshes ) {
2012-09-21 15:35:32 +02:00
my @extents = $mesh -> extents ;
2012-10-27 21:39:57 +02:00
my $model_object = $new_model -> add_object ( vertices => $mesh -> vertices );
$model_object -> add_volume ( facets => $mesh -> facets );
2012-09-12 16:30:44 +02:00
my $object = Slic3r::GUI::Plater::Object -> new (
name => basename ( $current_object -> input_file ),
input_file => $current_object -> input_file ,
input_file_object_id => undef ,
2012-10-27 21:39:57 +02:00
model_object => $model_object ,
2012-09-21 15:35:32 +02:00
instances => [ map [ $extents [ X ][ MIN ], $extents [ Y ][ MIN ]], 1 .. $current_copies_num ],
2012-09-12 16:30:44 +02:00
);
push @ { $self -> { objects } }, $object ;
$self -> object_loaded ( $# { $self -> { objects } }, no_arrange => 1 );
2012-05-01 00:30:46 +02:00
}
2012-09-21 15:35:32 +02:00
$self -> recenter ;
$self -> { canvas } -> Refresh ;
2012-05-01 00:30:46 +02:00
}
2012-04-30 14:56:01 +02:00
sub export_gcode {
my $self = shift ;
2012-05-05 21:08:15 +02:00
if ( $self -> { export_thread }) {
2012-07-01 19:24:06 +02:00
Wx::MessageDialog -> new ( $self , "Another slicing job is currently running." , 'Error' , wxOK | wxICON_ERROR ) -> ShowModal ;
2012-05-05 21:08:15 +02:00
return ;
}
2012-09-12 16:30:44 +02:00
# get config before spawning the thread because ->config needs GetParent and it's not available there
my $print = $self -> _init_print ;
2012-08-01 16:06:03 +02:00
2012-05-05 21:08:15 +02:00
# select output file
$self -> { output_file } = $ main:: opt { output };
{
2012-09-12 16:30:44 +02:00
$self -> { output_file } = $print -> expanded_output_filepath ( $self -> { output_file }, $self -> { objects }[ 0 ] -> input_file );
2012-05-05 21:08:15 +02:00
my $dlg = Wx::FileDialog -> new ( $self , 'Save G-code file as:' , dirname ( $self -> { output_file }),
2012-09-23 14:48:58 +02:00
basename ( $self -> { output_file }), & Slic3r::GUI::SkeinPanel::FILE_WILDCARDS -> { gcode }, wxFD_SAVE );
2012-05-05 21:08:15 +02:00
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return ;
}
$self -> { output_file } = $ Slic3r::GUI::SkeinPanel:: last_output_file = $dlg -> GetPath ;
$dlg -> Destroy ;
}
$self -> statusbar -> StartBusy ;
2012-05-19 21:13:10 +02:00
if ( $ Slic3r:: have_threads ) {
2012-05-05 21:08:15 +02:00
$self -> { export_thread } = threads -> create ( sub {
$self -> export_gcode2 (
2012-09-12 16:30:44 +02:00
$print ,
2012-05-05 21:08:15 +02:00
$self -> { output_file },
progressbar => sub { Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $PROGRESS_BAR_EVENT , shared_clone ([ @_ ]))) },
message_dialog => sub { Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $MESSAGE_DIALOG_EVENT , shared_clone ([ @_ ]))) },
on_completed => sub { Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $EXPORT_COMPLETED_EVENT , shared_clone ([ @_ ]))) },
catch_error => sub {
2012-05-19 21:13:10 +02:00
Slic3r::GUI:: catch_error ( $self , $_ [ 0 ], sub {
Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $MESSAGE_DIALOG_EVENT , shared_clone ([ @_ ])));
Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $EXPORT_FAILED_EVENT , undef ));
});
2012-05-05 21:08:15 +02:00
},
);
});
$self -> statusbar -> SetCancelCallback ( sub {
2012-09-21 17:45:54 +02:00
$self -> { export_thread } -> kill ( 'KILL' ) -> join ;
2012-05-05 21:08:15 +02:00
$self -> { export_thread } = undef ;
$self -> statusbar -> StopBusy ;
$self -> statusbar -> SetStatusText ( "Export cancelled" );
});
} else {
$self -> export_gcode2 (
2012-09-12 16:30:44 +02:00
$print ,
2012-05-05 21:08:15 +02:00
$self -> { output_file },
progressbar => sub {
my ( $percent , $message ) = @_ ;
$self -> statusbar -> SetProgress ( $percent );
2012-07-01 23:29:56 +02:00
$self -> statusbar -> SetStatusText ( "$message…" );
2012-05-05 21:08:15 +02:00
},
message_dialog => sub { Wx::MessageDialog -> new ( $self , @_ ) -> ShowModal },
on_completed => sub { $self -> on_export_completed ( @_ ) },
2012-05-23 11:47:52 +02:00
catch_error => sub { Slic3r::GUI:: catch_error ( $self , @_ ) && $self -> on_export_failed },
2012-05-05 21:08:15 +02:00
);
}
}
2012-09-12 16:30:44 +02:00
sub _init_print {
my $self = shift ;
2013-01-03 15:49:20 +01:00
my %extra_variables = ();
if ( $self -> skeinpanel -> { mode } eq 'expert' ) {
$extra_variables { "${_}_preset" } = $self -> skeinpanel -> { options_tabs }{ $_ } -> current_preset -> { name }
for qw(print filament printer) ;
}
2012-09-12 16:30:44 +02:00
return Slic3r::Print -> new (
config => $self -> skeinpanel -> config ,
2013-01-03 15:49:20 +01:00
extra_variables => { %extra_variables },
2012-09-12 16:30:44 +02:00
);
}
2012-05-05 21:08:15 +02:00
sub export_gcode2 {
my $self = shift ;
2012-09-12 16:30:44 +02:00
my ( $print , $output_file , %params ) = @_ ;
2012-05-05 21:08:15 +02:00
$ Slic3r::Geometry::Clipper:: clipper = Math::Clipper -> new ;
local $SIG { 'KILL' } = sub {
Slic3r:: debugf "Exporting cancelled; exiting thread...\n" ;
threads -> exit ();
2012-05-19 21:13:10 +02:00
} if $ Slic3r:: have_threads ;
2012-05-05 21:08:15 +02:00
2012-04-30 14:56:01 +02:00
eval {
2012-07-27 21:13:03 +02:00
$print -> config -> validate ;
2012-09-12 16:30:44 +02:00
$print -> add_model ( $self -> make_model );
2012-05-23 11:47:52 +02:00
$print -> validate ;
2012-04-30 14:56:01 +02:00
{
my @warnings = ();
local $SIG { __WARN__ } = sub { push @warnings , $_ [ 0 ] };
my %params = (
output_file => $output_file ,
2012-05-05 21:08:15 +02:00
status_cb => sub { $params { progressbar } -> ( @_ ) },
2012-04-30 14:56:01 +02:00
);
if ( $params { export_svg }) {
$print -> export_svg ( %params );
} else {
$print -> export_gcode ( %params );
}
2012-05-29 17:02:47 +02:00
Slic3r::GUI:: warning_catcher ( $self , $ Slic3r:: have_threads ? sub {
2012-05-05 21:08:15 +02:00
Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $MESSAGE_DIALOG_EVENT , shared_clone ([ @_ ])));
2012-05-29 17:02:47 +02:00
} : undef ) -> ( $_ ) for @warnings ;
2012-04-30 14:56:01 +02:00
}
my $message = "Your files were successfully sliced" ;
2012-07-08 22:16:46 +02:00
if ( $print -> processing_time ) {
$message .= ' in' ;
my $minutes = int ( $print -> processing_time / 60 );
$message .= sprintf " %d minutes and" , $minutes if $minutes ;
$message .= sprintf " %.1f seconds" , $print -> processing_time - $minutes * 60 ;
}
2012-08-07 21:00:03 +02:00
$message .= "." ;
2012-05-05 21:08:15 +02:00
$params { on_completed } -> ( $message );
2012-04-30 14:56:01 +02:00
};
2012-05-19 21:13:10 +02:00
$params { catch_error } -> ();
2012-04-30 14:56:01 +02:00
}
2012-05-05 21:08:15 +02:00
sub on_export_completed {
my $self = shift ;
my ( $message ) = @_ ;
2012-05-20 17:21:31 +02:00
$self -> { export_thread } -> detach if $self -> { export_thread };
2012-05-05 21:26:19 +02:00
$self -> { export_thread } = undef ;
$self -> statusbar -> SetCancelCallback ( undef );
2012-05-05 21:08:15 +02:00
$self -> statusbar -> StopBusy ;
$self -> statusbar -> SetStatusText ( "G-code file exported to $self->{output_file}" );
2012-07-15 23:57:31 +02:00
& Wx::wxTheApp -> notify ( $message );
2012-05-05 21:08:15 +02:00
}
2012-05-19 21:13:10 +02:00
sub on_export_failed {
my $self = shift ;
2012-05-20 17:21:31 +02:00
$self -> { export_thread } -> detach if $self -> { export_thread };
2012-05-19 21:13:10 +02:00
$self -> { export_thread } = undef ;
$self -> statusbar -> SetCancelCallback ( undef );
$self -> statusbar -> StopBusy ;
$self -> statusbar -> SetStatusText ( "Export failed" );
}
2012-04-30 17:10:54 +02:00
sub export_stl {
my $self = shift ;
2012-08-29 17:11:56 +02:00
my $output_file = $self -> _get_export_file ( 'STL' ) or return ;
Slic3r::Format::STL -> write_file ( $output_file , $self -> make_model , binary => 1 );
$self -> statusbar -> SetStatusText ( "STL file exported to $output_file" );
}
sub export_amf {
my $self = shift ;
my $output_file = $self -> _get_export_file ( 'AMF' ) or return ;
Slic3r::Format::AMF -> write_file ( $output_file , $self -> make_model );
$self -> statusbar -> SetStatusText ( "AMF file exported to $output_file" );
}
sub _get_export_file {
my $self = shift ;
my ( $format ) = @_ ;
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml' ;
2012-04-30 17:10:54 +02:00
my $output_file = $ main:: opt { output };
{
2012-09-12 16:30:44 +02:00
$output_file = $self -> _init_print -> expanded_output_filepath ( $output_file , $self -> { objects }[ 0 ] -> input_file );
2012-08-29 17:11:56 +02:00
$output_file =~ s/\.gcode$/$suffix/i ;
my $dlg = Wx::FileDialog -> new ( $self , "Save $format file as:" , dirname ( $output_file ),
2012-09-23 14:48:58 +02:00
basename ( $output_file ), & Slic3r::GUI::SkeinPanel:: MODEL_WILDCARD , wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2012-04-30 17:10:54 +02:00
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
2012-08-29 17:11:56 +02:00
return undef ;
2012-04-30 17:10:54 +02:00
}
$output_file = $ Slic3r::GUI::SkeinPanel:: last_output_file = $dlg -> GetPath ;
$dlg -> Destroy ;
}
2012-08-29 17:11:56 +02:00
return $output_file ;
2012-08-29 16:49:38 +02:00
}
sub make_model {
my $self = shift ;
my $model = Slic3r::Model -> new ;
2012-10-21 20:56:19 +02:00
foreach my $plater_object ( @ { $self -> { objects }}) {
my $model_object = $plater_object -> get_model_object ;
my $new_model_object = $model -> add_object (
vertices => $model_object -> vertices ,
input_file => $plater_object -> input_file ,
2013-03-10 14:58:49 +01:00
layer_height_ranges => $plater_object -> layer_height_ranges ,
2012-09-12 16:30:44 +02:00
);
2012-10-21 20:56:19 +02:00
foreach my $volume ( @ { $model_object -> volumes }) {
$new_model_object -> add_volume (
material_id => $volume -> material_id ,
facets => $volume -> facets ,
);
2012-11-16 22:41:54 +01:00
$model -> set_material ( $volume -> material_id || 0 , {});
2012-10-21 20:56:19 +02:00
}
$new_model_object -> scale ( $plater_object -> scale );
$new_model_object -> add_instance (
rotation => $plater_object -> rotate ,
2012-09-12 16:30:44 +02:00
offset => [ @$_ ],
2012-10-21 20:56:19 +02:00
) for @ { $plater_object -> instances };
2012-04-30 17:10:54 +02:00
}
2012-08-29 16:49:38 +02:00
return $model ;
2012-04-30 17:10:54 +02:00
}
2012-04-30 14:56:01 +02:00
sub make_thumbnail {
my $self = shift ;
my ( $obj_idx ) = @_ ;
2012-11-19 18:31:41 +01:00
my $object = $self -> { objects }[ $obj_idx ];
$object -> thumbnail_scaling_factor ( $self -> { scaling_factor });
2012-04-30 20:59:14 +02:00
my $cb = sub {
2012-11-24 00:13:04 +01:00
$ Slic3r::Geometry::Clipper:: clipper = Math::Clipper -> new if $ Slic3r:: have_threads ;
2012-11-19 18:31:41 +01:00
my $thumbnail = $object -> make_thumbnail ;
2012-04-30 20:59:14 +02:00
2012-05-19 21:13:10 +02:00
if ( $ Slic3r:: have_threads ) {
2012-08-29 19:37:27 +02:00
Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $THUMBNAIL_DONE_EVENT , shared_clone ([ $obj_idx , $thumbnail ])));
2012-04-30 22:34:41 +02:00
threads -> exit ;
} else {
2012-10-01 18:12:14 +02:00
$self -> on_thumbnail_made ( $obj_idx );
2012-04-30 22:34:41 +02:00
}
2012-04-30 20:59:14 +02:00
};
2012-05-20 16:29:52 +02:00
$ Slic3r:: have_threads ? threads -> create ( $cb ) -> detach : $cb -> ();
2012-04-30 14:56:01 +02:00
}
2012-08-29 19:37:27 +02:00
sub on_thumbnail_made {
2012-04-30 22:34:41 +02:00
my $self = shift ;
2012-10-01 18:12:14 +02:00
my ( $obj_idx ) = @_ ;
2012-10-22 14:02:58 +02:00
$self -> { objects }[ $obj_idx ] -> free_model_object ;
2012-04-30 22:34:41 +02:00
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
2012-04-30 14:56:01 +02:00
sub recenter {
my $self = shift ;
2012-09-12 16:30:44 +02:00
return unless @ { $self -> { objects }};
2012-04-30 14:56:01 +02:00
# calculate displacement needed to center the print
2012-09-12 16:30:44 +02:00
my @print_bb = Slic3r::Geometry:: bounding_box ([
map {
my $obj = $_ ;
map {
my $instance = $_ ;
2012-11-19 17:39:16 +01:00
$instance , [ map $instance -> [ $_ ] + $obj -> size -> [ $_ ], X , Y ];
2012-09-12 16:30:44 +02:00
} @ { $obj -> instances };
} @ { $self -> { objects }},
]);
2012-04-30 14:56:01 +02:00
$self -> { shift } = [
2012-09-12 16:30:44 +02:00
( $self -> { canvas } -> GetSize -> GetWidth - $self -> to_pixel ( $print_bb [ X2 ] + $print_bb [ X1 ])) / 2 ,
( $self -> { canvas } -> GetSize -> GetHeight - $self -> to_pixel ( $print_bb [ Y2 ] + $print_bb [ Y1 ])) / 2 ,
2012-04-30 14:56:01 +02:00
];
}
2012-07-27 21:13:03 +02:00
sub on_config_change {
my $self = shift ;
my ( $opt_key , $value ) = @_ ;
2012-08-07 20:14:28 +02:00
if ( $opt_key eq 'extruders_count' && defined $value ) {
my $choices = $self -> { preset_choosers }{ filament };
while ( @$choices < $value ) {
2012-10-24 20:24:11 +02:00
my @presets = $choices -> [ 0 ] -> GetStrings ;
push @$choices , Wx::Choice -> new ( $self , - 1 , wxDefaultPosition , [ 150 , - 1 ], [ @presets ]);
2012-08-07 20:14:28 +02:00
$self -> { preset_choosers_sizers }{ filament } -> Add ( $choices -> [ - 1 ], 0 , wxEXPAND | wxBOTTOM , FILAMENT_CHOOSERS_SPACING );
2012-10-24 20:24:11 +02:00
EVT_CHOICE ( $choices -> [ - 1 ], $choices -> [ - 1 ], sub { $self -> on_select_preset ( 'filament' , @_ ) });
my $i = first { $choices -> [ - 1 ] -> GetString ( $_ ) eq ( $ Slic3r::GUI:: Settings -> { presets }{ "filament_" . $#$choices } || '' ) } 0 .. $#presets ;
$choices -> [ - 1 ] -> SetSelection ( $i || 0 );
2012-08-07 20:14:28 +02:00
}
while ( @$choices > $value ) {
$self -> { preset_choosers_sizers }{ filament } -> Remove ( - 1 );
$choices -> [ - 1 ] -> Destroy ;
pop @$choices ;
}
$self -> Layout ;
2012-08-09 00:41:03 +02:00
} elsif ( $self -> { config } -> has ( $opt_key )) {
2012-08-06 19:18:31 +02:00
$self -> { config } -> set ( $opt_key , $value );
$self -> _update_bed_size if $opt_key eq 'bed_size' ;
}
2012-07-27 21:13:03 +02:00
}
sub _update_bed_size {
2012-04-30 22:34:41 +02:00
my $self = shift ;
# supposing the preview canvas is square, calculate the scaling factor
# to constrain print bed area inside preview
2012-07-27 21:13:03 +02:00
my $bed_size = $self -> { config } -> bed_size ;
2012-08-06 19:18:31 +02:00
my $canvas_side = CANVAS_SIZE -> [ X ]; # when the canvas is not rendered yet, its GetSize() method returns 0,0
2012-07-27 21:13:03 +02:00
my $bed_largest_side = $bed_size -> [ X ] > $bed_size -> [ Y ] ? $bed_size -> [ X ] : $bed_size -> [ Y ];
2012-04-30 22:34:41 +02:00
$self -> { scaling_factor } = $canvas_side / $bed_largest_side ;
}
2012-07-27 21:13:03 +02:00
# this is called on the canvas
2012-04-30 14:56:01 +02:00
sub repaint {
my ( $self , $event ) = @_ ;
my $parent = $self -> GetParent ;
my $dc = Wx::PaintDC -> new ( $self );
my $size = $self -> GetSize ;
my @size = ( $size -> GetWidth , $size -> GetHeight );
# draw grid
$dc -> SetPen ( $parent -> { grid_pen });
my $step = 10 * $parent -> { scaling_factor };
for ( my $x = $step ; $x <= $size [ X ]; $x += $step ) {
$dc -> DrawLine ( $x , 0 , $x , $size [ Y ]);
}
for ( my $y = $step ; $y <= $size [ Y ]; $y += $step ) {
$dc -> DrawLine ( 0 , $y , $size [ X ], $y );
}
2012-05-04 11:47:53 +02:00
# draw print center
2012-09-11 18:11:46 +02:00
if ( @ { $parent -> { objects }}) {
2012-05-04 11:47:53 +02:00
$dc -> SetPen ( $parent -> { print_center_pen });
$dc -> DrawLine ( $size [ X ] /2, 0, $size[X]/ 2 , $size [ Y ]);
$dc -> DrawLine ( 0 , $size [ Y ] /2, $size[X], $size[Y]/ 2 );
$dc -> SetTextForeground ( Wx::Colour -> new ( 0 , 0 , 0 ));
$dc -> SetFont ( Wx::Font -> new ( 10 , wxDEFAULT , wxNORMAL , wxNORMAL ));
2012-07-27 21:13:03 +02:00
$dc -> DrawLabel ( "X = " . $parent -> { config } -> print_center -> [ X ], Wx::Rect -> new ( 0 , 0 , $self -> GetSize -> GetWidth , $self -> GetSize -> GetHeight ), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM );
$dc -> DrawRotatedText ( "Y = " . $parent -> { config } -> print_center -> [ Y ], 0 , $size [ Y ] / 2 + 15 , 90 );
2012-05-04 11:47:53 +02:00
}
2012-04-30 14:56:01 +02:00
# draw frame
2012-07-01 19:24:06 +02:00
$dc -> SetPen ( wxBLACK_PEN );
2012-04-30 14:56:01 +02:00
$dc -> SetBrush ( $parent -> { transparent_brush });
$dc -> DrawRectangle ( 0 , 0 , @size );
# draw text if plate is empty
2012-09-12 16:30:44 +02:00
if ( ! @ { $parent -> { objects }}) {
2012-04-30 14:56:01 +02:00
$dc -> SetTextForeground ( Wx::Colour -> new ( 150 , 50 , 50 ));
2012-05-04 11:47:53 +02:00
$dc -> SetFont ( Wx::Font -> new ( 14 , wxDEFAULT , wxNORMAL , wxNORMAL ));
2012-07-27 21:13:03 +02:00
$dc -> DrawLabel ( CANVAS_TEXT , Wx::Rect -> new ( 0 , 0 , $self -> GetSize -> GetWidth , $self -> GetSize -> GetHeight ), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
2012-04-30 14:56:01 +02:00
}
# draw thumbnails
2012-07-01 19:24:06 +02:00
$dc -> SetPen ( wxBLACK_PEN );
2012-04-30 14:56:01 +02:00
@ { $parent -> { object_previews }} = ();
2012-09-11 18:11:46 +02:00
for my $obj_idx ( 0 .. $# { $parent -> { objects }}) {
2012-09-12 16:30:44 +02:00
my $object = $parent -> { objects }[ $obj_idx ];
2013-03-30 00:47:13 +01:00
next unless $object -> thumbnail && @ { $object -> thumbnail -> expolygons };
2012-09-12 16:30:44 +02:00
for my $instance_idx ( 0 .. $# { $object -> instances }) {
my $instance = $object -> instances -> [ $instance_idx ];
push @ { $parent -> { object_previews }}, [ $obj_idx , $instance_idx , $object -> thumbnail -> clone ];
2012-11-24 00:13:04 +01:00
$_ -> translate ( map $parent -> to_pixel ( $instance -> [ $_ ]) + $parent -> { shift }[ $_ ], ( X , Y ))
for @ { $parent -> { object_previews } -> [ - 1 ][ 2 ] -> expolygons };
2012-04-30 14:56:01 +02:00
2012-07-25 10:06:45 +02:00
my $drag_object = $self -> { drag_object };
2012-09-12 16:30:44 +02:00
if ( defined $drag_object && $obj_idx == $drag_object -> [ 0 ] && $instance_idx == $drag_object -> [ 1 ]) {
2012-07-25 10:06:45 +02:00
$dc -> SetBrush ( $parent -> { dragged_brush });
} elsif ( grep { $_ -> [ 0 ] == $obj_idx } @ { $parent -> { selected_objects }}) {
2012-04-30 14:56:01 +02:00
$dc -> SetBrush ( $parent -> { selected_brush });
} else {
$dc -> SetBrush ( $parent -> { objects_brush });
}
2012-11-24 00:13:04 +01:00
$dc -> DrawPolygon ( $parent -> _y ( $_ ), 0 , 0 ) for map $_ -> contour , @ { $parent -> { object_previews } -> [ - 1 ][ 2 ] -> expolygons };
2012-05-23 11:47:52 +02:00
# if sequential printing is enabled and we have more than one object
2012-09-12 16:30:44 +02:00
if ( $parent -> { config } -> complete_objects && ( map @ { $_ -> instances }, @ { $parent -> { objects }}) > 1 ) {
2012-11-24 00:13:04 +01:00
my $convex_hull = Slic3r::Polygon -> new ( convex_hull ([ map @ { $_ -> contour }, @ { $parent -> { object_previews } -> [ - 1 ][ 2 ] -> expolygons } ]));
2013-03-26 14:03:24 +01:00
my ( $clearance ) = offset ([ $convex_hull ], $parent -> { config } -> extruder_clearance_radius / 2 * $parent -> { scaling_factor }, 1 , JT_ROUND );
2012-05-23 11:47:52 +02:00
$dc -> SetPen ( $parent -> { clearance_pen });
$dc -> SetBrush ( $parent -> { transparent_brush });
$dc -> DrawPolygon ( $parent -> _y ( $clearance ), 0 , 0 );
}
2012-04-30 14:56:01 +02:00
}
}
# draw skirt
2012-07-27 21:13:03 +02:00
if ( @ { $parent -> { object_previews }} && $parent -> { config } -> skirts ) {
2012-11-24 00:13:04 +01:00
my $convex_hull = Slic3r::Polygon -> new ( convex_hull ([ map @ { $_ -> contour }, map @ { $_ -> [ 2 ] -> expolygons }, @ { $parent -> { object_previews }} ]));
2013-03-26 14:03:24 +01:00
( $convex_hull ) = offset ([ $convex_hull ], $parent -> { config } -> skirt_distance * $parent -> { scaling_factor }, 1 , JT_ROUND );
2012-04-30 14:56:01 +02:00
$dc -> SetPen ( $parent -> { skirt_pen });
$dc -> SetBrush ( $parent -> { transparent_brush });
$dc -> DrawPolygon ( $parent -> _y ( $convex_hull ), 0 , 0 ) if $convex_hull ;
}
$event -> Skip ;
}
sub mouse_event {
my ( $self , $event ) = @_ ;
my $parent = $self -> GetParent ;
my $point = $event -> GetPosition ;
my $pos = $parent -> _y ([[ $point -> x , $point -> y ]]) -> [ 0 ]; #]]
if ( $event -> ButtonDown ( & Wx:: wxMOUSE_BTN_LEFT )) {
$parent -> { selected_objects } = [] ;
$parent -> { list } -> Select ( $parent -> { list } -> GetFirstSelected , 0 );
$parent -> selection_changed ( 0 );
for my $preview ( @ { $parent -> { object_previews }}) {
2012-09-12 16:30:44 +02:00
my ( $obj_idx , $instance_idx , $thumbnail ) = @$preview ;
2012-11-24 00:13:04 +01:00
if ( first { $_ -> contour -> encloses_point ( $pos ) } @ { $thumbnail -> expolygons }) {
2012-09-12 16:30:44 +02:00
$parent -> { selected_objects } = [ [ $obj_idx , $instance_idx ] ];
$parent -> { list } -> Select ( $obj_idx , 1 );
2012-04-30 14:56:01 +02:00
$parent -> selection_changed ( 1 );
2012-09-12 16:30:44 +02:00
my $instance = $parent -> { objects }[ $obj_idx ] -> instances -> [ $instance_idx ];
$self -> { drag_start_pos } = [ map $pos -> [ $_ ] - $parent -> { shift }[ $_ ] - $parent -> to_pixel ( $instance -> [ $_ ]), X , Y ]; # displacement between the click and the instance origin
2012-04-30 14:56:01 +02:00
$self -> { drag_object } = $preview ;
}
}
$parent -> Refresh ;
} elsif ( $event -> ButtonUp ( & Wx:: wxMOUSE_BTN_LEFT )) {
$parent -> recenter ;
$parent -> Refresh ;
$self -> { drag_start_pos } = undef ;
$self -> { drag_object } = undef ;
2012-07-25 00:51:41 +02:00
$self -> SetCursor ( wxSTANDARD_CURSOR );
2012-10-24 22:44:08 +02:00
} elsif ( $event -> ButtonDClick ) {
$parent -> list_item_activated ( undef , $parent -> { selected_objects } -> [ 0 ][ 0 ])
if @ { $parent -> { selected_objects }};
2012-04-30 14:56:01 +02:00
} elsif ( $event -> Dragging ) {
return if ! $self -> { drag_start_pos }; # concurrency problems
2012-09-12 16:30:44 +02:00
for my $preview ( $self -> { drag_object }) {
my ( $obj_idx , $instance_idx , $thumbnail ) = @$preview ;
my $instance = $parent -> { objects }[ $obj_idx ] -> instances -> [ $instance_idx ];
$instance -> [ $_ ] = $parent -> to_units ( $pos -> [ $_ ] - $self -> { drag_start_pos }[ $_ ] - $parent -> { shift }[ $_ ]) for X , Y ;
$instance = $parent -> _y ([ $instance ]) -> [ 0 ];
2012-04-30 14:56:01 +02:00
$parent -> Refresh ;
}
2012-07-25 00:15:32 +02:00
} elsif ( $event -> Moving ) {
my $cursor = wxSTANDARD_CURSOR ;
for my $preview ( @ { $parent -> { object_previews }}) {
2012-11-24 00:13:04 +01:00
if ( first { $_ -> contour -> encloses_point ( $pos ) } @ { $preview -> [ 2 ] -> expolygons }) {
2012-07-25 00:15:32 +02:00
$cursor = Wx::Cursor -> new ( wxCURSOR_HAND );
last ;
}
}
$self -> SetCursor ( $cursor );
2012-04-30 14:56:01 +02:00
}
}
sub list_item_deselected {
my ( $self , $event ) = @_ ;
if ( $self -> { list } -> GetFirstSelected == - 1 ) {
$self -> { selected_objects } = [] ;
$self -> { canvas } -> Refresh ;
$self -> selection_changed ( 0 );
}
}
sub list_item_selected {
my ( $self , $event ) = @_ ;
my $obj_idx = $event -> GetIndex ;
$self -> { selected_objects } = [ grep $_ -> [ 0 ] == $obj_idx , @ { $self -> { object_previews }} ];
$self -> { canvas } -> Refresh ;
$self -> selection_changed ( 1 );
}
2012-10-24 22:44:08 +02:00
sub list_item_activated {
my ( $self , $event , $obj_idx ) = @_ ;
$obj_idx // = $event -> GetIndex ;
2013-03-10 12:33:10 +01:00
my $dlg = Slic3r::GUI::Plater::ObjectDialog -> new ( $self ,
2012-10-24 22:44:08 +02:00
object => $self -> { objects }[ $obj_idx ],
);
$dlg -> ShowModal ;
}
2012-04-30 14:56:01 +02:00
sub object_list_changed {
my $self = shift ;
2012-09-12 16:30:44 +02:00
my $method = @ { $self -> { objects }} ? 'Enable' : 'Disable' ;
2012-05-01 00:30:46 +02:00
$self -> { "btn_$_" } -> $method
2012-05-04 11:22:56 +02:00
for grep $self -> { "btn_$_" }, qw(reset arrange export_gcode export_stl) ;
2012-04-30 14:56:01 +02:00
}
sub selection_changed {
my $self = shift ;
my ( $have_sel ) = @_ ;
my $method = $have_sel ? 'Enable' : 'Disable' ;
2012-05-01 00:30:46 +02:00
$self -> { "btn_$_" } -> $method
2012-05-04 11:22:56 +02:00
for grep $self -> { "btn_$_" }, qw(remove increase decrease rotate45cw rotate45ccw rotate changescale split) ;
if ( $self -> { htoolbar }) {
2012-08-02 21:49:26 +02:00
$self -> { htoolbar } -> EnableTool ( $_ , $have_sel )
for ( TB_MORE , TB_LESS , TB_45CW , TB_45CCW , TB_ROTATE , TB_SCALE , TB_SPLIT );
2012-05-04 11:22:56 +02:00
}
2012-04-30 14:56:01 +02:00
}
2012-08-29 19:37:27 +02:00
sub selected_object {
2012-04-30 14:56:01 +02:00
my $self = shift ;
2012-08-29 19:37:27 +02:00
my $obj_idx = $self -> { selected_objects }[ 0 ] ? $self -> { selected_objects }[ 0 ][ 0 ] : $self -> { list } -> GetFirstSelected ;
return ( $obj_idx , $self -> { objects }[ $obj_idx ]),
2012-04-30 14:56:01 +02:00
}
2012-05-04 12:56:15 +02:00
sub statusbar {
my $self = shift ;
2012-07-27 21:13:03 +02:00
return $self -> skeinpanel -> GetParent -> { statusbar };
2012-05-04 12:56:15 +02:00
}
2012-04-30 14:56:01 +02:00
sub to_pixel {
my $self = shift ;
2012-09-11 18:11:46 +02:00
return $_ [ 0 ] * $self -> { scaling_factor };
2012-04-30 14:56:01 +02:00
}
2012-09-12 16:30:44 +02:00
sub to_units {
2012-04-30 14:56:01 +02:00
my $self = shift ;
2012-09-11 18:11:46 +02:00
return $_ [ 0 ] / $self -> { scaling_factor };
2012-04-30 14:56:01 +02:00
}
sub _y {
my $self = shift ;
my ( $points ) = @_ ;
my $height = $self -> { canvas } -> GetSize -> GetHeight ;
return [ map [ $_ -> [ X ], $height - $_ -> [ Y ] ], @$points ];
}
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 {
my $class = shift ;
my ( $window ) = @_ ;
my $self = $class -> SUPER:: new ;
$self -> { window } = $window ;
return $self ;
}
sub OnDropFiles {
my $self = shift ;
my ( $x , $y , $filenames ) = @_ ;
2012-07-21 14:45:45 +02:00
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@_ = ();
2013-03-19 12:24:39 +01:00
# only accept STL, OBJ and AMF files
return 0 if grep ! /\.(?:stl|obj|amf(?:\.xml)?)$/i , @$filenames ;
2012-04-30 14:56:01 +02:00
$self -> { window } -> load_file ( $_ ) for @$filenames ;
}
2012-08-29 19:37:27 +02:00
package Slic3r::GUI::Plater::Object ;
use Moo ;
2012-10-22 15:29:54 +02:00
use Math::ConvexHull::MonotoneChain qw(convex_hull) ;
2012-10-24 22:44:08 +02:00
use Slic3r::Geometry qw(X Y Z) ;
2012-08-29 19:37:27 +02:00
has 'name' => ( is => 'rw' , required => 1 );
has 'input_file' => ( is => 'rw' , required => 1 );
2012-10-22 14:02:58 +02:00
has 'input_file_object_id' => ( is => 'rw' ); # undef means keep model object
has 'model_object' => ( is => 'rw' , required => 1 , trigger => 1 );
2012-08-29 19:37:27 +02:00
has 'size' => ( is => 'rw' );
has 'scale' => ( is => 'rw' , default => sub { 1 });
has 'rotate' => ( is => 'rw' , default => sub { 0 });
2012-09-12 16:30:44 +02:00
has 'instances' => ( is => 'rw' , default => sub { [] }); # upward Y axis
2012-08-29 19:37:27 +02:00
has 'thumbnail' => ( is => 'rw' );
2012-11-19 17:39:16 +01:00
has 'thumbnail_scaling_factor' => ( is => 'rw' );
2013-03-10 14:58:49 +01:00
has 'layer_height_ranges' => ( is => 'rw' , default => sub { [] }); # [ z_min, z_max, layer_height ]
2012-08-29 19:37:27 +02:00
2012-10-24 22:44:08 +02:00
# statistics
has 'facets' => ( is => 'rw' );
has 'vertices' => ( is => 'rw' );
has 'materials' => ( is => 'rw' );
has 'is_manifold' => ( is => 'rw' );
2012-10-22 14:02:58 +02:00
sub _trigger_model_object {
2012-08-29 19:37:27 +02:00
my $self = shift ;
2012-10-24 22:44:08 +02:00
if ( $self -> model_object ) {
my $mesh = $self -> model_object -> mesh ;
$self -> size ([ $mesh -> size ]);
$self -> facets ( scalar @ { $mesh -> facets });
$self -> vertices ( scalar @ { $mesh -> vertices });
$self -> materials ( $self -> model_object -> materials_count );
}
}
sub check_manifoldness {
my $self = shift ;
2013-02-24 18:17:08 +01:00
$self -> is_manifold ( $self -> get_model_object -> check_manifoldness );
2012-10-24 22:44:08 +02:00
return $self -> is_manifold ;
2012-09-12 16:30:44 +02:00
}
2012-10-22 14:02:58 +02:00
sub free_model_object {
2012-10-01 18:12:14 +02:00
my $self = shift ;
# only delete mesh from memory if we can retrieve it from the original file
return unless $self -> input_file && $self -> input_file_object_id ;
2012-10-22 14:02:58 +02:00
$self -> model_object ( undef );
2012-10-01 18:12:14 +02:00
}
2012-10-21 20:56:19 +02:00
sub get_model_object {
2012-09-12 16:30:44 +02:00
my $self = shift ;
2013-03-10 14:58:49 +01:00
my $object = $self -> model_object ;
if ( ! $object ) {
my $model = Slic3r::Model -> read_from_file ( $self -> input_file );
$object = $model -> objects -> [ $self -> input_file_object_id ];
}
return $object ;
2012-08-29 19:37:27 +02:00
}
sub instances_count {
my $self = shift ;
return scalar @ { $self -> instances };
}
sub make_thumbnail {
my $self = shift ;
2012-10-22 14:02:58 +02:00
my @points = map [ @$_ [ X , Y ] ], @ { $self -> model_object -> mesh -> vertices };
2012-11-24 00:13:04 +01:00
my $mesh = $self -> model_object -> mesh ;
my $thumbnail = Slic3r::ExPolygon::Collection -> new (
2012-11-29 19:13:52 +01:00
expolygons => ( @ { $mesh -> facets } <= 5000 )
2012-11-24 00:13:04 +01:00
? $mesh -> horizontal_projection
: [ Slic3r::ExPolygon -> new ( convex_hull ( $mesh -> vertices )) ],
);
for ( map @$_ , map @$_ , @ { $thumbnail -> expolygons }) {
2012-11-19 18:31:41 +01:00
@$_ = map $_ * $self -> thumbnail_scaling_factor , @$_ ;
2012-08-29 19:37:27 +02:00
}
2013-03-30 00:52:45 +01:00
# only simplify expolygons larger than the threshold
@ { $thumbnail -> expolygons } = map { ( $_ -> area >= 1 ) ? $_ -> simplify ( 0.5 ) : $_ } @ { $thumbnail -> expolygons };
2012-11-29 19:13:52 +01:00
foreach my $expolygon ( @ { $thumbnail -> expolygons }) {
$expolygon -> rotate ( Slic3r::Geometry:: deg2rad ( $self -> rotate ));
$expolygon -> scale ( $self -> scale );
2012-11-24 00:13:04 +01:00
}
2012-11-29 19:13:52 +01:00
@ { $thumbnail -> expolygons } = grep @$_ , @ { $thumbnail -> expolygons };
2012-11-24 00:13:04 +01:00
$thumbnail -> align_to_origin ;
$self -> thumbnail ( $thumbnail ); # ignored in multi-threaded environments
2012-10-22 14:02:58 +02:00
$self -> free_model_object ;
2012-09-12 16:30:44 +02:00
2012-11-24 00:13:04 +01:00
return $thumbnail ;
2012-08-29 19:37:27 +02:00
}
sub set_rotation {
my $self = shift ;
my ( $angle ) = @_ ;
2012-09-12 16:30:44 +02:00
if ( $self -> thumbnail ) {
$self -> thumbnail -> rotate ( Slic3r::Geometry:: deg2rad ( $angle - $self -> rotate ));
$self -> thumbnail -> align_to_origin ;
2012-11-19 17:39:16 +01:00
my $z_size = $self -> size -> [ Z ];
$self -> size ([ ( map $_ / $self -> thumbnail_scaling_factor , @ { $self -> thumbnail -> size }), $z_size ]);
2012-09-12 16:30:44 +02:00
}
2012-08-29 19:37:27 +02:00
$self -> rotate ( $angle );
}
sub set_scale {
my $self = shift ;
my ( $scale ) = @_ ;
2012-09-12 16:30:44 +02:00
my $factor = $scale / $self -> scale ;
return if $factor == 1 ;
2012-10-24 22:44:08 +02:00
$self -> size -> [ $_ ] *= $factor for X , Y , Z ;
2012-09-12 16:30:44 +02:00
if ( $self -> thumbnail ) {
2012-11-24 00:13:04 +01:00
$_ -> scale ( $factor ) for @ { $self -> thumbnail -> expolygons };
$self -> thumbnail -> align_to_origin ;
2012-09-12 16:30:44 +02:00
}
2012-08-29 19:37:27 +02:00
$self -> scale ( $scale );
}
2012-04-30 14:56:01 +02:00
1 ;