2012-04-30 14:56:01 +02:00
package Slic3r::GUI::Dashboard ;
use strict ;
use warnings ;
2012-04-30 20:59:14 +02:00
my $have_threads = eval "use threads; 1" ;
use threads::shared ;
2012-04-30 14:56:01 +02:00
use utf8 ;
use File::Basename qw(basename dirname) ;
use Math::ConvexHull qw(convex_hull) ;
2012-04-30 17:10:54 +02:00
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale) ;
2012-04-30 18:54:36 +02:00
use Slic3r::Geometry::Clipper qw(JT_ROUND) ;
2012-04-30 14:56:01 +02:00
use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_WARNING wxICON_ERROR wxICON_QUESTION
wxOK wxCANCEL wxID_OK wxFD_OPEN wxFD_SAVE wxDEFAULT wxNORMAL) ;
2012-04-30 20:59:14 +02:00
use Wx::Event qw(EVT_BUTTON EVT_PAINT EVT_MOUSE_EVENTS EVT_LIST_ITEM_SELECTED EVT_LIST_ITEM_DESELECTED
EVT_COMMAND) ;
2012-04-30 14:56:01 +02:00
use base 'Wx::Panel' ;
2012-04-30 20:59:14 +02:00
my $THUMBNAIL_DONE_EVENT : shared = Wx:: NewEventType ;
2012-04-30 14:56:01 +02:00
sub new {
my $class = shift ;
my ( $parent ) = @_ ;
my $self = $class -> SUPER:: new ( $parent , - 1 );
$self -> { canvas } = Wx::Panel -> new ( $self , - 1 , [ - 1 , - 1 ], [ 300 , 300 ]);
$self -> { canvas } -> SetBackgroundColour ( Wx:: wxWHITE );
EVT_PAINT ( $self -> { canvas }, \& repaint );
EVT_MOUSE_EVENTS ( $self -> { canvas }, \& mouse_event );
$self -> { objects_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 210 , 210 , 210 ), & Wx:: wxSOLID );
$self -> { selected_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 255 , 128 , 128 ), & Wx:: wxSOLID );
$self -> { transparent_brush } = Wx::Brush -> new ( Wx::Colour -> new ( 0 , 0 , 0 ), & Wx:: wxTRANSPARENT );
$self -> { grid_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 230 , 230 , 230 ), 1 , & Wx:: wxSOLID );
$self -> { skirt_pen } = Wx::Pen -> new ( Wx::Colour -> new ( 150 , 150 , 150 ), 1 , & Wx:: wxSOLID );
$self -> { list } = Wx::ListView -> new ( $self , - 1 , [ - 1 , - 1 ], [ - 1 , 180 ], & Wx:: wxLC_SINGLE_SEL | & Wx:: wxLC_REPORT | & Wx:: wxBORDER_DEFAULT );
$self -> { list } -> InsertColumn ( 0 , "Name" , & Wx:: wxLIST_FORMAT_LEFT , 300 );
$self -> { list } -> InsertColumn ( 1 , "Copies" , & Wx:: wxLIST_FORMAT_CENTER , 50 );
$self -> { list } -> InsertColumn ( 2 , "Scale" , & Wx:: wxLIST_FORMAT_CENTER , 50 );
EVT_LIST_ITEM_SELECTED ( $self , $self -> { list }, \& list_item_selected );
EVT_LIST_ITEM_DESELECTED ( $self , $self -> { list }, \& list_item_deselected );
2012-05-01 13:56:11 +02:00
$self -> { btn_load } = Wx::Button -> new ( $self , - 1 , "Add…" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_remove } = Wx::Button -> new ( $self , - 1 , "Delete" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_increase } = Wx::Button -> new ( $self , - 1 , "+1 copy" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_decrease } = Wx::Button -> new ( $self , - 1 , "-1 copy" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_rotate45cw } = Wx::Button -> new ( $self , - 1 , "Rotate (45° cw)" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_rotate45ccw } = Wx::Button -> new ( $self , - 1 , "Rotate (45° ccw)" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_rotate } = Wx::Button -> new ( $self , - 1 , "Rotate…" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_reset } = Wx::Button -> new ( $self , - 1 , "Delete All" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_arrange } = Wx::Button -> new ( $self , - 1 , "Autoarrange" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_changescale } = Wx::Button -> new ( $self , - 1 , "Change Scale…" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_split } = Wx::Button -> new ( $self , - 1 , "Split" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
$self -> { btn_export_gcode } = Wx::Button -> new ( $self , - 1 , "Export G-code…" , [ - 1 , - 1 ], [ - 1 , - 1 ], & Wx:: wxBU_LEFT );
2012-04-30 14:56:01 +02:00
$self -> { btn_export_gcode } -> SetDefault ;
2012-04-30 17:10:54 +02:00
$self -> { btn_export_stl } = Wx::Button -> new ( $self , - 1 , "Export STL…" );
2012-05-01 16:49:34 +02:00
if ( & Wx:: wxVERSION_STRING =~ / 2\.9\.[1-9]/ ) {
$self -> { btn_load } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/brick_add.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_remove } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/brick_delete.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_increase } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/add.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_decrease } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/delete.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_rotate45cw } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/arrow_rotate_clockwise.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_rotate45ccw } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/arrow_rotate_anticlockwise.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_rotate } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/arrow_rotate_clockwise.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_reset } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/cross.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_arrange } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/bricks.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_changescale } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/arrow_out.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_split } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/shape_ungroup.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_export_gcode } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/layers.png" , & Wx:: wxBITMAP_TYPE_PNG ));
$self -> { btn_export_stl } -> SetBitmap ( Wx::Bitmap -> new ( "$FindBin::Bin/var/brick_go.png" , & Wx:: wxBITMAP_TYPE_PNG ));
}
2012-04-30 14:56:01 +02:00
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
EVT_BUTTON ( $self , $self -> { btn_load }, \& load );
EVT_BUTTON ( $self , $self -> { btn_remove }, \& remove );
EVT_BUTTON ( $self , $self -> { btn_increase }, \& increase );
EVT_BUTTON ( $self , $self -> { btn_decrease }, \& decrease );
2012-04-30 22:14:27 +02:00
EVT_BUTTON ( $self , $self -> { btn_rotate45cw }, sub { $_ [ 0 ] -> rotate ( - 45 ) });
EVT_BUTTON ( $self , $self -> { btn_rotate45ccw }, sub { $_ [ 0 ] -> rotate ( 45 ) });
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_changescale }, \& changescale );
2012-04-30 22:14:27 +02:00
EVT_BUTTON ( $self , $self -> { btn_rotate }, sub { $_ [ 0 ] -> rotate ( undef ) });
2012-05-01 00:30:46 +02:00
EVT_BUTTON ( $self , $self -> { btn_split }, \& split_object );
2012-04-30 14:56:01 +02:00
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-04-30 17:10:54 +02:00
$_ -> SetDropTarget ( Slic3r::GUI::Dashboard::DropTarget -> new ( $self ))
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 };
$self -> { thumbnails }[ $obj_idx ] = $thumbnail ;
2012-04-30 22:34:41 +02:00
$self -> make_thumbnail2 ;
2012-04-30 20:59:14 +02:00
});
2012-04-30 22:34:41 +02:00
$self -> update_bed_size ;
2012-04-30 14:56:01 +02:00
$self -> { print } = Slic3r::Print -> new ;
$self -> { thumbnails } = [] ; # polygons, each one aligned to 0,0
$self -> { scale } = [] ;
$self -> { object_previews } = [] ; # [ obj_idx, copy_idx, positioned polygon ]
$self -> { selected_objects } = [] ;
$self -> recenter ;
{
2012-05-01 01:33:12 +02:00
my @col1 = qw(load remove reset arrange export_gcode export_stl) ;
my @col2 = qw(increase decrease rotate45cw rotate45ccw rotate changescale split) ;
2012-05-01 13:56:11 +02:00
my $buttons = Wx::GridBagSizer -> new ( 5 , 5 );
2012-05-01 13:27:57 +02:00
$buttons -> Add ( $self -> { "btn_$col1[$_]" }, Wx::GBPosition -> new ( $_ , 0 ), Wx::GBSpan -> new ( 1 , 1 ), wxEXPAND | wxALL )
for 0 .. $#col1 ;
$buttons -> Add ( $self -> { "btn_$col2[$_]" }, Wx::GBPosition -> new ( $_ , 1 ), Wx::GBSpan -> new ( 1 , 1 ), wxEXPAND | wxALL )
for 0 .. $#col2 ;
2012-04-30 14:56:01 +02:00
my $vertical_sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2012-05-01 13:27:57 +02:00
$vertical_sizer -> Add ( $self -> { list }, 0 , wxEXPAND | wxALL , 10 );
2012-05-01 01:33:12 +02:00
$vertical_sizer -> Add ( $buttons );
2012-04-30 14:56:01 +02:00
my $sizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
$sizer -> Add ( $self -> { canvas }, 0 , wxALL , 10 );
$sizer -> Add ( $vertical_sizer , 1 , wxEXPAND | wxALL , 10 );
$sizer -> SetSizeHints ( $self );
$self -> SetSizer ( $sizer );
}
return $self ;
}
sub load {
my $self = shift ;
my $dir = $ Slic3r::GUI::SkeinPanel:: last_skein_dir || $ Slic3r::GUI::SkeinPanel:: last_config_dir || "" ;
my $dialog = Wx::FileDialog -> new ( $self , 'Choose a STL or AMF file:' , $dir , "" , $ Slic3r::GUI::SkeinPanel:: model_wildcard , wxFD_OPEN );
if ( $dialog -> ShowModal != wxID_OK ) {
$dialog -> Destroy ;
return ;
}
my $input_file = $dialog -> GetPaths ;
$dialog -> Destroy ;
return $self -> load_file ( $input_file );
}
sub load_file {
my $self = shift ;
my ( $input_file ) = @_ ;
$ Slic3r::GUI::SkeinPanel:: last_input_file = $input_file ;
2012-04-30 21:49:44 +02:00
my $process_dialog = Wx::ProgressDialog -> new ( 'Loading...' , "Processing input file..." , 100 , $self , 0 );
$process_dialog -> Pulse ;
2012-04-30 14:56:01 +02:00
local $SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $self );
2012-05-01 00:30:46 +02:00
$self -> { print } -> add_object_from_file ( $input_file );
2012-04-30 14:56:01 +02:00
my $obj_idx = $# { $self -> { print } -> objects };
2012-04-30 21:49:44 +02:00
$process_dialog -> Destroy ;
2012-04-30 14:56:01 +02:00
2012-05-01 00:30:46 +02:00
$self -> object_loaded ( $obj_idx );
}
sub object_loaded {
my $self = shift ;
my ( $obj_idx , %params ) = @_ ;
my $object = $self -> { print } -> objects -> [ $obj_idx ];
$self -> { list } -> InsertStringItem ( $obj_idx , basename ( $object -> input_file ));
2012-04-30 14:56:01 +02:00
$self -> { list } -> SetItem ( $obj_idx , 1 , "1" );
$self -> { list } -> SetItem ( $obj_idx , 2 , "100%" );
push @ { $self -> { scale }}, 1 ;
$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 ;
foreach my $pobj ( @ { $self -> { selected_objects }}) {
my ( $obj_idx , $copy_idx ) = ( $pobj -> [ 0 ], $pobj -> [ 1 ]);
$self -> { print } -> copies -> [ $obj_idx ][ $copy_idx ] = undef ;
}
my @objects_to_remove = ();
for my $obj_idx ( 0 .. $# { $self -> { print } -> objects }) {
my $copies = $self -> { print } -> copies -> [ $obj_idx ];
# filter out removed copies
@$copies = grep defined $_ , @$copies ;
# update copies count in list
$self -> { list } -> SetItem ( $obj_idx , 1 , scalar @$copies );
# if no copies are left, remove the object itself
push @objects_to_remove , $obj_idx if ! @$copies ;
}
for my $obj_idx ( sort { $b <=> $a } @objects_to_remove ) {
splice @ { $self -> { print } -> objects }, $obj_idx , 1 ;
splice @ { $self -> { print } -> copies }, $obj_idx , 1 ;
splice @ { $self -> { thumbnails }}, $obj_idx , 1 ;
splice @ { $self -> { scale }}, $obj_idx , 1 ;
$self -> { list } -> DeleteItem ( $obj_idx );
}
$self -> { selected_objects } = [] ;
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
sub reset {
my $self = shift ;
@ { $self -> { print } -> objects } = ();
@ { $self -> { print } -> copies } = ();
@ { $self -> { thumbnails }} = ();
@ { $self -> { scale }} = ();
$self -> { list } -> DeleteAllItems ;
$self -> { selected_objects } = [] ;
$self -> selection_changed ( 0 );
$self -> object_list_changed ;
$self -> { canvas } -> Refresh ;
}
sub increase {
my $self = shift ;
my $obj_idx = $self -> selected_object_idx ;
my $copies = $self -> { print } -> copies -> [ $obj_idx ];
push @$copies , [ $copies -> [ - 1 ] -> [ X ] + scale 10 , $copies -> [ - 1 ] -> [ Y ] + scale 10 ];
$self -> { list } -> SetItem ( $obj_idx , 1 , scalar @$copies );
$self -> arrange ;
}
sub decrease {
my $self = shift ;
my $obj_idx = $self -> selected_object_idx ;
$self -> { selected_objects } = [ + ( grep { $_ -> [ 0 ] == $obj_idx } @ { $self -> { object_previews }})[ - 1 ] ];
$self -> remove ;
if ( $self -> { print } -> objects -> [ $obj_idx ]) {
$self -> { list } -> Select ( $obj_idx , 0 );
$self -> { list } -> Select ( $obj_idx , 1 );
}
}
sub rotate {
my $self = shift ;
my ( $angle ) = @_ ;
my $obj_idx = $self -> selected_object_idx ;
my $object = $self -> { print } -> objects -> [ $obj_idx ];
2012-04-30 22:14:27 +02:00
if ( ! defined $angle ) {
$angle = Wx:: GetNumberFromUser ( "" , "Enter the rotation angle:" , "Rotate" , 0 , - 364 , 364 , $self );
return if ! $angle || $angle == - 1 ;
}
2012-04-30 14:56:01 +02:00
# rotate, realign to 0,0 and update size
$object -> mesh -> rotate ( $angle );
$object -> mesh -> align_to_origin ;
my @size = $object -> mesh -> size ;
$object -> x_length ( $size [ X ]);
$object -> y_length ( $size [ Y ]);
$self -> make_thumbnail ( $obj_idx );
$self -> arrange ;
}
sub arrange {
my $self = shift ;
2012-04-30 17:25:08 +02:00
eval {
$self -> { print } -> arrange_objects ;
};
Slic3r::GUI:: warning_catcher ( $self ) -> ( $@ ) if $@ ;
2012-04-30 14:56:01 +02:00
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
sub changescale {
my $self = shift ;
my $obj_idx = $self -> selected_object_idx ;
my $scale = $self -> { scale }[ $obj_idx ];
$scale = Wx:: GetNumberFromUser ( "" , "Enter the scale % for the selected object:" , "Scale" , $scale * 100 , 0 , 1000 , $self );
return if ! $scale || $scale == - 1 ;
my $object = $self -> { print } -> objects -> [ $obj_idx ];
my $mesh = $object -> mesh ;
$mesh -> scale ( $scale /100 / $self -> { scale }[ $obj_idx ]);
$object -> mesh -> align_to_origin ;
my @size = $object -> mesh -> size ;
$object -> x_length ( $size [ X ]);
$object -> y_length ( $size [ Y ]);
$self -> { scale }[ $obj_idx ] = $scale / 100 ;
$self -> { list } -> SetItem ( $obj_idx , 2 , "$scale%" );
$self -> make_thumbnail ( $obj_idx );
2012-04-30 17:25:08 +02:00
$self -> arrange ;
2012-04-30 14:56:01 +02:00
}
2012-05-01 00:30:46 +02:00
sub split_object {
my $self = shift ;
my $obj_idx = $self -> selected_object_idx ;
my $current_object = $self -> { print } -> objects -> [ $obj_idx ];
my $mesh = $current_object -> mesh -> clone ;
$mesh -> scale ( $ Slic3r:: scaling_factor );
foreach my $mesh ( $mesh -> split_mesh ) {
my $object = $self -> { print } -> add_object_from_mesh ( $mesh );
$object -> input_file ( $current_object -> input_file );
$self -> object_loaded ( $# { $self -> { print } -> objects }, no_arrange => 1 );
}
$self -> { list } -> Select ( $obj_idx , 1 );
$self -> remove ;
$self -> arrange ;
}
2012-04-30 14:56:01 +02:00
sub export_gcode {
my $self = shift ;
my $process_dialog ;
eval {
# validate configuration
Slic3r::Config -> validate ;
my $print = $self -> { print };
# select output file
my $output_file = $ main:: opt { output };
{
$output_file = $print -> expanded_output_filepath ( $output_file );
my $dlg = Wx::FileDialog -> new ( $self , 'Save G-code file as:' , dirname ( $output_file ),
basename ( $output_file ), $ Slic3r::GUI::SkeinPanel:: gcode_wildcard , wxFD_SAVE );
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return ;
}
$output_file = $ Slic3r::GUI::SkeinPanel:: last_output_file = $dlg -> GetPath ;
$dlg -> Destroy ;
}
# show processbar dialog
$process_dialog = Wx::ProgressDialog -> new ( 'Slicing...' , "Processing input file..." ,
100 , $self , 0 );
$process_dialog -> Pulse ;
{
my @warnings = ();
local $SIG { __WARN__ } = sub { push @warnings , $_ [ 0 ] };
my %params = (
output_file => $output_file ,
status_cb => sub {
my ( $percent , $message ) = @_ ;
if ( & Wx:: wxVERSION_STRING =~ / 2\.(8\.|9\.[2-9])/ ) {
$process_dialog -> Update ( $percent , "$message..." );
}
},
keep_meshes => 1 ,
);
if ( $params { export_svg }) {
$print -> export_svg ( %params );
} else {
$print -> export_gcode ( %params );
}
Slic3r::GUI:: warning_catcher ( $self ) -> ( $_ ) for @warnings ;
}
$process_dialog -> Destroy ;
undef $process_dialog ;
my $message = "Your files were successfully sliced" ;
$message .= sprintf " in %d minutes and %.3f seconds" ,
int ( $print -> processing_time / 60 ),
$print -> processing_time - int ( $print -> processing_time / 60 ) * 60
if $print -> processing_time ;
$message .= "." ;
eval {
$self -> { growler } -> notify ( Event => 'SKEIN_DONE' , Title => 'Slicing Done!' , Message => $message )
if ( $self -> { growler });
};
Wx::MessageDialog -> new ( $self , $message , 'Done!' ,
wxOK | wxICON_INFORMATION ) -> ShowModal ;
};
Slic3r::GUI:: catch_error ( $self , sub { $process_dialog -> Destroy if $process_dialog });
}
2012-04-30 17:10:54 +02:00
sub export_stl {
my $self = shift ;
my $print = $self -> { print };
# select output file
my $output_file = $ main:: opt { output };
{
$output_file = $print -> expanded_output_filepath ( $output_file );
$output_file =~ s/\.gcode$/.stl/i ;
my $dlg = Wx::FileDialog -> new ( $self , 'Save STL file as:' , dirname ( $output_file ),
basename ( $output_file ), $ Slic3r::GUI::SkeinPanel:: model_wildcard , wxFD_SAVE );
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return ;
}
$output_file = $ Slic3r::GUI::SkeinPanel:: last_output_file = $dlg -> GetPath ;
$dlg -> Destroy ;
}
my $mesh = Slic3r::TriangleMesh -> new ( facets => [] , vertices => [] );
for my $obj_idx ( 0 .. $# { $print -> objects }) {
for my $copy ( @ { $print -> copies -> [ $obj_idx ]}) {
my $cloned_mesh = $print -> objects -> [ $obj_idx ] -> mesh -> clone ;
$cloned_mesh -> move ( @$copy );
my $vertices_offset = scalar @ { $mesh -> vertices };
push @ { $mesh -> vertices }, @ { $cloned_mesh -> vertices };
push @ { $mesh -> facets }, map [ $_ -> [ 0 ], map $vertices_offset + $_ , @$_ [ 1 , 2 , 3 ] ], @ { $cloned_mesh -> facets };
}
}
$mesh -> scale ( $ Slic3r:: scaling_factor );
$mesh -> align_to_origin ;
Slic3r::Format::STL -> write_file ( $output_file , $mesh , 1 );
}
2012-04-30 14:56:01 +02:00
sub make_thumbnail {
my $self = shift ;
my ( $obj_idx ) = @_ ;
2012-04-30 20:59:14 +02:00
my $cb = sub {
my $object = $self -> { print } -> objects -> [ $obj_idx ];
my @points = map [ @$_ [ X , Y ] ], @ { $object -> mesh -> vertices };
my $convex_hull = Slic3r::Polygon -> new ( convex_hull ( \ @points ));
for ( @$convex_hull ) {
@$_ = map $self -> to_pixel ( $_ ), @$_ ;
}
$convex_hull -> simplify ( 0.3 );
$self -> { thumbnails } -> [ $obj_idx ] = $convex_hull ; # ignored in multithread environment
2012-04-30 22:34:41 +02:00
if ( $have_threads ) {
Wx:: PostEvent ( $self , Wx::PlThreadEvent -> new ( - 1 , $THUMBNAIL_DONE_EVENT , shared_clone ([ $obj_idx , $convex_hull ])));
threads -> exit ;
} else {
$self -> make_thumbnail2 ;
}
2012-04-30 20:59:14 +02:00
};
$have_threads ? threads -> create ( $cb ) : $cb -> ();
2012-04-30 14:56:01 +02:00
}
2012-04-30 22:34:41 +02:00
sub make_thumbnail2 {
my $self = shift ;
$self -> recenter ;
$self -> { canvas } -> Refresh ;
}
2012-04-30 14:56:01 +02:00
sub recenter {
my $self = shift ;
# calculate displacement needed to center the print
my @print_bb = $self -> { print } -> bounding_box ;
@print_bb = ( 0 , 0 , 0 , 0 ) if ! defined $print_bb [ 0 ];
$self -> { shift } = [
( $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 22:34:41 +02:00
sub update_bed_size {
my $self = shift ;
# supposing the preview canvas is square, calculate the scaling factor
# to constrain print bed area inside preview
my $canvas_side = $self -> { canvas } -> GetSize -> GetWidth ;
my $bed_largest_side = $ Slic3r:: bed_size -> [ X ] > $ Slic3r:: bed_size -> [ Y ]
? $ Slic3r:: bed_size -> [ Y ] : $ Slic3r:: bed_size -> [ X ];
my $old_scaling_factor = $self -> { scaling_factor };
$self -> { scaling_factor } = $canvas_side / $bed_largest_side ;
if ( defined $old_scaling_factor && $self -> { scaling_factor } != $old_scaling_factor ) {
$self -> make_thumbnail ( $_ ) for 0 .. $# { $self -> { thumbnails }};
}
}
2012-04-30 14:56:01 +02:00
sub repaint {
my ( $self , $event ) = @_ ;
my $parent = $self -> GetParent ;
my $print = $parent -> { print };
my $dc = Wx::PaintDC -> new ( $self );
my $size = $self -> GetSize ;
my @size = ( $size -> GetWidth , $size -> GetHeight );
2012-04-30 22:34:41 +02:00
# calculate scaling factor for preview
$parent -> update_bed_size ;
2012-04-30 14:56:01 +02:00
# 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 );
}
# draw frame
$dc -> SetPen ( Wx:: wxBLACK_PEN );
$dc -> SetBrush ( $parent -> { transparent_brush });
$dc -> DrawRectangle ( 0 , 0 , @size );
# draw text if plate is empty
if ( ! @ { $print -> objects }) {
$dc -> SetTextForeground ( Wx::Colour -> new ( 150 , 50 , 50 ));
$dc -> DrawLabel ( "Drag your objects here" , Wx::Rect -> new ( 0 , 0 , $self -> GetSize -> GetWidth , $self -> GetSize -> GetHeight ), & Wx:: wxALIGN_CENTER_HORIZONTAL | & Wx:: wxALIGN_CENTER_VERTICAL );
}
# draw thumbnails
$dc -> SetPen ( Wx:: wxBLACK_PEN );
@ { $parent -> { object_previews }} = ();
for my $obj_idx ( 0 .. $# { $print -> objects }) {
2012-04-30 17:55:32 +02:00
next unless $parent -> { thumbnails }[ $obj_idx ];
2012-04-30 14:56:01 +02:00
for my $copy_idx ( 0 .. $# { $print -> copies -> [ $obj_idx ]}) {
my $copy = $print -> copies -> [ $obj_idx ][ $copy_idx ];
push @ { $parent -> { object_previews }}, [ $obj_idx , $copy_idx , $parent -> { thumbnails }[ $obj_idx ] -> clone ];
$parent -> { object_previews } -> [ - 1 ][ 2 ] -> translate ( map $parent -> to_pixel ( $copy -> [ $_ ]) + $parent -> { shift }[ $_ ], ( X , Y ));
if ( grep { $_ -> [ 0 ] == $obj_idx } @ { $parent -> { selected_objects }}) {
$dc -> SetBrush ( $parent -> { selected_brush });
} else {
$dc -> SetBrush ( $parent -> { objects_brush });
}
$dc -> DrawPolygon ( $parent -> _y ( $parent -> { object_previews } -> [ - 1 ][ 2 ]), 0 , 0 );
}
}
# draw skirt
if ( @ { $parent -> { object_previews }} && $ Slic3r:: skirts ) {
my $convex_hull = Slic3r::Polygon -> new ( convex_hull ([ map @ { $_ -> [ 2 ]}, @ { $parent -> { object_previews }} ]));
2012-04-30 18:54:36 +02:00
$convex_hull = + ( $convex_hull -> offset ( $ Slic3r:: skirt_distance * $parent -> { scaling_factor }, 1 , JT_ROUND ))[ 0 ];
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 $print = $parent -> { print };
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 }}) {
if ( $preview -> [ 2 ] -> encloses_point ( $pos )) {
$parent -> { selected_objects } = [ $preview ];
$parent -> { list } -> Select ( $preview -> [ 0 ], 1 );
$parent -> selection_changed ( 1 );
my $copy = $print -> copies -> [ $preview -> [ 0 ] ] -> [ $preview -> [ 1 ] ];
$self -> { drag_start_pos } = [ map $pos -> [ $_ ] - $parent -> { shift }[ $_ ] - $parent -> to_pixel ( $copy -> [ $_ ]), X , Y ]; # displacement between the click and the copy's origin
$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 ;
} elsif ( $event -> Dragging ) {
return if ! $self -> { drag_start_pos }; # concurrency problems
for my $obj ( $self -> { drag_object }) {
my $copy = $print -> copies -> [ $obj -> [ 0 ] ] -> [ $obj -> [ 1 ] ];
$copy -> [ $_ ] = $parent -> to_scaled ( $pos -> [ $_ ] - $self -> { drag_start_pos }[ $_ ] - $parent -> { shift }[ $_ ]) for X , Y ;
$parent -> Refresh ;
}
}
}
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 );
}
sub object_list_changed {
my $self = shift ;
my $method = $self -> { print } && @ { $self -> { print } -> objects } ? 'Enable' : 'Disable' ;
2012-05-01 00:30:46 +02:00
$self -> { "btn_$_" } -> $method
for 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
for qw(remove increase decrease rotate45cw rotate45ccw rotate changescale split) ;
2012-04-30 14:56:01 +02:00
}
sub selected_object_idx {
my $self = shift ;
2012-04-30 23:24:01 +02:00
return $self -> { selected_objects }[ 0 ] ? $self -> { selected_objects }[ 0 ][ 0 ] : $self -> { list } -> GetFirstSelected ;
2012-04-30 14:56:01 +02:00
}
sub to_pixel {
my $self = shift ;
return unscale $_ [ 0 ] * $self -> { scaling_factor };
}
sub to_scaled {
my $self = shift ;
return scale $_ [ 0 ] / $self -> { scaling_factor };
}
sub _y {
my $self = shift ;
my ( $points ) = @_ ;
my $height = $self -> { canvas } -> GetSize -> GetHeight ;
return [ map [ $_ -> [ X ], $height - $_ -> [ Y ] ], @$points ];
}
package Slic3r::GUI::Dashboard::DropTarget ;
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 ) = @_ ;
# only accept STL and AMF files
return 0 if grep ! /\.(?:stl|amf(?:\.xml)?)$/i , @$filenames ;
$self -> { window } -> load_file ( $_ ) for @$filenames ;
}
1 ;