mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	Merge with master
This commit is contained in:
		
						commit
						de92f45eaf
					
				
					 125 changed files with 38665 additions and 4069 deletions
				
			
		|  | @ -31,7 +31,6 @@ use Slic3r::GUI::ProgressStatusBar; | |||
| use Slic3r::GUI::OptionsGroup; | ||||
| use Slic3r::GUI::OptionsGroup::Field; | ||||
| use Slic3r::GUI::SystemInfo; | ||||
| use Slic3r::GUI::Tab; | ||||
| 
 | ||||
| our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; | ||||
| our $have_LWP    = eval "use LWP::UserAgent; 1"; | ||||
|  | @ -41,16 +40,17 @@ use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU); | |||
| use base 'Wx::App'; | ||||
| 
 | ||||
| use constant FILE_WILDCARDS => { | ||||
|     known   => 'Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA', | ||||
|     known   => 'Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA', | ||||
|     stl     => 'STL files (*.stl)|*.stl;*.STL', | ||||
|     obj     => 'OBJ files (*.obj)|*.obj;*.OBJ', | ||||
|     amf     => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML', | ||||
|     threemf => '3MF files (*.3mf)|*.3mf;*.3MF', | ||||
|     prusa   => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA', | ||||
|     ini     => 'INI files *.ini|*.ini;*.INI', | ||||
|     gcode   => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC', | ||||
|     svg     => 'SVG files *.svg|*.svg;*.SVG', | ||||
| }; | ||||
| use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)}; | ||||
| use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf threemf prusa)}; | ||||
| 
 | ||||
| # Datadir provided on the command line. | ||||
| our $datadir; | ||||
|  | @ -67,6 +67,10 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | |||
| $medium_font->SetPointSize(12); | ||||
| our $grey = Wx::Colour->new(200,200,200); | ||||
| 
 | ||||
| # Events to be sent from a C++ menu implementation: | ||||
| # 1) To inform about a change of the application language. | ||||
| our $LANGUAGE_CHANGE_EVENT    = Wx::NewEventType; | ||||
| 
 | ||||
