2012-05-04 08:15:33 +00:00
package Slic3r::GUI::Plater ;
2012-04-30 12:56:01 +00:00
use strict ;
use warnings ;
use utf8 ;
use File::Basename qw( basename dirname ) ;
2012-10-24 18:24:11 +00:00
use List::Util qw( max sum first ) ;
2013-08-26 23:26:44 +00:00
use Slic3r::Geometry::Clipper qw( offset JT_ROUND ) ;
2013-11-24 21:42:52 +00:00
use Slic3r::Geometry qw( X Y Z MIN MAX convex_hull scale unscale ) ;
2012-05-19 19:13:10 +00:00
use threads::shared qw( shared_clone ) ;
2012-07-24 22:15:32 +00:00
use Wx qw( :bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window ) ;
2012-10-24 20:44:08 +00: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 12:56:01 +00:00
use base 'Wx::Panel' ;
2013-08-25 10:22:05 +00:00
use constant TB_LOAD = > & Wx:: NewId ;
use constant TB_REMOVE = > & Wx:: NewId ;
use constant TB_RESET = > & Wx:: NewId ;
use constant TB_ARRANGE = > & Wx:: NewId ;
use constant TB_EXPORT_GCODE = > & Wx:: NewId ;
use constant TB_EXPORT_STL = > & Wx:: NewId ;
2012-08-02 19:11:36 +00:00
use constant TB_MORE = > & Wx:: NewId ;
2013-08-25 13:45:22 +00:00
use constant TB_FEWER = > & Wx:: NewId ;
2012-08-02 19:11:36 +00:00
use constant TB_45CW = > & Wx:: NewId ;
use constant TB_45CCW = > & Wx:: NewId ;
use constant TB_ROTATE = > & Wx:: NewId ;
use constant TB_SCALE = > & Wx:: NewId ;
use constant TB_SPLIT = > & Wx:: NewId ;
2013-08-25 16:01:59 +00:00
use constant TB_VIEW = > & Wx:: NewId ;
use constant TB_SETTINGS = > & Wx:: NewId ;
2012-05-04 09:22:56 +00:00
2013-10-16 13:13:39 +00:00
# package variables to avoid passing lexicals to threads
our $ THUMBNAIL_DONE_EVENT : shared = Wx:: NewEventType ;
our $ PROGRESS_BAR_EVENT : shared = Wx:: NewEventType ;
our $ MESSAGE_DIALOG_EVENT : shared = Wx:: NewEventType ;
our $ EXPORT_COMPLETED_EVENT : shared = Wx:: NewEventType ;
our $ EXPORT_FAILED_EVENT : shared = Wx:: NewEventType ;
2012-04-30 18:59:14 +00:00
2013-08-25 10:22:05 +00:00
use constant CANVAS_SIZE = > [ 335 , 335 ] ;
2012-07-27 19:13:03 +00: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 16:44:47 +00:00
use constant FILAMENT_CHOOSERS_SPACING = > 3 ;
2012-07-27 19:13:03 +00:00
2012-04-30 12:56:01 +00:00
sub new {
my $ class = shift ;
my ( $ parent ) = @ _ ;
2012-07-24 12:28:21 +00:00
my $ self = $ class - > SUPER:: new ( $ parent , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL ) ;
2012-07-27 19:13:03 +00:00
$ self - > { config } = Slic3r::Config - > new_from_defaults ( qw(
2012-08-07 18:14:28 +00:00
bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance
2012-07-27 19:13:03 +00:00
) ) ;
2012-09-12 14:30:44 +00:00
$ self - > { objects } = [] ;
$ self - > { selected_objects } = [] ;
2012-04-30 12:56:01 +00:00
2012-08-06 17:18:31 +00:00
$ self - > { canvas } = Wx::Panel - > new ( $ self , - 1 , wxDefaultPosition , CANVAS_SIZE , wxTAB_TRAVERSAL ) ;
2012-04-30 12:56:01 +00:00
$ self - > { canvas } - > SetBackgroundColour ( Wx:: wxWHITE ) ;
EVT_PAINT ( $ self - > { canvas } , \ & repaint ) ;
EVT_MOUSE_EVENTS ( $ self - > { canvas } , \ & mouse_event ) ;
2012-07-01 17:24:06 +00: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 08:06:45 +00:00
$ self - > { dragged_brush } = Wx::Brush - > new ( Wx::Colour - > new ( 128 , 128 , 255 ) , wxSOLID ) ;
2012-07-01 17:24:06 +00: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-05-04 09:22:56 +00:00
# toolbar for object manipulation
2012-05-20 14:24:10 +00:00
if ( ! & Wx:: wxMSW ) {
2012-05-04 10:56:15 +00:00
Wx::ToolTip:: Enable ( 1 ) ;
2012-07-24 12:28:21 +00:00
$ self - > { htoolbar } = Wx::ToolBar - > new ( $ self , - 1 , wxDefaultPosition , wxDefaultSize , wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL ) ;
2013-08-25 10:22:05 +00:00
$ self - > { htoolbar } - > AddTool ( TB_LOAD , "Add…" , Wx::Bitmap - > new ( "$Slic3r::var/brick_add.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_REMOVE , "Delete" , Wx::Bitmap - > new ( "$Slic3r::var/brick_delete.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_RESET , "Delete All" , Wx::Bitmap - > new ( "$Slic3r::var/cross.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 14:10:53 +00:00
$ self - > { htoolbar } - > AddTool ( TB_ARRANGE , "Arrange" , Wx::Bitmap - > new ( "$Slic3r::var/bricks.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 10:22:05 +00:00
$ self - > { htoolbar } - > AddSeparator ;
2012-07-01 17:24:06 +00:00
$ self - > { htoolbar } - > AddTool ( TB_MORE , "More" , Wx::Bitmap - > new ( "$Slic3r::var/add.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 13:45:22 +00:00
$ self - > { htoolbar } - > AddTool ( TB_FEWER , "Fewer" , Wx::Bitmap - > new ( "$Slic3r::var/delete.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2012-05-04 09:22:56 +00:00
$ self - > { htoolbar } - > AddSeparator ;
2012-07-01 17:24:06 +00:00
$ self - > { htoolbar } - > AddTool ( TB_45CCW , "45° ccw" , Wx::Bitmap - > new ( "$Slic3r::var/arrow_rotate_anticlockwise.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_45CW , "45° cw" , Wx::Bitmap - > new ( "$Slic3r::var/arrow_rotate_clockwise.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2012-07-01 21:29:56 +00:00
$ self - > { htoolbar } - > AddTool ( TB_ROTATE , "Rotate…" , Wx::Bitmap - > new ( "$Slic3r::var/arrow_rotate_clockwise.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_SCALE , "Scale…" , Wx::Bitmap - > new ( "$Slic3r::var/arrow_out.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2012-07-01 17:24:06 +00:00
$ self - > { htoolbar } - > AddTool ( TB_SPLIT , "Split" , Wx::Bitmap - > new ( "$Slic3r::var/shape_ungroup.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2013-08-25 16:01:59 +00:00
$ self - > { htoolbar } - > AddSeparator ;
$ self - > { htoolbar } - > AddTool ( TB_VIEW , "View" , Wx::Bitmap - > new ( "$Slic3r::var/package.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
$ self - > { htoolbar } - > AddTool ( TB_SETTINGS , "Settings…" , Wx::Bitmap - > new ( "$Slic3r::var/cog.png" , wxBITMAP_TYPE_PNG ) , '' ) ;
2012-05-20 14:24:10 +00:00
} else {
2013-08-25 14:10:53 +00:00
my % tbar_buttons = (
load = > "Add…" ,
remove = > "Delete" ,
reset = > "Delete All" ,
arrange = > "Arrange" ,
increase = > "" ,
decrease = > "" ,
rotate45ccw = > "" ,
rotate45cw = > "" ,
rotate = > "Rotate…" ,
changescale = > "Scale…" ,
split = > "Split" ,
2013-08-25 16:01:59 +00:00
view = > "View" ,
settings = > "Settings…" ,
2013-08-25 14:10:53 +00:00
) ;
2012-05-20 14:24:10 +00:00
$ self - > { btoolbar } = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
2013-08-25 16:01:59 +00:00
for ( qw( load remove reset arrange increase decrease rotate45ccw rotate45cw rotate changescale split view settings ) ) {
2012-07-02 23:20:30 +00:00
$ self - > { "btn_$_" } = Wx::Button - > new ( $ self , - 1 , $ tbar_buttons { $ _ } , wxDefaultPosition , wxDefaultSize , wxBU_EXACTFIT ) ;
2012-05-20 14:24:10 +00:00
$ self - > { btoolbar } - > Add ( $ self - > { "btn_$_" } ) ;
}
2012-05-04 09:22:56 +00:00
}
2012-07-24 12:42:38 +00:00
2013-08-25 10:22:05 +00:00
$ self - > { list } = Wx::ListView - > new ( $ self , - 1 , wxDefaultPosition , wxDefaultSize , wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS ) ;
$ self - > { list } - > InsertColumn ( 0 , "Name" , wxLIST_FORMAT_LEFT , 145 ) ;
$ self - > { list } - > InsertColumn ( 1 , "Copies" , wxLIST_FORMAT_CENTER , 45 ) ;
2012-07-24 12:42:38 +00:00
$ self - > { list } - > InsertColumn ( 2 , "Scale" , wxLIST_FORMAT_CENTER , wxLIST_AUTOSIZE_USEHEADER ) ;
EVT_LIST_ITEM_SELECTED ( $ self , $ self - > { list } , \ & list_item_selected ) ;
EVT_LIST_ITEM_DESELECTED ( $ self , $ self - > { list } , \ & list_item_deselected ) ;
2012-10-24 20:44:08 +00:00
EVT_LIST_ITEM_ACTIVATED ( $ self , $ self - > { list } , \ & list_item_activated ) ;
2012-07-24 12:42:38 +00:00
EVT_KEY_DOWN ( $ self - > { list } , sub {
my ( $ list , $ event ) = @ _ ;
if ( $ event - > GetKeyCode == WXK_TAB ) {
$ list - > Navigate ( $ event - > ShiftDown ? & Wx:: wxNavigateBackward : & Wx:: wxNavigateForward ) ;
} else {
$ event - > Skip ;
}
} ) ;
2012-05-04 09:22:56 +00:00
2013-08-25 10:22:05 +00:00
# right pane buttons
2012-07-02 23:20:30 +00:00
$ 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 ) ;
2013-08-25 10:22:05 +00:00
$ self - > { btn_export_gcode } - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
$ self - > { btn_export_stl } - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2012-05-04 09:22:56 +00:00
2013-08-25 16:01:59 +00:00
if ( $ Slic3r:: GUI:: have_button_icons ) {
2012-05-04 09:22:56 +00:00
my % icons = qw(
load brick_add . png
remove brick_delete . png
reset cross . png
arrange bricks . png
2012-05-19 13:02:23 +00:00
export_gcode cog_go . png
2012-05-04 09:22:56 +00:00
export_stl brick_go . png
increase add . png
decrease delete . png
rotate45cw arrow_rotate_clockwise . png
rotate45ccw arrow_rotate_anticlockwise . png
rotate arrow_rotate_clockwise . png
changescale arrow_out . png
split shape_ungroup . png
2013-08-25 16:08:56 +00:00
view package . png
settings cog . png
2012-05-04 09:22:56 +00:00
) ;
2012-05-20 14:24:10 +00:00
for ( grep $ self - > { "btn_$_" } , keys % icons ) {
2012-07-01 17:24:06 +00:00
$ self - > { "btn_$_" } - > SetBitmap ( Wx::Bitmap - > new ( "$Slic3r::var/$icons{$_}" , wxBITMAP_TYPE_PNG ) ) ;
2012-05-20 14:24:10 +00:00
}
2012-05-01 14:49:34 +00:00
}
2012-04-30 12:56:01 +00:00
$ self - > selection_changed ( 0 ) ;
$ self - > object_list_changed ;
EVT_BUTTON ( $ self , $ self - > { btn_export_gcode } , \ & export_gcode ) ;
2012-04-30 15:10:54 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_export_stl } , \ & export_stl ) ;
2012-04-30 12:56:01 +00:00
2012-05-04 09:22:56 +00:00
if ( $ self - > { htoolbar } ) {
2013-08-25 10:22:05 +00:00
EVT_TOOL ( $ self , TB_LOAD , \ & load ) ;
EVT_TOOL ( $ self , TB_REMOVE , sub { $ self - > remove ( ) } ) ; # explicitly pass no argument to remove
EVT_TOOL ( $ self , TB_RESET , \ & reset ) ;
EVT_TOOL ( $ self , TB_ARRANGE , \ & arrange ) ;
2012-05-04 09:22:56 +00:00
EVT_TOOL ( $ self , TB_MORE , \ & increase ) ;
2013-08-25 13:45:22 +00:00
EVT_TOOL ( $ self , TB_FEWER , \ & decrease ) ;
2012-05-04 09:22:56 +00:00
EVT_TOOL ( $ self , TB_45CW , sub { $ _ [ 0 ] - > rotate ( - 45 ) } ) ;
EVT_TOOL ( $ self , TB_45CCW , sub { $ _ [ 0 ] - > rotate ( 45 ) } ) ;
2012-05-04 10:56:15 +00:00
EVT_TOOL ( $ self , TB_ROTATE , sub { $ _ [ 0 ] - > rotate ( undef ) } ) ;
EVT_TOOL ( $ self , TB_SCALE , \ & changescale ) ;
2012-05-04 09:22:56 +00:00
EVT_TOOL ( $ self , TB_SPLIT , \ & split_object ) ;
2013-08-25 16:01:59 +00:00
EVT_TOOL ( $ self , TB_VIEW , sub { $ _ [ 0 ] - > object_preview_dialog } ) ;
EVT_TOOL ( $ self , TB_SETTINGS , sub { $ _ [ 0 ] - > object_settings_dialog } ) ;
2012-05-04 09:22:56 +00:00
} else {
2013-08-25 14:10:53 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_load } , \ & load ) ;
EVT_BUTTON ( $ self , $ self - > { btn_remove } , sub { $ self - > remove ( ) } ) ; # explicitly pass no argument to remove
EVT_BUTTON ( $ self , $ self - > { btn_reset } , \ & reset ) ;
EVT_BUTTON ( $ self , $ self - > { btn_arrange } , \ & arrange ) ;
2012-05-04 09:22:56 +00:00
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 ) ;
2013-08-25 16:01:59 +00:00
EVT_BUTTON ( $ self , $ self - > { btn_view } , sub { $ _ [ 0 ] - > object_preview_dialog } ) ;
EVT_BUTTON ( $ self , $ self - > { btn_settings } , sub { $ _ [ 0 ] - > object_settings_dialog } ) ;
2012-05-04 09:22:56 +00:00
}
2012-05-04 08:15:33 +00:00
$ _ - > SetDropTarget ( Slic3r::GUI::Plater::DropTarget - > new ( $ self ) )
2012-04-30 15:10:54 +00:00
for $ self , $ self - > { canvas } , $ self - > { list } ;
2012-04-30 12:56:01 +00:00
2012-04-30 18:59:14 +00:00
EVT_COMMAND ( $ self , - 1 , $ THUMBNAIL_DONE_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
2013-11-12 13:30:13 +00:00
my ( $ obj_idx ) = @ { $ event - > GetData } ;
2013-06-07 11:54:40 +00:00
return if ! $ self - > { objects } [ $ obj_idx ] ; # object was deleted before thumbnail generation completed
2013-07-13 22:38:01 +00:00
2012-10-01 16:12:14 +00:00
$ self - > on_thumbnail_made ( $ obj_idx ) ;
2012-04-30 18:59:14 +00:00
} ) ;
2012-05-05 19:08:15 +00:00
EVT_COMMAND ( $ self , - 1 , $ PROGRESS_BAR_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
my ( $ percent , $ message ) = @ { $ event - > GetData } ;
$ self - > statusbar - > SetProgress ( $ percent ) ;
2012-07-01 21:29:56 +00:00
$ self - > statusbar - > SetStatusText ( "$message…" ) ;
2012-05-05 19:08:15 +00: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 19:13:10 +00:00
EVT_COMMAND ( $ self , - 1 , $ EXPORT_FAILED_EVENT , sub {
my ( $ self , $ event ) = @ _ ;
$ self - > on_export_failed ;
} ) ;
2012-07-27 19:13:03 +00:00
$ self - > _update_bed_size ;
2012-04-30 12:56:01 +00:00
$ self - > recenter ;
{
2013-08-25 10:22:05 +00:00
my $ presets ;
2013-01-03 14:49:20 +00:00
if ( $ self - > skeinpanel - > { mode } eq 'expert' ) {
2013-08-25 10:22:05 +00:00
$ presets = Wx::BoxSizer - > new ( wxVERTICAL ) ;
2013-01-03 14:49:20 +00:00
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 ) ;
2013-08-25 10:22:05 +00:00
$ text - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
my $ choice = Wx::Choice - > new ( $ self , - 1 , wxDefaultPosition , [ 140 , - 1 ] , [] ) ;
$ choice - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2013-01-03 14:49:20 +00:00
$ 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 ) ;
2013-08-25 10:22:05 +00:00
$ presets - > Add ( $ text , 0 , wxALIGN_LEFT | wxRIGHT , 4 ) ;
$ presets - > Add ( $ self - > { preset_choosers_sizers } { $ group } , 0 , wxALIGN_CENTER_VERTICAL | wxBOTTOM , 8 ) ;
}
}
my $ object_info_sizer ;
{
my $ box = Wx::StaticBox - > new ( $ self , - 1 , "Info" ) ;
$ object_info_sizer = Wx::StaticBoxSizer - > new ( $ box , wxVERTICAL ) ;
2013-08-25 15:26:55 +00:00
my $ grid_sizer = Wx::FlexGridSizer - > new ( 3 , 4 , 5 , 5 ) ;
$ grid_sizer - > SetFlexibleDirection ( wxHORIZONTAL ) ;
$ grid_sizer - > AddGrowableCol ( 1 , 1 ) ;
$ grid_sizer - > AddGrowableCol ( 3 , 1 ) ;
$ object_info_sizer - > Add ( $ grid_sizer , 0 , wxEXPAND ) ;
2013-08-25 10:22:05 +00:00
my @ info = (
2013-08-25 15:26:55 +00:00
size = > "Size" ,
volume = > "Volume" ,
facets = > "Facets" ,
materials = > "Materials" ,
manifold = > "Manifold" ,
2013-08-25 10:22:05 +00:00
) ;
while ( my $ field = shift @ info ) {
my $ label = shift @ info ;
my $ text = Wx::StaticText - > new ( $ self , - 1 , "$label:" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT ) ;
$ text - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
$ grid_sizer - > Add ( $ text , 0 ) ;
$ self - > { "object_info_$field" } = Wx::StaticText - > new ( $ self , - 1 , "" , wxDefaultPosition , wxDefaultSize , wxALIGN_LEFT ) ;
$ self - > { "object_info_$field" } - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2013-08-25 15:26:55 +00:00
if ( $ field eq 'manifold' ) {
$ self - > { object_info_manifold_warning_icon } = Wx::StaticBitmap - > new ( $ self , - 1 , Wx::Bitmap - > new ( "$Slic3r::var/error.png" , wxBITMAP_TYPE_PNG ) ) ;
$ self - > { object_info_manifold_warning_icon } - > Hide ;
my $ h_sizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
$ h_sizer - > Add ( $ self - > { object_info_manifold_warning_icon } , 0 ) ;
$ h_sizer - > Add ( $ self - > { "object_info_$field" } , 0 ) ;
$ grid_sizer - > Add ( $ h_sizer , 0 , wxEXPAND ) ;
} else {
$ grid_sizer - > Add ( $ self - > { "object_info_$field" } , 0 ) ;
}
2013-01-03 14:49:20 +00:00
}
}
2012-06-19 15:23:10 +00:00
2013-08-25 10:22:05 +00:00
my $ right_buttons_sizer = Wx::BoxSizer - > new ( wxVERTICAL ) ;
$ right_buttons_sizer - > Add ( $ presets , 0 , wxEXPAND , 0 ) if defined $ presets ;
$ right_buttons_sizer - > Add ( $ self - > { btn_export_gcode } , 0 , wxEXPAND | wxTOP , 8 ) ;
$ right_buttons_sizer - > Add ( $ self - > { btn_export_stl } , 0 , wxEXPAND | wxTOP , 2 ) ;
my $ right_top_sizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
$ right_top_sizer - > Add ( $ self - > { list } , 1 , wxEXPAND | wxLEFT , 5 ) ;
$ right_top_sizer - > Add ( $ right_buttons_sizer , 0 , wxEXPAND | wxALL , 10 ) ;
my $ right_sizer = Wx::BoxSizer - > new ( wxVERTICAL ) ;
$ right_sizer - > Add ( $ right_top_sizer , 1 , wxEXPAND | wxBOTTOM , 10 ) ;
$ right_sizer - > Add ( $ object_info_sizer , 0 , wxEXPAND | wxLEFT | wxRIGHT , 5 ) ;
my $ hsizer = Wx::BoxSizer - > new ( wxHORIZONTAL ) ;
$ hsizer - > Add ( $ self - > { canvas } , 0 , wxTOP , 1 ) ;
$ hsizer - > Add ( $ right_sizer , 1 , wxEXPAND | wxBOTTOM , 0 ) ;
my $ sizer = Wx::BoxSizer - > new ( wxVERTICAL ) ;
$ sizer - > Add ( $ self - > { htoolbar } , 0 , wxEXPAND , 0 ) if $ self - > { htoolbar } ;
$ sizer - > Add ( $ self - > { btoolbar } , 0 , wxEXPAND , 0 ) if $ self - > { btoolbar } ;
$ sizer - > Add ( $ hsizer , 1 , wxEXPAND , 0 ) ;
2012-04-30 12:56:01 +00:00
$ sizer - > SetSizeHints ( $ self ) ;
$ self - > SetSizer ( $ sizer ) ;
}
return $ self ;
}
2012-10-24 18:24:11 +00: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 19:13:03 +00:00
sub skeinpanel {
my $ self = shift ;
return $ self - > GetParent - > GetParent ;
}
2012-08-07 16:44:47 +00: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 12:56:01 +00:00
sub load {
my $ self = shift ;
2012-07-30 10:08:28 +00:00
my $ dir = $ Slic3r:: GUI:: Settings - > { recent } { skein_directory } || $ Slic3r:: GUI:: Settings - > { recent } { config_directory } || '' ;
2012-10-01 14:49:02 +00: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 12:56:01 +00:00
if ( $ dialog - > ShowModal != wxID_OK ) {
$ dialog - > Destroy ;
return ;
}
2012-05-31 07:26:30 +00:00
my @ input_files = $ dialog - > GetPaths ;
2012-04-30 12:56:01 +00:00
$ dialog - > Destroy ;
2012-05-31 07:26:30 +00:00
$ self - > load_file ( $ _ ) for @ input_files ;
2012-04-30 12:56:01 +00:00
}
sub load_file {
my $ self = shift ;
my ( $ input_file ) = @ _ ;
2013-08-25 01:21:20 +00:00
my $ basename = basename ( $ input_file ) ;
2012-07-30 10:08:28 +00:00
$ Slic3r:: GUI:: Settings - > { recent } { skein_directory } = dirname ( $ input_file ) ;
Slic3r::GUI - > save_settings ;
2012-04-30 12:56:01 +00:00
2012-07-01 21:29:56 +00:00
my $ process_dialog = Wx::ProgressDialog - > new ( 'Loading…' , "Processing input file…" , 100 , $ self , 0 ) ;
2012-04-30 19:49:44 +00:00
$ process_dialog - > Pulse ;
2012-08-29 17:37:27 +00:00
2012-04-30 12:56:01 +00:00
local $ SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $ self ) ;
2012-08-29 17:37:27 +00:00
my $ model = Slic3r::Model - > read_from_file ( $ input_file ) ;
for my $ i ( 0 .. $# { $ model - > objects } ) {
my $ object = Slic3r::GUI::Plater::Object - > new (
2013-08-25 01:21:20 +00:00
name = > $ basename ,
2012-08-29 17:37:27 +00:00
input_file = > $ input_file ,
input_file_object_id = > $ i ,
2013-08-25 17:52:32 +00:00
model = > $ model ,
model_object_idx = > $ i ,
mesh_stats = > $ model - > objects - > [ $ i ] - > mesh_stats , # so that we can free model
2012-08-29 17:37:27 +00:00
instances = > [
$ model - > objects - > [ $ i ] - > instances
? ( map $ _ - > offset , @ { $ model - > objects - > [ $ i ] - > instances } )
2013-11-24 21:42:52 +00:00
: Slic3r::Point - > new ( 0 , 0 ) ,
2012-08-29 17:37:27 +00:00
] ,
) ;
2012-09-12 14:30:44 +00:00
# we only consider the rotation of the first instance for now
2013-06-12 14:53:19 +00:00
$ object - > rotate ( $ model - > objects - > [ $ i ] - > instances - > [ 0 ] - > rotation )
2012-09-12 14:30:44 +00:00
if $ model - > objects - > [ $ i ] - > instances ;
2012-08-29 17:37:27 +00:00
push @ { $ self - > { objects } } , $ object ;
2012-09-12 14:30:44 +00:00
$ self - > object_loaded ( $# { $ self - > { objects } } , no_arrange = > ( @ { $ object - > instances } > 1 ) ) ;
2012-08-29 17:37:27 +00:00
}
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
$ process_dialog - > Destroy ;
2013-08-25 16:01:59 +00:00
$ self - > statusbar - > SetStatusText ( "Loaded $basename" ) ;
2012-04-30 22:30:46 +00:00
}
sub object_loaded {
my $ self = shift ;
my ( $ obj_idx , % params ) = @ _ ;
2012-08-29 17:37:27 +00:00
my $ object = $ self - > { objects } [ $ obj_idx ] ;
$ self - > { list } - > InsertStringItem ( $ obj_idx , $ object - > name ) ;
2013-08-25 14:10:53 +00:00
$ self - > { list } - > SetItemFont ( $ obj_idx , Wx::Font - > new ( 10 , wxDEFAULT , wxNORMAL , wxNORMAL ) )
if $ self - > { list } - > can ( 'SetItemFont' ) ; # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
2013-08-25 10:22:05 +00:00
2012-08-29 17:37:27 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ object - > instances_count ) ;
$ self - > { list } - > SetItem ( $ obj_idx , 2 , ( $ object - > scale * 100 ) . "%" ) ;
2012-04-30 12:56:01 +00:00
$ self - > make_thumbnail ( $ obj_idx ) ;
2012-04-30 22:30:46 +00:00
$ self - > arrange unless $ params { no_arrange } ;
2012-04-30 12:56:01 +00:00
$ self - > { list } - > Update ;
$ self - > { list } - > Select ( $ obj_idx , 1 ) ;
$ self - > object_list_changed ;
}
sub remove {
my $ self = shift ;
2012-08-11 14:00:41 +00:00
my ( $ obj_idx ) = @ _ ;
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
# if no object index is supplied, remove the selected one
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
2012-04-30 12:56:01 +00:00
}
2012-08-29 17:37:27 +00:00
splice @ { $ self - > { objects } } , $ obj_idx , 1 ;
$ self - > { list } - > DeleteItem ( $ obj_idx ) ;
2012-04-30 12:56:01 +00: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 17:37:27 +00:00
@ { $ self - > { objects } } = ( ) ;
2012-04-30 12:56:01 +00: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 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
my $ instances = $ object - > instances ;
2013-11-24 21:42:52 +00:00
push @$ instances , my $ new_instance = $ instances - > [ - 1 ] - > clone ;
$ new_instance - > translate ( scale ( 10 ) , scale ( 10 ) ) ;
2012-08-29 17:37:27 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ object - > instances_count ) ;
2012-04-30 12:56:01 +00:00
$ self - > arrange ;
}
sub decrease {
my $ self = shift ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2012-09-12 14:30:44 +00:00
if ( $ object - > instances_count >= 2 ) {
pop @ { $ object - > instances } ;
2012-10-15 08:59:54 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 1 , $ object - > instances_count ) ;
2012-09-12 14:30:44 +00:00
} else {
$ self - > remove ;
}
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
if ( $ self - > { objects } [ $ obj_idx ] ) {
2012-04-30 12:56:01 +00:00
$ self - > { list } - > Select ( $ obj_idx , 0 ) ;
$ self - > { list } - > Select ( $ obj_idx , 1 ) ;
}
2012-09-12 14:30:44 +00:00
$ self - > recenter ;
$ self - > { canvas } - > Refresh ;
2012-04-30 12:56:01 +00:00
}
sub rotate {
my $ self = shift ;
my ( $ angle ) = @ _ ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2012-04-30 12:56:01 +00:00
2012-11-19 16:39:16 +00:00
# we need thumbnail to be computed before allowing rotation
return if ! $ object - > thumbnail ;
2012-04-30 20:14:27 +00:00
if ( ! defined $ angle ) {
2012-08-29 17:37:27 +00:00
$ angle = Wx:: GetNumberFromUser ( "" , "Enter the rotation angle:" , "Rotate" , $ object - > rotate , - 364 , 364 , $ self ) ;
2012-04-30 20:14:27 +00:00
return if ! $ angle || $ angle == - 1 ;
2013-06-13 08:27:47 +00:00
$ angle = 0 - $ angle ; # rotate clockwise (be consistent with button icon)
2012-04-30 20:14:27 +00:00
}
2013-06-12 14:53:19 +00:00
$ object - > rotate ( $ object - > rotate + $ angle ) ;
2013-08-25 10:22:05 +00:00
$ self - > selection_changed ( 1 ) ; # refresh info (size etc.)
2012-04-30 12:56:01 +00:00
$ self - > recenter ;
$ self - > { canvas } - > Refresh ;
}
sub changescale {
my $ self = shift ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2013-06-12 14:53:19 +00:00
# we need thumbnail to be computed before allowing scaling
return if ! $ object - > thumbnail ;
2012-06-29 18:46:51 +00:00
# max scale factor should be above 2540 to allow importing files exported in inches
2013-03-29 23:47:13 +00:00
my $ scale = Wx:: GetNumberFromUser ( "" , "Enter the scale % for the selected object:" , "Scale" , $ object - > scale * 100 , 0 , 100000 , $ self ) ;
2012-04-30 12:56:01 +00:00
return if ! $ scale || $ scale == - 1 ;
2012-09-12 14:30:44 +00:00
$ self - > { list } - > SetItem ( $ obj_idx , 2 , "$scale%" ) ;
2013-07-26 12:26:45 +00:00
$ object - > changescale ( $ scale / 100 ) ;
2013-08-25 10:22:05 +00:00
$ self - > selection_changed ( 1 ) ; # refresh info (size, volume etc.)
2012-08-29 17:37:27 +00:00
$ self - > arrange ;
}
sub arrange {
my $ self = shift ;
2012-05-04 10:56:15 +00:00
2012-08-29 17:37:27 +00:00
my $ total_parts = sum ( map $ _ - > instances_count , @ { $ self - > { objects } } ) or return ;
my @ size = ( ) ;
for my $ a ( X , Y ) {
2013-06-07 21:16:02 +00:00
$ size [ $ a ] = max ( map $ _ - > transformed_size - > [ $ a ] , @ { $ self - > { objects } } ) ;
2012-08-29 17:37:27 +00:00
}
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
eval {
my $ config = $ self - > skeinpanel - > config ;
my @ positions = Slic3r::Geometry:: arrange
2012-09-12 14:30:44 +00:00
( $ total_parts , @ size , @ { $ config - > bed_size } , $ config - > min_object_distance , $ self - > skeinpanel - > config ) ;
@$ _ = @ { shift @ positions }
for map @ { $ _ - > instances } , @ { $ self - > { objects } } ;
2012-08-29 17:37:27 +00:00
} ;
# ignore arrange warnings on purpose
2012-04-30 12:56:01 +00:00
2012-08-29 17:37:27 +00:00
$ self - > recenter ;
$ self - > { canvas } - > Refresh ;
2012-04-30 12:56:01 +00:00
}
2012-04-30 22:30:46 +00:00
sub split_object {
my $ self = shift ;
2012-08-29 17:37:27 +00:00
my ( $ obj_idx , $ current_object ) = $ self - > selected_object ;
my $ current_copies_num = $ current_object - > instances_count ;
2012-10-21 18:56:19 +00:00
my $ model_object = $ current_object - > get_model_object ;
if ( @ { $ model_object - > volumes } > 1 ) {
2013-06-02 14:58:23 +00:00
Slic3r::GUI:: warning_catcher ( $ self ) - > ( "The selected object couldn't be split because it contains more than one volume/material." ) ;
2012-10-21 18:56:19 +00:00
return ;
}
my $ mesh = $ model_object - > mesh ;
2012-09-21 13:35:32 +00:00
$ mesh - > align_to_origin ;
2012-04-30 22:30:46 +00:00
2013-09-19 10:25:00 +00:00
$ mesh - > repair ;
my @ new_meshes = @ { $ mesh - > split } ;
2012-06-06 17:00:34 +00:00
if ( @ new_meshes == 1 ) {
2013-06-02 14:58:23 +00:00
Slic3r::GUI:: warning_catcher ( $ self ) - > ( "The selected object couldn't be split because it already contains a single part." ) ;
2012-06-06 17:00:34 +00:00
return ;
}
2012-08-10 14:05:16 +00:00
# remove the original object before spawning the object_loaded event, otherwise
# we'll pass the wrong $obj_idx to it (which won't be recognized after the
# thumbnail thread returns)
$ self - > remove ( $ obj_idx ) ;
2012-10-27 19:39:57 +00: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 08:17:36 +00:00
foreach my $ mesh ( @ new_meshes ) {
2013-09-19 10:25:00 +00:00
$ mesh - > repair ;
2013-06-17 10:11:28 +00:00
my $ bb = $ mesh - > bounding_box ;
2013-09-19 10:25:00 +00:00
my $ model_object = $ new_model - > add_object ;
$ model_object - > add_volume ( mesh = > $ mesh ) ;
2012-09-12 14:30:44 +00: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 ,
2013-08-25 17:52:32 +00:00
model = > $ new_model ,
model_object_idx = > $# { $ new_model - > objects } ,
2013-11-24 13:28:17 +00:00
mesh_stats = > $ mesh - > stats , # so that we can free model
instances = > [ map $ bb - > min_point - > pp , 1 .. $ current_copies_num ] ,
2012-09-12 14:30:44 +00:00
) ;
2013-11-24 13:28:17 +00:00
2012-09-12 14:30:44 +00:00
push @ { $ self - > { objects } } , $ object ;
$ self - > object_loaded ( $# { $ self - > { objects } } , no_arrange = > 1 ) ;
2012-04-30 22:30:46 +00:00
}
2012-09-21 13:35:32 +00:00
$ self - > recenter ;
$ self - > { canvas } - > Refresh ;
2012-04-30 22:30:46 +00:00
}
2012-04-30 12:56:01 +00:00
sub export_gcode {
my $ self = shift ;
2012-05-05 19:08:15 +00:00
if ( $ self - > { export_thread } ) {
2012-07-01 17:24:06 +00:00
Wx::MessageDialog - > new ( $ self , "Another slicing job is currently running." , 'Error' , wxOK | wxICON_ERROR ) - > ShowModal ;
2012-05-05 19:08:15 +00:00
return ;
}
2013-09-19 14:00:47 +00:00
# get config before spawning the thread because it needs GetParent and it's not available there
2013-10-16 13:13:39 +00:00
our $ config = $ self - > skeinpanel - > config ;
our $ extra_variables = $ self - > skeinpanel - > extra_variables ;
2012-08-01 14:06:03 +00:00
2012-05-05 19:08:15 +00:00
# select output file
$ self - > { output_file } = $ main:: opt { output } ;
{
2013-09-19 14:00:47 +00:00
$ self - > { output_file } = $ self - > skeinpanel - > init_print - > expanded_output_filepath ( $ self - > { output_file } , $ self - > { objects } [ 0 ] - > input_file ) ;
2013-04-27 19:07:30 +00:00
my $ dlg = Wx::FileDialog - > new ( $ self , 'Save G-code file as:' , Slic3r::GUI - > output_path ( dirname ( $ self - > { output_file } ) ) ,
2012-09-23 12:48:58 +00:00
basename ( $ self - > { output_file } ) , & Slic3r::GUI::SkeinPanel::FILE_WILDCARDS - > { gcode } , wxFD_SAVE ) ;
2012-05-05 19:08:15 +00:00
if ( $ dlg - > ShowModal != wxID_OK ) {
$ dlg - > Destroy ;
return ;
}
2013-04-27 19:07:30 +00:00
$ Slic3r:: GUI:: Settings - > { _ } { last_output_path } = dirname ( $ dlg - > GetPath ) ;
Slic3r::GUI - > save_settings ;
2012-05-05 19:08:15 +00:00
$ self - > { output_file } = $ Slic3r:: GUI:: SkeinPanel:: last_output_file = $ dlg - > GetPath ;
$ dlg - > Destroy ;
}
$ self - > statusbar - > StartBusy ;
2013-05-20 08:57:27 +00:00
2013-08-25 17:52:32 +00:00
$ _ - > free_model_object for @ { $ self - > { objects } } ;
2013-05-20 08:57:27 +00:00
# It looks like declaring a local $SIG{__WARN__} prevents the ugly
# "Attempt to free unreferenced scalar" warning...
local $ SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $ self ) ;
2012-05-19 19:13:10 +00:00
if ( $ Slic3r:: have_threads ) {
2013-05-19 15:34:33 +00:00
@ _ = ( ) ;
2013-10-16 13:13:39 +00:00
# some perls (including 5.14.2) crash on threads->exit if we pass lexicals to the thread
our $ _thread_self = $ self ;
2012-05-05 19:08:15 +00:00
$ self - > { export_thread } = threads - > create ( sub {
2013-10-16 13:13:39 +00:00
$ _thread_self - > export_gcode2 (
2013-09-19 14:00:47 +00:00
$ config ,
$ extra_variables ,
2013-10-16 13:13:39 +00:00
$ _thread_self - > { output_file } ,
progressbar = > sub { Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ PROGRESS_BAR_EVENT , shared_clone ( [ @ _ ] ) ) ) } ,
message_dialog = > sub { Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ MESSAGE_DIALOG_EVENT , shared_clone ( [ @ _ ] ) ) ) } ,
on_completed = > sub { Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ EXPORT_COMPLETED_EVENT , shared_clone ( [ @ _ ] ) ) ) } ,
2012-05-05 19:08:15 +00:00
catch_error = > sub {
2013-10-16 13:13:39 +00:00
Slic3r::GUI:: catch_error ( $ _thread_self , $ _ [ 0 ] , sub {
Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ MESSAGE_DIALOG_EVENT , shared_clone ( [ @ _ ] ) ) ) ;
Wx:: PostEvent ( $ _thread_self , Wx::PlThreadEvent - > new ( - 1 , $ EXPORT_FAILED_EVENT , undef ) ) ;
2012-05-19 19:13:10 +00:00
} ) ;
2012-05-05 19:08:15 +00:00
} ,
) ;
2013-07-11 14:17:36 +00:00
Slic3r:: thread_cleanup ( ) ;
2012-05-05 19:08:15 +00:00
} ) ;
$ self - > statusbar - > SetCancelCallback ( sub {
2012-09-21 15:45:54 +00:00
$ self - > { export_thread } - > kill ( 'KILL' ) - > join ;
2012-05-05 19:08:15 +00:00
$ self - > { export_thread } = undef ;
$ self - > statusbar - > StopBusy ;
$ self - > statusbar - > SetStatusText ( "Export cancelled" ) ;
} ) ;
} else {
$ self - > export_gcode2 (
2013-09-19 14:00:47 +00:00
$ config ,
$ extra_variables ,
2012-05-05 19:08:15 +00:00
$ self - > { output_file } ,
progressbar = > sub {
my ( $ percent , $ message ) = @ _ ;
$ self - > statusbar - > SetProgress ( $ percent ) ;
2012-07-01 21:29:56 +00:00
$ self - > statusbar - > SetStatusText ( "$message…" ) ;
2012-05-05 19:08:15 +00:00
} ,
message_dialog = > sub { Wx::MessageDialog - > new ( $ self , @ _ ) - > ShowModal } ,
on_completed = > sub { $ self - > on_export_completed ( @ _ ) } ,
2012-05-23 09:47:52 +00:00
catch_error = > sub { Slic3r::GUI:: catch_error ( $ self , @ _ ) && $ self - > on_export_failed } ,
2012-05-05 19:08:15 +00:00
) ;
}
}
sub export_gcode2 {
my $ self = shift ;
2013-09-19 14:00:47 +00:00
my ( $ config , $ extra_variables , $ output_file , % params ) = @ _ ;
2012-05-05 19:08:15 +00:00
local $ SIG { 'KILL' } = sub {
Slic3r:: debugf "Exporting cancelled; exiting thread...\n" ;
2013-10-14 20:07:41 +00:00
Slic3r:: thread_cleanup ( ) ;
2012-05-05 19:08:15 +00:00
threads - > exit ( ) ;
2012-05-19 19:13:10 +00:00
} if $ Slic3r:: have_threads ;
2012-05-05 19:08:15 +00:00
2013-09-19 14:00:47 +00:00
my $ print = Slic3r::Print - > new (
config = > $ config ,
extra_variables = > $ extra_variables ,
) ;
2012-04-30 12:56:01 +00:00
eval {
2012-07-27 19:13:03 +00:00
$ print - > config - > validate ;
2012-09-12 14:30:44 +00:00
$ print - > add_model ( $ self - > make_model ) ;
2012-05-23 09:47:52 +00:00
$ print - > validate ;
2012-04-30 12:56:01 +00:00
{
my @ warnings = ( ) ;
local $ SIG { __WARN__ } = sub { push @ warnings , $ _ [ 0 ] } ;
my % params = (
output_file = > $ output_file ,
2012-05-05 19:08:15 +00:00
status_cb = > sub { $ params { progressbar } - > ( @ _ ) } ,
2013-08-28 18:13:18 +00:00
quiet = > 1 ,
2012-04-30 12:56:01 +00:00
) ;
if ( $ params { export_svg } ) {
$ print - > export_svg ( % params ) ;
} else {
$ print - > export_gcode ( % params ) ;
}
2012-05-29 15:02:47 +00:00
Slic3r::GUI:: warning_catcher ( $ self , $ Slic3r:: have_threads ? sub {
2012-05-05 19:08:15 +00:00
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ MESSAGE_DIALOG_EVENT , shared_clone ( [ @ _ ] ) ) ) ;
2012-05-29 15:02:47 +00:00
} : undef ) - > ( $ _ ) for @ warnings ;
2012-04-30 12:56:01 +00:00
}
my $ message = "Your files were successfully sliced" ;
2012-07-08 20:16:46 +00: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 19:00:03 +00:00
$ message . = "." ;
2012-05-05 19:08:15 +00:00
$ params { on_completed } - > ( $ message ) ;
2012-04-30 12:56:01 +00:00
} ;
2012-05-19 19:13:10 +00:00
$ params { catch_error } - > ( ) ;
2012-04-30 12:56:01 +00:00
}
2012-05-05 19:08:15 +00:00
sub on_export_completed {
my $ self = shift ;
my ( $ message ) = @ _ ;
2012-05-20 15:21:31 +00:00
$ self - > { export_thread } - > detach if $ self - > { export_thread } ;
2012-05-05 19:26:19 +00:00
$ self - > { export_thread } = undef ;
$ self - > statusbar - > SetCancelCallback ( undef ) ;
2012-05-05 19:08:15 +00:00
$ self - > statusbar - > StopBusy ;
$ self - > statusbar - > SetStatusText ( "G-code file exported to $self->{output_file}" ) ;
2012-07-15 21:57:31 +00:00
& Wx::wxTheApp - > notify ( $ message ) ;
2012-05-05 19:08:15 +00:00
}
2012-05-19 19:13:10 +00:00
sub on_export_failed {
my $ self = shift ;
2012-05-20 15:21:31 +00:00
$ self - > { export_thread } - > detach if $ self - > { export_thread } ;
2012-05-19 19:13:10 +00:00
$ self - > { export_thread } = undef ;
$ self - > statusbar - > SetCancelCallback ( undef ) ;
$ self - > statusbar - > StopBusy ;
$ self - > statusbar - > SetStatusText ( "Export failed" ) ;
}
2012-04-30 15:10:54 +00:00
sub export_stl {
my $ self = shift ;
2012-08-29 15:11:56 +00: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 15:10:54 +00:00
my $ output_file = $ main:: opt { output } ;
{
2013-04-28 21:26:50 +00:00
$ output_file = $ self - > skeinpanel - > init_print - > expanded_output_filepath ( $ output_file , $ self - > { objects } [ 0 ] - > input_file ) ;
2012-08-29 15:11:56 +00:00
$ output_file =~ s/\.gcode$/$suffix/i ;
my $ dlg = Wx::FileDialog - > new ( $ self , "Save $format file as:" , dirname ( $ output_file ) ,
2012-09-23 12:48:58 +00:00
basename ( $ output_file ) , & Slic3r::GUI::SkeinPanel:: MODEL_WILDCARD , wxFD_SAVE | wxFD_OVERWRITE_PROMPT ) ;
2012-04-30 15:10:54 +00:00
if ( $ dlg - > ShowModal != wxID_OK ) {
$ dlg - > Destroy ;
2012-08-29 15:11:56 +00:00
return undef ;
2012-04-30 15:10:54 +00:00
}
$ output_file = $ Slic3r:: GUI:: SkeinPanel:: last_output_file = $ dlg - > GetPath ;
$ dlg - > Destroy ;
}
2012-08-29 15:11:56 +00:00
return $ output_file ;
2012-08-29 14:49:38 +00:00
}
sub make_model {
my $ self = shift ;
my $ model = Slic3r::Model - > new ;
2012-10-21 18:56:19 +00:00
foreach my $ plater_object ( @ { $ self - > { objects } } ) {
my $ model_object = $ plater_object - > get_model_object ;
2013-05-14 14:31:50 +00:00
2012-10-21 18:56:19 +00:00
my $ new_model_object = $ model - > add_object (
input_file = > $ plater_object - > input_file ,
2013-08-25 12:37:50 +00:00
config = > $ plater_object - > config ,
2013-03-10 13:58:49 +00:00
layer_height_ranges = > $ plater_object - > layer_height_ranges ,
2013-08-25 17:52:32 +00:00
material_mapping = > $ plater_object - > material_mapping ,
2012-09-12 14:30:44 +00:00
) ;
2012-10-21 18:56:19 +00:00
foreach my $ volume ( @ { $ model_object - > volumes } ) {
$ new_model_object - > add_volume (
material_id = > $ volume - > material_id ,
2013-09-16 07:58:09 +00:00
mesh = > $ volume - > mesh ,
2012-10-21 18:56:19 +00:00
) ;
2012-11-16 21:41:54 +00:00
$ model - > set_material ( $ volume - > material_id || 0 , { } ) ;
2012-10-21 18:56:19 +00:00
}
2013-06-06 18:53:56 +00:00
$ new_model_object - > align_to_origin ;
2012-10-21 18:56:19 +00:00
$ new_model_object - > add_instance (
2013-05-31 17:41:31 +00:00
rotation = > $ plater_object - > rotate , # around center point
2013-06-13 12:33:10 +00:00
scaling_factor = > $ plater_object - > scale ,
2013-09-05 12:23:31 +00:00
offset = > Slic3r::Point - > new ( @$ _ ) ,
2012-10-21 18:56:19 +00:00
) for @ { $ plater_object - > instances } ;
2012-04-30 15:10:54 +00:00
}
2013-06-02 14:56:08 +00:00
$ model - > align_to_origin ;
2012-08-29 14:49:38 +00:00
return $ model ;
2012-04-30 15:10:54 +00:00
}
2012-04-30 12:56:01 +00:00
sub make_thumbnail {
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
2012-11-19 17:31:41 +00:00
my $ object = $ self - > { objects } [ $ obj_idx ] ;
$ object - > thumbnail_scaling_factor ( $ self - > { scaling_factor } ) ;
2013-11-12 13:30:13 +00:00
$ object - > thumbnail ( Slic3r::ExPolygon::Collection - > new ) ;
2012-04-30 18:59:14 +00:00
my $ cb = sub {
2013-11-12 13:30:13 +00:00
$ object - > make_thumbnail ;
2012-04-30 18:59:14 +00:00
2012-05-19 19:13:10 +00:00
if ( $ Slic3r:: have_threads ) {
2013-11-12 13:30:13 +00:00
Wx:: PostEvent ( $ self , Wx::PlThreadEvent - > new ( - 1 , $ THUMBNAIL_DONE_EVENT , shared_clone ( [ $ obj_idx ] ) ) ) ;
2013-07-11 17:34:37 +00:00
Slic3r:: thread_cleanup ( ) ;
2012-04-30 20:34:41 +00:00
threads - > exit ;
} else {
2012-10-01 16:12:14 +00:00
$ self - > on_thumbnail_made ( $ obj_idx ) ;
2012-04-30 20:34:41 +00:00
}
2012-04-30 18:59:14 +00:00
} ;
2013-05-19 15:34:33 +00:00
@ _ = ( ) ;
2012-05-20 14:29:52 +00:00
$ Slic3r:: have_threads ? threads - > create ( $ cb ) - > detach : $ cb - > ( ) ;
2012-04-30 12:56:01 +00:00
}
2012-08-29 17:37:27 +00:00
sub on_thumbnail_made {
2012-04-30 20:34:41 +00:00
my $ self = shift ;
2012-10-01 16:12:14 +00:00
my ( $ obj_idx ) = @ _ ;
2013-11-12 13:30:13 +00:00
$ self - > { objects } [ $ obj_idx ] - > _transform_thumbnail ;
2012-04-30 20:34:41 +00:00
$ self - > recenter ;
$ self - > { canvas } - > Refresh ;
}
2012-04-30 12:56:01 +00:00
sub recenter {
my $ self = shift ;
2012-09-12 14:30:44 +00:00
return unless @ { $ self - > { objects } } ;
2012-04-30 12:56:01 +00:00
# calculate displacement needed to center the print
2013-11-24 21:42:52 +00:00
my @ bbs = ( ) ;
foreach my $ plobj ( @ { $ self - > { objects } } ) {
my $ bb = $ plobj - > transformed_bounding_box ; # in scaled coordinates
foreach my $ inst ( @ { $ plobj - > instances } ) {
push @ bbs , my $ inst_bb = $ bb - > clone ;
$ inst_bb - > translate ( @$ inst , 0 ) ;
}
}
my $ print_bb = Slic3r::Geometry::BoundingBox - > merge ( @ bbs ) ;
my $ print_size = $ print_bb - > size ;
2013-06-13 12:33:10 +00:00
# $self->{shift} contains the offset in pixels to add to object instances in order to center them
# it is expressed in upwards Y
2012-04-30 12:56:01 +00:00
$ self - > { shift } = [
2013-06-16 10:21:25 +00:00
$ self - > to_pixel ( - $ print_bb - > x_min ) + ( $ self - > { canvas } - > GetSize - > GetWidth - $ self - > to_pixel ( $ print_size - > [ X ] ) ) / 2 ,
$ self - > to_pixel ( - $ print_bb - > y_min ) + ( $ self - > { canvas } - > GetSize - > GetHeight - $ self - > to_pixel ( $ print_size - > [ Y ] ) ) / 2 ,
2012-04-30 12:56:01 +00:00
] ;
}
2012-07-27 19:13:03 +00:00
sub on_config_change {
my $ self = shift ;
my ( $ opt_key , $ value ) = @ _ ;
2012-08-07 18:14:28 +00:00
if ( $ opt_key eq 'extruders_count' && defined $ value ) {
my $ choices = $ self - > { preset_choosers } { filament } ;
while ( @$ choices < $ value ) {
2012-10-24 18:24:11 +00:00
my @ presets = $ choices - > [ 0 ] - > GetStrings ;
push @$ choices , Wx::Choice - > new ( $ self , - 1 , wxDefaultPosition , [ 150 , - 1 ] , [ @ presets ] ) ;
2013-08-25 16:04:45 +00:00
$ choices - > [ - 1 ] - > SetFont ( $ Slic3r:: GUI:: small_font ) ;
2012-08-07 18:14:28 +00:00
$ self - > { preset_choosers_sizers } { filament } - > Add ( $ choices - > [ - 1 ] , 0 , wxEXPAND | wxBOTTOM , FILAMENT_CHOOSERS_SPACING ) ;
2012-10-24 18:24:11 +00: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 18:14:28 +00:00
}
while ( @$ choices > $ value ) {
$ self - > { preset_choosers_sizers } { filament } - > Remove ( - 1 ) ;
$ choices - > [ - 1 ] - > Destroy ;
pop @$ choices ;
}
$ self - > Layout ;
2012-08-08 22:41:03 +00:00
} elsif ( $ self - > { config } - > has ( $ opt_key ) ) {
2012-08-06 17:18:31 +00:00
$ self - > { config } - > set ( $ opt_key , $ value ) ;
$ self - > _update_bed_size if $ opt_key eq 'bed_size' ;
}
2012-07-27 19:13:03 +00:00
}
sub _update_bed_size {
2012-04-30 20:34:41 +00:00
my $ self = shift ;
# supposing the preview canvas is square, calculate the scaling factor
# to constrain print bed area inside preview
2013-06-13 12:33:10 +00:00
# when the canvas is not rendered yet, its GetSize() method returns 0,0
2013-11-24 21:42:52 +00:00
# scaling_factor is expressed in pixel / mm
2013-06-13 12:33:10 +00:00
$ self - > { scaling_factor } = CANVAS_SIZE - > [ X ] / max ( @ { $ self - > { config } - > bed_size } ) ;
2013-06-13 18:05:32 +00:00
$ _ - > thumbnail_scaling_factor ( $ self - > { scaling_factor } ) for @ { $ self - > { objects } } ;
2013-06-13 09:27:15 +00:00
$ self - > recenter ;
2012-04-30 20:34:41 +00:00
}
2012-07-27 19:13:03 +00:00
# this is called on the canvas
2012-04-30 12:56:01 +00: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 09:47:53 +00:00
# draw print center
2012-09-11 16:11:46 +00:00
if ( @ { $ parent - > { objects } } ) {
2012-05-04 09:47:53 +00: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 19:13:03 +00: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 09:47:53 +00:00
}
2012-04-30 12:56:01 +00:00
# draw frame
2013-08-25 10:22:05 +00:00
if ( 0 ) {
$ dc - > SetPen ( wxBLACK_PEN ) ;
$ dc - > SetBrush ( $ parent - > { transparent_brush } ) ;
$ dc - > DrawRectangle ( 0 , 0 , @ size ) ;
}
2012-04-30 12:56:01 +00:00
# draw text if plate is empty
2012-09-12 14:30:44 +00:00
if ( ! @ { $ parent - > { objects } } ) {
2012-04-30 12:56:01 +00:00
$ dc - > SetTextForeground ( Wx::Colour - > new ( 150 , 50 , 50 ) ) ;
2012-05-04 09:47:53 +00:00
$ dc - > SetFont ( Wx::Font - > new ( 14 , wxDEFAULT , wxNORMAL , wxNORMAL ) ) ;
2012-07-27 19:13:03 +00: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 12:56:01 +00:00
}
# draw thumbnails
2012-07-01 17:24:06 +00:00
$ dc - > SetPen ( wxBLACK_PEN ) ;
2012-04-30 12:56:01 +00:00
@ { $ parent - > { object_previews } } = ( ) ;
2012-09-11 16:11:46 +00:00
for my $ obj_idx ( 0 .. $# { $ parent - > { objects } } ) {
2012-09-12 14:30:44 +00:00
my $ object = $ parent - > { objects } [ $ obj_idx ] ;
2013-08-26 15:58:37 +00:00
next unless defined $ object - > thumbnail ;
2012-09-12 14:30:44 +00:00
for my $ instance_idx ( 0 .. $# { $ object - > instances } ) {
my $ instance = $ object - > instances - > [ $ instance_idx ] ;
2013-06-07 21:16:02 +00:00
2013-07-13 22:38:01 +00:00
my $ thumbnail = $ object - > transformed_thumbnail - > clone ;
2013-11-24 21:42:52 +00:00
$ thumbnail - > translate ( map $ _ * $ parent - > { scaling_factor } , @$ instance ) ;
$ thumbnail - > translate ( map scale ( $ parent - > { shift } [ $ _ ] ) , ( X , Y ) ) ;
push @ { $ parent - > { object_previews } } , [ $ obj_idx , $ instance_idx , $ thumbnail ] ; # $thumbnail has scaled coordinates
2012-04-30 12:56:01 +00:00
2012-07-25 08:06:45 +00:00
my $ drag_object = $ self - > { drag_object } ;
2012-09-12 14:30:44 +00:00
if ( defined $ drag_object && $ obj_idx == $ drag_object - > [ 0 ] && $ instance_idx == $ drag_object - > [ 1 ] ) {
2012-07-25 08:06:45 +00:00
$ dc - > SetBrush ( $ parent - > { dragged_brush } ) ;
} elsif ( grep { $ _ - > [ 0 ] == $ obj_idx } @ { $ parent - > { selected_objects } } ) {
2012-04-30 12:56:01 +00:00
$ dc - > SetBrush ( $ parent - > { selected_brush } ) ;
} else {
$ dc - > SetBrush ( $ parent - > { objects_brush } ) ;
}
2013-11-24 21:42:52 +00:00
foreach my $ expolygon ( @$ thumbnail ) {
my $ points = $ expolygon - > contour - > pp ;
foreach my $ point ( @$ points ) {
$ point - > [ X ] *= & Slic3r:: SCALING_FACTOR ;
$ point - > [ Y ] *= & Slic3r:: SCALING_FACTOR ;
}
$ dc - > DrawPolygon ( $ parent - > _y ( $ points ) , 0 , 0 ) ;
}
2012-05-23 09:47:52 +00:00
# if sequential printing is enabled and we have more than one object
2012-09-12 14:30:44 +00:00
if ( $ parent - > { config } - > complete_objects && ( map @ { $ _ - > instances } , @ { $ parent - > { objects } } ) > 1 ) {
2013-11-24 21:42:52 +00:00
my @ points = map @ { $ _ - > contour } , @ { $ parent - > { object_previews } - > [ - 1 ] [ 2 ] } ;
if ( @ points >= 3 ) {
my ( $ clearance ) = @ { offset ( [ convex_hull ( \ @ points ) ] , scale ( $ parent - > { config } - > extruder_clearance_radius / 2 ) * $ parent - > { scaling_factor } , 100 , JT_ROUND ) } ;
$ clearance - > scale ( & Slic3r:: SCALING_FACTOR ) ;
$ dc - > SetPen ( $ parent - > { clearance_pen } ) ;
$ dc - > SetBrush ( $ parent - > { transparent_brush } ) ;
$ dc - > DrawPolygon ( $ parent - > _y ( $ clearance ) , 0 , 0 ) ;
}
2012-05-23 09:47:52 +00:00
}
2012-04-30 12:56:01 +00:00
}
}
# draw skirt
2012-07-27 19:13:03 +00:00
if ( @ { $ parent - > { object_previews } } && $ parent - > { config } - > skirts ) {
2013-11-24 21:42:52 +00:00
my @ points = map @ { $ _ - > contour } , map @ { $ _ - > [ 2 ] } , @ { $ parent - > { object_previews } } ;
if ( @ points >= 3 ) {
my ( $ convex_hull ) = @ { offset ( [ convex_hull ( \ @ points ) ] , scale ( $ parent - > { config } - > skirt_distance ) * $ parent - > { scaling_factor } , 1 , JT_ROUND ) } ;
$ convex_hull - > scale ( & Slic3r:: SCALING_FACTOR ) ;
$ dc - > SetPen ( $ parent - > { skirt_pen } ) ;
$ dc - > SetBrush ( $ parent - > { transparent_brush } ) ;
$ dc - > DrawPolygon ( $ parent - > _y ( $ convex_hull ) , 0 , 0 ) ;
}
2012-04-30 12:56:01 +00:00
}
$ event - > Skip ;
}
sub mouse_event {
my ( $ self , $ event ) = @ _ ;
my $ parent = $ self - > GetParent ;
my $ point = $ event - > GetPosition ;
2013-11-24 21:42:52 +00:00
my $ pos = Slic3r::Point - > new_scale ( @ { $ parent - > _y ( [ [ $ point - > x , $ point - > y ] ] ) - > [ 0 ] } ) ; #]] in scaled pixels
2012-04-30 12:56:01 +00:00
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 14:30:44 +00:00
my ( $ obj_idx , $ instance_idx , $ thumbnail ) = @$ preview ;
2013-11-21 15:21:42 +00:00
if ( defined first { $ _ - > contour - > contains_point ( $ pos ) } @$ thumbnail ) {
2012-09-12 14:30:44 +00:00
$ parent - > { selected_objects } = [ [ $ obj_idx , $ instance_idx ] ] ;
$ parent - > { list } - > Select ( $ obj_idx , 1 ) ;
2012-04-30 12:56:01 +00:00
$ parent - > selection_changed ( 1 ) ;
2012-09-12 14:30:44 +00:00
my $ instance = $ parent - > { objects } [ $ obj_idx ] - > instances - > [ $ instance_idx ] ;
2013-11-24 21:42:52 +00:00
$ self - > { drag_start_pos } = [ map $ pos - > [ $ _ ] - scale ( $ parent - > { shift } [ $ _ ] ) - scale ( $ parent - > to_pixel ( $ instance - > [ $ _ ] ) ) , X , Y ] ; # displacement between the click and the instance origin
2012-04-30 12:56:01 +00: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-24 22:51:41 +00:00
$ self - > SetCursor ( wxSTANDARD_CURSOR ) ;
2012-10-24 20:44:08 +00:00
} elsif ( $ event - > ButtonDClick ) {
2013-08-25 16:01:59 +00:00
$ parent - > object_preview_dialog if @ { $ parent - > { selected_objects } } ;
2012-04-30 12:56:01 +00:00
} elsif ( $ event - > Dragging ) {
return if ! $ self - > { drag_start_pos } ; # concurrency problems
2012-09-12 14:30:44 +00:00
for my $ preview ( $ self - > { drag_object } ) {
my ( $ obj_idx , $ instance_idx , $ thumbnail ) = @$ preview ;
2013-11-24 21:42:52 +00:00
$ parent - > { objects } [ $ obj_idx ] - > instances - > [ $ instance_idx ] = Slic3r::Point - > new (
map { $ parent - > to_units ( unscale ( $ pos - > [ $ _ ] - $ self - > { drag_start_pos } [ $ _ ] ) - $ parent - > { shift } [ $ _ ] ) } ( X , Y ) ,
) ;
2012-04-30 12:56:01 +00:00
$ parent - > Refresh ;
}
2012-07-24 22:15:32 +00:00
} elsif ( $ event - > Moving ) {
my $ cursor = wxSTANDARD_CURSOR ;
for my $ preview ( @ { $ parent - > { object_previews } } ) {
2013-11-21 15:21:42 +00:00
if ( defined first { $ _ - > contour - > contains_point ( $ pos ) } @ { $ preview - > [ 2 ] } ) {
2012-07-24 22:15:32 +00:00
$ cursor = Wx::Cursor - > new ( wxCURSOR_HAND ) ;
last ;
}
}
$ self - > SetCursor ( $ cursor ) ;
2012-04-30 12:56:01 +00: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 20:44:08 +00:00
sub list_item_activated {
my ( $ self , $ event , $ obj_idx ) = @ _ ;
$ obj_idx // = $ event - > GetIndex ;
2013-08-25 16:01:59 +00:00
$ self - > object_preview_dialog ( $ obj_idx ) ;
2013-08-25 13:45:22 +00:00
}
2013-08-25 16:01:59 +00:00
sub object_preview_dialog {
2013-08-25 13:45:22 +00:00
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
}
2013-09-16 08:18:42 +00:00
if ( ! $ Slic3r:: GUI:: have_OpenGL ) {
Slic3r::GUI:: show_error ( $ self , "Please install the OpenGL modules to use this feature (see build instructions)." ) ;
return ;
}
2013-08-25 16:01:59 +00:00
my $ dlg = Slic3r::GUI::Plater::ObjectPreviewDialog - > new ( $ self ,
object = > $ self - > { objects } [ $ obj_idx ] ,
) ;
$ dlg - > ShowModal ;
}
sub object_settings_dialog {
my $ self = shift ;
my ( $ obj_idx ) = @ _ ;
if ( ! defined $ obj_idx ) {
( $ obj_idx , undef ) = $ self - > selected_object ;
}
2013-08-25 20:24:43 +00:00
# validate config before opening the settings dialog because
# that dialog can't be closed if validation fails, but user
# can't fix any error which is outside that dialog
return unless $ self - > validate_config ;
2013-08-25 16:01:59 +00:00
my $ dlg = Slic3r::GUI::Plater::ObjectSettingsDialog - > new ( $ self ,
2012-10-24 20:44:08 +00:00
object = > $ self - > { objects } [ $ obj_idx ] ,
) ;
$ dlg - > ShowModal ;
}
2012-04-30 12:56:01 +00:00
sub object_list_changed {
my $ self = shift ;
2013-08-25 16:01:59 +00:00
my $ have_objects = @ { $ self - > { objects } } ? 1 : 0 ;
my $ method = $ have_objects ? 'Enable' : 'Disable' ;
2012-04-30 22:30:46 +00:00
$ self - > { "btn_$_" } - > $ method
2012-05-04 09:22:56 +00:00
for grep $ self - > { "btn_$_" } , qw( reset arrange export_gcode export_stl ) ;
2013-08-25 16:01:59 +00:00
if ( $ self - > { htoolbar } ) {
$ self - > { htoolbar } - > EnableTool ( $ _ , $ have_objects )
for ( TB_RESET , TB_ARRANGE ) ;
}
2012-04-30 12:56:01 +00:00
}
sub selection_changed {
my $ self = shift ;
my ( $ have_sel ) = @ _ ;
my $ method = $ have_sel ? 'Enable' : 'Disable' ;
2012-04-30 22:30:46 +00:00
$ self - > { "btn_$_" } - > $ method
2013-08-25 16:01:59 +00:00
for grep $ self - > { "btn_$_" } , qw( remove increase decrease rotate45cw rotate45ccw rotate changescale split view settings ) ;
2012-05-04 09:22:56 +00:00
if ( $ self - > { htoolbar } ) {
2012-08-02 19:49:26 +00:00
$ self - > { htoolbar } - > EnableTool ( $ _ , $ have_sel )
2013-08-25 16:01:59 +00:00
for ( TB_REMOVE , TB_MORE , TB_FEWER , TB_45CW , TB_45CCW , TB_ROTATE , TB_SCALE , TB_SPLIT , TB_VIEW , TB_SETTINGS ) ;
2013-08-25 10:22:05 +00:00
}
if ( $ self - > { object_info_size } ) { # have we already loaded the info pane?
if ( $ have_sel ) {
my ( $ obj_idx , $ object ) = $ self - > selected_object ;
2013-11-24 21:42:52 +00:00
$ self - > { object_info_size } - > SetLabel ( sprintf ( "%.2f x %.2f x %.2f" , map unscale ( $ _ ) , @ { $ object - > transformed_size } ) ) ;
2013-08-25 15:26:55 +00:00
$ self - > { object_info_materials } - > SetLabel ( $ object - > materials ) ;
2013-08-25 10:22:05 +00:00
if ( my $ stats = $ object - > mesh_stats ) {
$ self - > { object_info_volume } - > SetLabel ( sprintf ( '%.2f' , $ stats - > { volume } * ( $ object - > scale ** 3 ) ) ) ;
2013-08-25 15:26:55 +00:00
$ self - > { object_info_facets } - > SetLabel ( sprintf ( '%d (%d shells)' , $ object - > facets , $ stats - > { number_of_parts } ) ) ;
if ( my $ errors = sum ( @$ stats { qw( degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges ) } ) ) {
$ self - > { object_info_manifold } - > SetLabel ( sprintf ( "Auto-repaired (%d errors)" , $ errors ) ) ;
$ self - > { object_info_manifold_warning_icon } - > Show ;
2013-08-25 16:01:59 +00:00
# we don't show normals_fixed because we never provide normals
# to admesh, so it generates normals for all facets
2013-08-25 15:26:55 +00:00
my $ message = sprintf '%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges' ,
@$ stats { qw( degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges ) } ;
$ self - > { object_info_manifold } - > SetToolTipString ( $ message ) ;
$ self - > { object_info_manifold_warning_icon } - > SetToolTipString ( $ message ) ;
} else {
$ self - > { object_info_manifold } - > SetLabel ( "Yes" ) ;
}
} else {
$ self - > { object_info_facets } - > SetLabel ( $ object - > facets ) ;
2013-08-25 10:22:05 +00:00
}
} else {
2013-08-25 15:26:55 +00:00
$ self - > { "object_info_$_" } - > SetLabel ( "" ) for qw( size volume facets materials manifold ) ;
$ self - > { object_info_manifold_warning_icon } - > Hide ;
$ self - > { object_info_manifold } - > SetToolTipString ( "" ) ;
2013-08-25 10:22:05 +00:00
}
2013-08-25 15:26:55 +00:00
$ self - > Layout ;
2012-05-04 09:22:56 +00:00
}
2012-04-30 12:56:01 +00:00
}
2012-08-29 17:37:27 +00:00
sub selected_object {
2012-04-30 12:56:01 +00:00
my $ self = shift ;
2012-08-29 17:37:27 +00: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 12:56:01 +00:00
}
2013-08-25 20:24:43 +00:00
sub validate_config {
my $ self = shift ;
eval {
$ self - > skeinpanel - > config - > validate ;
} ;
return 0 if Slic3r::GUI:: catch_error ( $ self ) ;
return 1 ;
}
2012-05-04 10:56:15 +00:00
sub statusbar {
my $ self = shift ;
2012-07-27 19:13:03 +00:00
return $ self - > skeinpanel - > GetParent - > { statusbar } ;
2012-05-04 10:56:15 +00:00
}
2012-04-30 12:56:01 +00:00
sub to_pixel {
my $ self = shift ;
2013-11-24 21:42:52 +00:00
return $ _ [ 0 ] * $ self - > { scaling_factor } * & Slic3r:: SCALING_FACTOR ;
2012-04-30 12:56:01 +00:00
}
2012-09-12 14:30:44 +00:00
sub to_units {
2012-04-30 12:56:01 +00:00
my $ self = shift ;
2013-11-24 21:42:52 +00:00
return $ _ [ 0 ] / $self->{scaling_factor} / & Slic3r:: SCALING_FACTOR ;
2012-04-30 12:56:01 +00:00
}
sub _y {
my $ self = shift ;
my ( $ points ) = @ _ ;
my $ height = $ self - > { canvas } - > GetSize - > GetHeight ;
return [ map [ $ _ - > [ X ] , $ height - $ _ - > [ Y ] ] , @$ points ] ;
}
2012-05-04 08:15:33 +00:00
package Slic3r::GUI::Plater::DropTarget ;
2012-04-30 12:56:01 +00:00
use Wx::DND ;
use base 'Wx::FileDropTarget' ;
sub new {
my $ class = shift ;
my ( $ window ) = @ _ ;
my $ self = $ class - > SUPER:: new ;
$ self - > { window } = $ window ;
return $ self ;
}
sub OnDropFiles {
my $ self = shift ;
my ( $ x , $ y , $ filenames ) = @ _ ;
2012-07-21 12:45:45 +00:00
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@ _ = ( ) ;
2013-03-19 11:24:39 +00:00
# only accept STL, OBJ and AMF files
return 0 if grep ! /\.(?:stl|obj|amf(?:\.xml)?)$/i , @$ filenames ;
2012-04-30 12:56:01 +00:00
$ self - > { window } - > load_file ( $ _ ) for @$ filenames ;
}
2012-08-29 17:37:27 +00:00
package Slic3r::GUI::Plater::Object ;
use Moo ;
2013-07-13 18:34:57 +00:00
use List::Util qw( first ) ;
2013-06-07 21:16:02 +00:00
use Slic3r::Geometry qw( X Y Z MIN MAX deg2rad ) ;
2012-08-29 17:37:27 +00:00
has 'name' = > ( is = > 'rw' , required = > 1 ) ;
has 'input_file' = > ( is = > 'rw' , required = > 1 ) ;
2012-10-22 12:02:58 +00:00
has 'input_file_object_id' = > ( is = > 'rw' ) ; # undef means keep model object
2013-08-25 17:52:32 +00:00
has 'model' = > ( is = > 'rw' , required = > 1 , trigger = > \ & _trigger_model_object ) ;
has 'model_object_idx' = > ( is = > 'rw' , required = > 1 , trigger = > \ & _trigger_model_object ) ;
2013-11-24 21:42:52 +00:00
has 'bounding_box' = > ( is = > 'rw' ) ; # scaled 3D bb of original object (aligned to origin) with no rotation or scaling
has 'convex_hull' = > ( is = > 'rw' ) ; # scaled 2D convex hull of original object (aligned to origin) with no rotation or scaling
2013-06-13 09:27:15 +00:00
has 'scale' = > ( is = > 'rw' , default = > sub { 1 } , trigger = > \ & _transform_thumbnail ) ;
has 'rotate' = > ( is = > 'rw' , default = > sub { 0 } , trigger = > \ & _transform_thumbnail ) ; # around object center point
2012-09-12 14:30:44 +00:00
has 'instances' = > ( is = > 'rw' , default = > sub { [] } ) ; # upward Y axis
2013-06-13 09:27:15 +00:00
has 'thumbnail' = > ( is = > 'rw' , trigger = > \ & _transform_thumbnail ) ;
2013-06-12 14:53:19 +00:00
has 'transformed_thumbnail' = > ( is = > 'rw' ) ;
2013-06-13 18:05:32 +00:00
has 'thumbnail_scaling_factor' = > ( is = > 'rw' , trigger = > \ & _transform_thumbnail ) ;
2013-08-25 12:37:50 +00:00
has 'config' = > ( is = > 'rw' , default = > sub { Slic3r::Config - > new } ) ;
2013-03-10 13:58:49 +00:00
has 'layer_height_ranges' = > ( is = > 'rw' , default = > sub { [] } ) ; # [ z_min, z_max, layer_height ]
2013-08-25 17:52:32 +00:00
has 'material_mapping' = > ( is = > 'rw' , default = > sub { { } } ) ; # { material_id => extruder_idx }
2013-11-24 13:28:17 +00:00
has 'mesh_stats' = > ( is = > 'ro' , required = > 1 ) ;
2012-08-29 17:37:27 +00:00
2012-10-24 20:44:08 +00:00
# statistics
has 'facets' = > ( is = > 'rw' ) ;
has 'vertices' = > ( is = > 'rw' ) ;
has 'materials' = > ( is = > 'rw' ) ;
has 'is_manifold' = > ( is = > 'rw' ) ;
2012-10-22 12:02:58 +00:00
sub _trigger_model_object {
2012-08-29 17:37:27 +00:00
my $ self = shift ;
2013-08-25 17:52:32 +00:00
if ( $ self - > model && defined $ self - > model_object_idx ) {
my $ model_object = $ self - > model - > objects - > [ $ self - > model_object_idx ] ;
$ model_object - > align_to_origin ;
$ self - > bounding_box ( $ model_object - > bounding_box ) ;
2013-11-24 21:42:52 +00:00
$ self - > bounding_box - > scale ( 1 / & Slic3r:: SCALING_FACTOR ) ;
2013-06-13 12:33:10 +00:00
2013-08-25 17:52:32 +00:00
my $ mesh = $ model_object - > mesh ;
2013-09-10 17:25:53 +00:00
$ mesh - > repair ;
2013-11-24 00:15:52 +00:00
$ self - > convex_hull ( $ mesh - > convex_hull ) ;
2013-09-11 18:00:51 +00:00
$ self - > facets ( $ mesh - > facets_count ) ;
2012-10-24 20:44:08 +00:00
$ self - > vertices ( scalar @ { $ mesh - > vertices } ) ;
2013-08-25 17:52:32 +00:00
$ self - > materials ( $ model_object - > materials_count ) ;
2012-10-24 20:44:08 +00:00
}
}
2013-07-26 12:26:45 +00:00
sub changescale {
my $ self = shift ;
my ( $ scale ) = @ _ ;
my $ variation = $ scale / $ self - > scale ;
foreach my $ range ( @ { $ self - > layer_height_ranges } ) {
$ range - > [ 0 ] *= $ variation ;
$ range - > [ 1 ] *= $ variation ;
}
$ self - > scale ( $ scale ) ;
}
2013-09-09 22:40:46 +00:00
sub needed_repair {
2012-10-24 20:44:08 +00:00
my $ self = shift ;
2013-09-09 22:40:46 +00:00
if ( $ self - > get_model_object - > needed_repair ) {
warn "Warning: the input file contains manifoldness errors. "
. "Slic3r repaired it successfully by guessing what the correct shape should be, "
. "but you might still want to inspect the G-code before printing.\n" ;
$ self - > is_manifold ( 0 ) ;
} else {
$ self - > is_manifold ( 1 ) ;
2013-07-13 18:34:57 +00:00
}
2012-10-24 20:44:08 +00:00
return $ self - > is_manifold ;
2012-09-12 14:30:44 +00:00
}
2012-10-22 12:02:58 +00:00
sub free_model_object {
2012-10-01 16:12:14 +00:00
my $ self = shift ;
# only delete mesh from memory if we can retrieve it from the original file
2013-05-14 14:31:50 +00:00
return unless $ self - > input_file && defined $ self - > input_file_object_id ;
2013-08-25 17:52:32 +00:00
$ self - > model ( undef ) ;
$ self - > model_object_idx ( undef ) ;
2012-10-01 16:12:14 +00:00
}
2012-10-21 18:56:19 +00:00
sub get_model_object {
2012-09-12 14:30:44 +00:00
my $ self = shift ;
2013-08-25 17:52:32 +00:00
if ( $ self - > model ) {
return $ self - > model - > objects - > [ $ self - > model_object_idx ] ;
2013-03-10 13:58:49 +00:00
}
2013-08-25 17:52:32 +00:00
return Slic3r:: Model
- > read_from_file ( $ self - > input_file )
- > objects
- > [ $ self - > input_file_object_id ] ;
2012-08-29 17:37:27 +00:00
}
sub instances_count {
my $ self = shift ;
return scalar @ { $ self - > instances } ;
}
sub make_thumbnail {
my $ self = shift ;
2013-08-25 17:52:32 +00:00
my $ mesh = $ self - > get_model_object - > mesh ; # $self->model_object is already aligned to origin
2013-09-10 17:25:53 +00:00
$ mesh - > repair ;
2013-08-05 18:48:09 +00:00
if ( @ { $ mesh - > facets } <= 5000 ) {
2013-08-25 20:33:50 +00:00
# remove polygons with area <= 1mm
my $ area_threshold = Slic3r::Geometry:: scale 1 ;
2013-11-12 13:30:13 +00:00
$ self - > thumbnail - > append (
2013-08-25 20:33:50 +00:00
grep $ _ - > area >= $ area_threshold ,
@ { $ mesh - > horizontal_projection } ,
) ;
2013-11-21 19:25:24 +00:00
$ self - > thumbnail - > simplify ( 0.5 ) ;
2013-08-05 18:48:09 +00:00
} else {
2013-11-24 21:42:52 +00:00
my $ convex_hull = Slic3r::ExPolygon - > new ( $ self - > convex_hull ) ;
2013-11-12 13:30:13 +00:00
$ self - > thumbnail - > append ( $ convex_hull ) ;
2013-08-05 18:48:09 +00:00
}
2013-08-05 18:21:08 +00:00
2013-11-12 13:30:13 +00:00
return $ self - > thumbnail ;
2012-08-29 17:37:27 +00:00
}
2013-06-12 14:53:19 +00:00
sub _transform_thumbnail {
2012-08-29 17:37:27 +00:00
my $ self = shift ;
2013-07-13 22:38:01 +00:00
return unless defined $ self - > thumbnail ;
2013-06-13 18:05:32 +00:00
my $ t = $ self - > _apply_transform ( $ self - > thumbnail ) ;
$ t - > scale ( $ self - > thumbnail_scaling_factor ) ;
2012-08-29 17:37:27 +00:00
2013-06-12 14:53:19 +00:00
$ self - > transformed_thumbnail ( $ t ) ;
2012-08-29 17:37:27 +00:00
}
2013-06-07 21:16:02 +00:00
# bounding box with applied rotation and scaling
sub transformed_bounding_box {
my $ self = shift ;
2013-06-15 13:50:02 +00:00
my $ bb = Slic3r::Geometry::BoundingBox - > new_from_points ( $ self - > _apply_transform ( $ self - > convex_hull ) ) ;
$ bb - > extents - > [ Z ] = $ self - > bounding_box - > clone - > extents - > [ Z ] ;
2013-07-26 12:30:00 +00:00
$ bb - > extents - > [ Z ] [ MAX ] *= $ self - > scale ;
2013-06-15 13:50:02 +00:00
return $ bb ;
2013-06-13 18:05:32 +00:00
}
sub _apply_transform {
my $ self = shift ;
my ( $ entity ) = @ _ ; # can be anything that implements ->clone(), ->rotate() and ->scale()
2013-06-07 21:16:02 +00:00
2013-06-13 18:05:32 +00:00
# the order of these transformations MUST be the same everywhere, including
# in Slic3r::Print->add_model()
2013-07-13 22:38:01 +00:00
my $ result = $ entity - > clone ;
2013-07-15 18:31:43 +00:00
$ result - > rotate ( deg2rad ( $ self - > rotate ) , $ self - > bounding_box - > center_2D ) ;
2013-07-13 22:38:01 +00:00
$ result - > scale ( $ self - > scale ) ;
return $ result ;
2013-06-07 21:16:02 +00:00
}
sub transformed_size {
my $ self = shift ;
return $ self - > transformed_bounding_box - > size ;
}
2012-04-30 12:56:01 +00:00
1 ;