| sub OnInit { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|  | @ -80,6 +84,7 @@ sub OnInit { | |||
|     # Mac: "~/Library/Application Support/Slic3r" | ||||
|     Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir); | ||||
|     Slic3r::GUI::set_wxapp($self); | ||||
|     Slic3r::GUI::load_language(); | ||||
|      | ||||
|     $self->{notifier} = Slic3r::GUI::Notifier->new; | ||||
|     $self->{app_config} = Slic3r::GUI::AppConfig->new; | ||||
|  | @ -114,10 +119,12 @@ sub OnInit { | |||
|         # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. | ||||
|         no_controller   => $self->{app_config}->get('no_controller'), | ||||
|         no_plater       => $no_plater, | ||||
|         lang_ch_event   => $LANGUAGE_CHANGE_EVENT, | ||||
|     ); | ||||
|     $self->SetTopWindow($frame); | ||||
| 
 | ||||
|     EVT_IDLE($frame, sub { | ||||
|     #EVT_IDLE($frame, sub { | ||||
|     EVT_IDLE($self->{mainframe}, sub { | ||||
|         while (my $cb = shift @cb) { | ||||
|             $cb->(); | ||||
|         } | ||||
|  | @ -132,10 +139,42 @@ sub OnInit { | |||
|             $self->{mainframe}->config_wizard(1); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     # The following event is emited by the C++ menu implementation of application language change. | ||||
|     EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{ | ||||
|         $self->recreate_GUI; | ||||
|     }); | ||||
|      | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| sub recreate_GUI{ | ||||
|     my ($self) = @_; | ||||
|     my $topwindow = $self->GetTopWindow(); | ||||
|     $self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new( | ||||
|         # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. | ||||
|         no_controller   => $self->{app_config}->get('no_controller'), | ||||
|         no_plater       => $no_plater, | ||||
|         lang_ch_event   => $LANGUAGE_CHANGE_EVENT, | ||||
|     ); | ||||
| 
 | ||||
|     if($topwindow) | ||||
|     { | ||||
|         $self->SetTopWindow($frame); | ||||
|         $topwindow->Destroy; | ||||
|     } | ||||
| 
 | ||||
|     my $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; | ||||
|     if ($run_wizard) { | ||||
|         # On OSX the UI was not initialized correctly if the wizard was called | ||||
|         # before the UI was up and running. | ||||
|         $self->CallAfter(sub { | ||||
|             # Run the config wizard, don't offer the "reset user profile" checkbox. | ||||
|             $self->{mainframe}->config_wizard(1); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub about { | ||||
|     my ($self) = @_; | ||||
|     my $about = Slic3r::GUI::AboutDialog->new(undef); | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| # Slic3r::GUI::3DScene; | ||||
| # | ||||
| # Slic3r::GUI::Plater::3D derives from Slic3r::GUI::3DScene, | ||||
| # Slic3r::GUI::Plater::3DPreview, Slic3r::GUI::Plater::3DToolpaths,  | ||||
| # Slic3r::GUI::Plater::3DPreview, | ||||
| # Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel | ||||
| # own $self->{canvas} of the Slic3r::GUI::3DScene type. | ||||
| # | ||||
|  | @ -66,8 +66,12 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init | |||
|                               _camera_target | ||||
|                               _camera_distance | ||||
|                               _zoom | ||||
|                                | ||||
|                               _legend_enabled | ||||
|                               _apply_zoom_to_volumes_filter | ||||
|                                                              | ||||
|                               ) ); | ||||
| 
 | ||||
|                                | ||||
| use constant TRACKBALLSIZE  => 0.8; | ||||
| use constant TURNTABLE_MODE => 1; | ||||
| use constant GROUND_Z       => -0.02; | ||||
|  | @ -137,7 +141,9 @@ sub new { | |||
|     $self->_stheta(45); | ||||
|     $self->_sphi(45); | ||||
|     $self->_zoom(1); | ||||
|     $self->_legend_enabled(0); | ||||
|     $self->use_plain_shader(0); | ||||
|     $self->_apply_zoom_to_volumes_filter(0); | ||||
| 
 | ||||
|     # Collection of GLVolume objects | ||||
|     $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new); | ||||
|  | @ -209,6 +215,11 @@ sub new { | |||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub set_legend_enabled { | ||||
|     my ($self, $value) = @_; | ||||
|    $self->_legend_enabled($value); | ||||
| } | ||||
| 
 | ||||
| sub Destroy { | ||||
|     my ($self) = @_; | ||||
|     $self->{layer_height_edit_timer}->Stop; | ||||
|  | @ -695,14 +706,18 @@ sub zoom_to_volume { | |||
| 
 | ||||
| sub zoom_to_volumes { | ||||
|     my ($self) = @_; | ||||
|     $self->_apply_zoom_to_volumes_filter(1); | ||||
|     $self->zoom_to_bounding_box($self->volumes_bounding_box); | ||||
|     $self->_apply_zoom_to_volumes_filter(0); | ||||
| } | ||||
| 
 | ||||
| sub volumes_bounding_box { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     my $bb = Slic3r::Geometry::BoundingBoxf3->new; | ||||
|     $bb->merge($_->transformed_bounding_box) for @{$self->volumes}; | ||||
|     foreach my $v (@{$self->volumes}) { | ||||
|         $bb->merge($v->transformed_bounding_box) if (! $self->_apply_zoom_to_volumes_filter || $v->zoom_to_volumes); | ||||
|     } | ||||
|     return $bb; | ||||
| } | ||||
| 
 | ||||
|  | @ -1316,6 +1331,9 @@ sub Render { | |||
|         glDisable(GL_BLEND); | ||||
|     } | ||||
| 
 | ||||
|     # draw gcode preview legend | ||||
|     $self->draw_legend; | ||||
|      | ||||
|     $self->draw_active_object_annotations; | ||||
|      | ||||
|     $self->SwapBuffers(); | ||||
|  | @ -1449,12 +1467,18 @@ sub _variable_layer_thickness_load_reset_image { | |||
| # Paint the tooltip. | ||||
| sub _render_image { | ||||
|     my ($self, $image, $l, $r, $b, $t) = @_; | ||||
|     $self->_render_texture($image->{texture_id}, $l, $r, $b, $t); | ||||
| } | ||||
| 
 | ||||
| sub _render_texture { | ||||
|     my ($self, $tex_id, $l, $r, $b, $t) = @_; | ||||
|      | ||||
|     glColor4f(1.,1.,1.,1.); | ||||
|     glDisable(GL_LIGHTING); | ||||
|     glEnable(GL_BLEND); | ||||
|     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
|     glEnable(GL_TEXTURE_2D); | ||||
|     glBindTexture(GL_TEXTURE_2D, $image->{texture_id}); | ||||
|     glBindTexture(GL_TEXTURE_2D, $tex_id); | ||||
|     glBegin(GL_QUADS); | ||||
|     glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); | ||||
|     glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); | ||||
|  | @ -1579,6 +1603,38 @@ sub draw_active_object_annotations { | |||
|     glEnable(GL_DEPTH_TEST); | ||||
| } | ||||
| 
 | ||||
| sub draw_legend { | ||||
|     my ($self) = @_; | ||||
|   | ||||
|     if ($self->_legend_enabled) | ||||
|     { | ||||
|         # If the legend texture has not been loaded into the GPU, do it now. | ||||
|         my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture; | ||||
|         if ($tex_id > 0) | ||||
|         { | ||||
|             my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width; | ||||
|             my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height; | ||||
|             if (($tex_w > 0) && ($tex_h > 0)) | ||||
|             { | ||||
|                 glDisable(GL_DEPTH_TEST); | ||||
|                 glPushMatrix(); | ||||
|                 glLoadIdentity(); | ||||
|          | ||||
|                 my ($cw, $ch) = $self->GetSizeWH; | ||||
|                  | ||||
|                 my $l = (-0.5 * $cw) / $self->_zoom; | ||||
|                 my $t = (0.5 * $ch) / $self->_zoom; | ||||
|                 my $r = $l + $tex_w / $self->_zoom; | ||||
|                 my $b = $t - $tex_h / $self->_zoom; | ||||
|                 $self->_render_texture($tex_id, $l, $r, $b, $t); | ||||
| 
 | ||||
|                 glPopMatrix(); | ||||
|                 glEnable(GL_DEPTH_TEST); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub opengl_info | ||||
| { | ||||
|     my ($self, %params) = @_; | ||||
|  | @ -1979,9 +2035,20 @@ sub load_wipe_tower_toolpaths { | |||
|         if ($print->step_done(STEP_WIPE_TOWER)); | ||||
| } | ||||
| 
 | ||||
| sub load_gcode_preview { | ||||
|     my ($self, $print, $gcode_preview_data, $colors) = @_; | ||||
| 
 | ||||
|     $self->SetCurrent($self->GetContext) if $self->UseVBOs; | ||||
|     Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs); | ||||
| } | ||||
| 
 | ||||
| sub set_toolpaths_range { | ||||
|     my ($self, $min_z, $max_z) = @_; | ||||
|     $self->volumes->set_range($min_z, $max_z); | ||||
| } | ||||
| 
 | ||||
| sub reset_legend_texture { | ||||
|     Slic3r::GUI::_3DScene::reset_legend_texture(); | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -11,13 +11,23 @@ use List::Util qw(min first); | |||
| use Slic3r::Geometry qw(X Y); | ||||
| use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog | ||||
|     :font :icon wxTheApp); | ||||
| use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED); | ||||
| use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED); | ||||
| use base 'Wx::Frame'; | ||||
| 
 | ||||
| our $qs_last_input_file; | ||||
| our $qs_last_output_file; | ||||
| our $last_config; | ||||
| 
 | ||||
| # Events to be sent from a C++ Tab implementation: | ||||
| # 1) To inform about a change of a configuration value. | ||||
| our $VALUE_CHANGE_EVENT    = Wx::NewEventType; | ||||
| # 2) To inform about a preset selection change or a "modified" status change. | ||||
| our $PRESETS_CHANGED_EVENT = Wx::NewEventType; | ||||
| # 3) To inform about a click on Browse button | ||||
| our $BUTTON_BROWSE_EVENT   = Wx::NewEventType; | ||||
| # 4) To inform about a click on Test button | ||||
| our $BUTTON_TEST_EVENT     = Wx::NewEventType; | ||||
| 
 | ||||
| sub new { | ||||
|     my ($class, %params) = @_; | ||||
|      | ||||
|  | @ -37,6 +47,7 @@ sub new { | |||
|     $self->{no_controller} = $params{no_controller}; | ||||
|     $self->{no_plater} = $params{no_plater}; | ||||
|     $self->{loaded} = 0; | ||||
|     $self->{lang_ch_event} = $params{lang_ch_event}; | ||||
|      | ||||
|     # initialize tabpanel and menubar | ||||
|     $self->_init_tabpanel; | ||||
|  | @ -106,33 +117,41 @@ sub _init_tabpanel { | |||
|             $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller"); | ||||
|         } | ||||
|     } | ||||
|     $self->{options_tabs} = {}; | ||||
|      | ||||
|     for my $tab_name (qw(print filament printer)) { | ||||
|         my $tab; | ||||
|         $tab = $self->{options_tabs}{$tab_name} = ("Slic3r::GUI::Tab::" . ucfirst $tab_name)->new( | ||||
|             $panel,  | ||||
|             no_controller => $self->{no_controller}); | ||||
|         # Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value. | ||||
|         $tab->on_value_change(sub { | ||||
|             my ($opt_key, $value) = @_; | ||||
|             my $config = $tab->{presets}->get_current_preset->config; | ||||
|             if ($self->{plater}) { | ||||
|                 $self->{plater}->on_config_change($config); # propagate config change events to the plater | ||||
|                 $self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count'; | ||||
|     #TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. | ||||
|     # The following event is emited by the C++ Tab implementation on config value change. | ||||
|     EVT_COMMAND($self, -1, $VALUE_CHANGE_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $str = $event->GetString; | ||||
|         my ($opt_key, $name) = ($str =~ /(.*) (.*)/); | ||||
|         #print "VALUE_CHANGE_EVENT: ", $opt_key, "\n"; | ||||
|         my $tab = Slic3r::GUI::get_preset_tab($name); | ||||
|         my $config = $tab->get_config; | ||||
|         if ($self->{plater}) { | ||||
|             $self->{plater}->on_config_change($config); # propagate config change events to the plater | ||||
|             if ($opt_key eq 'extruders_count'){ | ||||
|                 my $value = $event->GetInt(); | ||||
|                 $self->{plater}->on_extruders_change($value); | ||||
|             } | ||||
|             # don't save while loading for the first time | ||||
|             $self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded}; | ||||
|         }); | ||||
|         # Install a callback for the tab to update the platter and print controller presets, when | ||||
|         # a preset changes at Slic3r::GUI::Tab. | ||||
|         $tab->on_presets_changed(sub { | ||||
|             if ($self->{plater}) { | ||||
|                 # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. | ||||
|                 $self->{plater}->update_presets($tab_name, @_); | ||||
|         } | ||||
|         # don't save while loading for the first time | ||||
|         $self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};         | ||||
|     }); | ||||
|     # The following event is emited by the C++ Tab implementation on preset selection, | ||||
|     # or when the preset's "modified" status changes. | ||||
|     EVT_COMMAND($self, -1, $PRESETS_CHANGED_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $tab_name = $event->GetString; | ||||
| 
 | ||||
|         my $tab = Slic3r::GUI::get_preset_tab($tab_name); | ||||
|         if ($self->{plater}) { | ||||
|             # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. | ||||
|             my $presets = $tab->get_presets; | ||||
|             if (defined $presets){ | ||||
|                 my $reload_dependent_tabs = $tab->get_dependent_tabs; | ||||
|                 $self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets); | ||||
|                 if ($tab_name eq 'printer') { | ||||
|                     # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. | ||||
|                     my ($presets, $reload_dependent_tabs) = @_; | ||||
|                     for my $tab_name_other (qw(print filament)) { | ||||
|                         # If the printer tells us that the print or filament preset has been switched or invalidated, | ||||
|                         # refresh the print or filament tab page. Otherwise just refresh the combo box. | ||||
|  | @ -141,23 +160,76 @@ sub _init_tabpanel { | |||
|                         $self->{options_tabs}{$tab_name_other}->$update_action; | ||||
|                     } | ||||
|                     # Update the controller printers. | ||||
|                     $self->{controller}->update_presets(@_) if $self->{controller}; | ||||
|                     $self->{controller}->update_presets($presets) if $self->{controller}; | ||||
|                 } | ||||
|                 $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); | ||||
|                 $self->{plater}->on_config_change($tab->get_config); | ||||
|             } | ||||
|         }); | ||||
|         # Load the currently selected preset into the GUI, update the preset selection box. | ||||
|         $tab->load_current_preset; | ||||
|         $panel->AddPage($tab, $tab->title); | ||||
|     } | ||||
|         } | ||||
|     }); | ||||
|     # The following event is emited by the C++ Tab implementation , | ||||
|     # when the Browse button was clicked | ||||
|     EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $msg = $event->GetString; | ||||
|         print "BUTTON_BROWSE_EVENT: ", $msg, "\n"; | ||||
| 
 | ||||
| #TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. | ||||
| #    Slic3r::GUI::create_preset_tab("print"); | ||||
|         # look for devices | ||||
|         my $entries; | ||||
|         { | ||||
|             my $res = Net::Bonjour->new('http'); | ||||
|             $res->discover; | ||||
|             $entries = [ $res->entries ]; | ||||
|         } | ||||
|         if (@{$entries}) { | ||||
|             my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); | ||||
|             my $tab = Slic3r::GUI::get_preset_tab("printer"); | ||||
|             $tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort) | ||||
|                 if $dlg->ShowModal == wxID_OK; | ||||
|         } else { | ||||
|             Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; | ||||
|         } | ||||
|     }); | ||||
|     # The following event is emited by the C++ Tab implementation , | ||||
|     # when the Test button was clicked | ||||
|     EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my $msg = $event->GetString; | ||||
|         print "BUTTON_TEST_EVENT: ", $msg, "\n"; | ||||
| 
 | ||||
|         my $ua = LWP::UserAgent->new; | ||||
|         $ua->timeout(10); | ||||
| 
 | ||||
|         my $config = Slic3r::GUI::get_preset_tab("printer")->get_config; | ||||
|         my $res = $ua->get( | ||||
|             "http://" . $config->octoprint_host . "/api/version", | ||||
|             'X-Api-Key' => $config->octoprint_apikey, | ||||
|         ); | ||||
|         if ($res->is_success) { | ||||
|             Slic3r::GUI::show_info($self, "Connection to OctoPrint works correctly.", "Success!"); | ||||
|         } else { | ||||
|             Slic3r::GUI::show_error($self, | ||||
|                 "I wasn't able to connect to OctoPrint (" . $res->status_line . "). " | ||||
|                 . "Check hostname and OctoPrint version (at least 1.1.0 is required)."); | ||||
|         } | ||||
|     }); | ||||
|     # A variable to inform C++ Tab implementation about disabling of Browse button | ||||
|     $self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ; | ||||
|     # A variable to inform C++ Tab implementation about user_agent | ||||
|     $self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;     | ||||
|     Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, wxTheApp->{app_config},  | ||||
|                                     $self->{no_controller}, $self->{is_disabled_button_browse}, | ||||
|                                     $self->{is_user_agent}, | ||||
|                                     $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT, | ||||
|                                     $BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT); | ||||
|     $self->{options_tabs} = {}; | ||||
|     for my $tab_name (qw(print filament printer)) { | ||||
|         $self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name"); | ||||
|     } | ||||
|      | ||||
|     if ($self->{plater}) { | ||||
|         $self->{plater}->on_select_preset(sub { | ||||
|             my ($group, $name) = @_; | ||||
| 	        $self->{options_tabs}{$group}->select_preset($name); | ||||
|             $self->{options_tabs}{$group}->select_preset($name); | ||||
|         }); | ||||
|         # load initial config | ||||
|         my $full_config = wxTheApp->{preset_bundle}->full_config; | ||||
|  | @ -244,6 +316,9 @@ sub _init_menubar { | |||
|         $self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', sub { | ||||
|             $plater->export_amf; | ||||
|         }, undef, 'brick_go.png'); | ||||
|         $self->_append_menu_item($self->{plater_menu}, "Export plate as 3MF...", 'Export current plate as 3MF', sub { | ||||
|             $plater->export_3mf; | ||||
|         }, undef, 'brick_go.png'); | ||||
|          | ||||
|         $self->{object_menu} = $self->{plater}->object_menu; | ||||
|         $self->on_plater_selection_changed(0); | ||||
|  | @ -341,9 +416,11 @@ sub _init_menubar { | |||
|         $menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu}; | ||||
|         $menubar->Append($windowMenu, "&Window"); | ||||
|         $menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu}; | ||||
|         # Add an optional debug menu  | ||||
|         # (Select application language from the list of installed languages) | ||||
|         # In production code, the add_debug_menu() call should do nothing. | ||||
|         Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event}); | ||||
|         $menubar->Append($helpMenu, "&Help"); | ||||
|         # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing. | ||||
|         Slic3r::GUI::add_debug_menu($menubar); | ||||
|         $self->SetMenuBar($menubar); | ||||
|     } | ||||
| } | ||||
|  | @ -374,7 +451,7 @@ sub quick_slice { | |||
|         # select input file | ||||
|         my $input_file; | ||||
|         if (!$params{reslice}) { | ||||
|             my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):',  | ||||
|             my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):',  | ||||
|                 wxTheApp->{app_config}->get_last_dir, "",  | ||||
|                 &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); | ||||
|             if ($dialog->ShowModal != wxID_OK) { | ||||
|  | @ -596,7 +673,7 @@ sub load_configbundle { | |||
|     wxTheApp->{app_config}->update_config_dir(dirname($file)); | ||||
| 
 | ||||
|     my $presets_imported = 0; | ||||
|     eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file, $reset_user_profile ? 1 : 0); }; | ||||
|     eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); }; | ||||
|     Slic3r::GUI::catch_error($self) and return; | ||||
| 
 | ||||
|     # Load the currently selected preset into the GUI, update the preset selection box. | ||||
|  | @ -611,7 +688,7 @@ sub load_configbundle { | |||
| # Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset. | ||||
| # Also update the platter with the new presets. | ||||
| sub load_config { | ||||
|     my ($self, $config) = @_;     | ||||
|     my ($self, $config) = @_; | ||||
|     $_->load_config($config) foreach values %{$self->{options_tabs}}; | ||||
|     $self->{plater}->on_config_change($config) if $self->{plater}; | ||||
| } | ||||
|  | @ -661,7 +738,7 @@ sub check_unsaved_changes { | |||
|      | ||||
|     my @dirty = (); | ||||
|     foreach my $tab (values %{$self->{options_tabs}}) { | ||||
|         push @dirty, $tab->title if $tab->{presets}->current_is_dirty; | ||||
|         push @dirty, $tab->title if $tab->current_preset_is_dirty; | ||||
|     } | ||||
|      | ||||
|     if (@dirty) { | ||||
|  |  | |||
|  | @ -158,7 +158,9 @@ sub _build_field { | |||
|      | ||||
|     my $opt_id = $opt->opt_id; | ||||
|     my $on_change = sub { | ||||
|         #! This function will be called from Field. | ||||
|         my ($opt_id, $value) = @_; | ||||
|         #! Call OptionGroup._on_change(...) | ||||
|         $self->_on_change($opt_id, $value) | ||||
|             unless $self->_disabled; | ||||
|     }; | ||||
|  | @ -213,6 +215,8 @@ sub _build_field { | |||
|     } | ||||
|     return undef if !$field; | ||||
|      | ||||
|     #! setting up a function that will be triggered when the field changes | ||||
|     #! think of it as $field->on_change = ($on_change) | ||||
|     $field->on_change($on_change); | ||||
|     $field->on_kill_focus($on_kill_focus); | ||||
|     $self->_fields->{$opt_id} = $field; | ||||
|  |  | |||
|  | @ -60,10 +60,14 @@ sub new { | |||
|     $self->{print} = Slic3r::Print->new; | ||||
|     # List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter. | ||||
|     $self->{objects} = []; | ||||
|     $self->{gcode_preview_data} = Slic3r::GCode::PreviewData->new; | ||||
|      | ||||
|     $self->{print}->set_status_cb(sub { | ||||
|         my ($percent, $message) = @_; | ||||
|         Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message]))); | ||||
|         my $event = Wx::CommandEvent->new($PROGRESS_BAR_EVENT); | ||||
|         $event->SetString($message); | ||||
|         $event->SetInt($percent); | ||||
|         Wx::PostEvent($self, $event); | ||||
|     }); | ||||
|      | ||||
|     # Initialize preview notebook | ||||
|  | @ -137,7 +141,7 @@ sub new { | |||
|      | ||||
|     # Initialize 3D toolpaths preview | ||||
|     if ($Slic3r::GUI::have_OpenGL) { | ||||
|         $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{config}); | ||||
|         $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); | ||||
|         $self->{preview3D}->canvas->on_viewport_changed(sub { | ||||
|             $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); | ||||
|         }); | ||||
|  | @ -153,8 +157,15 @@ sub new { | |||
|      | ||||
|     EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { | ||||
|         my $preview = $self->{preview_notebook}->GetCurrentPage; | ||||
|         $self->{preview3D}->load_print(1) if ($preview == $self->{preview3D}); | ||||
|         $preview->OnActivate if $preview->can('OnActivate'); | ||||
|         if ($preview == $self->{preview3D}) | ||||
|         { | ||||
|             $self->{preview3D}->canvas->set_legend_enabled(1); | ||||
|             $self->{preview3D}->load_print(1); | ||||
|         } else { | ||||
|             $self->{preview3D}->canvas->set_legend_enabled(0); | ||||
|         } | ||||
| 
 | ||||
|         $preview->OnActivate if $preview->can('OnActivate');         | ||||
|     }); | ||||
|      | ||||
|     # toolbar for object manipulation | ||||
|  | @ -307,23 +318,22 @@ sub new { | |||
|      | ||||
|     EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my ($percent, $message) = @{$event->GetData}; | ||||
|         $self->on_progress_event($percent, $message); | ||||
|         $self->on_progress_event($event->GetInt, $event->GetString); | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $ERROR_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         Slic3r::GUI::show_error($self, @{$event->GetData}); | ||||
|         Slic3r::GUI::show_error($self, $event->GetString); | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         $self->on_export_completed($event->GetData); | ||||
|         $self->on_export_completed($event->GetInt); | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $PROCESS_COMPLETED_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         $self->on_process_completed($event->GetData); | ||||
|         $self->on_process_completed($event->GetInt ? undef : $event->GetString); | ||||
|     }); | ||||
|      | ||||
|     { | ||||
|  | @ -365,7 +375,9 @@ sub new { | |||
|                 my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); | ||||
|                 $text->SetFont($Slic3r::GUI::small_font); | ||||
|                 my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); | ||||
|                 EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); | ||||
|                 if ($group eq 'filament') { | ||||
|                     EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); | ||||
|                 } | ||||
|                 $self->{preset_choosers}{$group} = [$choice]; | ||||
|                 # setup the listener | ||||
|                 EVT_COMBOBOX($choice, $choice, sub { | ||||
|  | @ -431,9 +443,10 @@ sub new { | |||
|             $print_info_sizer->Add($grid_sizer, 0, wxEXPAND); | ||||
|             my @info = ( | ||||
|                 fil_m   => "Used Filament (m)", | ||||
|                 fil_mm3 => "Used Filament (mm^3)", | ||||
|                 fil_mm3 => "Used Filament (mm\x{00B3})", | ||||
|                 fil_g   => "Used Filament (g)", | ||||
|                 cost    => "Cost", | ||||
|                 time    => "Estimated printing time", | ||||
|             ); | ||||
|             while (my $field = shift @info) { | ||||
|                 my $label = shift @info; | ||||
|  | @ -609,7 +622,7 @@ sub load_files { | |||
|     # One of the files is potentionally a bundle of files. Don't bundle them, but load them one by one. | ||||
|     # Only bundle .stls or .objs if the printer has multiple extruders. | ||||
|     my $one_by_one = (@$nozzle_dmrs <= 1) || (@$input_files == 1) ||  | ||||
|         defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files); | ||||
|        defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.[aA][mM][fF].[xX][mM][lL]$/ || $_ =~ /.[zZ][iI][pP].[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files); | ||||
|          | ||||
|     my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file\n" . basename($input_files->[0]), 100, $self, 0); | ||||
|     $process_dialog->Pulse; | ||||
|  | @ -627,8 +640,19 @@ sub load_files { | |||
|         my $input_file = $input_files->[$i]; | ||||
|         $process_dialog->Update(100. * $i / @$input_files, "Processing input file\n" . basename($input_file)); | ||||
| 
 | ||||
|         my $model = eval { Slic3r::Model->read_from_file($input_file, 0) }; | ||||
|         Slic3r::GUI::show_error($self, $@) if $@; | ||||
|         my $model; | ||||
|         if (($input_file =~ /.3[mM][fF]$/) || ($input_file =~ /.[zZ][iI][pP].[aA][mM][fF]$/)) | ||||
|         { | ||||
|             $model = eval { Slic3r::Model->read_from_archive($input_file, wxTheApp->{preset_bundle}, 0) }; | ||||
|             Slic3r::GUI::show_error($self, $@) if $@; | ||||
|             $_->load_current_preset for (values %{$self->GetFrame->{options_tabs}}); | ||||
|             wxTheApp->{app_config}->update_config_dir(dirname($input_file)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             $model = eval { Slic3r::Model->read_from_file($input_file, 0) }; | ||||
|             Slic3r::GUI::show_error($self, $@) if $@; | ||||
|         } | ||||
| 
 | ||||
|         next if ! defined $model; | ||||
|          | ||||
|  | @ -1148,6 +1172,7 @@ sub async_apply_config { | |||
|     # Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. | ||||
|     # Otherwise they will be just refreshed. | ||||
|     if ($invalidated) { | ||||
|         $self->{gcode_preview_data}->reset; | ||||
|         $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; | ||||
|         $self->{preview3D}->reload_print if $self->{preview3D}; | ||||
|     } | ||||
|  | @ -1183,12 +1208,15 @@ sub start_background_process { | |||
|         eval { | ||||
|             $self->{print}->process; | ||||
|         }; | ||||
|         my $event = Wx::CommandEvent->new($PROCESS_COMPLETED_EVENT); | ||||
|         if ($@) { | ||||
|             Slic3r::debugf "Background process error: $@\n"; | ||||
|             Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROCESS_COMPLETED_EVENT, $@)); | ||||
|             $event->SetInt(0); | ||||
|             $event->SetString($@); | ||||
|         } else { | ||||
|             Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROCESS_COMPLETED_EVENT, undef)); | ||||
|             $event->SetInt(1); | ||||
|         } | ||||
|         Wx::PostEvent($self, $event); | ||||
|         Slic3r::thread_cleanup(); | ||||
|     }); | ||||
|     Slic3r::debugf "Background processing started.\n"; | ||||
|  | @ -1364,14 +1392,21 @@ sub on_process_completed { | |||
|          | ||||
|         $self->{export_thread} = Slic3r::spawn_thread(sub { | ||||
|             eval { | ||||
|                 $_thread_self->{print}->export_gcode(output_file => $_thread_self->{export_gcode_output_file}); | ||||
|                 $_thread_self->{print}->export_gcode(output_file => $_thread_self->{export_gcode_output_file}, gcode_preview_data => $_thread_self->{gcode_preview_data}); | ||||
|             }; | ||||
|             my $export_completed_event = Wx::CommandEvent->new($EXPORT_COMPLETED_EVENT); | ||||
|             if ($@) { | ||||
|                 Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $ERROR_EVENT, shared_clone([ $@ ]))); | ||||
|                 Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, 0)); | ||||
|                 { | ||||
|                     my $error_event = Wx::CommandEvent->new($ERROR_EVENT); | ||||
|                     $error_event->SetString($@); | ||||
|                     Wx::PostEvent($_thread_self, $error_event); | ||||
|                 } | ||||
|                 $export_completed_event->SetInt(0); | ||||
|                 $export_completed_event->SetString($@); | ||||
|             } else { | ||||
|                 Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, 1)); | ||||
|                 $export_completed_event->SetInt(1); | ||||
|             } | ||||
|             Wx::PostEvent($_thread_self, $export_completed_event); | ||||
|             Slic3r::thread_cleanup(); | ||||
|         }); | ||||
|         Slic3r::debugf "Background G-code export started.\n"; | ||||
|  | @ -1428,11 +1463,16 @@ sub on_export_completed { | |||
|     $self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost)); | ||||
|     $self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight)); | ||||
|     $self->{"print_info_fil_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume)); | ||||
|     $self->{"print_info_time"}->SetLabel($self->{print}->estimated_print_time); | ||||
|     $self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000)); | ||||
|     $self->{"print_info_box_show"}->(1); | ||||
| 
 | ||||
|     # this updates buttons status | ||||
|     $self->object_list_changed; | ||||
|      | ||||
|     # refresh preview | ||||
|     $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; | ||||
|     $self->{preview3D}->reload_print if $self->{preview3D}; | ||||
| } | ||||
| 
 | ||||
| sub do_print { | ||||
|  | @ -1460,15 +1500,19 @@ sub send_gcode { | |||
|     my $ua = LWP::UserAgent->new; | ||||
|     $ua->timeout(180); | ||||
|      | ||||
|     my $enc_path = Slic3r::encode_path($self->{send_gcode_file}); | ||||
|     my $res = $ua->post( | ||||
|         "http://" . $self->{config}->octoprint_host . "/api/files/local", | ||||
|         Content_Type => 'form-data', | ||||
|         'X-Api-Key' => $self->{config}->octoprint_apikey, | ||||
|         Content => [ | ||||
|             # OctoPrint doesn't like Windows paths so we use basename() | ||||
|             # Also, since we need to read from filesystem we process it through encode_path() | ||||
|             file => [ $enc_path, basename($enc_path) ], | ||||
|             file => [  | ||||
|                 # On Windows, the path has to be encoded in local code page for perl to be able to open it. | ||||
|                 Slic3r::encode_path($self->{send_gcode_file}), | ||||
|                 # Remove the UTF-8 flag from the perl string, so the LWP::UserAgent can insert  | ||||
|                 # the UTF-8 encoded string into the request as a byte stream. | ||||
|                 Slic3r::path_to_filename_raw($self->{send_gcode_file}) | ||||
|             ], | ||||
|             print => $self->{send_gcode_file_print} ? 1 : 0, | ||||
|         ], | ||||
|     ); | ||||
|      | ||||
|  | @ -1540,15 +1584,50 @@ sub export_amf { | |||
|     return if !@{$self->{objects}}; | ||||
|     # Ask user for a file name to write into. | ||||
|     my $output_file = $self->_get_export_file('AMF') or return; | ||||
|     $self->{model}->store_amf($output_file); | ||||
|     $self->statusbar->SetStatusText("AMF file exported to $output_file"); | ||||
|     my $res = $self->{model}->store_amf($output_file, $self->{print}); | ||||
|     if ($res) | ||||
|     { | ||||
|         $self->statusbar->SetStatusText("AMF file exported to $output_file"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         $self->statusbar->SetStatusText("Error exporting AMF file $output_file"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub export_3mf { | ||||
|     my ($self) = @_; | ||||
|     return if !@{$self->{objects}}; | ||||
|     # Ask user for a file name to write into. | ||||
|     my $output_file = $self->_get_export_file('3MF') or return; | ||||
|     my $res = $self->{model}->store_3mf($output_file, $self->{print}); | ||||
|     if ($res) | ||||
|     { | ||||
|         $self->statusbar->SetStatusText("3MF file exported to $output_file"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         $self->statusbar->SetStatusText("Error exporting 3MF file $output_file"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| # Ask user to select an output file for a given file format (STl, AMF, 3MF). | ||||
| # Propose a default file name based on the 'output_filename_format' configuration value. | ||||
| sub _get_export_file { | ||||
|     my ($self, $format) = @_;     | ||||
|     my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; | ||||
|     my $suffix = ''; | ||||
|     if ($format eq 'STL') | ||||
|     { | ||||
|         $suffix = '.stl'; | ||||
|     } | ||||
|     elsif ($format eq 'AMF') | ||||
|     { | ||||
|         $suffix = '.zip.amf'; | ||||
|     } | ||||
|     elsif ($format eq '3MF') | ||||
|     { | ||||
|         $suffix = '.3mf'; | ||||
|     } | ||||
|     my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') }; | ||||
|     Slic3r::GUI::catch_error($self) and return undef; | ||||
|     $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; | ||||
|  | @ -2057,8 +2136,8 @@ sub OnDropFiles { | |||
|     # stop scalars leaking on older perl | ||||
|     # https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602 | ||||
|     @_ = (); | ||||
|     # only accept STL, OBJ and AMF files | ||||
|     return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames; | ||||
|     # only accept STL, OBJ, AMF, 3MF and PRUSA files | ||||
|     return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF]|[3][mM][fF]|[aA][mM][fF].[xX][mM][lL]|[zZ][iI][pP].[aA][mM][lL]|[pP][rR][uU][sS][aA])$/, @$filenames; | ||||
|     $self->{window}->load_files($filenames); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,20 +4,22 @@ use warnings; | |||
| use utf8; | ||||
| 
 | ||||
| use Slic3r::Print::State ':steps'; | ||||
| use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE); | ||||
| use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX); | ||||
| use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY); | ||||
| use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX); | ||||
| use base qw(Wx::Panel Class::Accessor); | ||||
| 
 | ||||
| __PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer)); | ||||
| __PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer auto_zoom)); | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $print, $config) = @_; | ||||
|     my ($parent, $print, $gcode_preview_data, $config) = @_; | ||||
|      | ||||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition); | ||||
|     $self->{config} = $config; | ||||
|     $self->{number_extruders} = 1; | ||||
|     # Show by feature type by default. | ||||
|     $self->{preferred_color_mode} = 'feature'; | ||||
|     $self->auto_zoom(1); | ||||
| 
 | ||||
|     # init GUI elements | ||||
|     my $canvas = Slic3r::GUI::3DScene->new($self); | ||||
|  | @ -56,10 +58,35 @@ sub new { | |||
|     $z_label_high->SetFont($Slic3r::GUI::small_font); | ||||
| 
 | ||||
|     $self->single_layer(0); | ||||
|     $self->{color_by_extruder} = 0; | ||||
|     my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, "1 Layer"); | ||||
|     my $checkbox_color_by_extruder = $self->{checkbox_color_by_extruder} = Wx::CheckBox->new($self, -1, "Tool"); | ||||
|      | ||||
|     my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, "View"); | ||||
|      | ||||
|     my $choice_view_type = $self->{choice_view_type} = Wx::Choice->new($self, -1); | ||||
|     $choice_view_type->Append("Feature type"); | ||||
|     $choice_view_type->Append("Height"); | ||||
|     $choice_view_type->Append("Width"); | ||||
|     $choice_view_type->Append("Speed"); | ||||
|     $choice_view_type->Append("Tool"); | ||||
|     $choice_view_type->SetSelection(0); | ||||
| 
 | ||||
|     my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, "Show"); | ||||
|      | ||||
|     my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new(); | ||||
|     $combochecklist_features->Create($self, -1, "Feature types", wxDefaultPosition, [200, -1], wxCB_READONLY); | ||||
|     #FIXME If the following line is removed, the combo box popup list will not react to mouse clicks. | ||||
|     # On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10. | ||||
|     $combochecklist_features->UseAltPopupWindow(); | ||||
|     $combochecklist_features->EnablePopupAnimation(0); | ||||
|     my $feature_text = "Feature types"; | ||||
|     my $feature_items = "Perimeter|External perimeter|Overhang perimeter|Internal infill|Solid infill|Top solid infill|Bridge infill|Gap fill|Skirt|Support material|Support material interface|Wipe tower"; | ||||
|     Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1); | ||||
|      | ||||
|     my $checkbox_travel         = $self->{checkbox_travel}          = Wx::CheckBox->new($self, -1, "Travel"); | ||||
|     my $checkbox_retractions    = $self->{checkbox_retractions}     = Wx::CheckBox->new($self, -1, "Retractions");     | ||||
|     my $checkbox_unretractions  = $self->{checkbox_unretractions}   = Wx::CheckBox->new($self, -1, "Unretractions"); | ||||
|     my $checkbox_shells         = $self->{checkbox_shells}          = Wx::CheckBox->new($self, -1, "Shells"); | ||||
| 
 | ||||
|     my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     my $vsizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|  | @ -72,11 +99,29 @@ sub new { | |||
|     $hsizer->Add($vsizer, 0, wxEXPAND, 0); | ||||
|     $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); | ||||
|     $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); | ||||
|     $vsizer_outer->Add($checkbox_color_by_extruder, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); | ||||
| 
 | ||||
|     my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     $bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->Add($choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->AddSpacer(10); | ||||
|     $bottom_sizer->Add($label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->Add($combochecklist_features, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->AddSpacer(20); | ||||
|     $bottom_sizer->Add($checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->AddSpacer(10); | ||||
|     $bottom_sizer->Add($checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->AddSpacer(10); | ||||
|     $bottom_sizer->Add($checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|     $bottom_sizer->AddSpacer(10); | ||||
|     $bottom_sizer->Add($checkbox_shells, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); | ||||
|      | ||||
|     my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); | ||||
|     $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); | ||||
| 
 | ||||
|     my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0); | ||||
|     $main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0);  | ||||
|      | ||||
|     EVT_SLIDER($self, $slider_low,  sub { | ||||
|         $slider_high->SetValue($slider_low->GetValue) if $self->single_layer; | ||||
|  | @ -147,18 +192,73 @@ sub new { | |||
|             $self->set_z_idx_high($slider_high->GetValue); | ||||
|         } | ||||
|     }); | ||||
|     EVT_CHECKBOX($self, $checkbox_color_by_extruder, sub { | ||||
|         $self->{color_by_extruder} = $checkbox_color_by_extruder->GetValue(); | ||||
|         $self->{preferred_color_mode} = $self->{color_by_extruder} ? 'tool' : 'feature'; | ||||
|     EVT_CHOICE($self, $choice_view_type, sub { | ||||
|         my $selection = $choice_view_type->GetCurrentSelection(); | ||||
|         $self->{preferred_color_mode} = ($selection == 4) ? 'tool' : 'feature'; | ||||
|         $self->gcode_preview_data->set_type($selection); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->reload_print; | ||||
|         $self->auto_zoom(1); | ||||
|     }); | ||||
|     EVT_CHECKLISTBOX($self, $combochecklist_features, sub { | ||||
|         my $flags = Slic3r::GUI::combochecklist_get_flags($combochecklist_features); | ||||
|          | ||||
|         $self->gcode_preview_data->set_extrusion_flags($flags); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->refresh_print; | ||||
|         $self->auto_zoom(1); | ||||
|     });     | ||||
|     EVT_CHECKBOX($self, $checkbox_travel, sub { | ||||
|         $self->gcode_preview_data->set_travel_visible($checkbox_travel->IsChecked()); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->refresh_print; | ||||
|         $self->auto_zoom(1); | ||||
|     });     | ||||
|     EVT_CHECKBOX($self, $checkbox_retractions, sub { | ||||
|         $self->gcode_preview_data->set_retractions_visible($checkbox_retractions->IsChecked()); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->refresh_print; | ||||
|         $self->auto_zoom(1); | ||||
|     }); | ||||
|     EVT_CHECKBOX($self, $checkbox_unretractions, sub { | ||||
|         $self->gcode_preview_data->set_unretractions_visible($checkbox_unretractions->IsChecked()); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->refresh_print; | ||||
|         $self->auto_zoom(1); | ||||
|     }); | ||||
|     EVT_CHECKBOX($self, $checkbox_shells, sub { | ||||
|         $self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked()); | ||||
|         $self->auto_zoom(0); | ||||
|         $self->refresh_print; | ||||
|         $self->auto_zoom(1); | ||||
|     }); | ||||
|      | ||||
|     $self->SetSizer($sizer); | ||||
|     $self->SetSizer($main_sizer); | ||||
|     $self->SetMinSize($self->GetSize); | ||||
|     $sizer->SetSizeHints($self); | ||||
|      | ||||
|     # init canvas | ||||
|     $self->print($print); | ||||
|     $self->gcode_preview_data($gcode_preview_data); | ||||
|      | ||||
|     # sets colors for gcode preview extrusion roles | ||||
|     my @extrusion_roles_colors = ( | ||||
|                                     'Perimeter'                  => 'FFA500', | ||||
|                                     'External perimeter'         => 'FFFF66', | ||||
|                                     'Overhang perimeter'         => '0000FF', | ||||
|                                     'Internal infill'            => 'FF0000', | ||||
|                                     'Solid infill'               => 'CD00CD', | ||||
|                                     'Top solid infill'           => 'FF3333', | ||||
|                                     'Bridge infill'              => '9999FF', | ||||
|                                     'Gap fill'                   => 'FFFFFF', | ||||
|                                     'Skirt'                      => '7F0000', | ||||
|                                     'Support material'           => '00FF00', | ||||
|                                     'Support material interface' => '008000', | ||||
|                                     'Wipe tower'                 => 'B3E3AB', | ||||
|                                  ); | ||||
|     $self->gcode_preview_data->set_extrusion_paths_colors(\@extrusion_roles_colors); | ||||
|      | ||||
|     $self->show_hide_ui_elements('none'); | ||||
|     $self->reload_print; | ||||
|      | ||||
|     return $self; | ||||
|  | @ -171,7 +271,19 @@ sub reload_print { | |||
|     $self->_loaded(0); | ||||
| 
 | ||||
|     if (! $self->IsShown && ! $force) { | ||||
|         $self->{reload_delayed} = 1; | ||||
| #        $self->{reload_delayed} = 1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     $self->load_print; | ||||
| } | ||||
| 
 | ||||
| sub refresh_print { | ||||
|     my ($self) = @_; | ||||
| 
 | ||||
|     $self->_loaded(0); | ||||
|      | ||||
|     if (! $self->IsShown) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -203,6 +315,9 @@ sub load_print { | |||
|         $self->set_z_range(0,0); | ||||
|         $self->slider_low->Hide; | ||||
|         $self->slider_high->Hide; | ||||
|         $self->{z_label_low}->SetLabel(""); | ||||
|         $self->{z_label_high}->SetLabel(""); | ||||
|         $self->canvas->reset_legend_texture(); | ||||
|         $self->canvas->Refresh;  # clears canvas | ||||
|         return; | ||||
|     } | ||||
|  | @ -232,21 +347,20 @@ sub load_print { | |||
|     $self->slider_high->Show; | ||||
|     $self->Layout; | ||||
| 
 | ||||
|     my $by_tool = $self->{color_by_extruder}; | ||||
|     if ($self->{preferred_color_mode} eq 'tool_or_feature') { | ||||
|         # It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature. | ||||
|         # Color by feature if it is a single extruder print. | ||||
|         my $extruders = $self->{print}->extruders; | ||||
|         $by_tool = scalar(@{$extruders}) > 1; | ||||
|         $self->{color_by_extruder} = $by_tool; | ||||
|         $self->{checkbox_color_by_extruder}->SetValue($by_tool); | ||||
|         my $type = (scalar(@{$extruders}) > 1) ? 4 : 0; | ||||
|         $self->gcode_preview_data->set_type($type); | ||||
|         $self->{choice_view_type}->SetSelection($type); | ||||
|         # If the ->SetSelection changed the following line, revert it to "decide yourself". | ||||
|         $self->{preferred_color_mode} = 'tool_or_feature'; | ||||
|     } | ||||
| 
 | ||||
|     # Collect colors per extruder. | ||||
|     # Leave it empty, if the print should be colored by a feature. | ||||
|     my @colors = (); | ||||
|     if ($by_tool) { | ||||
|     if (! $self->gcode_preview_data->empty() || $self->gcode_preview_data->type == 4) { | ||||
|         my @extruder_colors = @{$self->{config}->extruder_colour}; | ||||
|         my @filament_colors = @{$self->{config}->filament_colour}; | ||||
|         for (my $i = 0; $i <= $#extruder_colors; $i += 1) { | ||||
|  | @ -258,18 +372,24 @@ sub load_print { | |||
|     } | ||||
| 
 | ||||
|     if ($self->IsShown) { | ||||
|         # load skirt and brim | ||||
|         $self->canvas->load_print_toolpaths($self->print, \@colors); | ||||
|         $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors); | ||||
|          | ||||
|         foreach my $object (@{$self->print->objects}) { | ||||
|             $self->canvas->load_print_object_toolpaths($object, \@colors); | ||||
|              | ||||
|             # Show the objects in very transparent color. | ||||
|             #my @volume_ids = $self->canvas->load_object($object->model_object); | ||||
|             #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; | ||||
|         if ($self->gcode_preview_data->empty) { | ||||
|             # load skirt and brim | ||||
|             $self->canvas->load_print_toolpaths($self->print, \@colors); | ||||
|             $self->canvas->load_wipe_tower_toolpaths($self->print, \@colors);         | ||||
|             foreach my $object (@{$self->print->objects}) { | ||||
|                 $self->canvas->load_print_object_toolpaths($object, \@colors);             | ||||
|                 # Show the objects in very transparent color. | ||||
|                 #my @volume_ids = $self->canvas->load_object($object->model_object); | ||||
|                 #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; | ||||
|             } | ||||
|             $self->show_hide_ui_elements('simple'); | ||||
|         } else { | ||||
|             $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors); | ||||
|             $self->show_hide_ui_elements('full'); | ||||
|         } | ||||
|         if ($self->auto_zoom) { | ||||
|             $self->canvas->zoom_to_volumes; | ||||
|         } | ||||
|         $self->canvas->zoom_to_volumes; | ||||
|         $self->_loaded(1); | ||||
|     } | ||||
|      | ||||
|  | @ -322,17 +442,27 @@ sub set_number_extruders { | |||
|     my ($self, $number_extruders) = @_; | ||||
|     if ($self->{number_extruders} != $number_extruders) { | ||||
|         $self->{number_extruders} = $number_extruders; | ||||
|         my $by_tool = $number_extruders > 1; | ||||
|         $self->{color_by_extruder} = $by_tool; | ||||
|         $self->{checkbox_color_by_extruder}->SetValue($by_tool); | ||||
|         $self->{preferred_color_mode} = $by_tool ? 'tool_or_feature' : 'feature'; | ||||
|         my $type = ($number_extruders > 1) ? | ||||
|               4  # color by a tool number | ||||
|             : 0; # color by a feature type | ||||
|         $self->{choice_view_type}->SetSelection($type); | ||||
|         $self->gcode_preview_data->set_type($type); | ||||
|         $self->{preferred_color_mode} = ($type == 4) ? 'tool_or_feature' : 'feature'; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub show_hide_ui_elements { | ||||
|     my ($self, $what) = @_; | ||||
|     my $method = ($what eq 'full') ? 'Enable' : 'Disable'; | ||||
|     $self->{$_}->$method for qw(label_show_features combochecklist_features checkbox_travel checkbox_retractions checkbox_unretractions checkbox_shells); | ||||
|     $method = ($what eq 'none') ? 'Disable' : 'Enable'; | ||||
|     $self->{$_}->$method for qw(label_view_type choice_view_type); | ||||
| } | ||||
| 
 | ||||
| # Called by the Platter wxNotebook when this page is activated. | ||||
| sub OnActivate { | ||||
|     my ($self) = @_; | ||||
|     $self->reload_print(1) if ($self->{reload_delayed}); | ||||
| #    my ($self) = @_; | ||||
| #    $self->reload_print(1) if ($self->{reload_delayed}); | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -1,156 +0,0 @@ | |||
| package Slic3r::GUI::Plater::3DToolpaths; | ||||
| use strict; | ||||
| use warnings; | ||||
| use utf8; | ||||
| 
 | ||||
| use Slic3r::Print::State ':steps'; | ||||
| use Wx qw(:misc :sizer :slider :statictext wxWHITE); | ||||
| use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN); | ||||
| use base qw(Wx::Panel Class::Accessor); | ||||
| 
 | ||||
| __PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider)); | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $print) = @_; | ||||
|      | ||||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition); | ||||
|      | ||||
|     # init GUI elements | ||||
|     my $canvas = Slic3r::GUI::3DScene->new($self); | ||||
|     $self->canvas($canvas); | ||||
|     my $slider = Wx::Slider->new( | ||||
|         $self, -1, | ||||
|         0,                              # default | ||||
|         0,                              # min | ||||
|         # we set max to a bogus non-zero value because the MSW implementation of wxSlider | ||||
|         # will skip drawing the slider if max <= min: | ||||
|         1,                              # max | ||||
|         wxDefaultPosition, | ||||
|         wxDefaultSize, | ||||
|         wxVERTICAL | wxSL_INVERSE, | ||||
|     ); | ||||
|     $self->slider($slider); | ||||
|      | ||||
|     my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, | ||||
|         [40,-1], wxALIGN_CENTRE_HORIZONTAL); | ||||
|     $z_label->SetFont($Slic3r::GUI::small_font); | ||||
|      | ||||
|     my $vsizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3); | ||||
|     $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3); | ||||
|      | ||||
|     my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); | ||||
|     $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); | ||||
|      | ||||
|     EVT_SLIDER($self, $slider, sub { | ||||
|         $self->set_z($self->{layers_z}[$slider->GetValue]) | ||||
|             if $self->enabled; | ||||
|     }); | ||||
|     EVT_KEY_DOWN($canvas, sub { | ||||
|         my ($s, $event) = @_; | ||||
|         if ($event->HasModifiers) { | ||||
|             $event->Skip; | ||||
|         } else { | ||||
|             my $key = $event->GetKeyCode; | ||||
|             if ($key == 85 || $key == 315) { | ||||
|                 $slider->SetValue($slider->GetValue + 1); | ||||
|                 $self->set_z($self->{layers_z}[$slider->GetValue]); | ||||
|             } elsif ($key == 68 || $key == 317) { | ||||
|                 $slider->SetValue($slider->GetValue - 1); | ||||
|                 $self->set_z($self->{layers_z}[$slider->GetValue]); | ||||
|             } else { | ||||
|                 $event->Skip; | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|      | ||||
|     $self->SetSizer($sizer); | ||||
|     $self->SetMinSize($self->GetSize); | ||||
|     $sizer->SetSizeHints($self); | ||||
|      | ||||
|     # init canvas | ||||
|     $self->print($print); | ||||
|     $self->reload_print; | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub reload_print { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     $self->canvas->reset_objects; | ||||
|     $self->_loaded(0); | ||||
|     $self->load_print; | ||||
| } | ||||
| 
 | ||||
| sub load_print { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     return if $self->_loaded; | ||||
|      | ||||
|     # we require that there's at least one object and the posSlice step | ||||
|     # is performed on all of them (this ensures that _shifted_copies was | ||||
|     # populated and we know the number of layers) | ||||
|     if (!$self->print->object_step_done(STEP_SLICE)) { | ||||
|         $self->enabled(0); | ||||
|         $self->slider->Hide; | ||||
|         $self->canvas->Refresh;  # clears canvas | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     my $z_idx; | ||||
|     { | ||||
|         my %z = ();  # z => 1 | ||||
|         foreach my $object (@{$self->{print}->objects}) { | ||||
|             foreach my $layer (@{$object->layers}, @{$object->support_layers}) { | ||||
|                 $z{$layer->print_z} = 1; | ||||
|             } | ||||
|         } | ||||
|         $self->enabled(1); | ||||
|         $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; | ||||
|         $self->slider->SetRange(0, scalar(@{$self->{layers_z}})-1); | ||||
|         if (($z_idx = $self->slider->GetValue) <= $#{$self->{layers_z}} && $self->slider->GetValue != 0) { | ||||
|             # use $z_idx | ||||
|         } else { | ||||
|             $self->slider->SetValue(scalar(@{$self->{layers_z}})-1); | ||||
|             $z_idx = @{$self->{layers_z}} ? -1 : undef; | ||||
|         } | ||||
|         $self->slider->Show; | ||||
|         $self->Layout; | ||||
|     } | ||||
|      | ||||
|     if ($self->IsShown) { | ||||
|         # load skirt and brim | ||||
|         $self->canvas->load_print_toolpaths($self->print); | ||||
|          | ||||
|         foreach my $object (@{$self->print->objects}) { | ||||
|             $self->canvas->load_print_object_toolpaths($object); | ||||
|              | ||||
|             # Show the objects in very transparent color. | ||||
|             #my @volume_ids = $self->canvas->load_object($object->model_object); | ||||
|             #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; | ||||
|         } | ||||
|         $self->canvas->zoom_to_volumes; | ||||
|         $self->_loaded(1); | ||||
|     } | ||||
|      | ||||
|     $self->set_z($self->{layers_z}[$z_idx]); | ||||
| } | ||||
| 
 | ||||
| sub set_z { | ||||
|     my ($self, $z) = @_; | ||||
|      | ||||
|     return if !$self->enabled; | ||||
|     $self->{z_label}->SetLabel(sprintf '%.2f', $z); | ||||
|     $self->canvas->set_toolpaths_range(0, $z); | ||||
|     $self->canvas->Refresh if $self->IsShown; | ||||
| } | ||||
| 
 | ||||
| sub set_bed_shape { | ||||
|     my ($self, $bed_shape) = @_; | ||||
|     $self->canvas->set_bed_shape($bed_shape); | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -81,13 +81,20 @@ sub export_gcode { | |||
|     $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); | ||||
| 
 | ||||
|     # The following line may die for multiple reasons. | ||||
|     Slic3r::GCode->new->do_export($self, $output_file); | ||||
|     my $gcode = Slic3r::GCode->new; | ||||
|     if (defined $params{gcode_preview_data}) { | ||||
|         $gcode->do_export_w_preview($self, $output_file, $params{gcode_preview_data}); | ||||
|     } else { | ||||
|         $gcode->do_export($self, $output_file); | ||||
|     } | ||||
|      | ||||
|     # run post-processing scripts | ||||
|     if (@{$self->config->post_process}) { | ||||
|         $self->status_cb->(95, "Running post-processing scripts"); | ||||
|         $self->config->setenv; | ||||
|         for my $script (@{$self->config->post_process}) { | ||||
|             # Ignore empty post processing script lines. | ||||
|             next if $script =~ /^\s*$/; | ||||
|             Slic3r::debugf "  '%s' '%s'\n", $script, $output_file; | ||||
|             # -x doesn't return true on Windows except for .exe files | ||||
|             if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Matena
						Lukas Matena