Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-09-12 12:59:02 +0300
committerbubnikv <bubnikv@gmail.com>2018-09-12 12:59:02 +0300
commit0235f1a8211cfecf75ac0a585295bc1378747972 (patch)
tree9c1d06a43a691d9691c7eff6f21646b0dcc8cc74 /lib
parent41ce69f327a2fe3324dc47756b64a6fdb001cfa3 (diff)
parenta97df55592f524eb73bd7d04ef8b7c1b3525d27d (diff)
Merged with dev
Diffstat (limited to 'lib')
-rw-r--r--lib/Slic3r/ExPolygon.pm6
-rw-r--r--lib/Slic3r/GUI.pm98
-rw-r--r--lib/Slic3r/GUI/2DBed.pm1
-rw-r--r--lib/Slic3r/GUI/3DScene.pm2046
-rw-r--r--lib/Slic3r/GUI/AboutDialog.pm122
-rw-r--r--lib/Slic3r/GUI/BedShapeDialog.pm316
-rw-r--r--lib/Slic3r/GUI/ConfigWizard.pm458
-rw-r--r--lib/Slic3r/GUI/Controller.pm4
-rw-r--r--lib/Slic3r/GUI/MainFrame.pm238
-rw-r--r--lib/Slic3r/GUI/OptionsGroup/Field.pm5
-rw-r--r--lib/Slic3r/GUI/Plater.pm1372
-rw-r--r--lib/Slic3r/GUI/Plater/2D.pm7
-rw-r--r--lib/Slic3r/GUI/Plater/2DToolpaths.pm69
-rw-r--r--lib/Slic3r/GUI/Plater/3D.pm227
-rw-r--r--lib/Slic3r/GUI/Plater/3DPreview.pm164
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectCutDialog.pm33
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm128
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm3
-rw-r--r--lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm2
-rw-r--r--lib/Slic3r/GUI/ProgressStatusBar.pm138
-rw-r--r--lib/Slic3r/Print.pm33
-rw-r--r--lib/Slic3r/Print/Simple.pm10
22 files changed, 1433 insertions, 4047 deletions
diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm
index dfad9db34..6adb650c2 100644
--- a/lib/Slic3r/ExPolygon.pm
+++ b/lib/Slic3r/ExPolygon.pm
@@ -7,12 +7,6 @@ use warnings;
use List::Util qw(first);
use Slic3r::Geometry::Clipper qw(union_ex diff_pl);
-sub wkt {
- my $self = shift;
- return sprintf "POLYGON(%s)",
- join ',', map "($_)", map { join ',', map "$_->[0] $_->[1]", @$_ } @$self;
-}
-
sub offset {
my $self = shift;
return Slic3r::Geometry::Clipper::offset(\@$self, @_);
diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 663c53892..483fd36f9 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -7,9 +7,6 @@ use File::Basename qw(basename);
use FindBin;
use List::Util qw(first);
use Slic3r::GUI::2DBed;
-use Slic3r::GUI::AboutDialog;
-use Slic3r::GUI::BedShapeDialog;
-use Slic3r::GUI::ConfigWizard;
use Slic3r::GUI::Controller;
use Slic3r::GUI::Controller::ManualControlDialog;
use Slic3r::GUI::Controller::PrinterPanel;
@@ -70,6 +67,8 @@ our $grey = Wx::Colour->new(200,200,200);
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
# 2) To inform about a change of Preferences.
our $PREFERENCES_EVENT = Wx::NewEventType;
+# To inform AppConfig about Slic3r version available online
+our $VERSION_ONLINE_EVENT = Wx::NewEventType;
sub OnInit {
my ($self) = @_;
@@ -86,7 +85,9 @@ sub OnInit {
Slic3r::GUI::set_wxapp($self);
$self->{app_config} = Slic3r::GUI::AppConfig->new;
+ Slic3r::GUI::set_app_config($self->{app_config});
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
+ Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
# just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
# supplied as argument to --datadir; in that case we should still run the wizard
@@ -95,28 +96,27 @@ sub OnInit {
warn $@ . "\n";
fatal_error(undef, $@);
}
- my $run_wizard = ! $self->{app_config}->exists;
+ my $app_conf_exists = $self->{app_config}->exists;
# load settings
- $self->{app_config}->load if ! $run_wizard;
+ $self->{app_config}->load if $app_conf_exists;
$self->{app_config}->set('version', $Slic3r::VERSION);
$self->{app_config}->save;
- Slic3r::GUI::set_app_config($self->{app_config});
+ $self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT);
+ Slic3r::GUI::set_preset_updater($self->{preset_updater});
+
Slic3r::GUI::load_language();
# Suppress the '- default -' presets.
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
- eval { $self->{preset_bundle}->load_presets };
+ eval { $self->{preset_bundle}->load_presets($self->{app_config}); };
if ($@) {
warn $@ . "\n";
show_error(undef, $@);
}
- eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
- $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
- Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
-
# application frame
+ print STDERR "Creating main frame...\n";
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
$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.
@@ -127,7 +127,7 @@ sub OnInit {
);
$self->SetTopWindow($frame);
- #EVT_IDLE($frame, sub {
+ # This makes CallAfter() work
EVT_IDLE($self->{mainframe}, sub {
while (my $cb = shift @cb) {
$cb->();
@@ -135,17 +135,32 @@ sub OnInit {
$self->{app_config}->save if $self->{app_config}->dirty;
});
- 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);
- });
- }
+ # On OS X the UI tends to freeze in weird ways if modal dialogs (config wizard, update notifications, ...)
+ # are shown before or in the same event callback with the main frame creation.
+ # Therefore we schedule them for later using CallAfter.
+ $self->CallAfter(sub {
+ eval {
+ if (! $self->{preset_updater}->config_update()) {
+ $self->{mainframe}->Close;
+ }
+ };
+ if ($@) {
+ show_error(undef, $@);
+ $self->{mainframe}->Close;
+ }
+ });
+
+ $self->CallAfter(sub {
+ if (! Slic3r::GUI::config_wizard_startup($app_conf_exists)) {
+ # Only notify if there was not wizard so as not to bother too much ...
+ $self->{preset_updater}->slic3r_update_notify();
+ }
+ $self->{preset_updater}->sync($self->{preset_bundle});
+ });
# The following event is emited by the C++ menu implementation of application language change.
EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{
+ print STDERR "LANGUAGE_CHANGE_EVENT\n";
$self->recreate_GUI;
});
@@ -154,10 +169,20 @@ sub OnInit {
$self->update_ui_from_settings;
});
+ # The following event is emited by PresetUpdater (C++) to inform about
+ # the newer Slic3r application version avaiable online.
+ EVT_COMMAND($self, -1, $VERSION_ONLINE_EVENT, sub {
+ my ($self, $event) = @_;
+ my $version = $event->GetString;
+ $self->{app_config}->set('version_online', $version);
+ $self->{app_config}->save;
+ });
+
return 1;
}
sub recreate_GUI{
+ print STDERR "recreate_GUI\n";
my ($self) = @_;
my $topwindow = $self->GetTopWindow();
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
@@ -174,22 +199,19 @@ sub recreate_GUI{
$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);
- });
- }
-}
+ EVT_IDLE($self->{mainframe}, sub {
+ while (my $cb = shift @cb) {
+ $cb->();
+ }
+ $self->{app_config}->save if $self->{app_config}->dirty;
+ });
-sub about {
- my ($self) = @_;
- my $about = Slic3r::GUI::AboutDialog->new(undef);
- $about->ShowModal;
- $about->Destroy;
+ # 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.
+ Slic3r::GUI::config_wizard_startup(1);
+ });
}
sub system_info {
@@ -201,8 +223,8 @@ sub system_info {
my $opengl_info_txt = '';
if (defined($self->{mainframe}) && defined($self->{mainframe}->{plater}) &&
defined($self->{mainframe}->{plater}->{canvas3D})) {
- $opengl_info = $self->{mainframe}->{plater}->{canvas3D}->opengl_info(format => 'html');
- $opengl_info_txt = $self->{mainframe}->{plater}->{canvas3D}->opengl_info;
+ $opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1);
+ $opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1);
}
my $about = Slic3r::GUI::SystemInfo->new(
parent => undef,
@@ -232,7 +254,7 @@ sub catch_error {
# static method accepting a wxWindow object as first parameter
sub show_error {
my ($parent, $message) = @_;
- Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal;
+ Slic3r::GUI::show_error_id($parent ? $parent->GetId() : 0, $message);
}
# static method accepting a wxWindow object as first parameter
diff --git a/lib/Slic3r/GUI/2DBed.pm b/lib/Slic3r/GUI/2DBed.pm
index ebbc70b6b..0891a4836 100644
--- a/lib/Slic3r/GUI/2DBed.pm
+++ b/lib/Slic3r/GUI/2DBed.pm
@@ -1,4 +1,5 @@
# Bed shape dialog
+# still used by the Slic3r::GUI::Controller::ManualControlDialog Perl module.
package Slic3r::GUI::2DBed;
use strict;
diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 72423b946..23decaa37 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -16,90 +16,10 @@ use strict;
use warnings;
use Wx qw(wxTheApp :timer :bitmap :icon :dialog);
-use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
use base qw(Wx::GLCanvas Class::Accessor);
-use Math::Trig qw(asin tan);
-use List::Util qw(reduce min max first);
-use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon);
-use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND);
use Wx::GLCanvas qw(:all);
-use Slic3r::Geometry qw(PI);
-
-# _dirty: boolean flag indicating, that the screen has to be redrawn on EVT_IDLE.
-# volumes: reference to vector of Slic3r::GUI::3DScene::Volume.
-# _camera_type: 'perspective' or 'ortho'
-__PACKAGE__->mk_accessors( qw(_quat _dirty init
- enable_picking
- enable_moving
- use_plain_shader
- on_viewport_changed
- on_hover
- on_select
- on_double_click
- on_right_click
- on_move
- on_model_update
- volumes
- _sphi _stheta
- cutting_plane_z
- cut_lines_vertices
- bed_shape
- bed_triangles
- bed_grid_lines
- bed_polygon
- background
- origin
- _mouse_pos
- _hover_volume_idx
-
- _drag_volume_idx
- _drag_start_pos
- _drag_volume_center_offset
- _drag_start_xy
- _dragged
-
- _layer_height_edited
-
- _camera_type
- _camera_target
- _camera_distance
- _zoom
-
- _legend_enabled
- _warning_enabled
- _apply_zoom_to_volumes_filter
-
- ) );
-
-use constant TRACKBALLSIZE => 0.8;
-use constant TURNTABLE_MODE => 1;
-use constant GROUND_Z => -0.02;
-# For mesh selection: Not selected - bright yellow.
-use constant DEFAULT_COLOR => [1,1,0];
-# For mesh selection: Selected - bright green.
-use constant SELECTED_COLOR => [0,1,0,1];
-# For mesh selection: Mouse hovers over the object, but object not selected yet - dark green.
-use constant HOVER_COLOR => [0.4,0.9,0,1];
-
-# phi / theta angles to orient the camera.
-use constant VIEW_DEFAULT => [45.0,45.0];
-use constant VIEW_LEFT => [90.0,90.0];
-use constant VIEW_RIGHT => [-90.0,90.0];
-use constant VIEW_TOP => [0.0,0.0];
-use constant VIEW_BOTTOM => [0.0,180.0];
-use constant VIEW_FRONT => [0.0,90.0];
-use constant VIEW_REAR => [180.0,90.0];
-
-use constant MANIPULATION_IDLE => 0;
-use constant MANIPULATION_DRAGGING => 1;
-use constant MANIPULATION_LAYER_HEIGHT => 2;
-
-use constant GIMBALL_LOCK_THETA_MAX => 170;
-
-use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70;
-use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22;
sub new {
my ($class, $parent) = @_;
@@ -123,1986 +43,28 @@ sub new {
# we request a depth buffer explicitely because it looks like it's not created by
# default on Linux, causing transparency issues
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib);
- if (Wx::wxVERSION >= 3.000003) {
- # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list.
- # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs.
- # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
- $self->GetContext();
- }
-
- $self->{can_multisample} = $can_multisample;
- $self->background(1);
- $self->_quat((0, 0, 0, 1));
- $self->_stheta(45);
- $self->_sphi(45);
- $self->_zoom(1);
- $self->_legend_enabled(0);
- $self->_warning_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);
-
- # 3D point in model space
- $self->_camera_type('ortho');
-# $self->_camera_type('perspective');
- $self->_camera_target(Slic3r::Pointf3->new(0,0,0));
- $self->_camera_distance(0.);
-
- $self->layer_editing_enabled(0);
- $self->{layer_height_edit_band_width} = 2.;
- $self->{layer_height_edit_strength} = 0.005;
- $self->{layer_height_edit_last_object_id} = -1;
- $self->{layer_height_edit_last_z} = 0.;
- $self->{layer_height_edit_last_action} = 0;
- $self->reset_objects;
-
- EVT_PAINT($self, sub {
- my $dc = Wx::PaintDC->new($self);
- $self->Render($dc);
- });
- EVT_SIZE($self, sub { $self->_dirty(1) });
- EVT_IDLE($self, sub {
- return unless $self->_dirty;
- return if !$self->IsShownOnScreen;
- $self->Resize( $self->GetSizeWH );
- $self->Refresh;
- });
- EVT_MOUSEWHEEL($self, \&mouse_wheel_event);
- EVT_MOUSE_EVENTS($self, \&mouse_event);
-# EVT_KEY_DOWN($self, sub {
- EVT_CHAR($self, sub {
- my ($s, $event) = @_;
- if ($event->HasModifiers) {
- $event->Skip;
- } else {
- my $key = $event->GetKeyCode;
- if ($key == ord('0')) {
- $self->select_view('iso');
- } elsif ($key == ord('1')) {
- $self->select_view('top');
- } elsif ($key == ord('2')) {
- $self->select_view('bottom');
- } elsif ($key == ord('3')) {
- $self->select_view('front');
- } elsif ($key == ord('4')) {
- $self->select_view('rear');
- } elsif ($key == ord('5')) {
- $self->select_view('left');
- } elsif ($key == ord('6')) {
- $self->select_view('right');
- } else {
- $event->Skip;
- }
- }
- });
-
- $self->{layer_height_edit_timer_id} = &Wx::NewId();
- $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id});
- EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub {
- my ($self, $event) = @_;
- return if $self->_layer_height_edited != 1;
- return if $self->{layer_height_edit_last_object_id} == -1;
- $self->_variable_layer_thickness_action(undef);
- });
+ Slic3r::GUI::_3DScene::add_canvas($self);
+ Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample);
return $self;
}
-sub set_legend_enabled {
- my ($self, $value) = @_;
- $self->_legend_enabled($value);
-}
-
-sub set_warning_enabled {
- my ($self, $value) = @_;
- $self->_warning_enabled($value);
-}
-
sub Destroy {
my ($self) = @_;
- $self->{layer_height_edit_timer}->Stop;
- $self->DestroyGL;
+ Slic3r::GUI::_3DScene::remove_canvas($self);
return $self->SUPER::Destroy;
}
-sub layer_editing_enabled {
- my ($self, $value) = @_;
- if (@_ == 2) {
- $self->{layer_editing_enabled} = $value;
- if ($value) {
- if (! $self->{layer_editing_initialized}) {
- # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders.
- # If compilation fails, a message box is shown with the error codes.
- $self->SetCurrent($self->GetContext);
- my $shader = new Slic3r::GUI::_3DScene::GLShader;
- my $error_message;
- if (! $shader->load($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) {
- # Compilation or linking of the shaders failed.
- $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n"
- . $shader->last_error;
- $shader = undef;
- } else {
- $self->{layer_height_edit_shader} = $shader;
- ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
- glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
- if (defined($error_message)) {
- # Don't enable the layer editing tool.
- $self->{layer_editing_enabled} = 0;
- # 2 means failed
- $self->{layer_editing_initialized} = 2;
- # Show the error message.
- Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self);
- } else {
- $self->{layer_editing_initialized} = 1;
- }
- } elsif ($self->{layer_editing_initialized} == 2) {
- # Initilization failed before. Don't try to initialize and disable layer editing.
- $self->{layer_editing_enabled} = 0;
- }
- }
- }
- return $self->{layer_editing_enabled};
-}
-
-sub layer_editing_allowed {
- my ($self) = @_;
- # Allow layer editing if either the shaders were not initialized yet and we don't know
- # whether it will be possible to initialize them,
- # or if the initialization was done already and it failed.
- return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2);
-}
-
-sub _first_selected_object_id_for_variable_layer_height_editing {
- my ($self) = @_;
- for my $i (0..$#{$self->volumes}) {
- if ($self->volumes->[$i]->selected) {
- my $object_id = int($self->volumes->[$i]->select_group_id / 1000000);
- # Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
- return ($object_id >= $self->{print}->object_count) ? -1 : $object_id
- if $object_id < 10000;
- }
- }
- return -1;
-}
-
-# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
-sub _variable_layer_thickness_bar_rect_screen {
- my ($self) = @_;
- my ($cw, $ch) = $self->GetSizeWH;
- return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT);
-}
-
-sub _variable_layer_thickness_bar_rect_viewport {
- my ($self) = @_;
- my ($cw, $ch) = $self->GetSizeWH;
- return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom));
-}
-
-# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
-sub _variable_layer_thickness_reset_rect_screen {
- my ($self) = @_;
- my ($cw, $ch) = $self->GetSizeWH;
- return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, $cw, $ch);
-}
-
-sub _variable_layer_thickness_reset_rect_viewport {
- my ($self) = @_;
- my ($cw, $ch) = $self->GetSizeWH;
- return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom);
-}
-
-sub _variable_layer_thickness_bar_rect_mouse_inside {
- my ($self, $mouse_evt) = @_;
- my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
- return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom;
-}
-
-sub _variable_layer_thickness_reset_rect_mouse_inside {
- my ($self, $mouse_evt) = @_;
- my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen;
- return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom;
-}
-
-sub _variable_layer_thickness_bar_mouse_cursor_z_relative {
- my ($self) = @_;
- my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
- my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
- return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ?
- # Inside the bar.
- ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) :
- # Outside the bar.
- -1000.;
-}
-
-sub _variable_layer_thickness_action {
- my ($self, $mouse_event, $do_modification) = @_;
- # A volume is selected. Test, whether hovering over a layer thickness bar.
- return if $self->{layer_height_edit_last_object_id} == -1;
- if (defined($mouse_event)) {
- my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
- $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z)
- * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top);
- $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1);
- }
- # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it?
- # Start a timer to refresh the print? schedule_background_process() ?
- # The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself.
- $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile(
- $self->{layer_height_edit_last_z},
- $self->{layer_height_edit_strength},
- $self->{layer_height_edit_band_width},
- $self->{layer_height_edit_last_action});
- $self->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture(
- $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1);
- $self->Refresh;
- # Automatic action on mouse down with the same coordinate.
- $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS);
-}
-
-sub mouse_event {
- my ($self, $e) = @_;
-
- my $pos = Slic3r::Pointf->new($e->GetPositionXY);
- my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id_for_variable_layer_height_editing : -1;
-
- if ($e->Entering && &Wx::wxMSW) {
- # wxMSW needs focus in order to catch mouse wheel events
- $self->SetFocus;
- $self->_drag_start_xy(undef);
- } elsif ($e->LeftDClick) {
- if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
- } elsif ($self->on_double_click) {
- $self->on_double_click->();
- }
- } elsif ($e->LeftDown || $e->RightDown) {
- # If user pressed left or right button we first check whether this happened
- # on a volume or not.
- my $volume_idx = $self->_hover_volume_idx // -1;
- $self->_layer_height_edited(0);
- if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
- # A volume is selected and the mouse is hovering over a layer thickness bar.
- # Start editing the layer height.
- $self->_layer_height_edited(1);
- $self->_variable_layer_thickness_action($e);
- } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) {
- $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile;
- # Index 2 means no editing, just wait for mouse up event.
- $self->_layer_height_edited(2);
- $self->Refresh;
- $self->Update;
- } else {
- # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y,
- # an converts the screen space coordinate to unscaled object space.
- my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos);
-
- # Select volume in this 3D canvas.
- # Don't deselect a volume if layer editing is enabled. We want the object to stay selected
- # during the scene manipulation.
- if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
- $self->deselect_volumes;
- $self->select_volume($volume_idx);
-
- if ($volume_idx != -1) {
- my $group_id = $self->volumes->[$volume_idx]->select_group_id;
- my @volumes;
- if ($group_id != -1) {
- $self->select_volume($_)
- for grep $self->volumes->[$_]->select_group_id == $group_id,
- 0..$#{$self->volumes};
- }
- }
-
- $self->Refresh;
- $self->Update;
- }
-
- # propagate event through callback
- $self->on_select->($volume_idx)
- if $self->on_select;
-
- if ($volume_idx != -1) {
- if ($e->LeftDown && $self->enable_moving) {
- # Only accept the initial position, if it is inside the volume bounding box.
- my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box;
- $volume_bbox->offset(1.);
- if ($volume_bbox->contains_point($pos3d)) {
- # The dragging operation is initiated.
- $self->_drag_volume_idx($volume_idx);
- $self->_drag_start_pos($pos3d);
- # Remember the shift to to the object center. The object center will later be used
- # to limit the object placement close to the bed.
- $self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center));
- }
- } elsif ($e->RightDown) {
- # if right clicking on volume, propagate event through callback
- $self->on_right_click->($e->GetPosition)
- if $self->on_right_click;
- }
- }
- }
- } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
- # Get new position at the same Z of the initial click point.
- my $cur_pos = Slic3r::Linef3->new(
- $self->mouse_to_3d($e->GetX, $e->GetY, 0),
- $self->mouse_to_3d($e->GetX, $e->GetY, 1))
- ->intersect_plane($self->_drag_start_pos->z);
- # Clip the new position, so the object center remains close to the bed.
- {
- $cur_pos->translate(@{$self->_drag_volume_center_offset});
- my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y));
- if (! $self->bed_polygon->contains_point($cur_pos2)) {
- my $ip = $self->bed_polygon->point_projection($cur_pos2);
- $cur_pos->set_x(unscale($ip->x));
- $cur_pos->set_y(unscale($ip->y));
- }
- $cur_pos->translate(@{$self->_drag_volume_center_offset->negative});
- }
- # Calculate the translation vector.
- my $vector = $self->_drag_start_pos->vector_to($cur_pos);
- # Get the volume being dragged.
- my $volume = $self->volumes->[$self->_drag_volume_idx];
- # Get all volumes belonging to the same group, if any.
- my @volumes = ($volume->drag_group_id == -1) ?
- ($volume) :
- grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
- # Apply new temporary volume origin and ignore Z.
- $_->translate($vector->x, $vector->y, 0) for @volumes;
- $self->_drag_start_pos($cur_pos);
- $self->_dragged(1);
- $self->Refresh;
- $self->Update;
- } elsif ($e->Dragging) {
- if ($self->_layer_height_edited && $object_idx_selected != -1) {
- $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1);
- } elsif ($e->LeftIsDown) {
- # if dragging over blank area with left button, rotate
- if (defined $self->_drag_start_pos) {
- my $orig = $self->_drag_start_pos;
- if (TURNTABLE_MODE) {
- # Turntable mode is enabled by default.
- $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
- $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
- $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
- $self->_stheta(0) if $self->_stheta < 0;
- } else {
- my $size = $self->GetClientSize;
- my @quat = trackball(
- $orig->x / ($size->width / 2) - 1,
- 1 - $orig->y / ($size->height / 2), #/
- $pos->x / ($size->width / 2) - 1,
- 1 - $pos->y / ($size->height / 2), #/
- );
- $self->_quat(mulquats($self->_quat, \@quat));
- }
- $self->on_viewport_changed->() if $self->on_viewport_changed;
- $self->Refresh;
- $self->Update;
- }
- $self->_drag_start_pos($pos);
- } elsif ($e->MiddleIsDown || $e->RightIsDown) {
- # If dragging over blank area with right button, pan.
- if (defined $self->_drag_start_xy) {
- # get point in model space at Z = 0
- my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0);
- my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0);
- $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative});
- $self->on_viewport_changed->() if $self->on_viewport_changed;
- $self->Refresh;
- $self->Update;
- }
- $self->_drag_start_xy($pos);
- }
- } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
- if ($self->_layer_height_edited) {
- $self->_layer_height_edited(undef);
- $self->{layer_height_edit_timer}->Stop;
- $self->on_model_update->()
- if ($object_idx_selected != -1 && $self->on_model_update);
- } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) {
- # get all volumes belonging to the same group, if any
- my @volume_idxs;
- my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id;
- if ($group_id == -1) {
- @volume_idxs = ($self->_drag_volume_idx);
- } else {
- @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id,
- 0..$#{$self->volumes};
- }
- $self->on_move->(@volume_idxs);
- }
- $self->_drag_volume_idx(undef);
- $self->_drag_start_pos(undef);
- $self->_drag_start_xy(undef);
- $self->_dragged(undef);
- } elsif ($e->Moving) {
- $self->_mouse_pos($pos);
- # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor
- # hovers over.
- if ($self->enable_picking) {
- $self->Update;
- $self->Refresh;
- }
- } else {
- $e->Skip();
- }
-}
-
-sub mouse_wheel_event {
- my ($self, $e) = @_;
-
- if ($e->MiddleIsDown) {
- # Ignore the wheel events if the middle button is pressed.
- return;
- }
-
- if ($self->layer_editing_enabled && $self->{print}) {
- my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing;
- if ($object_idx_selected != -1) {
- # A volume is selected. Test, whether hovering over a layer thickness bar.
- if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
- # Adjust the width of the selection.
- $self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5);
- $self->Refresh;
- return;
- }
- }
- }
-
- # Calculate the zoom delta and apply it to the current zoom factor
- my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
- $zoom = max(min($zoom, 4), -4);
- $zoom /= 10;
- $zoom = $self->_zoom / (1-$zoom);
- # Don't allow to zoom too far outside the scene.
- my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box);
- $zoom_min *= 0.4 if defined $zoom_min;
- $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min;
- $self->_zoom($zoom);
-
- # In order to zoom around the mouse point we need to translate
- # the camera target
- my $size = Slic3r::Pointf->new($self->GetSizeWH);
- my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
- $self->_camera_target->translate(
- # ($pos - $size/2) represents the vector from the viewport center
- # to the mouse point. By multiplying it by $zoom we get the new,
- # transformed, length of such vector.
- # Since we want that point to stay fixed, we move our camera target
- # in the opposite direction by the delta of the length of such vector
- # ($zoom - 1). We then scale everything by 1/$self->_zoom since
- # $self->_camera_target is expressed in terms of model units.
- -($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
- -($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
- 0,
- ) if 0;
- $self->on_viewport_changed->() if $self->on_viewport_changed;
- $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen;
- $self->Refresh;
-}
-
-# Reset selection.
-sub reset_objects {
- my ($self) = @_;
- if ($self->GetContext) {
- $self->SetCurrent($self->GetContext);
- $self->volumes->release_geometry;
- }
- $self->volumes->erase;
- $self->_dirty(1);
-}
-
-# Setup camera to view all objects.
-sub set_viewport_from_scene {
- my ($self, $scene) = @_;
-
- $self->_sphi($scene->_sphi);
- $self->_stheta($scene->_stheta);
- $self->_camera_target($scene->_camera_target);
- $self->_zoom($scene->_zoom);
- $self->_quat($scene->_quat);
- $self->_dirty(1);
-}
-
-# Set the camera to a default orientation,
-# zoom to volumes.
-sub select_view {
- my ($self, $direction) = @_;
- my $dirvec;
- if (ref($direction)) {
- $dirvec = $direction;
- } else {
- if ($direction eq 'iso') {
- $dirvec = VIEW_DEFAULT;
- } elsif ($direction eq 'left') {
- $dirvec = VIEW_LEFT;
- } elsif ($direction eq 'right') {
- $dirvec = VIEW_RIGHT;
- } elsif ($direction eq 'top') {
- $dirvec = VIEW_TOP;
- } elsif ($direction eq 'bottom') {
- $dirvec = VIEW_BOTTOM;
- } elsif ($direction eq 'front') {
- $dirvec = VIEW_FRONT;
- } elsif ($direction eq 'rear') {
- $dirvec = VIEW_REAR;
- }
- }
- my $bb = $self->volumes_bounding_box;
- if (! $bb->empty) {
- $self->_sphi($dirvec->[0]);
- $self->_stheta($dirvec->[1]);
- # Avoid gimball lock.
- $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
- $self->_stheta(0) if $self->_stheta < 0;
- # View everything.
- $self->zoom_to_bounding_box($bb);
- $self->on_viewport_changed->() if $self->on_viewport_changed;
- $self->Refresh;
- }
-}
-
-sub get_zoom_to_bounding_box_factor {
- my ($self, $bb) = @_;
- return undef if ($bb->empty);
- my $max_size = max(@{$bb->size}) * 2;
- return ($max_size == 0) ? undef : min($self->GetSizeWH) / $max_size;
-}
-
-sub zoom_to_bounding_box {
- my ($self, $bb) = @_;
- # Calculate the zoom factor needed to adjust viewport to bounding box.
- my $zoom = $self->get_zoom_to_bounding_box_factor($bb);
- if (defined $zoom) {
- $self->_zoom($zoom);
- # center view around bounding box center
- $self->_camera_target($bb->center);
- $self->on_viewport_changed->() if $self->on_viewport_changed;
- }
-}
-
-sub zoom_to_bed {
- my ($self) = @_;
-
- if ($self->bed_shape) {
- $self->zoom_to_bounding_box($self->bed_bounding_box);
- }
-}
-
-sub zoom_to_volume {
- my ($self, $volume_idx) = @_;
-
- my $volume = $self->volumes->[$volume_idx];
- my $bb = $volume->transformed_bounding_box;
- $self->zoom_to_bounding_box($bb);
-}
-
-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;
- foreach my $v (@{$self->volumes}) {
- $bb->merge($v->transformed_bounding_box) if (! $self->_apply_zoom_to_volumes_filter || $v->zoom_to_volumes);
- }
- return $bb;
-}
-
-sub bed_bounding_box {
- my ($self) = @_;
-
- my $bb = Slic3r::Geometry::BoundingBoxf3->new;
- if ($self->bed_shape) {
- $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape};
- }
- return $bb;
-}
-
-sub max_bounding_box {
- my ($self) = @_;
-
- my $bb = $self->bed_bounding_box;
- $bb->merge($self->volumes_bounding_box);
- return $bb;
-}
-
-# Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane
-# to support the scene objects.
-sub set_auto_bed_shape {
- my ($self, $bed_shape) = @_;
-
- # draw a default square bed around object center
- my $max_size = max(@{ $self->volumes_bounding_box->size });
- my $center = $self->volumes_bounding_box->center;
- $self->set_bed_shape([
- [ $center->x - $max_size, $center->y - $max_size ], #--
- [ $center->x + $max_size, $center->y - $max_size ], #--
- [ $center->x + $max_size, $center->y + $max_size ], #++
- [ $center->x - $max_size, $center->y + $max_size ], #++
- ]);
- # Set the origin for painting of the coordinate system axes.
- $self->origin(Slic3r::Pointf->new(@$center[X,Y]));
-}
-
-# Set the bed shape to a single closed 2D polygon (array of two element arrays),
-# triangulate the bed and store the triangles into $self->bed_triangles,
-# fills the $self->bed_grid_lines and sets $self->origin.
-# Sets $self->bed_polygon to limit the object placement.
-sub set_bed_shape {
- my ($self, $bed_shape) = @_;
-
- $self->bed_shape($bed_shape);
-
- # triangulate bed
- my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]);
- my $bed_bb = $expolygon->bounding_box;
-
- {
- my @points = ();
- foreach my $triangle (@{ $expolygon->triangulate }) {
- push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle;
- }
- $self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points));
- }
-
- {
- my @polylines = ();
- for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) {
- push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]);
- }
- for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) {
- push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]);
- }
- # clip with a slightly grown expolygon because our lines lay on the contours and
- # may get erroneously clipped
- my @lines = map Slic3r::Line->new(@$_[0,-1]),
- @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])};
-
- # append bed contours
- push @lines, map @{$_->lines}, @$expolygon;
-
- my @points = ();
- foreach my $line (@lines) {
- push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #))
- }
- $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
- }
-
- # Set the origin for painting of the coordinate system axes.
- $self->origin(Slic3r::Pointf->new(0,0));
-
- $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone);
-}
-
-sub deselect_volumes {
- my ($self) = @_;
- $_->set_selected(0) for @{$self->volumes};
-}
-
-sub select_volume {
- my ($self, $volume_idx) = @_;
- $self->volumes->[$volume_idx]->set_selected(1)
- if $volume_idx != -1;
-}
-
-sub SetCuttingPlane {
- my ($self, $z, $expolygons) = @_;
-
- $self->cutting_plane_z($z);
-
- # grow slices in order to display them better
- $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
-
- my @verts = ();
- foreach my $line (map @{$_->lines}, map @$_, @$expolygons) {
- push @verts, (
- unscale($line->a->x), unscale($line->a->y), $z, #))
- unscale($line->b->x), unscale($line->b->y), $z, #))
- );
- }
- $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts));
-}
-
-# Given an axis and angle, compute quaternion.
-sub axis_to_quat {
- my ($ax, $phi) = @_;
-
- my $lena = sqrt(reduce { $a + $b } (map { $_ * $_ } @$ax));
- my @q = map { $_ * (1 / $lena) } @$ax;
- @q = map { $_ * sin($phi / 2.0) } @q;
- $q[$#q + 1] = cos($phi / 2.0);
- return @q;
-}
-
-# Project a point on the virtual trackball.
-# If it is inside the sphere, map it to the sphere, if it outside map it
-# to a hyperbola.
-sub project_to_sphere {
- my ($r, $x, $y) = @_;
-
- my $d = sqrt($x * $x + $y * $y);
- if ($d < $r * 0.70710678118654752440) { # Inside sphere
- return sqrt($r * $r - $d * $d);
- } else { # On hyperbola
- my $t = $r / 1.41421356237309504880;
- return $t * $t / $d;
- }
-}
-
-sub cross {
- my ($v1, $v2) = @_;
-
- return (@$v1[1] * @$v2[2] - @$v1[2] * @$v2[1],
- @$v1[2] * @$v2[0] - @$v1[0] * @$v2[2],
- @$v1[0] * @$v2[1] - @$v1[1] * @$v2[0]);
-}
-
-# Simulate a track-ball. Project the points onto the virtual trackball,
-# then figure out the axis of rotation, which is the cross product of
-# P1 P2 and O P1 (O is the center of the ball, 0,0,0) Note: This is a
-# deformed trackball-- is a trackball in the center, but is deformed
-# into a hyperbolic sheet of rotation away from the center.
-# It is assumed that the arguments to this routine are in the range
-# (-1.0 ... 1.0).
-sub trackball {
- my ($p1x, $p1y, $p2x, $p2y) = @_;
-
- if ($p1x == $p2x && $p1y == $p2y) {
- # zero rotation
- return (0.0, 0.0, 0.0, 1.0);
- }
-
- # First, figure out z-coordinates for projection of P1 and P2 to
- # deformed sphere
- my @p1 = ($p1x, $p1y, project_to_sphere(TRACKBALLSIZE, $p1x, $p1y));
- my @p2 = ($p2x, $p2y, project_to_sphere(TRACKBALLSIZE, $p2x, $p2y));
-
- # axis of rotation (cross product of P1 and P2)
- my @a = cross(\@p2, \@p1);
-
- # Figure out how much to rotate around that axis.
- my @d = map { $_ * $_ } (map { $p1[$_] - $p2[$_] } 0 .. $#p1);
- my $t = sqrt(reduce { $a + $b } @d) / (2.0 * TRACKBALLSIZE);
-
- # Avoid problems with out-of-control values...
- $t = 1.0 if ($t > 1.0);
- $t = -1.0 if ($t < -1.0);
- my $phi = 2.0 * asin($t);
-
- return axis_to_quat(\@a, $phi);
-}
-
-# Build a rotation matrix, given a quaternion rotation.
-sub quat_to_rotmatrix {
- my ($q) = @_;
-
- my @m = ();
-
- $m[0] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[2] * @$q[2]);
- $m[1] = 2.0 * (@$q[0] * @$q[1] - @$q[2] * @$q[3]);
- $m[2] = 2.0 * (@$q[2] * @$q[0] + @$q[1] * @$q[3]);
- $m[3] = 0.0;
-
- $m[4] = 2.0 * (@$q[0] * @$q[1] + @$q[2] * @$q[3]);
- $m[5] = 1.0 - 2.0 * (@$q[2] * @$q[2] + @$q[0] * @$q[0]);
- $m[6] = 2.0 * (@$q[1] * @$q[2] - @$q[0] * @$q[3]);
- $m[7] = 0.0;
-
- $m[8] = 2.0 * (@$q[2] * @$q[0] - @$q[1] * @$q[3]);
- $m[9] = 2.0 * (@$q[1] * @$q[2] + @$q[0] * @$q[3]);
- $m[10] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[0] * @$q[0]);
- $m[11] = 0.0;
-
- $m[12] = 0.0;
- $m[13] = 0.0;
- $m[14] = 0.0;
- $m[15] = 1.0;
-
- return @m;
-}
-
-sub mulquats {
- my ($q1, $rq) = @_;
-
- return (@$q1[3] * @$rq[0] + @$q1[0] * @$rq[3] + @$q1[1] * @$rq[2] - @$q1[2] * @$rq[1],
- @$q1[3] * @$rq[1] + @$q1[1] * @$rq[3] + @$q1[2] * @$rq[0] - @$q1[0] * @$rq[2],
- @$q1[3] * @$rq[2] + @$q1[2] * @$rq[3] + @$q1[0] * @$rq[1] - @$q1[1] * @$rq[0],
- @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2])
-}
-
-# Convert the screen space coordinate to an object space coordinate.
-# If the Z screen space coordinate is not provided, a depth buffer value is substituted.
-sub mouse_to_3d {
- my ($self, $x, $y, $z) = @_;
-
- return unless $self->GetContext;
- $self->SetCurrent($self->GetContext);
-
- my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items
- my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items
- my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items
-
- $y = $viewport[3] - $y;
- $z //= glReadPixels_p($x, $y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT);
- my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport);
- return Slic3r::Pointf3->new(@projected);
-}
-
-sub GetContext {
- my ($self) = @_;
- return $self->{context} ||= Wx::GLContext->new($self);
-}
-
-sub SetCurrent {
- my ($self, $context) = @_;
- return $self->SUPER::SetCurrent($context);
-}
-
-sub UseVBOs {
- my ($self) = @_;
-
- if (! defined ($self->{use_VBOs})) {
- my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl');
- if ($use_legacy eq '1') {
- # Disable OpenGL 2.0 rendering.
- $self->{use_VBOs} = 0;
- # Don't enable the layer editing tool.
- $self->{layer_editing_enabled} = 0;
- # 2 means failed
- $self->{layer_editing_initialized} = 2;
- return 0;
- }
- # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
- # first when an OpenGL widget is shown for the first time. How ugly.
- return 0 if (! $self->init && $^O eq 'linux');
- # Don't use VBOs if anything fails.
- $self->{use_VBOs} = 0;
- if ($self->GetContext) {
- $self->SetCurrent($self->GetContext);
- Slic3r::GUI::_3DScene::_glew_init;
- my @gl_version = split(/\./, glGetString(GL_VERSION));
- $self->{use_VBOs} = int($gl_version[0]) >= 2;
- # print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n";
- }
- }
- return $self->{use_VBOs};
-}
-
-sub Resize {
- my ($self, $x, $y) = @_;
-
- return unless $self->GetContext;
- $self->_dirty(0);
-
- $self->SetCurrent($self->GetContext);
- glViewport(0, 0, $x, $y);
-
- $x /= $self->_zoom;
- $y /= $self->_zoom;
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- if ($self->_camera_type eq 'ortho') {
- #FIXME setting the size of the box 10x larger than necessary
- # is only a workaround for an incorrectly set camera.
- # This workaround harms Z-buffer accuracy!
-# my $depth = 1.05 * $self->max_bounding_box->radius();
- my $depth = 10.0 * $self->max_bounding_box->radius();
- glOrtho(
- -$x/2, $x/2, -$y/2, $y/2,
- -$depth, $depth,
- );
- } else {
- die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective');
- my $bbox_r = $self->max_bounding_box->radius();
- my $fov = PI * 45. / 180.;
- my $fov_tan = tan(0.5 * $fov);
- my $cam_distance = 0.5 * $bbox_r / $fov_tan;
- $self->_camera_distance($cam_distance);
- my $nr = $cam_distance - $bbox_r * 1.1;
- my $fr = $cam_distance + $bbox_r * 1.1;
- $nr = 1 if ($nr < 1);
- $fr = $nr + 1 if ($fr < $nr + 1);
- my $h2 = $fov_tan * $nr;
- my $w2 = $h2 * $x / $y;
- glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr);
- }
-
- glMatrixMode(GL_MODELVIEW);
-}
-
-sub InitGL {
- my $self = shift;
-
- return if $self->init;
- return unless $self->GetContext;
- $self->init(1);
-
- # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
- # first when an OpenGL widget is shown for the first time. How ugly.
- # In that case the volumes are wainting to be moved to Vertex Buffer Objects
- # after the OpenGL context is being initialized.
- $self->volumes->finalize_geometry(1)
- if ($^O eq 'linux' && $self->UseVBOs);
-
- glClearColor(0, 0, 0, 1);
- glColor3f(1, 0, 0);
- glEnable(GL_DEPTH_TEST);
- glClearDepth(1.0);
- glDepthFunc(GL_LEQUAL);
- glEnable(GL_CULL_FACE);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- # Set antialiasing/multisampling
- glDisable(GL_LINE_SMOOTH);
- glDisable(GL_POLYGON_SMOOTH);
-
- # See "GL_MULTISAMPLE and GL_ARRAY_BUFFER_ARB messages on failed launch"
- # https://github.com/alexrj/Slic3r/issues/4085
- eval {
- # Disable the multi sampling by default, so the picking by color will work correctly.
- glDisable(GL_MULTISAMPLE);
- };
- # Disable multi sampling if the eval failed.
- $self->{can_multisample} = 0 if $@;
-
- # ambient lighting
- glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1);
-
- glEnable(GL_LIGHTING);
- glEnable(GL_LIGHT0);
- glEnable(GL_LIGHT1);
-
- # light from camera
- glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
- glLightfv_p(GL_LIGHT1, GL_SPECULAR, 0.3, 0.3, 0.3, 1);
- glLightfv_p(GL_LIGHT1, GL_DIFFUSE, 0.2, 0.2, 0.2, 1);
-
- # Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
- glShadeModel(GL_SMOOTH);
-
-# glMaterialfv_p(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 0.5, 0.3, 0.3, 1);
-# glMaterialfv_p(GL_FRONT_AND_BACK, GL_SPECULAR, 1, 1, 1, 1);
-# glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50);
-# glMaterialfv_p(GL_FRONT_AND_BACK, GL_EMISSION, 0.1, 0, 0, 0.9);
-
- # A handy trick -- have surface material mirror the color.
- glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
- glEnable(GL_COLOR_MATERIAL);
- glEnable(GL_MULTISAMPLE) if ($self->{can_multisample});
-
- if ($self->UseVBOs) {
- my $shader = new Slic3r::GUI::_3DScene::GLShader;
- if (! $shader->load($self->_fragment_shader_Gouraud, $self->_vertex_shader_Gouraud)) {
-# if (! $shader->load($self->_fragment_shader_Phong, $self->_vertex_shader_Phong)) {
- print "Compilaton of path shader failed: \n" . $shader->last_error . "\n";
- $shader = undef;
- } else {
- $self->{plain_shader} = $shader;
- }
- }
-}
-
-sub DestroyGL {
- my $self = shift;
- if ($self->GetContext) {
- $self->SetCurrent($self->GetContext);
- if ($self->{plain_shader}) {
- $self->{plain_shader}->release;
- delete $self->{plain_shader};
- }
- if ($self->{layer_height_edit_shader}) {
- $self->{layer_height_edit_shader}->release;
- delete $self->{layer_height_edit_shader};
- }
- $self->volumes->release_geometry;
- }
-}
-
-sub Render {
- my ($self, $dc) = @_;
-
- # prevent calling SetCurrent() when window is not shown yet
- return unless $self->IsShownOnScreen;
- return unless my $context = $self->GetContext;
- $self->SetCurrent($context);
- $self->InitGL;
-
- glClearColor(1, 1, 1, 1);
- glClearDepth(1);
- glDepthFunc(GL_LESS);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- {
- # Shift the perspective camera.
- my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance);
- glTranslatef(@$camera_pos);
- }
-
- if (TURNTABLE_MODE) {
- # Turntable mode is enabled by default.
- glRotatef(-$self->_stheta, 1, 0, 0); # pitch
- glRotatef($self->_sphi, 0, 0, 1); # yaw
- } else {
- my @rotmat = quat_to_rotmatrix($self->quat);
- glMultMatrixd_p(@rotmat[0..15]);
- }
- glTranslatef(@{ $self->_camera_target->negative });
-
- # light from above
- glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0);
- glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
- glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
-
- # Head light
- glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
-
- if ($self->enable_picking) {
- if (my $pos = $self->_mouse_pos) {
- # Render the object for picking.
- # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
- # Better to use software ray-casting on a bounding-box hierarchy.
- glPushAttrib(GL_ENABLE_BIT);
- glDisable(GL_MULTISAMPLE) if ($self->{can_multisample});
- glDisable(GL_LIGHTING);
- glDisable(GL_BLEND);
- $self->draw_volumes(1);
- glPopAttrib();
- glFlush();
- my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ];
- my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256;
- $self->_hover_volume_idx(undef);
- $_->set_hover(0) for @{$self->volumes};
- if ($volume_idx <= $#{$self->volumes}) {
- $self->_hover_volume_idx($volume_idx);
-
- $self->volumes->[$volume_idx]->set_hover(1);
- my $group_id = $self->volumes->[$volume_idx]->select_group_id;
- if ($group_id != -1) {
- $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes};
- }
-
- $self->on_hover->($volume_idx) if $self->on_hover;
- }
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- }
- }
-
- # draw fixed background
- if ($self->background) {
- glDisable(GL_LIGHTING);
- glPushMatrix();
- glLoadIdentity();
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- # Draws a bluish bottom to top gradient over the complete screen.
- glDisable(GL_DEPTH_TEST);
- glBegin(GL_QUADS);
- glColor3f(0.0,0.0,0.0);
- glVertex3f(-1.0,-1.0, 1.0);
- glVertex3f( 1.0,-1.0, 1.0);
- glColor3f(10/255,98/255,144/255);
- glVertex3f( 1.0, 1.0, 1.0);
- glVertex3f(-1.0, 1.0, 1.0);
- glEnd();
- glPopMatrix();
- glEnable(GL_DEPTH_TEST);
-
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glEnable(GL_LIGHTING);
- }
-
- # draw ground and axes
- glDisable(GL_LIGHTING);
-
- # draw ground
- my $ground_z = GROUND_Z;
- if ($self->bed_triangles) {
- glDisable(GL_DEPTH_TEST);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glColor4f(0.8, 0.6, 0.5, 0.4);
- glNormal3d(0,0,1);
- glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_triangles->ptr());
- glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3);
- glDisableClientState(GL_VERTEX_ARRAY);
-
- # we need depth test for grid, otherwise it would disappear when looking
- # the object from below
- glEnable(GL_DEPTH_TEST);
-
- # draw grid
- glLineWidth(3);
- glColor4f(0.2, 0.2, 0.2, 0.4);
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_grid_lines->ptr());
- glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3);
- glDisableClientState(GL_VERTEX_ARRAY);
-
- glDisable(GL_BLEND);
- }
-
- my $volumes_bb = $self->volumes_bounding_box;
-
- {
- # draw axes
- # disable depth testing so that axes are not covered by ground
- glDisable(GL_DEPTH_TEST);
- my $origin = $self->origin;
- my $axis_len = max(
- 0.3 * max(@{ $self->bed_bounding_box->size }),
- 2 * max(@{ $volumes_bb->size }),
- );
- glLineWidth(2);
- glBegin(GL_LINES);
- # draw line for x axis
- glColor3f(1, 0, 0);
- glVertex3f(@$origin, $ground_z);
- glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,,
- # draw line for y axis
- glColor3f(0, 1, 0);
- glVertex3f(@$origin, $ground_z);
- glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++
- glEnd();
- # draw line for Z axis
- # (re-enable depth test so that axis is correctly shown when objects are behind it)
- glEnable(GL_DEPTH_TEST);
- glBegin(GL_LINES);
- glColor3f(0, 0, 1);
- glVertex3f(@$origin, $ground_z);
- glVertex3f(@$origin, $ground_z+$axis_len);
- glEnd();
- }
-
- glEnable(GL_LIGHTING);
-
- # draw objects
- if (! $self->use_plain_shader) {
- $self->draw_volumes;
- } elsif ($self->UseVBOs) {
- if ($self->enable_picking) {
- $self->mark_volumes_for_layer_height;
- $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height'));
- $self->volumes->update_outside_state($self->{config}, 0);
- }
- $self->{plain_shader}->enable if $self->{plain_shader};
- $self->volumes->render_VBOs;
- $self->{plain_shader}->disable;
- } else {
- $self->volumes->render_legacy;
- }
-
- # draw cutting plane
- if (defined $self->cutting_plane_z) {
- my $plane_z = $self->cutting_plane_z;
- my $bb = $volumes_bb;
- glDisable(GL_CULL_FACE);
- glDisable(GL_LIGHTING);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glBegin(GL_QUADS);
- glColor4f(0.8, 0.8, 0.8, 0.5);
- glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z);
- glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z);
- glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z);
- glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z);
- glEnd();
- glEnable(GL_CULL_FACE);
- glDisable(GL_BLEND);
- }
-
- # draw warning message
- $self->draw_warning;
-
- # draw gcode preview legend
- $self->draw_legend;
-
- $self->draw_active_object_annotations;
-
- $self->SwapBuffers();
-}
-
-sub draw_volumes {
- # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
- my ($self, $fakecolor) = @_;
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_NORMAL_ARRAY);
-
- foreach my $volume_idx (0..$#{$self->volumes}) {
- my $volume = $self->volumes->[$volume_idx];
-
- if ($fakecolor) {
- # Object picking mode. Render the object with a color encoding the object index.
- my $r = ($volume_idx & 0x000000FF) >> 0;
- my $g = ($volume_idx & 0x0000FF00) >> 8;
- my $b = ($volume_idx & 0x00FF0000) >> 16;
- glColor4f($r/255.0, $g/255.0, $b/255.0, 1);
- } elsif ($volume->selected) {
- glColor4f(@{ &SELECTED_COLOR });
- } elsif ($volume->hover) {
- glColor4f(@{ &HOVER_COLOR });
- } else {
- glColor4f(@{ $volume->color });
- }
-
- $volume->render;
- }
- glDisableClientState(GL_NORMAL_ARRAY);
- glDisable(GL_BLEND);
-
- if (defined $self->cutting_plane_z) {
- glLineWidth(2);
- glColor3f(0, 0, 0);
- glVertexPointer_c(3, GL_FLOAT, 0, $self->cut_lines_vertices->ptr());
- glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3);
- glVertexPointer_c(3, GL_FLOAT, 0, 0);
- }
- glDisableClientState(GL_VERTEX_ARRAY);
-}
-
-sub mark_volumes_for_layer_height {
- my ($self) = @_;
-
- foreach my $volume_idx (0..$#{$self->volumes}) {
- my $volume = $self->volumes->[$volume_idx];
- my $object_id = int($volume->select_group_id / 1000000);
- if ($self->layer_editing_enabled && $volume->selected && $self->{layer_height_edit_shader} &&
- $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) {
- $volume->set_layer_height_texture_data($self->{layer_preview_z_texture_id}, $self->{layer_height_edit_shader}->shader_program_id,
- $self->{print}->get_object($object_id), $self->_variable_layer_thickness_bar_mouse_cursor_z_relative, $self->{layer_height_edit_band_width});
- } else {
- $volume->reset_layer_height_texture_data();
- }
- }
-}
-
-sub _load_image_set_texture {
- my ($self, $file_name) = @_;
- # Load a PNG with an alpha channel.
- my $img = Wx::Image->new;
- $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG);
- # Get RGB & alpha raw data from wxImage, interleave them into a Perl array.
- my @rgb = unpack 'C*', $img->GetData();
- my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3);
- my $n_pixels = int(@alpha);
- my @data = (0)x($n_pixels * 4);
- for (my $i = 0; $i < $n_pixels; $i += 1) {
- $data[$i*4 ] = $rgb[$i*3];
- $data[$i*4+1] = $rgb[$i*3+1];
- $data[$i*4+2] = $rgb[$i*3+2];
- $data[$i*4+3] = $alpha[$i];
- }
- # Initialize a raw bitmap data.
- my $params = {
- loaded => 1,
- valid => $n_pixels > 0,
- width => $img->GetWidth,
- height => $img->GetHeight,
- data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data),
- texture_id => glGenTextures_p(1)
- };
- # Create and initialize a texture with the raw data.
- glBindTexture(GL_TEXTURE_2D, $params->{texture_id});
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
- glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr);
- glBindTexture(GL_TEXTURE_2D, 0);
- return $params;
-}
-
-sub _variable_layer_thickness_load_overlay_image {
- my ($self) = @_;
- $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png')
- if (! $self->{layer_preview_annotation}->{loaded});
- return $self->{layer_preview_annotation}->{valid};
-}
-
-sub _variable_layer_thickness_load_reset_image {
- my ($self) = @_;
- $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png')
- if (! $self->{layer_preview_reset_image}->{loaded});
- return $self->{layer_preview_reset_image}->{valid};
-}
-
-# 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, $tex_id);
- glBegin(GL_QUADS);
- glTexCoord2d(0.,1.); glVertex3f($l, $b, 0);
- glTexCoord2d(1.,1.); glVertex3f($r, $b, 0);
- glTexCoord2d(1.,0.); glVertex3f($r, $t, 0);
- glTexCoord2d(0.,0.); glVertex3f($l, $t, 0);
- glEnd();
- glBindTexture(GL_TEXTURE_2D, 0);
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
- glEnable(GL_LIGHTING);
-}
-
-sub draw_active_object_annotations {
- # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
- my ($self) = @_;
-
- return if (! $self->{layer_height_edit_shader} || ! $self->layer_editing_enabled);
-
- # Find the selected volume, over which the layer editing is active.
- my $volume;
- foreach my $volume_idx (0..$#{$self->volumes}) {
- my $v = $self->volumes->[$volume_idx];
- if ($v->selected && $v->has_layer_height_texture) {
- $volume = $v;
- last;
- }
- }
- return if (! $volume);
-
- # If the active object was not allocated at the Print, go away. This should only be a momentary case between an object addition / deletion
- # and an update by Platter::async_apply_config.
- my $object_idx = int($volume->select_group_id / 1000000);
- return if $object_idx >= $self->{print}->object_count;
-
- # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
- # where x, y is the window size divided by $self->_zoom.
- my ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport;
- my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport;
- my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative;
-
- $self->{layer_height_edit_shader}->enable;
- $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id);
- $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height);
- $self->{layer_height_edit_shader}->set_uniform('z_cursor', $volume->bounding_box->z_max * $z_cursor_relative);
- $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width});
- glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
- glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height,
- GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0);
- glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,
- GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1);
-
- # Render the color bar.
- glDisable(GL_DEPTH_TEST);
- # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
- # where x, y is the window size divided by $self->_zoom.
- glPushMatrix();
- glLoadIdentity();
- # Paint the overlay.
- glBegin(GL_QUADS);
- glVertex3f($bar_left, $bar_bottom, 0);
- glVertex3f($bar_right, $bar_bottom, 0);
- glVertex3f($bar_right, $bar_top, $volume->bounding_box->z_max);
- glVertex3f($bar_left, $bar_top, $volume->bounding_box->z_max);
- glEnd();
- glBindTexture(GL_TEXTURE_2D, 0);
- $self->{layer_height_edit_shader}->disable;
-
- # Paint the tooltip.
- if ($self->_variable_layer_thickness_load_overlay_image) {
- my $gap = 10/$self->_zoom;
- my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap);
- $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b);
- }
-
- # Paint the reset button.
- if ($self->_variable_layer_thickness_load_reset_image) {
- $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top);
- }
-
- # Paint the graph.
- #FIXME show some kind of legend.
- my $print_object = $self->{print}->get_object($object_idx);
- my $max_z = unscale($print_object->size->z);
- my $profile = $print_object->model_object->layer_height_profile;
- my $layer_height = $print_object->config->get('layer_height');
- my $layer_height_max = 10000000000.;
- {
- # Get a maximum layer height value.
- #FIXME This is a duplicate code of Slicing.cpp.
- my $nozzle_diameters = $print_object->print->config->get('nozzle_diameter');
- my $layer_heights_min = $print_object->print->config->get('min_layer_height');
- my $layer_heights_max = $print_object->print->config->get('max_layer_height');
- for (my $i = 0; $i < scalar(@{$nozzle_diameters}); $i += 1) {
- my $lh_min = ($layer_heights_min->[$i] == 0.) ? 0.07 : max(0.01, $layer_heights_min->[$i]);
- my $lh_max = ($layer_heights_max->[$i] == 0.) ? (0.75 * $nozzle_diameters->[$i]) : $layer_heights_max->[$i];
- $layer_height_max = min($layer_height_max, max($lh_min, $lh_max));
- }
- }
- # Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
- $layer_height_max *= 1.12;
- # Baseline
- glColor3f(0., 0., 0.);
- glBegin(GL_LINE_STRIP);
- glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom);
- glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_top);
- glEnd();
- # Curve
- glColor3f(0., 0., 1.);
- glBegin(GL_LINE_STRIP);
- for (my $i = 0; $i < int(@{$profile}); $i += 2) {
- my $z = $profile->[$i];
- my $h = $profile->[$i+1];
- glVertex3f($bar_left + $h * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
- }
- glEnd();
- # Revert the matrices.
- glPopMatrix();
- glEnable(GL_DEPTH_TEST);
-}
-
-sub draw_legend {
- my ($self) = @_;
-
- if (!$self->_legend_enabled) {
- return;
- }
-
- # 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 draw_warning {
- my ($self) = @_;
-
- if (!$self->_warning_enabled) {
- return;
- }
-
- # If the warning texture has not been loaded into the GPU, do it now.
- my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture;
- if ($tex_id > 0)
- {
- my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width;
- my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height;
- if (($tex_w > 0) && ($tex_h > 0))
- {
- glDisable(GL_DEPTH_TEST);
- glPushMatrix();
- glLoadIdentity();
-
- my ($cw, $ch) = $self->GetSizeWH;
-
- my $l = (-0.5 * $tex_w) / $self->_zoom;
- my $t = (-0.5 * $ch + $tex_h) / $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) = @_;
- my %tag = Slic3r::tags($params{format});
-
- my $gl_version = glGetString(GL_VERSION);
- my $gl_vendor = glGetString(GL_VENDOR);
- my $gl_renderer = glGetString(GL_RENDERER);
- my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
-
- my $out = '';
- $out .= "$tag{h2start}OpenGL installation$tag{h2end}$tag{eol}";
- $out .= " $tag{bstart}Using POGL$tag{bend} v$OpenGL::BUILD_VERSION$tag{eol}";
- $out .= " $tag{bstart}GL version: $tag{bend}${gl_version}$tag{eol}";
- $out .= " $tag{bstart}vendor: $tag{bend}${gl_vendor}$tag{eol}";
- $out .= " $tag{bstart}renderer: $tag{bend}${gl_renderer}$tag{eol}";
- $out .= " $tag{bstart}GLSL version: $tag{bend}${glsl_version}$tag{eol}";
-
- # Check for other OpenGL extensions
- $out .= "$tag{h2start}Installed extensions (* implemented in the module):$tag{h2end}$tag{eol}";
- my $extensions = glGetString(GL_EXTENSIONS);
- my @extensions = split(' ',$extensions);
- foreach my $ext (sort @extensions) {
- my $stat = glpCheckExtension($ext);
- $out .= sprintf("%s ${ext}$tag{eol}", $stat?' ':'*');
- $out .= sprintf(" ${stat}$tag{eol}") if ($stat && $stat !~ m|^$ext |);
- }
-
- return $out;
-}
-
-sub _report_opengl_state
-{
- my ($self, $comment) = @_;
- my $err = glGetError();
- return 0 if ($err == 0);
-
- # gluErrorString() hangs. Don't use it.
-# my $errorstr = gluErrorString();
- my $errorstr = '';
- if ($err == 0x0500) {
- $errorstr = 'GL_INVALID_ENUM';
- } elsif ($err == GL_INVALID_VALUE) {
- $errorstr = 'GL_INVALID_VALUE';
- } elsif ($err == GL_INVALID_OPERATION) {
- $errorstr = 'GL_INVALID_OPERATION';
- } elsif ($err == GL_STACK_OVERFLOW) {
- $errorstr = 'GL_STACK_OVERFLOW';
- } elsif ($err == GL_OUT_OF_MEMORY) {
- $errorstr = 'GL_OUT_OF_MEMORY';
- } else {
- $errorstr = 'unknown';
- }
- if (defined($comment)) {
- printf("OpenGL error at %s, nr %d (0x%x): %s\n", $comment, $err, $err, $errorstr);
- } else {
- printf("OpenGL error nr %d (0x%x): %s\n", $err, $err, $errorstr);
- }
-}
-
-sub _vertex_shader_Gouraud {
- return <<'VERTEX';
-#version 110
-
-#define INTENSITY_CORRECTION 0.6
-
-// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
-const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
-#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-#define LIGHT_TOP_SPECULAR (0.25 * INTENSITY_CORRECTION)
-#define LIGHT_TOP_SHININESS 200.0
-
-// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
-const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
-#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-//#define LIGHT_FRONT_SHININESS 5.0
-
-#define INTENSITY_AMBIENT 0.3
-
-const vec3 ZERO = vec3(0.0, 0.0, 0.0);
-
-struct PrintBoxDetection
-{
- vec3 min;
- vec3 max;
- // xyz contains the offset, if w == 1.0 detection needs to be performed
- vec4 volume_origin;
-};
-
-uniform PrintBoxDetection print_box;
-
-// x = tainted, y = specular;
-varying vec2 intensity;
-
-varying vec3 delta_box_min;
-varying vec3 delta_box_max;
-
-void main()
-{
- vec3 eye = -normalize((gl_ModelViewMatrix * gl_Vertex).xyz);
-
- // First transform the normal into camera space and normalize the result.
- vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
-
- // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
- // Also since we're talking about a directional light, the position field is actually direction.
- vec3 halfVector = normalize(LIGHT_TOP_DIR + eye);
-
- // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
- // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
- float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
-
- intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
- intensity.y = 0.0;
-
- if (NdotL > 0.0)
- intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS);
-
- // Perform the same lighting calculation for the 2nd light source (no specular applied).
- NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
- intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-
- // compute deltas for out of print volume detection (world coordinates)
- if (print_box.volume_origin.w == 1.0)
- {
- vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz;
- delta_box_min = v - print_box.min;
- delta_box_max = v - print_box.max;
- }
- else
- {
- delta_box_min = ZERO;
- delta_box_max = ZERO;
- }
-
- gl_Position = ftransform();
-}
-
-VERTEX
-}
-
-sub _fragment_shader_Gouraud {
- return <<'FRAGMENT';
-#version 110
-
-const vec3 ZERO = vec3(0.0, 0.0, 0.0);
-
-// x = tainted, y = specular;
-varying vec2 intensity;
-
-varying vec3 delta_box_min;
-varying vec3 delta_box_max;
-
-uniform vec4 uniform_color;
-
-void main()
-{
- gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 0.0) + uniform_color * intensity.x;
-
- // if the fragment is outside the print volume darken it and set it as transparent
- if (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO)))
- gl_FragColor = vec4(mix(gl_FragColor.xyz, ZERO, 0.5), 0.5 * uniform_color.a);
- else
- gl_FragColor.a = uniform_color.a;
-}
-
-FRAGMENT
-}
-
-sub _vertex_shader_Phong {
- return <<'VERTEX';
-#version 110
-
-varying vec3 normal;
-varying vec3 eye;
-void main(void)
-{
- eye = normalize(vec3(gl_ModelViewMatrix * gl_Vertex));
- normal = normalize(gl_NormalMatrix * gl_Normal);
- gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
-}
-VERTEX
-}
-
-sub _fragment_shader_Phong {
- return <<'FRAGMENT';
-#version 110
-
-#define INTENSITY_CORRECTION 0.7
-
-#define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31
-#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-#define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION)
-//#define LIGHT_TOP_SHININESS 50.
-#define LIGHT_TOP_SHININESS 10.
-
-#define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43
-#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-#define LIGHT_FRONT_SHININESS 50.
-
-#define INTENSITY_AMBIENT 0.0
-
-varying vec3 normal;
-varying vec3 eye;
-uniform vec4 uniform_color;
-void main() {
-
- float intensity_specular = 0.;
- float intensity_tainted = 0.;
- float intensity = max(dot(normal,vec3(LIGHT_TOP_DIR)), 0.0);
- // if the vertex is lit compute the specular color
- if (intensity > 0.0) {
- intensity_tainted = LIGHT_TOP_DIFFUSE * intensity;
- // compute the half vector
- vec3 h = normalize(vec3(LIGHT_TOP_DIR) + eye);
- // compute the specular term into spec
- intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(h, normal), 0.0), LIGHT_TOP_SHININESS);
- }
- intensity = max(dot(normal,vec3(LIGHT_FRONT_DIR)), 0.0);
- // if the vertex is lit compute the specular color
- if (intensity > 0.0) {
- intensity_tainted += LIGHT_FRONT_DIFFUSE * intensity;
- // compute the half vector
-// vec3 h = normalize(vec3(LIGHT_FRONT_DIR) + eye);
- // compute the specular term into spec
-// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS);
- }
-
- gl_FragColor = max(
- vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted,
- INTENSITY_AMBIENT * uniform_color);
- gl_FragColor.a = uniform_color.a;
-}
-FRAGMENT
-}
-
-sub _vertex_shader_variable_layer_height {
- return <<'VERTEX';
-#version 110
-
-#define INTENSITY_CORRECTION 0.6
-
-const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
-#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-#define LIGHT_TOP_SPECULAR (0.25 * INTENSITY_CORRECTION)
-#define LIGHT_TOP_SHININESS 200.0
-
-const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
-#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-//#define LIGHT_FRONT_SHININESS 5.0
-
-#define INTENSITY_AMBIENT 0.3
-
-uniform float z_to_texture_row;
-
-// x = tainted, y = specular;
-varying vec2 intensity;
-
-varying float object_z;
-
-void main()
-{
- vec3 eye = -normalize((gl_ModelViewMatrix * gl_Vertex).xyz);
-
- // First transform the normal into camera space and normalize the result.
- vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
-
- // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
- // Also since we're talking about a directional light, the position field is actually direction.
- vec3 halfVector = normalize(LIGHT_TOP_DIR + eye);
-
- // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
- // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
- float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
-
- intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
- intensity.y = 0.0;
-
- if (NdotL > 0.0)
- intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS);
-
- // Perform the same lighting calculation for the 2nd light source (no specular)
- NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
-
- intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-
- // Scaled to widths of the Z texture.
- object_z = gl_Vertex.z;
-
- gl_Position = ftransform();
-}
-
-VERTEX
-}
-
-sub _fragment_shader_variable_layer_height {
- return <<'FRAGMENT';
-#version 110
-
-#define M_PI 3.1415926535897932384626433832795
-
-// 2D texture (1D texture split by the rows) of color along the object Z axis.
-uniform sampler2D z_texture;
-// Scaling from the Z texture rows coordinate to the normalized texture row coordinate.
-uniform float z_to_texture_row;
-uniform float z_texture_row_to_normalized;
-uniform float z_cursor;
-uniform float z_cursor_band_width;
-
-// x = tainted, y = specular;
-varying vec2 intensity;
-
-varying float object_z;
-
-void main()
-{
- float object_z_row = z_to_texture_row * object_z;
- // Index of the row in the texture.
- float z_texture_row = floor(object_z_row);
- // Normalized coordinate from 0. to 1.
- float z_texture_col = object_z_row - z_texture_row;
- float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
- // Calculate level of detail from the object Z coordinate.
- // This makes the slowly sloping surfaces to be show with high detail (with stripes),
- // and the vertical surfaces to be shown with low detail (no stripes)
- float z_in_cells = object_z_row * 190.;
- // Gradient of Z projected on the screen.
- float dx_vtc = dFdx(z_in_cells);
- float dy_vtc = dFdy(z_in_cells);
- float lod = clamp(0.5 * log2(max(dx_vtc*dx_vtc, dy_vtc*dy_vtc)), 0., 1.);
- // Sample the Z texture. Texture coordinates are normalized to <0, 1>.
- vec4 color =
- mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.),
- texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod);
-
- // Mix the final color.
- gl_FragColor =
- vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend);
-}
-
-FRAGMENT
-}
-
# The 3D canvas to display objects and tool paths.
package Slic3r::GUI::3DScene;
use base qw(Slic3r::GUI::3DScene::Base);
-use OpenGL qw(:glconstants :gluconstants :glufunctions);
-use List::Util qw(first min max);
-use Slic3r::Geometry qw(scale unscale epsilon);
-use Slic3r::Print::State ':steps';
-
-__PACKAGE__->mk_accessors(qw(
- color_by
- select_by
- drag_by
-));
-
sub new {
my $class = shift;
- my $self = $class->SUPER::new(@_);
- $self->color_by('volume'); # object | volume
- $self->select_by('object'); # object | volume | instance
- $self->drag_by('instance'); # object | instance
-
+ my $self = $class->SUPER::new(@_);
return $self;
}
-sub load_object {
- my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
-
- $self->SetCurrent($self->GetContext) if $self->UseVBOs;
-
- my $model_object;
- if ($model->isa('Slic3r::Model::Object')) {
- $model_object = $model;
- $model = $model_object->model;
- $obj_idx = 0;
- } else {
- $model_object = $model->get_object($obj_idx);
- }
-
- $instance_idxs ||= [0..$#{$model_object->instances}];
- my $volume_indices = $self->volumes->load_object(
- $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by,
- $self->UseVBOs);
- return @{$volume_indices};
-}
-
-# Create 3D thick extrusion lines for a skirt and brim.
-# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes.
-sub load_print_toolpaths {
- my ($self, $print, $colors) = @_;
-
- $self->SetCurrent($self->GetContext) if $self->UseVBOs;
- Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs)
- if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM));
-}
-
-# Create 3D thick extrusion lines for object forming extrusions.
-# Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
-# one for perimeters, one for infill and one for supports.
-sub load_print_object_toolpaths {
- my ($self, $object, $colors) = @_;
-
- $self->SetCurrent($self->GetContext) if $self->UseVBOs;
- Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs);
-}
-
-# Create 3D thick extrusion lines for wipe tower extrusions.
-sub load_wipe_tower_toolpaths {
- my ($self, $print, $colors) = @_;
-
- $self->SetCurrent($self->GetContext) if $self->UseVBOs;
- Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs)
- 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();
-}
-
-sub get_current_print_zs {
- my ($self) = @_;
-
- my $count = $self->volumes->get_current_print_zs();
- return $count;
-}
-
1;
diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm
deleted file mode 100644
index 0879ea35b..000000000
--- a/lib/Slic3r/GUI/AboutDialog.pm
+++ /dev/null
@@ -1,122 +0,0 @@
-package Slic3r::GUI::AboutDialog;
-use strict;
-use warnings;
-use utf8;
-
-use Wx qw(:font :html :misc :dialog :sizer :systemsettings :frame :id);
-use Wx::Event qw(EVT_HTML_LINK_CLICKED EVT_LEFT_DOWN EVT_BUTTON);
-use Wx::Print;
-use Wx::Html;
-use base 'Wx::Dialog';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, -1, 'About Slic3r', wxDefaultPosition, [600, 340], wxCAPTION);
-
- $self->SetBackgroundColour(Wx::wxWHITE);
- my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
- $self->SetSizer($hsizer);
-
- # logo
- my $logo = Slic3r::GUI::AboutDialog::Logo->new($self, -1, wxDefaultPosition, wxDefaultSize);
- $logo->SetBackgroundColour(Wx::wxWHITE);
- $hsizer->Add($logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
-
- my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
- $hsizer->Add($vsizer, 1, wxEXPAND, 0);
-
- # title
- my $title = Wx::StaticText->new($self, -1, $Slic3r::FORK_NAME, wxDefaultPosition, wxDefaultSize);
- my $title_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
- $title_font->SetWeight(wxFONTWEIGHT_BOLD);
- $title_font->SetFamily(wxFONTFAMILY_ROMAN);
- $title_font->SetPointSize(24);
- $title->SetFont($title_font);
- $vsizer->Add($title, 0, wxALIGN_LEFT | wxTOP, 30);
-
- # version
- my $version = Wx::StaticText->new($self, -1, "Version $Slic3r::VERSION", wxDefaultPosition, wxDefaultSize);
- my $version_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
- $version_font->SetPointSize(&Wx::wxMSW ? 9 : 11);
- $version->SetFont($version_font);
- $vsizer->Add($version, 0, wxALIGN_LEFT | wxBOTTOM, 10);
-
- # text
- my $text =
- '<html>' .
- '<body bgcolor="#ffffff" link="#808080">' .
- '<font color="#808080">' .
- 'Copyright &copy; 2016 Vojtech Bubnik, Prusa Research. <br />' .
- 'Copyright &copy; 2011-2016 Alessandro Ranellucci. <br />' .
- '<a href="http://slic3r.org/">Slic3r</a> is licensed under the ' .
- '<a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License, version 3</a>.' .
- '<br /><br /><br />' .
- 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake and numerous others. ' .
- 'Manual by Gary Hodgson. Inspired by the RepRap community. <br />' .
- 'Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James. ' .
- '</font>' .
- '</body>' .
- '</html>';
- my $html = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER);
- my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
- my $size = &Wx::wxMSW ? 8 : 10;
- $html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size, $size, $size, $size, $size, $size, $size]);
- $html->SetBorders(2);
- $html->SetPage($text);
- $vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
- EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked);
-
- my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
- $self->SetEscapeId(wxID_CLOSE);
- EVT_BUTTON($self, wxID_CLOSE, sub {
- $self->EndModal(wxID_CLOSE);
- $self->Close;
- });
- $vsizer->Add($buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
-
- EVT_LEFT_DOWN($self, sub { $self->Close });
- EVT_LEFT_DOWN($logo, sub { $self->Close });
-
- return $self;
-}
-
-sub link_clicked {
- my ($self, $event) = @_;
-
- Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref);
- $event->Skip(0);
-}
-
-package Slic3r::GUI::AboutDialog::Logo;
-use Wx qw(:bitmap :dc);
-use Wx::Event qw(EVT_PAINT);
-use base 'Wx::Panel';
-
-sub new {
- my $class = shift;
- my $self = $class->SUPER::new(@_);
-
- $self->{logo} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
- $self->SetMinSize(Wx::Size->new($self->{logo}->GetWidth, $self->{logo}->GetHeight));
-
- EVT_PAINT($self, \&repaint);
-
- return $self;
-}
-
-sub repaint {
- my ($self, $event) = @_;
-
- my $dc = Wx::PaintDC->new($self);
- $dc->SetBackgroundMode(wxTRANSPARENT);
-
- my $size = $self->GetSize;
- my $logo_w = $self->{logo}->GetWidth;
- my $logo_h = $self->{logo}->GetHeight;
- $dc->DrawBitmap($self->{logo}, ($size->GetWidth - $logo_w) / 2, ($size->GetHeight - $logo_h) / 2, 1);
-
- $event->Skip;
-}
-
-1;
diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm
deleted file mode 100644
index 70c8e0256..000000000
--- a/lib/Slic3r/GUI/BedShapeDialog.pm
+++ /dev/null
@@ -1,316 +0,0 @@
-# The bed shape dialog.
-# The dialog opens from Print Settins tab -> Bed Shape: Set...
-
-package Slic3r::GUI::BedShapeDialog;
-use strict;
-use warnings;
-use utf8;
-
-use List::Util qw(min max);
-use Slic3r::Geometry qw(X Y unscale);
-use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
-use Wx::Event qw(EVT_CLOSE);
-use base 'Wx::Dialog';
-
-sub new {
- my $class = shift;
- my ($parent, $default) = @_;
- my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
-
- $self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default);
-
- my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
- $main_sizer->Add($panel, 1, wxEXPAND);
- $main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND);
-
- $self->SetSizer($main_sizer);
- $self->SetMinSize($self->GetSize);
- $main_sizer->SetSizeHints($self);
-
- # needed to actually free memory
- EVT_CLOSE($self, sub {
- $self->EndModal(wxID_OK);
- $self->Destroy;
- });
-
- return $self;
-}
-
-sub GetValue {
- my ($self) = @_;
- return $self->{panel}->GetValue;
-}
-
-package Slic3r::GUI::BedShapePanel;
-
-use List::Util qw(min max sum first);
-use Scalar::Util qw(looks_like_number);
-use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon);
-use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL);
-use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON);
-use base 'Wx::Panel';
-
-use constant SHAPE_RECTANGULAR => 0;
-use constant SHAPE_CIRCULAR => 1;
-use constant SHAPE_CUSTOM => 2;
-
-sub new {
- my $class = shift;
- my ($parent, $default) = @_;
- my $self = $class->SUPER::new($parent, -1);
-
- $self->on_change(undef);
-
- my $box = Wx::StaticBox->new($self, -1, "Shape");
- my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
-
- # shape options
- $self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP);
- $sbsizer->Add($self->{shape_options_book});
-
- $self->{optgroups} = [];
- {
- my $optgroup = $self->_init_shape_options_page('Rectangular');
- $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
- opt_id => 'rect_size',
- type => 'point',
- label => 'Size',
- tooltip => 'Size in X and Y of the rectangular plate.',
- default => [200,200],
- ));
- $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
- opt_id => 'rect_origin',
- type => 'point',
- label => 'Origin',
- tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.',
- default => [0,0],
- ));
- }
- {
- my $optgroup = $self->_init_shape_options_page('Circular');
- $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
- opt_id => 'diameter',
- type => 'f',
- label => 'Diameter',
- tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.',
- sidetext => 'mm',
- default => 200,
- ));
- }
- {
- my $optgroup = $self->_init_shape_options_page('Custom');
- $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
- full_width => 1,
- widget => sub {
- my ($parent) = @_;
-
- my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize);
- EVT_BUTTON($self, $btn, sub { $self->_load_stl });
- return $btn;
- }
- ));
- }
-
- EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub {
- $self->_update_shape;
- });
-
- # right pane with preview canvas
- my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self);
-
- # main sizer
- my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
- $top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
- $top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas;
-
- $self->SetSizerAndFit($top_sizer);
-
- $self->_set_shape($default);
- $self->_update_preview;
-
- return $self;
-}
-
-sub on_change {
- my ($self, $cb) = @_;
- $self->{on_change} = $cb // sub {};
-}
-
-# Called from the constructor.
-# Set the initial bed shape from a list of points.
-# Deduce the bed shape type (rect, circle, custom)
-# This routine shall be smart enough if the user messes up
-# with the list of points in the ini file directly.
-sub _set_shape {
- my ($self, $points) = @_;
-
- # is this a rectangle?
- if (@$points == 4) {
- my $polygon = Slic3r::Polygon->new_scale(@$points);
- my $lines = $polygon->lines;
- if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) {
- # okay, it's a rectangle
-
- # find origin
- # the || 0 hack prevents "-0" which might confuse the user
- my $x_min = min(map $_->[X], @$points) || 0;
- my $x_max = max(map $_->[X], @$points) || 0;
- my $y_min = min(map $_->[Y], @$points) || 0;
- my $y_max = max(map $_->[Y], @$points) || 0;
- my $origin = [-$x_min, -$y_min];
-
- $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
- my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
- $optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]);
- $optgroup->set_value('rect_origin', $origin);
- $self->_update_shape;
- return;
- }
- }
-
- # is this a circle?
- {
- # Analyze the array of points. Do they reside on a circle?
- my $polygon = Slic3r::Polygon->new_scale(@$points);
- my $center = $polygon->bounding_box->center;
- my @vertex_distances = map $center->distance_to($_), @$polygon;
- my $avg_dist = sum(@vertex_distances)/@vertex_distances;
- if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) {
- # all vertices are equidistant to center
- $self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR);
- my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR];
- $optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2)));
- $self->_update_shape;
- return;
- }
- }
-
- if (@$points < 3) {
- # Invalid polygon. Revert to default bed dimensions.
- $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
- my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
- $optgroup->set_value('rect_size', [200, 200]);
- $optgroup->set_value('rect_origin', [0, 0]);
- $self->_update_shape;
- return;
- }
-
- # This is a custom bed shape, use the polygon provided.
- $self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
- # Copy the polygon to the canvas, make a copy of the array.
- $self->{canvas}->bed_shape([@$points]);
- $self->_update_shape;
-}
-
-# Update the bed shape from the dialog fields.
-sub _update_shape {
- my ($self) = @_;
-
- my $page_idx = $self->{shape_options_book}->GetSelection;
- if ($page_idx == SHAPE_RECTANGULAR) {
- my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size');
- my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin');
- my ($x, $y) = @$rect_size;
- return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things
- return if !$x || !$y or $x == 0 or $y == 0;
- my ($x0, $y0) = (0,0);
- my ($x1, $y1) = ($x ,$y);
- {
- my ($dx, $dy) = @$rect_origin;
- return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things
- $x0 -= $dx;
- $x1 -= $dx;
- $y0 -= $dy;
- $y1 -= $dy;
- }
- $self->{canvas}->bed_shape([
- [$x0,$y0],
- [$x1,$y0],
- [$x1,$y1],
- [$x0,$y1],
- ]);
- } elsif ($page_idx == SHAPE_CIRCULAR) {
- my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter');
- return if !$diameter or $diameter == 0;
- my $r = $diameter/2;
- my $twopi = 2*PI;
- my $edges = 60;
- my $polygon = Slic3r::Polygon->new_scale(
- map [ $r * cos $_, $r * sin $_ ],
- map { $twopi/$edges*$_ } 1..$edges
- );
- $self->{canvas}->bed_shape([
- map [ unscale($_->x), unscale($_->y) ], @$polygon #))
- ]);
- }
-
- $self->{on_change}->();
- $self->_update_preview;
-}
-
-sub _update_preview {
- my ($self) = @_;
- $self->{canvas}->Refresh if $self->{canvas};
- $self->Refresh;
-}
-
-# Called from the constructor.
-# Create a panel for a rectangular / circular / custom bed shape.
-sub _init_shape_options_page {
- my ($self, $title) = @_;
-
- my $panel = Wx::Panel->new($self->{shape_options_book});
- my $optgroup;
- push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new(
- parent => $panel,
- title => 'Settings',
- label_width => 100,
- on_change => sub {
- my ($opt_id) = @_;
- #$self->{"_$opt_id"} = $optgroup->get_value($opt_id);
- $self->_update_shape;
- },
- );
- $panel->SetSizerAndFit($optgroup->sizer);
- $self->{shape_options_book}->AddPage($panel, $title);
-
- return $optgroup;
-}
-
-# Loads an stl file, projects it to the XY plane and calculates a polygon.
-sub _load_stl {
- my ($self) = @_;
-
- my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
- if ($dialog->ShowModal != wxID_OK) {
- $dialog->Destroy;
- return;
- }
- my $input_file = $dialog->GetPaths;
- $dialog->Destroy;
-
- my $model = Slic3r::Model->read_from_file($input_file);
- my $mesh = $model->mesh;
- my $expolygons = $mesh->horizontal_projection;
-
- if (@$expolygons == 0) {
- Slic3r::GUI::show_error($self, "The selected file contains no geometry.");
- return;
- }
- if (@$expolygons > 1) {
- Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported.");
- return;
- }
-
- my $polygon = $expolygons->[0]->contour;
- $self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]);
- $self->_update_preview();
-}
-
-# Returns the resulting bed shape polygon. This value will be stored to the ini file.
-sub GetValue {
- my ($self) = @_;
- return $self->{canvas}->bed_shape;
-}
-
-1;
diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm
deleted file mode 100644
index a32d345ed..000000000
--- a/lib/Slic3r/GUI/ConfigWizard.pm
+++ /dev/null
@@ -1,458 +0,0 @@
-# The config wizard is executed when the Slic3r is first started.
-# The wizard helps the user to specify the 3D printer properties.
-
-package Slic3r::GUI::ConfigWizard;
-use strict;
-use warnings;
-use utf8;
-
-use Wx;
-use base 'Wx::Wizard';
-
-# adhere to various human interface guidelines
-our $wizard = 'Wizard';
-$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
-
-sub new {
- my ($class, $parent, $presets, $fresh_start) = @_;
- my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
-
- # initialize an empty repository
- $self->{config} = Slic3r::Config->new;
-
- my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self, $fresh_start);
- $self->add_page($welcome_page);
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Filament->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Temperature->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::BedTemperature->new($self));
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Finished->new($self));
-
- $_->build_index for @{$self->{pages}};
-
- $welcome_page->set_selection_presets([@{$presets}, 'Other']);
-
- return $self;
-}
-
-sub add_page {
- my ($self, $page) = @_;
-
- my $n = push @{$self->{pages}}, $page;
- # add first page to the page area sizer
- $self->GetPageAreaSizer->Add($page) if $n == 1;
- # link pages
- $self->{pages}[$n-2]->set_next_page($page) if $n >= 2;
- $page->set_previous_page($self->{pages}[$n-2]) if $n >= 2;
-}
-
-sub run {
- my ($self) = @_;
- my $result;
- if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
- my $preset_name = $self->{pages}[0]->{preset_name};
- $result = {
- preset_name => $preset_name,
- reset_user_profile => $self->{pages}[0]->{reset_user_profile}
- };
- if ($preset_name eq 'Other') {
- # it would be cleaner to have these defined inside each page class,
- # in some event getting called before leaving the page
- # set first_layer_height + layer_height based on nozzle_diameter
- my $nozzle = $self->{config}->nozzle_diameter;
- $self->{config}->set('first_layer_height', $nozzle->[0]);
- $self->{config}->set('layer_height', $nozzle->[0] - 0.1);
-
- # set first_layer_temperature to temperature + 5
- $self->{config}->set('first_layer_temperature', [$self->{config}->temperature->[0] + 5]);
-
- # set first_layer_bed_temperature to temperature + 5
- $self->{config}->set('first_layer_bed_temperature',
- [ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
- $result->{config} = $self->{config};
- }
- }
- $self->Destroy;
- return $result;
-}
-
-package Slic3r::GUI::ConfigWizard::Index;
-use Wx qw(:bitmap :dc :font :misc :sizer :systemsettings :window);
-use Wx::Event qw(EVT_ERASE_BACKGROUND EVT_PAINT);
-use base 'Wx::Panel';
-
-sub new {
- my $class = shift;
- my ($parent, $title) = @_;
- my $self = $class->SUPER::new($parent);
-
- push @{$self->{titles}}, $title;
- $self->{own_index} = 0;
-
- $self->{bullets}->{before} = Wx::Bitmap->new(Slic3r::var("bullet_black.png"), wxBITMAP_TYPE_PNG);
- $self->{bullets}->{own} = Wx::Bitmap->new(Slic3r::var("bullet_blue.png"), wxBITMAP_TYPE_PNG);
- $self->{bullets}->{after} = Wx::Bitmap->new(Slic3r::var("bullet_white.png"), wxBITMAP_TYPE_PNG);
-
- $self->{background} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG);
- $self->SetMinSize(Wx::Size->new($self->{background}->GetWidth, $self->{background}->GetHeight));
-
- EVT_PAINT($self, \&repaint);
-
- return $self;
-}
-
-sub repaint {
- my ($self, $event) = @_;
- my $size = $self->GetClientSize;
- my $gap = 5;
-
- my $dc = Wx::PaintDC->new($self);
- $dc->SetBackgroundMode(wxTRANSPARENT);
- $dc->SetFont($self->GetFont);
- $dc->SetTextForeground($self->GetForegroundColour);
-
- my $background_h = $self->{background}->GetHeight;
- my $background_w = $self->{background}->GetWidth;
- $dc->DrawBitmap($self->{background}, ($size->GetWidth - $background_w) / 2, ($size->GetHeight - $background_h) / 2, 1);
-
- my $label_h = $self->{bullets}->{own}->GetHeight;
- $label_h = $dc->GetCharHeight if $dc->GetCharHeight > $label_h;
- my $label_w = $size->GetWidth;
-
- my $i = 0;
- foreach (@{$self->{titles}}) {
- my $bullet = $self->{bullets}->{own};
- $bullet = $self->{bullets}->{before} if $i < $self->{own_index};
- $bullet = $self->{bullets}->{after} if $i > $self->{own_index};
-
- $dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index};
- $dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h));
- # Only show the first bullet if this is the only wizard page to be displayed.
- last if $i == 0 && $self->{just_welcome};
- $i++;
- }
-
- $event->Skip;
-}
-
-sub prepend_title {
- my $self = shift;
- my ($title) = @_;
-
- unshift @{$self->{titles}}, $title;
- $self->{own_index}++;
- $self->Refresh;
-}
-
-sub append_title {
- my $self = shift;
- my ($title) = @_;
-
- push @{$self->{titles}}, $title;
- $self->Refresh;
-}
-
-package Slic3r::GUI::ConfigWizard::Page;
-use Wx qw(:font :misc :sizer :staticline :systemsettings);
-use base 'Wx::WizardPage';
-
-sub new {
- my $class = shift;
- my ($parent, $title, $short_title) = @_;
- my $self = $class->SUPER::new($parent);
-
- my $sizer = Wx::FlexGridSizer->new(0, 2, 10, 10);
- $sizer->AddGrowableCol(1, 1);
- $sizer->AddGrowableRow(1, 1);
- $sizer->AddStretchSpacer(0);
- $self->SetSizer($sizer);
-
- # title
- my $text = Wx::StaticText->new($self, -1, $title, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
- my $bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
- $bold_font->SetWeight(wxFONTWEIGHT_BOLD);
- $bold_font->SetPointSize(14);
- $text->SetFont($bold_font);
- $sizer->Add($text, 0, wxALIGN_LEFT, 0);
-
- # index
- $self->{short_title} = $short_title ? $short_title : $title;
- $self->{index} = Slic3r::GUI::ConfigWizard::Index->new($self, $self->{short_title});
- $sizer->Add($self->{index}, 1, wxEXPAND | wxTOP | wxRIGHT, 10);
-
- # contents
- $self->{width} = 430;
- $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
- $sizer->Add($self->{vsizer}, 1);
-
- return $self;
-}
-
-sub append_text {
- my $self = shift;
- my ($text) = @_;
-
- my $para = Wx::StaticText->new($self, -1, $text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
- $para->Wrap($self->{width});
- $para->SetMinSize([$self->{width}, -1]);
- $self->{vsizer}->Add($para, 0, wxALIGN_LEFT | wxTOP | wxBOTTOM, 10);
-}
-
-sub append_option {
- my $self = shift;
- my ($full_key) = @_;
-
- # populate repository with the factory default
- my ($opt_key, $opt_index) = split /#/, $full_key, 2;
- $self->config->apply(Slic3r::Config::new_from_defaults_keys([$opt_key]));
-
- # draw the control
- my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
- parent => $self,
- title => '',
- config => $self->config,
- full_labels => 1,
- );
- $optgroup->append_single_option_line($opt_key, $opt_index);
- $self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
-}
-
-sub append_panel {
- my ($self, $panel) = @_;
- $self->{vsizer}->Add($panel, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
-}
-
-sub set_previous_page {
- my $self = shift;
- my ($previous_page) = @_;
- $self->{previous_page} = $previous_page;
-}
-
-sub GetPrev {
- my $self = shift;
- return $self->{previous_page};
-}
-
-sub set_next_page {
- my $self = shift;
- my ($next_page) = @_;
- $self->{next_page} = $next_page;
-}
-
-sub GetNext {
- my $self = shift;
- return $self->{next_page};
-}
-
-sub get_short_title {
- my $self = shift;
- return $self->{short_title};
-}
-
-sub build_index {
- my $self = shift;
-
- my $page = $self;
- $self->{index}->prepend_title($page->get_short_title) while ($page = $page->GetPrev);
- $page = $self;
- $self->{index}->append_title($page->get_short_title) while ($page = $page->GetNext);
-}
-
-sub config {
- my ($self) = @_;
- return $self->GetParent->{config};
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Welcome;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-use Wx qw(:misc :sizer wxID_FORWARD);
-use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE EVT_CHECKBOX);
-
-sub new {
- my ($class, $parent, $fresh_start) = @_;
- my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
- $self->{full_wizard_workflow} = 1;
- $self->{reset_user_profile} = 0;
-
- # Test for the existence of the old config path.
- my $message_has_legacy;
- {
- my $datadir = Slic3r::data_dir;
- if ($datadir =~ /Slic3rPE/) {
- # Check for existence of the legacy Slic3r directory.
- my $datadir_legacy = substr $datadir, 0, -2;
- my $dir_enc = Slic3r::encode_path($datadir_legacy);
- if (-e $dir_enc && -d $dir_enc &&
- -e ($dir_enc . '/print') && -d ($dir_enc . '/print') &&
- -e ($dir_enc . '/filament') && -d ($dir_enc . '/filament') &&
- -e ($dir_enc . '/printer') && -d ($dir_enc . '/printer') &&
- -e ($dir_enc . '/slic3r.ini')) {
- $message_has_legacy = "Starting with Slic3r 1.38.4, the user profile directory has been renamed to $datadir. You may consider closing Slic3r and renaming $datadir_legacy to $datadir.";
- }
- }
- }
-
- $self->append_text('Hello, welcome to Slic3r Prusa Edition! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.');
- $self->append_text('Please select your printer vendor and printer type. If your printer is not listed, you may try your luck and select a similar one. If you select "Other", this ' . lc($wizard) . ' will let you set the basic 3D printer parameters.');
- $self->append_text($message_has_legacy) if defined $message_has_legacy;
- # To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.');
- $self->append_text('If you received a configuration file or a config bundle from your 3D printer vendor, cancel this '.lc($wizard).' and use the "File->Load Config" or "File->Load Config Bundle" menu.');
-
- $self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
- $self->{vsizer}->Add($choice, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
- if (! $fresh_start) {
- $self->{reset_checkbox} = Wx::CheckBox->new($self, -1, "Reset user profile, install from scratch");
- $self->{vsizer}->Add($self->{reset_checkbox}, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
- }
-
- EVT_CHOICE($parent, $choice, sub {
- my $sel = $self->{choice}->GetStringSelection;
- $self->{preset_name} = $sel;
- $self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq ''));
- });
-
- if (! $fresh_start) {
- EVT_CHECKBOX($self, $self->{reset_checkbox}, sub {
- $self->{reset_user_profile} = $self->{reset_checkbox}->GetValue();
- });
- }
-
- EVT_ACTIVATE($parent, sub {
- $self->set_full_wizard_workflow($self->{preset_name} eq 'Other');
- });
-
- return $self;
-}
-
-sub set_full_wizard_workflow {
- my ($self, $full_workflow) = @_;
- $self->{full_wizard_workflow} = $full_workflow;
- $self->{index}->{just_welcome} = !$full_workflow;
- $self->{index}->Refresh;
- my $next_button = $self->GetParent->FindWindow(wxID_FORWARD);
- $next_button->SetLabel($full_workflow ? "&Next >" : "&Finish");
-}
-
-# Set the preset names, select the first item.
-sub set_selection_presets {
- my ($self, $names) = @_;
- $self->{choice}->Append($names);
- $self->{choice}->SetSelection(0);
- $self->{preset_name} = $names->[0];
-}
-
-sub GetNext {
- my $self = shift;
- return $self->{full_wizard_workflow} ? $self->{next_page} : undef;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Firmware;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Firmware Type');
-
- $self->append_text('Choose the type of firmware used by your printer, then click Next.');
- $self->append_option('gcode_flavor');
-
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Bed;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Bed Size');
-
- $self->append_text('Set the shape of your printer\'s bed, then click Next.');
-
- $self->config->apply(Slic3r::Config::new_from_defaults_keys(['bed_shape']));
- $self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape);
- $self->{bed_shape_panel}->on_change(sub {
- $self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue);
- });
- $self->append_panel($self->{bed_shape_panel});
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Nozzle;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Nozzle Diameter');
-
- $self->append_text('Enter the diameter of your printer\'s hot end nozzle, then click Next.');
- $self->append_option('nozzle_diameter#0');
-
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Filament;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Filament Diameter');
-
- $self->append_text('Enter the diameter of your filament, then click Next.');
- $self->append_text('Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.');
- $self->append_option('filament_diameter#0');
-
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Temperature;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Extrusion Temperature');
-
- $self->append_text('Enter the temperature needed for extruding your filament, then click Next.');
- $self->append_text('A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.');
- $self->append_option('temperature#0');
-
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::BedTemperature;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Bed Temperature');
-
- $self->append_text('Enter the bed temperature needed for getting your filament to stick to your heated bed, then click Next.');
- $self->append_text('A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.');
- $self->append_option('bed_temperature#0');
-
- return $self;
-}
-
-package Slic3r::GUI::ConfigWizard::Page::Finished;
-use base 'Slic3r::GUI::ConfigWizard::Page';
-
-sub new {
- my $class = shift;
- my ($parent) = @_;
- my $self = $class->SUPER::new($parent, 'Congratulations!', 'Finish');
-
- $self->append_text("You have successfully completed the Slic3r Configuration $wizard. " .
- 'Slic3r is now configured for your printer and filament.');
- $self->append_text('To close this '.lc($wizard).' and apply the newly created configuration, click Finish.');
-
- return $self;
-}
-
-1;
diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm
index 6aa7b34cb..f7d90c796 100644
--- a/lib/Slic3r/GUI/Controller.pm
+++ b/lib/Slic3r/GUI/Controller.pm
@@ -7,7 +7,7 @@ use strict;
use warnings;
use utf8;
-use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
+use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog wxBORDER_NONE);
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
use base qw(Wx::ScrolledWindow Class::Accessor);
use List::Util qw(first);
@@ -34,7 +34,7 @@ sub new {
# button for adding new printer panels
{
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
- wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
+ wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
$btn->SetToolTipString("Add printer…")
if $btn->can('SetToolTipString');
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index 98fdbcd3b..6ecfa6860 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -9,7 +9,7 @@ use File::Basename qw(basename dirname);
use FindBin;
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
+use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog :dirdialog
:font :icon wxTheApp);
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
use base 'Wx::Frame';
@@ -19,6 +19,7 @@ use Wx::Locale gettext => 'L';
our $qs_last_input_file;
our $qs_last_output_file;
our $last_config;
+our $appController;
# Events to be sent from a C++ Tab implementation:
# 1) To inform about a change of a configuration value.
@@ -29,12 +30,23 @@ our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
our $PROGRESS_BAR_EVENT = Wx::NewEventType;
# 4) To display an error dialog box from a thread on the UI thread.
our $ERROR_EVENT = Wx::NewEventType;
+# 5) To inform about a change of object selection
+our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
+# 6) To inform about a change of object settings
+our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
+# 7) To inform about a remove of object
+our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
+# 8) To inform about a update of the scene
+our $UPDATE_SCENE_EVENT = Wx::NewEventType;
sub new {
my ($class, %params) = @_;
-
+
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
- Slic3r::GUI::set_main_frame($self);
+ Slic3r::GUI::set_main_frame($self);
+
+ $appController = Slic3r::AppController->new();
+
if ($^O eq 'MSWin32') {
# Load the icon either from the exe, or from the ico file.
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
@@ -43,7 +55,7 @@ sub new {
} else {
$self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
}
-
+
# store input params
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
$self->{no_controller} = $params{no_controller};
@@ -52,11 +64,6 @@ sub new {
$self->{lang_ch_event} = $params{lang_ch_event};
$self->{preferences_event} = $params{preferences_event};
- # initialize status bar
- $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
- $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
- $self->SetStatusBar($self->{statusbar});
-
# initialize tabpanel and menubar
$self->_init_tabpanel;
$self->_init_menubar;
@@ -65,9 +72,21 @@ sub new {
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
# (SetAutoPop is not available on GTK.)
eval { Wx::ToolTip::SetAutoPop(32767) };
-
- $self->{loaded} = 1;
+ # initialize status bar
+ $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new();
+ $self->{statusbar}->Embed;
+ $self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
+ # Make the global status bar and its progress indicator available in C++
+ $appController->set_global_progress_indicator($self->{statusbar});
+
+ $appController->set_model($self->{plater}->{model});
+ $appController->set_print($self->{plater}->{print});
+
+ $self->{plater}->{appController} = $appController;
+
+ $self->{loaded} = 1;
+
# initialize layout
{
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@@ -85,7 +104,7 @@ sub new {
# declare events
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
- if ($event->CanVeto && !$self->check_unsaved_changes) {
+ if ($event->CanVeto && !Slic3r::GUI::check_unsaved_changes) {
$event->Veto;
return;
}
@@ -94,12 +113,17 @@ sub new {
# Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
# but in rare cases it may not have been called yet.
wxTheApp->{app_config}->save;
+ $self->{plater}->{print} = undef if($self->{plater});
+ Slic3r::GUI::_3DScene::remove_all_canvases();
+ Slic3r::GUI::deregister_on_request_update_callback();
# propagate event
$event->Skip;
});
$self->update_ui_from_settings;
-
+
+ Slic3r::GUI::update_mode();
+
return $self;
}
@@ -112,10 +136,19 @@ sub _init_tabpanel {
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage;
$panel->OnActivate if $panel->can('OnActivate');
+
+ for my $tab_name (qw(print filament printer)) {
+ Slic3r::GUI::get_preset_tab("$tab_name")->OnActivate if ("$tab_name" eq $panel->GetName);
+ }
});
if (!$self->{no_plater}) {
- $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), L("Plater"));
+ $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel,
+ event_object_selection_changed => $OBJECT_SELECTION_CHANGED_EVENT,
+ event_object_settings_changed => $OBJECT_SETTINGS_CHANGED_EVENT,
+ event_remove_object => $OBJECT_REMOVE_EVENT,
+ event_update_scene => $UPDATE_SCENE_EVENT,
+ ), L("Plater"));
if (!$self->{no_controller}) {
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
}
@@ -136,6 +169,10 @@ sub _init_tabpanel {
my $value = $event->GetInt();
$self->{plater}->on_extruders_change($value);
}
+ if ($opt_key eq 'printer_technology'){
+ my $value = $event->GetInt();# 0 ~ "ptFFF"; 1 ~ "ptSLA"
+ $self->{plater}->show_preset_comboboxes($value);
+ }
}
# don't save while loading for the first time
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
@@ -148,14 +185,15 @@ sub _init_tabpanel {
my $tab = Slic3r::GUI::get_preset_tab($tab_name);
if ($self->{plater}) {
- # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
+ # Update preset combo boxes (Print settings, Filament, Material, 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);
+ $self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
if ($tab_name eq 'printer') {
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
- for my $tab_name_other (qw(print filament)) {
+ for my $tab_name_other (qw(print filament sla_material)) {
# 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.
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
@@ -169,9 +207,43 @@ sub _init_tabpanel {
}
}
});
+
+ # The following event is emited by the C++ Tab implementation on object selection change.
+ EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
+ my ($self, $event) = @_;
+ my $obj_idx = $event->GetId;
+ my $child = $event->GetInt == 1 ? 1 : undef;
+
+ $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
+ $self->{plater}->item_changed_selection($obj_idx);
+ });
+
+ # The following event is emited by the C++ GUI implementation on object settings change.
+ EVT_COMMAND($self, -1, $OBJECT_SETTINGS_CHANGED_EVENT, sub {
+ my ($self, $event) = @_;
+
+ my $line = $event->GetString;
+ my ($obj_idx, $parts_changed, $part_settings_changed) = split('',$line);
+
+ $self->{plater}->changed_object_settings($obj_idx, $parts_changed, $part_settings_changed);
+ });
+
+ # The following event is emited by the C++ GUI implementation on object remove.
+ EVT_COMMAND($self, -1, $OBJECT_REMOVE_EVENT, sub {
+ my ($self, $event) = @_;
+ $self->{plater}->remove();
+ });
+
+ # The following event is emited by the C++ GUI implementation on extruder change for object.
+ EVT_COMMAND($self, -1, $UPDATE_SCENE_EVENT, sub {
+ my ($self, $event) = @_;
+ $self->{plater}->update();
+ });
+
+
Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
$self->{options_tabs} = {};
- for my $tab_name (qw(print filament printer)) {
+ for my $tab_name (qw(print filament sla_material printer)) {
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
}
@@ -201,8 +273,14 @@ sub _init_tabpanel {
# load initial config
my $full_config = wxTheApp->{preset_bundle}->full_config;
$self->{plater}->on_config_change($full_config);
+
# Show a correct number of filament fields.
- $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
+ if (defined $full_config->nozzle_diameter){ # nozzle_diameter is undefined when SLA printer is selected
+ $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
+ }
+
+ # Show correct preset comboboxes according to the printer_technology
+ $self->{plater}->show_preset_comboboxes(($full_config->printer_technology eq "FFF") ? 0 : 1);
}
}
@@ -212,7 +290,7 @@ sub _init_menubar {
# File menu
my $fileMenu = Wx::Menu->new;
{
- wxTheApp->append_menu_item($fileMenu, L("Open STL/OBJ/AMF…\tCtrl+O"), L('Open a model'), sub {
+ wxTheApp->append_menu_item($fileMenu, L("Open STL/OBJ/AMF/3MF…\tCtrl+O"), L('Open a model'), sub {
$self->{plater}->add if $self->{plater};
}, undef, undef); #'brick_add.png');
$self->_append_menu_item($fileMenu, L("&Load Config…\tCtrl+L"), L('Load exported configuration file'), sub {
@@ -251,6 +329,9 @@ sub _init_menubar {
$self->_append_menu_item($fileMenu, L("Slice to SV&G…\tCtrl+G"), L('Slice file to a multi-layer SVG'), sub {
$self->quick_slice(save_as => 1, export_svg => 1);
}, undef, 'shape_handles.png');
+ $self->_append_menu_item($fileMenu, L("Slice to PNG…"), L('Slice file to a set of PNG files'), sub {
+ $self->slice_to_png; #$self->quick_slice(save_as => 0, export_png => 1);
+ }, undef, 'shape_handles.png');
$self->{menu_item_reslice_now} = $self->_append_menu_item(
$fileMenu, L("(&Re)Slice Now\tCtrl+S"), L('Start new slicing process'),
sub { $self->reslice_now; }, undef, 'shape_handles.png');
@@ -259,12 +340,6 @@ sub _init_menubar {
$self->repair_stl;
}, undef, 'wrench.png');
$fileMenu->AppendSeparator();
- # Cmd+, is standard on OS X - what about other operating systems?
- $self->_append_menu_item($fileMenu, L("Preferences…\tCtrl+,"), L('Application preferences'), sub {
- # Opening the C++ preferences dialog.
- Slic3r::GUI::open_preferences_dialog($self->{preferences_event});
- }, wxID_PREFERENCES);
- $fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, L("&Quit"), L('Quit Slic3r'), sub {
$self->Close(0);
}, wxID_EXIT);
@@ -341,11 +416,6 @@ sub _init_menubar {
# Help menu
my $helpMenu = Wx::Menu->new;
{
- $self->_append_menu_item($helpMenu, L("&Configuration ").$Slic3r::GUI::ConfigWizard::wizard."…", L("Run Configuration ").$Slic3r::GUI::ConfigWizard::wizard, sub {
- # Run the config wizard, offer the "reset user profile" checkbox.
- $self->config_wizard(0);
- });
- $helpMenu->AppendSeparator();
$self->_append_menu_item($helpMenu, L("Prusa 3D Drivers"), L('Open the Prusa3D drivers download page in your browser'), sub {
Wx::LaunchDefaultBrowser('http://www.prusa3d.com/drivers/');
});
@@ -366,11 +436,14 @@ sub _init_menubar {
$self->_append_menu_item($helpMenu, L("System Info"), L('Show system information'), sub {
wxTheApp->system_info;
});
+ $self->_append_menu_item($helpMenu, L("Show &Configuration Folder"), L('Show user configuration folder (datadir)'), sub {
+ Slic3r::GUI::desktop_open_datadir_folder();
+ });
$self->_append_menu_item($helpMenu, L("Report an Issue"), L('Report an issue on the Slic3r Prusa Edition'), sub {
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/issues/new');
});
$self->_append_menu_item($helpMenu, L("&About Slic3r"), L('Show about dialog'), sub {
- wxTheApp->about;
+ Slic3r::GUI::about;
});
}
@@ -384,11 +457,9 @@ sub _init_menubar {
$menubar->Append($self->{object_menu}, L("&Object")) if $self->{object_menu};
$menubar->Append($windowMenu, L("&Window"));
$menubar->Append($self->{viewMenu}, L("&View")) if $self->{viewMenu};
- # Add an optional debug menu
- # (Select application language from the list of installed languages)
- Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
+ # Add additional menus from C++
+ Slic3r::GUI::add_menus($menubar, $self->{preferences_event}, $self->{lang_ch_event});
$menubar->Append($helpMenu, L("&Help"));
- # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
$self->SetMenuBar($menubar);
}
}
@@ -406,10 +477,18 @@ sub on_plater_selection_changed {
for $self->{object_menu}->GetMenuItems;
}
+sub slice_to_png {
+ my $self = shift;
+ $self->{plater}->stop_background_process;
+ $self->{plater}->async_apply_config;
+ $appController->print_ctl()->slice_to_png();
+}
+
# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
sub quick_slice {
my ($self, %params) = @_;
+ my $progress_dialog;
eval {
# validate configuration
my $config = wxTheApp->{preset_bundle}->full_config();
@@ -482,12 +561,25 @@ sub quick_slice {
$qs_last_output_file = $output_file unless $params{export_svg};
wxTheApp->{app_config}->update_last_output_dir(dirname($output_file));
$dlg->Destroy;
+ } elsif($params{export_png}) {
+ $output_file = $sprint->output_filepath;
+ $output_file =~ s/\.[gG][cC][oO][dD][eE]$/.zip/;
+ # my $dlg = Wx::DirDialog->new($self, L('Choose output directory'));
+ my $dlg = Wx::FileDialog->new($self, L('Save zip file as:'),
+ wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
+ basename($output_file), '*.zip', wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if ($dlg->ShowModal != wxID_OK) {
+ $dlg->Destroy;
+ return;
+ }
+ $output_file = $dlg->GetPath;
+ $dlg->Destroy;
}
# show processbar dialog
- $self->{progress_dialog} = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
- 100, $self, 0);
- $self->{progress_dialog}->Pulse;
+ $progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
+ 100, $self, 4);
+ $progress_dialog->Pulse;
{
my @warnings = ();
@@ -496,7 +588,11 @@ sub quick_slice {
$sprint->output_file($output_file);
if ($params{export_svg}) {
$sprint->export_svg;
- } else {
+ }
+ elsif($params{export_png}) {
+ $sprint->export_png;
+ }
+ else {
$sprint->export_gcode;
}
Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
@@ -578,7 +674,7 @@ sub export_config {
sub load_config_file {
my ($self, $file) = @_;
if (!$file) {
- return unless $self->check_unsaved_changes;
+ return unless Slic3r::GUI::check_unsaved_changes;
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
"config.ini",
@@ -597,7 +693,7 @@ sub load_config_file {
sub export_configbundle {
my ($self) = @_;
- return unless $self->check_unsaved_changes;
+ return unless Slic3r::GUI::check_unsaved_changes;
# validate current configuration in case it's dirty
eval { wxTheApp->{preset_bundle}->full_config->validate; };
Slic3r::GUI::catch_error($self) and return;
@@ -621,7 +717,7 @@ sub export_configbundle {
# but that behavior was not documented and likely buggy.
sub load_configbundle {
my ($self, $file, $reset_user_profile) = @_;
- return unless $self->check_unsaved_changes;
+ return unless Slic3r::GUI::check_unsaved_changes;
if (!$file) {
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
@@ -655,64 +751,6 @@ sub load_config {
$self->{plater}->on_config_change($config) if $self->{plater};
}
-sub config_wizard {
- my ($self, $fresh_start) = @_;
- # Exit wizard if there are unsaved changes and the user cancels the action.
- return unless $self->check_unsaved_changes;
- # Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
- my $directory = Slic3r::resources_dir() . "/profiles";
- my @profiles = ();
- if (opendir(DIR, Slic3r::encode_path($directory))) {
- while (my $file = readdir(DIR)) {
- if ($file =~ /\.ini$/) {
- $file =~ s/\.ini$//;
- push @profiles, Slic3r::decode_path($file);
- }
- }
- closedir(DIR);
- }
- # Open the wizard.
- if (my $result = Slic3r::GUI::ConfigWizard->new($self, \@profiles, $fresh_start)->run) {
- eval {
- if ($result->{reset_user_profile}) {
- wxTheApp->{preset_bundle}->reset(1);
- }
- if (defined $result->{config}) {
- # Load and save the settings into print, filament and printer presets.
- wxTheApp->{preset_bundle}->load_config('My Settings', $result->{config});
- } else {
- # Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
- wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $result->{preset_name} . '.ini');
- }
- };
- Slic3r::GUI::catch_error($self) and return;
- # Load the currently selected preset into the GUI, update the preset selection box.
- foreach my $tab (values %{$self->{options_tabs}}) {
- $tab->load_current_preset;
- }
- }
-}
-
-# This is called when closing the application, when loading a config file or when starting the config wizard
-# to notify the user whether he is aware that some preset changes will be lost.
-sub check_unsaved_changes {
- my $self = shift;
-
- my @dirty = ();
- foreach my $tab (values %{$self->{options_tabs}}) {
- push @dirty, $tab->title if $tab->current_preset_is_dirty;
- }
-
- if (@dirty) {
- my $titles = join ', ', @dirty;
- my $confirm = Wx::MessageDialog->new($self, L("You have unsaved changes ").($titles).L(". Discard changes and continue anyway?"),
- L('Unsaved Presets'), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
- return $confirm->ShowModal == wxID_YES;
- }
-
- return 1;
-}
-
sub select_tab {
my ($self, $tab) = @_;
$self->{tabpanel}->SetSelection($tab);
diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm
index 4ef2ce2ca..1a53daeb5 100644
--- a/lib/Slic3r/GUI/OptionsGroup/Field.pm
+++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm
@@ -552,8 +552,9 @@ sub BUILD {
$sizer->Add($textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
EVT_SLIDER($self->parent, $slider, sub {
- if (! $self->disable_change_event) {
- $self->textctrl->SetLabel($self->get_value);
+ if (! $self->disable_change_event) {
+ # wxTextCtrl::SetLabel() does not work on Linux, use wxTextCtrl::SetValue() instead
+ $self->textctrl->SetValue($self->get_value);
$self->_on_change($self->option->opt_id);
}
});
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index ae31ca764..4d70076ef 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -13,6 +13,7 @@ use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_LEFT_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
+use Slic3r::Geometry qw(PI);
use base 'Wx::Panel';
use constant TB_ADD => &Wx::NewId;
@@ -38,18 +39,25 @@ our $SLICING_COMPLETED_EVENT = Wx::NewEventType;
our $PROCESS_COMPLETED_EVENT = Wx::NewEventType;
my $PreventListEvents = 0;
+our $appController;
sub new {
- my ($class, $parent) = @_;
+ my ($class, $parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
Slic3r::GUI::set_plater($self);
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
- serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
- nozzle_diameter single_extruder_multi_material
- wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
- max_print_height
+ serial_port serial_speed host_type print_host printhost_apikey printhost_cafile
+ nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
+ wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model
)]);
+
+ # store input params
+ $self->{event_object_selection_changed} = $params{event_object_selection_changed};
+ $self->{event_object_settings_changed} = $params{event_object_settings_changed};
+ $self->{event_remove_object} = $params{event_remove_object};
+ $self->{event_update_scene} = $params{event_update_scene};
+
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
$self->{model} = Slic3r::Model->new;
# C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
@@ -67,81 +75,241 @@ sub new {
Slic3r::GUI::set_print_callback_event($self->{print}, $Slic3r::GUI::MainFrame::PROGRESS_BAR_EVENT);
# Initialize preview notebook
- $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM);
+ $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [-1,335], wxNB_BOTTOM);
# Initialize handlers for canvases
my $on_select_object = sub {
- my ($obj_idx) = @_;
- # Ignore the special objects (the wipe tower proxy and such).
- $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef);
+ my ($obj_idx, $vol_idx) = @_;
+
+ if (($obj_idx != -1) && ($vol_idx == -1)) {
+ # Ignore the special objects (the wipe tower proxy and such).
+ $self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef);
+ $self->item_changed_selection($obj_idx) if (defined($obj_idx));
+ }
};
my $on_double_click = sub {
$self->object_settings_dialog if $self->selected_object;
};
my $on_right_click = sub {
- my ($canvas, $click_pos) = @_;
-
+ my ($canvas, $click_pos_x, $click_pos_y) = @_;
+
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $menu = $self->object_menu;
- $canvas->PopupMenu($menu, $click_pos);
+ $canvas->PopupMenu($menu, $click_pos_x, $click_pos_y);
$menu->Destroy;
};
my $on_instances_moved = sub {
$self->update;
};
+ # callback to enable/disable action buttons
+ my $enable_action_buttons = sub {
+ my ($enable) = @_;
+ $self->{btn_export_gcode}->Enable($enable);
+ $self->{btn_reslice}->Enable($enable);
+ $self->{btn_print}->Enable($enable);
+ $self->{btn_send_gcode}->Enable($enable);
+ };
+
+ # callback to react to gizmo scale
+ my $on_gizmo_scale_uniformly = sub {
+ my ($scale) = @_;
+
+ my ($obj_idx, $object) = $self->selected_object;
+ return if !defined $obj_idx;
+
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ my $model_instance = $model_object->instances->[0];
+
+ $self->stop_background_process;
+
+ my $variation = $scale / $model_instance->scaling_factor;
+ #FIXME Scale the layer height profile?
+ foreach my $range (@{ $model_object->layer_height_ranges }) {
+ $range->[0] *= $variation;
+ $range->[1] *= $variation;
+ }
+ $_->set_scaling_factor($scale) for @{ $model_object->instances };
+
+ # Set object scale on c++ side
+# Slic3r::GUI::set_object_scale($obj_idx, $model_object->instances->[0]->scaling_factor * 100);
+
+# $object->transform_thumbnail($self->{model}, $obj_idx);
+
+ #update print and start background processing
+ $self->{print}->add_model_object($model_object, $obj_idx);
+
+ $self->selection_changed(1); # refresh info (size, volume etc.)
+ $self->update;
+ $self->schedule_background_process;
+ };
+
+ # callback to react to gizmo rotate
+ my $on_gizmo_rotate = sub {
+ my ($angle) = @_;
+ $self->rotate(rad2deg($angle), Z, 'absolute');
+ };
+
+ # callback to react to gizmo flatten
+ my $on_gizmo_flatten = sub {
+ my ($angle, $axis_x, $axis_y, $axis_z) = @_;
+ $self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0;
+ };
+
+ # callback to update object's geometry info while using gizmos
+ my $on_update_geometry_info = sub {
+ my ($size_x, $size_y, $size_z, $scale_factor) = @_;
+
+ my ($obj_idx, $object) = $self->selected_object;
+
+ if ((defined $obj_idx) && ($self->{object_info_size})) { # have we already loaded the info pane?
+ $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", $size_x, $size_y, $size_z));
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ if (my $stats = $model_object->mesh_stats) {
+ $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * $scale_factor**3));
+ }
+ }
+ };
+
+ # callbacks for toolbar
+ my $on_action_add = sub {
+ $self->add;
+ };
+
+ my $on_action_delete = sub {
+ $self->remove();
+ };
+
+ my $on_action_deleteall = sub {
+ $self->reset;
+ };
+
+ my $on_action_arrange = sub {
+ $self->arrange;
+ };
+
+ my $on_action_more = sub {
+ $self->increase;
+ };
+
+ my $on_action_fewer = sub {
+ $self->decrease;
+ };
+
+ my $on_action_split = sub {
+ $self->split_object;
+ };
+
+ my $on_action_cut = sub {
+ $self->object_cut_dialog;
+ };
+
+ my $on_action_settings = sub {
+ $self->object_settings_dialog;
+ };
+
+ my $on_action_layersediting = sub {
+ my $state = Slic3r::GUI::_3DScene::is_toolbar_item_pressed($self->{canvas3D}, "layersediting");
+ $self->on_layer_editing_toggled($state);
+ };
+
+ my $on_action_selectbyparts = sub {
+ my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D});
+ if ($curr eq 'volume') {
+ Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object');
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
+ }
+ elsif ($curr eq 'object') {
+ Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume');
+ my $selections = [];
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
+
+ my ($obj_idx, $object) = $self->selected_object;
+ if (defined $obj_idx) {
+ my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx);
+ Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1);
+ }
+ }
+ };
+
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
$self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D'));
- $self->{canvas3D}->set_on_select_object($on_select_object);
- $self->{canvas3D}->set_on_double_click($on_double_click);
- $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
- $self->{canvas3D}->set_on_arrange(sub { $self->arrange });
- $self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') });
- $self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') });
- $self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
- $self->{canvas3D}->set_on_increase_objects(sub { $self->increase() });
- $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() });
- $self->{canvas3D}->set_on_remove_object(sub { $self->remove() });
- $self->{canvas3D}->set_on_instances_moved($on_instances_moved);
- $self->{canvas3D}->use_plain_shader(1);
- $self->{canvas3D}->set_on_wipe_tower_moved(sub {
- my ($new_pos_3f) = @_;
+ Slic3r::GUI::_3DScene::register_on_select_object_callback($self->{canvas3D}, $on_select_object);
+ Slic3r::GUI::_3DScene::register_on_double_click_callback($self->{canvas3D}, $on_double_click);
+ Slic3r::GUI::_3DScene::register_on_right_click_callback($self->{canvas3D}, sub { $on_right_click->($self->{canvas3D}, @_); });
+ Slic3r::GUI::_3DScene::register_on_arrange_callback($self->{canvas3D}, sub { $self->arrange });
+ Slic3r::GUI::_3DScene::register_on_rotate_object_left_callback($self->{canvas3D}, sub { $self->rotate(-45, Z, 'relative') });
+ Slic3r::GUI::_3DScene::register_on_rotate_object_right_callback($self->{canvas3D}, sub { $self->rotate( 45, Z, 'relative') });
+ Slic3r::GUI::_3DScene::register_on_scale_object_uniformly_callback($self->{canvas3D}, sub { $self->changescale(undef) });
+ Slic3r::GUI::_3DScene::register_on_increase_objects_callback($self->{canvas3D}, sub { $self->increase() });
+ Slic3r::GUI::_3DScene::register_on_decrease_objects_callback($self->{canvas3D}, sub { $self->decrease() });
+ Slic3r::GUI::_3DScene::register_on_remove_object_callback($self->{canvas3D}, sub { $self->remove() });
+ Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved);
+ Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons);
+ Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly);
+ Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate);
+ Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten);
+ Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info);
+ Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add);
+ Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete);
+ Slic3r::GUI::_3DScene::register_action_deleteall_callback($self->{canvas3D}, $on_action_deleteall);
+ Slic3r::GUI::_3DScene::register_action_arrange_callback($self->{canvas3D}, $on_action_arrange);
+ Slic3r::GUI::_3DScene::register_action_more_callback($self->{canvas3D}, $on_action_more);
+ Slic3r::GUI::_3DScene::register_action_fewer_callback($self->{canvas3D}, $on_action_fewer);
+ Slic3r::GUI::_3DScene::register_action_split_callback($self->{canvas3D}, $on_action_split);
+ Slic3r::GUI::_3DScene::register_action_cut_callback($self->{canvas3D}, $on_action_cut);
+ Slic3r::GUI::_3DScene::register_action_settings_callback($self->{canvas3D}, $on_action_settings);
+ Slic3r::GUI::_3DScene::register_action_layersediting_callback($self->{canvas3D}, $on_action_layersediting);
+ Slic3r::GUI::_3DScene::register_action_selectbyparts_callback($self->{canvas3D}, $on_action_selectbyparts);
+ Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
+ Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1);
+ Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1);
+ Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1);
+
+ Slic3r::GUI::_3DScene::register_on_wipe_tower_moved_callback($self->{canvas3D}, sub {
+ my ($x, $y) = @_;
my $cfg = Slic3r::Config->new;
- $cfg->set('wipe_tower_x', $new_pos_3f->x);
- $cfg->set('wipe_tower_y', $new_pos_3f->y);
+ $cfg->set('wipe_tower_x', $x);
+ $cfg->set('wipe_tower_y', $y);
$self->GetFrame->{options_tabs}{print}->load_config($cfg);
});
- $self->{canvas3D}->set_on_model_update(sub {
+
+ Slic3r::GUI::_3DScene::register_on_model_update_callback($self->{canvas3D}, sub {
if (wxTheApp->{app_config}->get("background_processing")) {
$self->schedule_background_process;
} else {
# Hide the print info box, it is no more valid.
- $self->{"print_info_box_show"}->(0);
+ $self->print_info_box_show(0);
}
});
- $self->{canvas3D}->on_viewport_changed(sub {
- $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
- });
+
+ Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); });
}
+
+ Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; });
- # Initialize 2D preview canvas
- $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
- $self->{preview_notebook}->AddPage($self->{canvas}, L('2D'));
- $self->{canvas}->on_select_object($on_select_object);
- $self->{canvas}->on_double_click($on_double_click);
- $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
- $self->{canvas}->on_instances_moved($on_instances_moved);
+# # Initialize 2D preview canvas
+# $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
+# $self->{preview_notebook}->AddPage($self->{canvas}, L('2D'));
+# $self->{canvas}->on_select_object($on_select_object);
+# $self->{canvas}->on_double_click($on_double_click);
+# $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
+# $self->{canvas}->on_instances_moved($on_instances_moved);
# Initialize 3D toolpaths preview
if ($Slic3r::GUI::have_OpenGL) {
$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);
- });
+ Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1);
+ Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1);
+ Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); });
$self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview'));
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
}
@@ -154,109 +322,90 @@ sub new {
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
my $preview = $self->{preview_notebook}->GetCurrentPage;
- if ($preview == $self->{preview3D})
- {
- $self->{preview3D}->canvas->set_legend_enabled(1);
- $self->{preview3D}->load_print(1);
- } else {
- $self->{preview3D}->canvas->set_legend_enabled(0);
+ if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) {
+ $preview->OnActivate if $preview->can('OnActivate');
+ } elsif ($preview == $self->{preview3D}) {
+ $self->{preview3D}->reload_print;
+ # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
+ Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas);
+ } elsif ($preview == $self->{canvas3D}) {
+ if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) {
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
+ }
+ # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
+ Slic3r::GUI::_3DScene::set_as_dirty($self->{canvas3D});
}
-
- $preview->OnActivate if $preview->can('OnActivate');
});
- # toolbar for object manipulation
- if (!&Wx::wxMSW) {
- Wx::ToolTip::Enable(1);
- $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
- $self->{htoolbar}->AddTool(TB_ADD, L("Add…"), Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_REMOVE, L("Delete"), Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_RESET, L("Delete All"), Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_ARRANGE, L("Arrange"), Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_MORE, L("More"), Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_FEWER, L("Fewer"), Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_45CCW, L("45° ccw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_45CW, L("45° cw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_SCALE, L("Scale…"), Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_SPLIT, L("Split"), Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_CUT, L("Cut…"), Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_SETTINGS, L("Settings…"), Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_LAYER_EDITING, L('Layer Editing'), Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
- } else {
- my %tbar_buttons = (
- add => L("Add…"),
- remove => L("Delete"),
- reset => L("Delete All"),
- arrange => L("Arrange"),
- increase => "",
- decrease => "",
- rotate45ccw => "",
- rotate45cw => "",
- changescale => L("Scale…"),
- split => L("Split"),
- cut => L("Cut…"),
- settings => L("Settings…"),
- layer_editing => L("Layer editing"),
- );
- $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
- for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
- $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
- $self->{btoolbar}->Add($self->{"btn_$_"});
- }
- $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
- $self->{btoolbar}->Add($self->{"btn_layer_editing"});
- }
-
- $self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
- wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
- $self->{list}->InsertColumn(0, L("Name"), wxLIST_FORMAT_LEFT, 145);
- $self->{list}->InsertColumn(1, L("Copies"), wxLIST_FORMAT_CENTER, 45);
- $self->{list}->InsertColumn(2, L("Scale"), wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER);
- EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected);
- EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected);
- EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated);
- 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;
- }
- });
+# # toolbar for object manipulation
+# if (!&Wx::wxMSW) {
+# Wx::ToolTip::Enable(1);
+# $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
+# $self->{htoolbar}->AddTool(TB_ADD, L("Add…"), Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_REMOVE, L("Delete"), Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_RESET, L("Delete All"), Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_ARRANGE, L("Arrange"), Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddSeparator;
+# $self->{htoolbar}->AddTool(TB_MORE, L("More"), Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_FEWER, L("Fewer"), Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddSeparator;
+# $self->{htoolbar}->AddTool(TB_45CCW, L("45° ccw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_45CW, L("45° cw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_SCALE, L("Scale…"), Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_SPLIT, L("Split"), Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_CUT, L("Cut…"), Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddSeparator;
+# $self->{htoolbar}->AddTool(TB_SETTINGS, L("Settings…"), Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
+# $self->{htoolbar}->AddTool(TB_LAYER_EDITING, L('Layer Editing'), Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
+# } else {
+# my %tbar_buttons = (
+# add => L("Add…"),
+# remove => L("Delete"),
+# reset => L("Delete All"),
+# arrange => L("Arrange"),
+# increase => "",
+# decrease => "",
+# rotate45ccw => "",
+# rotate45cw => "",
+# changescale => L("Scale…"),
+# split => L("Split"),
+# cut => L("Cut…"),
+# settings => L("Settings…"),
+# layer_editing => L("Layer editing"),
+# );
+# $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
+# for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
+# $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
+# $self->{btoolbar}->Add($self->{"btn_$_"});
+# }
+# $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
+# $self->{btoolbar}->Add($self->{"btn_layer_editing"});
+# }
+
+ ### Panel for right column
+# $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+ $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
+ $self->{right_panel}->SetScrollbars(0, 1, 1, 1);
# right pane buttons
- $self->{btn_export_gcode} = Wx::Button->new($self, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
- $self->{btn_reslice} = Wx::Button->new($self, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
- $self->{btn_print} = Wx::Button->new($self, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
- $self->{btn_send_gcode} = Wx::Button->new($self, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
- $self->{btn_export_stl} = Wx::Button->new($self, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
+ $self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxNO_BORDER);#, wxBU_LEFT);
+ $self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
+ $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
+ $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
+ $self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
$self->{btn_print}->Hide;
$self->{btn_send_gcode}->Hide;
+# export_gcode cog_go.png
my %icons = qw(
- add brick_add.png
- remove brick_delete.png
- reset cross.png
- arrange bricks.png
- export_gcode cog_go.png
print arrow_up.png
send_gcode arrow_up.png
reslice reslice.png
export_stl brick_go.png
-
- increase add.png
- decrease delete.png
- rotate45cw arrow_rotate_clockwise.png
- rotate45ccw arrow_rotate_anticlockwise.png
- changescale arrow_out.png
- split shape_ungroup.png
- cut package.png
- settings cog.png
);
for (grep $self->{"btn_$_"}, keys %icons) {
$self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new(Slic3r::var($icons{$_}), wxBITMAP_TYPE_PNG));
@@ -275,43 +424,45 @@ sub new {
EVT_BUTTON($self, $self->{btn_reslice}, \&reslice);
EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl);
- if ($self->{htoolbar}) {
- EVT_TOOL($self, TB_ADD, sub { $self->add; });
- EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove
- EVT_TOOL($self, TB_RESET, sub { $self->reset; });
- EVT_TOOL($self, TB_ARRANGE, sub { $self->arrange; });
- EVT_TOOL($self, TB_MORE, sub { $self->increase; });
- EVT_TOOL($self, TB_FEWER, sub { $self->decrease; });
- EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45, Z, 'relative') });
- EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45, Z, 'relative') });
- EVT_TOOL($self, TB_SCALE, sub { $self->changescale(undef); });
- EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
- EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
- EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
- EVT_TOOL($self, TB_LAYER_EDITING, sub {
- my $state = $self->{canvas3D}->layer_editing_enabled;
- $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
- $self->on_layer_editing_toggled(! $state);
- });
- } else {
- EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
- EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
- EVT_BUTTON($self, $self->{btn_reset}, sub { $self->reset; });
- EVT_BUTTON($self, $self->{btn_arrange}, sub { $self->arrange; });
- EVT_BUTTON($self, $self->{btn_increase}, sub { $self->increase; });
- EVT_BUTTON($self, $self->{btn_decrease}, sub { $self->decrease; });
- EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45, Z, 'relative') });
- EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45, Z, 'relative') });
- EVT_BUTTON($self, $self->{btn_changescale}, sub { $self->changescale(undef); });
- EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
- EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
- EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
- EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
- }
+# if ($self->{htoolbar}) {
+# EVT_TOOL($self, TB_ADD, sub { $self->add; });
+# EVT_TOOL($self, TB_REMOVE, sub { $self->remove() }); # explicitly pass no argument to remove
+# EVT_TOOL($self, TB_RESET, sub { $self->reset; });
+# EVT_TOOL($self, TB_ARRANGE, sub { $self->arrange; });
+# EVT_TOOL($self, TB_MORE, sub { $self->increase; });
+# EVT_TOOL($self, TB_FEWER, sub { $self->decrease; });
+# EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45, Z, 'relative') });
+# EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45, Z, 'relative') });
+# EVT_TOOL($self, TB_SCALE, sub { $self->changescale(undef); });
+# EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
+# EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
+# EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
+# EVT_TOOL($self, TB_LAYER_EDITING, sub {
+# my $state = Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D});
+# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
+# $self->on_layer_editing_toggled(! $state);
+# });
+# } else {
+# EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
+# EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
+# EVT_BUTTON($self, $self->{btn_remove}, sub { Slic3r::GUI::remove_obj() }); # explicitly pass no argument to remove
+# EVT_BUTTON($self, $self->{btn_reset}, sub { $self->reset; });
+# EVT_BUTTON($self, $self->{btn_arrange}, sub { $self->arrange; });
+# EVT_BUTTON($self, $self->{btn_increase}, sub { $self->increase; });
+# EVT_BUTTON($self, $self->{btn_decrease}, sub { $self->decrease; });
+# EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45, Z, 'relative') });
+# EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45, Z, 'relative') });
+# EVT_BUTTON($self, $self->{btn_changescale}, sub { $self->changescale(undef); });
+# EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
+# EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
+# EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
+# EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
+# }
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
for grep defined($_),
- $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
+ $self, $self->{canvas3D}, $self->{preview3D}, $self->{list};
+# $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D};
EVT_COMMAND($self, -1, $SLICING_COMPLETED_EVENT, sub {
my ($self, $event) = @_;
@@ -332,36 +483,37 @@ sub new {
});
}
- $self->{canvas}->update_bed_size;
+# $self->{canvas}->update_bed_size;
if ($self->{canvas3D}) {
- $self->{canvas3D}->update_bed_size;
- $self->{canvas3D}->zoom_to_bed;
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape);
+ Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D});
}
if ($self->{preview3D}) {
- $self->{preview3D}->set_bed_shape($self->{config}->bed_shape);
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape);
}
$self->update;
{
my $presets;
{
- $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 2, 1, 2);
+ $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(4, 2, 1, 2);
$presets->AddGrowableCol(1, 1);
$presets->SetFlexibleDirection(wxHORIZONTAL);
my %group_labels = (
print => L('Print settings'),
filament => L('Filament'),
+ sla_material=> L('SLA material'),
printer => L('Printer'),
);
- # UI Combo boxes for a print, multiple filaments, and a printer.
+ # UI Combo boxes for a print, multiple filaments, SLA material and a printer.
# Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase,
# once a printer preset with multiple extruders is activated.
# $self->{preset_choosers}{$group}[$idx]
$self->{preset_choosers} = {};
- for my $group (qw(print filament printer)) {
- my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
+ for my $group (qw(print filament sla_material printer)) {
+ my $text = Wx::StaticText->new($self->{right_panel}, -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);
+ my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
if ($group eq 'filament') {
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } );
}
@@ -376,17 +528,35 @@ sub new {
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
$presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 1);
}
+ $presets->Layout;
}
- my $frequently_changed_parameters_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
- Slic3r::GUI::add_frequently_changed_parameters($self, $frequently_changed_parameters_sizer, $presets);
-
+ my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL);
+ Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets);
+
+ my $expert_mode_part_sizer = Wx::BoxSizer->new(wxVERTICAL);
+ Slic3r::GUI::add_expert_mode_part( $self->{right_panel}, $expert_mode_part_sizer,
+ $self->{model},
+ $self->{event_object_selection_changed},
+ $self->{event_object_settings_changed},
+ $self->{event_remove_object},
+ $self->{event_update_scene});
+# if ($expert_mode_part_sizer->IsShown(2)==1)
+# {
+# $expert_mode_part_sizer->Layout;
+# $expert_mode_part_sizer->Show(2, 0); # ? Why doesn't work
+# $self->{right_panel}->Layout;
+# }
+
my $object_info_sizer;
{
- my $box = Wx::StaticBox->new($self, -1, L("Info"));
+# my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info"));
+ my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info"));
+ $box->SetFont($Slic3r::GUI::small_bold_font);
$object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
- $object_info_sizer->SetMinSize([350,-1]);
- my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
+ $object_info_sizer->SetMinSize([300,-1]);
+ #!my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
+ my $grid_sizer = Wx::FlexGridSizer->new(2, 4, 5, 5);
$grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
$grid_sizer->AddGrowableCol(1, 1);
$grid_sizer->AddGrowableCol(3, 1);
@@ -401,54 +571,46 @@ sub new {
);
while (my $field = shift @info) {
my $label = shift @info;
- my $text = Wx::StaticText->new($self, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+# my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+ my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
$text->SetFont($Slic3r::GUI::small_font);
- $grid_sizer->Add($text, 0);
+ #!$grid_sizer->Add($text, 0);
- $self->{"object_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+# $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+ $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
$self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
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;
+# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
+ $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
+ #$self->{object_info_manifold_warning_icon}->Hide;
+ $self->{"object_info_manifold_warning_icon_show"} = sub {
+ if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) {
+ Slic3r::GUI::set_show_manifold_warning_icon($_[0]);
+ my $mode = wxTheApp->{app_config}->get("view_mode");
+ return if ($mode eq "" || $mode eq "simple");
+ $self->{object_info_manifold_warning_icon}->Show($_[0]);
+ $self->Layout
+ }
+ };
+ $self->{"object_info_manifold_warning_icon_show"}->(0);
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);
+ $h_sizer->Add($text, 0);
+ $h_sizer->Add($self->{object_info_manifold_warning_icon}, 0, wxLEFT, 2);
+ $h_sizer->Add($self->{"object_info_$field"}, 0, wxLEFT, 2);
+ #!$grid_sizer->Add($h_sizer, 0, wxEXPAND);
+ $object_info_sizer->Add($h_sizer, 0, wxEXPAND|wxTOP, 4);
} else {
+ $grid_sizer->Add($text, 0);
$grid_sizer->Add($self->{"object_info_$field"}, 0);
}
}
}
- my $print_info_sizer;
- {
- my $box = Wx::StaticBox->new($self, -1, L("Sliced Info"));
- $print_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
- $print_info_sizer->SetMinSize([350,-1]);
- my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
- $grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
- $grid_sizer->AddGrowableCol(1, 1);
- $grid_sizer->AddGrowableCol(3, 1);
- $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
- my @info = (
- fil_m => L("Used Filament (m)"),
- fil_mm3 => L("Used Filament (mm³)"),
- fil_g => L("Used Filament (g)"),
- cost => L("Cost"),
- time => L("Estimated printing time"),
- );
- while (my $field = shift @info) {
- my $label = shift @info;
- my $text = Wx::StaticText->new($self, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
- $text->SetFont($Slic3r::GUI::small_font);
- $grid_sizer->Add($text, 0);
-
- $self->{"print_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
- $self->{"print_info_$field"}->SetFont($Slic3r::GUI::small_font);
- $grid_sizer->Add($self->{"print_info_$field"}, 0);
- }
- }
+ my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new(
+# Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL);
+ Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL);
+ $print_info_sizer->SetMinSize([300,-1]);
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$self->{buttons_sizer} = $buttons_sizer;
@@ -457,39 +619,64 @@ sub new {
$buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0);
- $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
+# $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5);
+# $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
+# $scrolled_window_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
+ #$buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
+
+ ### Sizer for info boxes
+ my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL);
+ $info_sizer->SetMinSize([318, -1]);
+ $info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
+ $info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
+
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
+ $self->{right_panel}->SetSizer($right_sizer);
+ $right_sizer->SetMinSize([320, -1]);
$right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
- $right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 10) if defined $frequently_changed_parameters_sizer;
- $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5);
- $right_sizer->Add($self->{list}, 1, wxEXPAND, 5);
- $right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
- $right_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
- # Callback for showing / hiding the print info box.
- $self->{"print_info_box_show"} = sub {
- if ($right_sizer->IsShown(5) != $_[0]) {
- $right_sizer->Show(5, $_[0]);
- $self->Layout
- }
- };
+ $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
+ $right_sizer->Add($expert_mode_part_sizer, 0, wxEXPAND | wxTOP, 10) if defined $expert_mode_part_sizer;
+ $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10);
+ $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20);
# Show the box initially, let it be shown after the slicing is finished.
- $self->{"print_info_box_show"}->(0);
+ $self->print_info_box_show(0);
+ $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 20);
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
$hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1);
- $hsizer->Add($right_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 3);
-
+ $hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 3);
+
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($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar};
+# $sizer->Add($self->{btoolbar}, 0, wxEXPAND, 0) if $self->{btoolbar};
$sizer->Add($hsizer, 1, wxEXPAND, 0);
$sizer->SetSizeHints($self);
$self->SetSizer($sizer);
+
+ # Send sizers/buttons to C++
+ Slic3r::GUI::set_objects_from_perl( $self->{right_panel},
+ $frequently_changed_parameters_sizer,
+ $expert_mode_part_sizer,
+ $info_sizer,
+ $self->{btn_export_gcode},
+ $self->{btn_export_stl},
+ $self->{btn_reslice},
+ $self->{btn_print},
+ $self->{btn_send_gcode},
+ $self->{object_info_manifold_warning_icon} );
+ }
+
+ # Last correct selected item for each preset
+ {
+ $self->{selected_item_print} = 0;
+ $self->{selected_item_filament} = 0;
+ $self->{selected_item_printer} = 0;
}
$self->update_ui_from_settings();
+ $self->Layout;
return $self;
}
@@ -513,9 +700,22 @@ sub _on_select_preset {
# Only update the platter UI for the 2nd and other filaments.
wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice);
} else {
+ my $selected_item = $choice->GetSelection();
+ return if ($selected_item == $self->{"selected_item_$group"} &&
+ !Slic3r::GUI::get_preset_tab($group)->current_preset_is_dirty);
+
+ my $selected_string = $choice->GetString($selected_item);
+ if ($selected_string eq ("------- ".L("System presets")." -------") ||
+ $selected_string eq ("------- ".L("User presets")." -------") ){
+ $choice->SetSelection($self->{"selected_item_$group"});
+ return;
+ }
+
# call GetSelection() in scalar context as it's context-aware
- $self->{on_select_preset}->($group, $choice->GetStringSelection)
- if $self->{on_select_preset};
+# $self->{on_select_preset}->($group, $choice->GetStringSelection)
+ $self->{on_select_preset}->($group, $selected_string)
+ if $self->{on_select_preset};
+ $self->{"selected_item_$group"} = $selected_item;
}
# Synchronize config.ini with the current selections.
wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
@@ -525,16 +725,17 @@ sub _on_select_preset {
sub on_layer_editing_toggled {
my ($self, $new_state) = @_;
- $self->{canvas3D}->layer_editing_enabled($new_state);
- if ($new_state && ! $self->{canvas3D}->layer_editing_enabled) {
+ Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, $new_state);
+ if ($new_state && ! Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D})) {
# Initialization of the OpenGL shaders failed. Disable the tool.
- if ($self->{htoolbar}) {
- $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0);
- $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0);
- } else {
- $self->{"btn_layer_editing"}->Disable;
- $self->{"btn_layer_editing"}->SetValue(0);
- }
+# if ($self->{htoolbar}) {
+# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0);
+# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0);
+# } else {
+# $self->{"btn_layer_editing"}->Disable;
+# $self->{"btn_layer_editing"}->SetValue(0);
+# }
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0);
}
$self->{canvas3D}->Refresh;
$self->{canvas3D}->Update;
@@ -556,16 +757,17 @@ sub update_ui_from_settings
}
}
-# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
+# Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
# Called by
# Slic3r::GUI::Tab::Print::_on_presets_changed
# Slic3r::GUI::Tab::Filament::_on_presets_changed
+# Slic3r::GUI::Tab::Material::_on_presets_changed
# Slic3r::GUI::Tab::Printer::_on_presets_changed
# when the presets are loaded or the user selects another preset.
# For Print settings and Printer, synchronize the selection index with their tabs.
# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection.
sub update_presets {
- # $group: one of qw(print filament printer)
+ # $group: one of qw(print filament sla_material printer)
# $presets: PresetCollection
my ($self, $group, $presets) = @_;
my @choosers = @{$self->{preset_choosers}{$group}};
@@ -581,6 +783,8 @@ sub update_presets {
}
} elsif ($group eq 'print') {
wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]);
+ } elsif ($group eq 'sla_material') {
+ wxTheApp->{preset_bundle}->sla_material->update_platter_ui($choosers[0]);
} elsif ($group eq 'printer') {
# Update the print choosers to only contain the compatible presets, update the dirty flags.
wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]);
@@ -638,6 +842,9 @@ sub load_files {
Slic3r::GUI::show_error($self, $@) if $@;
$_->load_current_preset for (values %{$self->GetFrame->{options_tabs}});
wxTheApp->{app_config}->update_config_dir(dirname($input_file));
+ # forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
+ # and if the config contains a "layer_height" different from the current defined one
+ $self->async_apply_config;
}
else
{
@@ -653,7 +860,16 @@ sub load_files {
. "Instead of considering them as multiple objects, should I consider\n"
. "this file as a single object having multiple parts?\n"),
L('Multi-part object detected'), wxICON_WARNING | wxYES | wxNO);
- $model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
+ $model->convert_multipart_object(scalar(@$nozzle_dmrs)) if $dialog->ShowModal() == wxID_YES;
+ }
+
+ # objects imported from 3mf require a call to center_around_origin to have gizmos working properly and this call
+ # need to be done after looks_like_multipart_object detection
+ if ($input_file =~ /.3[mM][fF]$/)
+ {
+ foreach my $model_object (@{$model->objects}) {
+ $model_object->center_around_origin; # also aligns object to Z = 0
+ }
}
if ($one_by_one) {
@@ -670,7 +886,7 @@ sub load_files {
. "Instead of considering them as multiple objects, should I consider\n"
. "these files to represent a single object having multiple parts?\n"),
L('Multi-part object detected'), wxICON_WARNING | wxYES | wxNO);
- $new_model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
+ $new_model->convert_multipart_object(scalar(@$nozzle_dmrs)) if $dialog->ShowModal() == wxID_YES;
push @obj_idx, $self->load_model_objects(@{$new_model->objects});
}
@@ -711,8 +927,14 @@ sub load_model_objects {
{
# if the object is too large (more than 5 times the bed), scale it down
my $size = $o->bounding_box->size;
- my $ratio = max(@$size[X,Y]) / unscale(max(@$bed_size[X,Y]));
- if ($ratio > 5) {
+ my $ratio = max($size->x / unscale($bed_size->x), $size->y / unscale($bed_size->y));
+ if ($ratio > 10000) {
+ # the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
+ # so scale down the mesh
+ $o->scale_xyz(Slic3r::Pointf3->new(1/$ratio, 1/$ratio, 1/$ratio));
+ $scaled_down = 1;
+ }
+ elsif ($ratio > 5) {
$_->set_scaling_factor(1/$ratio) for @{$o->instances};
$scaled_down = 1;
}
@@ -738,24 +960,19 @@ sub load_model_objects {
foreach my $obj_idx (@obj_idx) {
my $object = $self->{objects}[$obj_idx];
my $model_object = $self->{model}->objects->[$obj_idx];
- $self->{list}->InsertStringItem($obj_idx, $object->name);
- $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
- if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
-
- $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
- $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
+
+ # Add object to list on c++ side
+ Slic3r::GUI::add_object_to_list($object->name, $model_object);
- $self->reset_thumbnail($obj_idx);
+
+# $self->reset_thumbnail($obj_idx);
}
$self->arrange if $need_arrange;
$self->update;
# zoom to objects
- $self->{canvas3D}->zoom_to_volumes
- if $self->{canvas3D};
+ Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D};
- $self->{list}->Update;
- $self->{list}->Select($obj_idx[-1], 1);
$self->object_list_changed;
$self->schedule_background_process;
@@ -789,7 +1006,8 @@ sub remove {
splice @{$self->{objects}}, $obj_idx, 1;
$self->{model}->delete_object($obj_idx);
$self->{print}->delete_object($obj_idx);
- $self->{list}->DeleteItem($obj_idx);
+ # Delete object from list on c++ side
+ Slic3r::GUI::delete_object_from_list();
$self->object_list_changed;
$self->select_object(undef);
@@ -808,7 +1026,8 @@ sub reset {
@{$self->{objects}} = ();
$self->{model}->clear_objects;
$self->{print}->clear_objects;
- $self->{list}->DeleteAllItems;
+ # Delete all objects from list on c++ side
+ Slic3r::GUI::delete_all_objects_from_list();
$self->object_list_changed;
$self->select_object(undef);
@@ -831,9 +1050,19 @@ sub increase {
);
$self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
}
- $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
- # Only autoarrange if user has autocentering enabled.
- wxTheApp->{app_config}->get("autocenter") ? $self->arrange : $self->update;
+ # Set count of object on c++ side
+ Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count);
+
+ # only autoarrange if user has autocentering enabled
+ $self->stop_background_process;
+ if (wxTheApp->{app_config}->get("autocenter")) {
+ $self->arrange;
+ } else {
+ $self->update;
+ }
+
+ $self->selection_changed; # refresh info (size, volume etc.)
+ $self->schedule_background_process;
}
sub decrease {
@@ -849,7 +1078,8 @@ sub decrease {
$model_object->delete_last_instance;
$self->{print}->objects->[$obj_idx]->delete_last_copy;
}
- $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
+ # Set conut of object on c++ side
+ Slic3r::GUI::set_object_count($obj_idx, $model_object->instances_count);
} elsif (defined $copies_asked) {
# The "decrease" came from the "set number of copies" dialog.
$self->stop_background_process;
@@ -858,11 +1088,7 @@ sub decrease {
# The "decrease" came from the "-" button. Don't allow the object to disappear.
return;
}
-
- if ($self->{objects}[$obj_idx]) {
- $self->{list}->Select($obj_idx, 0);
- $self->{list}->Select($obj_idx, 1);
- }
+
$self->update;
}
@@ -872,9 +1098,10 @@ sub set_number_of_copies {
my ($obj_idx, $object) = $self->selected_object;
my $model_object = $self->{model}->objects->[$obj_idx];
# prompt user
- my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
+ my $copies = -1;
+ $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
my $diff = $copies - $model_object->instances_count;
- if ($diff == 0) {
+ if ($diff == 0 || $copies == -1) {
# no variation
} elsif ($diff > 0) {
$self->increase($diff);
@@ -906,45 +1133,68 @@ sub _get_number_from_user {
}
sub rotate {
- my ($self, $angle, $axis, $relative_key) = @_;
+ my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_;
$relative_key //= 'absolute'; # relative or absolute coordinates
- $axis //= Z; # angle is in degrees
-
+ $axis_x //= 0;
+ $axis_y //= 0;
+ $axis_z //= 0;
my $relative = $relative_key eq 'relative';
-
+
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
-
+
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
-
+
if (!defined $angle) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
$angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default);
return if $angle eq '';
}
+
+ # Let's calculate vector of rotation axis (if we don't have it already)
+ if (defined $axis) {
+ if ($axis == X) {
+ $axis_x = 1;
+ }
+ if ($axis == Y) {
+ $axis_y = 1;
+ }
+ }
$self->stop_background_process;
- if ($axis == Z) {
+ if (defined $axis && $axis == Z) {
my $new_angle = deg2rad($angle);
- $_->set_rotation(($relative ? $_->rotation : 0.) + $new_angle) for @{ $model_object->instances };
- $object->transform_thumbnail($self->{model}, $obj_idx);
+ foreach my $inst (@{ $model_object->instances }) {
+ my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle;
+ while ($rotation > 2.0 * PI) {
+ $rotation -= 2.0 * PI;
+ }
+ while ($rotation < 0.0) {
+ $rotation += 2.0 * PI;
+ }
+ $inst->set_rotation($rotation);
+ Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D});
+ }
+# $object->transform_thumbnail($self->{model}, $obj_idx);
} else {
- # rotation around X and Y needs to be performed on mesh
- # so we first apply any Z rotation
- if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
- $_->set_rotation(0) for @{ $model_object->instances };
+ if (defined $axis) {
+ # rotation around X and Y needs to be performed on mesh
+ # so we first apply any Z rotation
+ if ($model_instance->rotation != 0) {
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, -1));
+ $_->set_rotation(0) for @{ $model_object->instances };
+ }
}
- $model_object->rotate(deg2rad($angle), $axis);
+ $model_object->rotate(deg2rad($angle), Slic3r::Pointf3->new($axis_x, $axis_y, $axis_z));
- # realign object to Z = 0
- $model_object->center_around_origin;
- $self->reset_thumbnail($obj_idx);
+# # realign object to Z = 0
+# $model_object->center_around_origin;
+# $self->reset_thumbnail($obj_idx);
}
-
+
# update print and start background processing
$self->{print}->add_model_object($model_object, $obj_idx);
@@ -963,15 +1213,15 @@ sub mirror {
# apply Z rotation before mirroring
if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
$_->set_rotation(0) for @{ $model_object->instances };
}
$model_object->mirror($axis);
- # realign object to Z = 0
- $model_object->center_around_origin;
- $self->reset_thumbnail($obj_idx);
+# # realign object to Z = 0
+# $model_object->center_around_origin;
+# $self->reset_thumbnail($obj_idx);
# update print and start background processing
$self->stop_background_process;
@@ -990,8 +1240,7 @@ sub changescale {
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
- my $object_size = $model_object->bounding_box->size;
- my $bed_size = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape})->bounding_box->size;
+ my $object_size = $model_object->instance_bounding_box(0)->size;
if (defined $axis) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
@@ -999,7 +1248,7 @@ sub changescale {
if ($tosize) {
my $cursize = $object_size->[$axis];
my $newsize = $self->_get_number_from_user(
- sprintf(L('Enter the new size for the selected object (print bed: %smm):'), unscale($bed_size->[$axis])),
+ L('Enter the new size for the selected object:'),
L("Scale along ").$axis_name, L('Invalid scaling value entered'), $cursize, 1);
return if $newsize eq '';
$scale = $newsize / $cursize * 100;
@@ -1010,7 +1259,7 @@ sub changescale {
# apply Z rotation before scaling
if ($model_instance->rotation != 0) {
- $model_object->rotate($model_instance->rotation, Z);
+ $model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
$_->set_rotation(0) for @{ $model_object->instances };
}
@@ -1020,7 +1269,7 @@ sub changescale {
#FIXME Scale the layer height profile when $axis == Z?
#FIXME Scale the layer height ranges $axis == Z?
# object was already aligned to Z = 0, so no need to realign it
- $self->reset_thumbnail($obj_idx);
+# $self->reset_thumbnail($obj_idx);
} else {
my $scale;
if ($tosize) {
@@ -1033,8 +1282,9 @@ sub changescale {
$scale = $self->_get_number_from_user(L('Enter the scale % for the selected object:'), L('Scale'), L('Invalid scaling value entered'), $model_instance->scaling_factor*100, 1);
return if ! defined($scale) || $scale eq '';
}
-
- $self->{list}->SetItem($obj_idx, 2, "$scale%");
+
+ # Set object scale on c++ side
+# Slic3r::GUI::set_object_scale($obj_idx, $scale);
$scale /= 100; # turn percent into factor
my $variation = $scale / $model_instance->scaling_factor;
@@ -1044,7 +1294,7 @@ sub changescale {
$range->[1] *= $variation;
}
$_->set_scaling_factor($scale) for @{ $model_object->instances };
- $object->transform_thumbnail($self->{model}, $obj_idx);
+# $object->transform_thumbnail($self->{model}, $obj_idx);
}
# update print and start background processing
@@ -1059,13 +1309,17 @@ sub arrange {
my ($self) = @_;
$self->stop_background_process;
- my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
- my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
+ # my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
+ # my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
+
+ # Update is not implemented in C++ so we cannot call this for now
+ $self->{appController}->arrange_model;
+
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed
# Force auto center of the aligned grid of of objects on the print bed.
- $self->update(1);
+ $self->update(0);
}
sub split_object {
@@ -1114,12 +1368,12 @@ sub async_apply_config {
my $was_running = $self->{background_slicing_process}->running;
my $invalidated = $self->{background_slicing_process}->apply_config(wxTheApp->{preset_bundle}->full_config);
# Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
- $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled);
+ $self->{canvas3D}->Refresh if Slic3r::GUI::_3DScene::is_layers_editing_enabled($self->{canvas3D});
# If the apply_config caused the calculation to stop, or it was not running yet:
if ($invalidated) {
if ($was_running) {
# Hide the slicing results if the current slicing status is no more valid.
- $self->{"print_info_box_show"}->(0);
+ $self->print_info_box_show(0)
}
if (wxTheApp->{app_config}->get("background_processing")) {
$self->{background_slicing_process}->start;
@@ -1130,6 +1384,12 @@ sub async_apply_config {
$self->{gcode_preview_data}->reset;
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
$self->{preview3D}->reload_print if $self->{preview3D};
+ # We also need to reload 3D scene because of the wipe tower preview box
+ if ($self->{config}->wipe_tower) {
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
+ }
}
}
}
@@ -1157,16 +1417,9 @@ sub start_background_process {
# Stop the background processing
sub stop_background_process {
my ($self) = @_;
- # Don't call async_apply_config() while stopped.
- $self->{apply_config_timer}->Stop;
- $self->statusbar->SetCancelCallback(undef);
- $self->statusbar->StopBusy;
- $self->statusbar->SetStatusText("");
- # Stop the background task, wait until the thread goes into the "Idle" state.
- $self->{background_slicing_process}->stop;
- # Update the UI with the slicing results.
- $self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
+ $self->{toolpaths2D}->reload_print if $self->{canvas3D};
$self->{preview3D}->reload_print if $self->{preview3D};
+ $self->schedule_background_process;
}
# Called by the "Slice now" button, which is visible only if the background processing is disabled.
@@ -1219,6 +1472,8 @@ sub export_gcode {
};
Slic3r::GUI::catch_error($self) and return;
+ # Copy the names of active presets into the placeholder parser.
+ wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser);
# select output file
if ($output_file) {
$self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) };
@@ -1269,6 +1524,21 @@ sub on_update_print_preview {
my ($self) = @_;
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
$self->{preview3D}->reload_print if $self->{preview3D};
+
+ # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
+}
+
+# This gets called also if we have no threads.
+sub on_progress_event {
+ my ($self, $percent, $message) = @_;
+
+ $self->statusbar->SetProgress($percent);
+ # TODO: three dot character is not properly translated into C++
+ # $self->statusbar->SetStatusText("$message…");
+ $self->statusbar->SetStatusText("$message...");
}
# Called when the G-code export finishes, either successfully or with an error.
@@ -1293,7 +1563,7 @@ sub on_process_completed {
$message = L("File added to print queue");
$do_print = 1;
} elsif ($self->{send_gcode_file}) {
- $message = L("Sending G-code file to the OctoPrint server...");
+ $message = L("Sending G-code file to the Printer Host ...");
$send_gcode = 1;
} else {
$message = L("G-code file exported to ") . $self->{export_gcode_output_file};
@@ -1309,18 +1579,18 @@ sub on_process_completed {
# Send $self->{send_gcode_file} to OctoPrint.
if ($send_gcode) {
- my $op = Slic3r::OctoPrint->new($self->{config});
- $op->send_gcode($self->GetId(), $Slic3r::GUI::MainFrame::PROGRESS_BAR_EVENT, $Slic3r::GUI::MainFrame::ERROR_EVENT, $self->{send_gcode_file});
+ my $host = Slic3r::PrintHost::get_print_host($self->{config});
+
+ if ($host->send_gcode($self->{send_gcode_file})) {
+ $self->statusbar->SetStatusText(L("Upload to host finished."));
+ } else {
+ $self->statusbar->SetStatusText("");
+ }
}
$self->{print_file} = undef;
$self->{send_gcode_file} = undef;
- $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);
+ $self->print_info_box_show(1);
# this updates buttons status
$self->object_list_changed;
@@ -1330,6 +1600,63 @@ sub on_process_completed {
$self->{preview3D}->reload_print if $self->{preview3D};
}
+# Fill in the "Sliced info" box with the result of the G-code generator.
+sub print_info_box_show {
+ my ($self, $show) = @_;
+# my $scrolled_window_panel = $self->{scrolled_window_panel};
+# my $scrolled_window_sizer = $self->{scrolled_window_sizer};
+# return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
+ my $panel = $self->{right_panel};
+ my $sizer = $self->{info_sizer};
+ return if (!$sizer || !$show && ($sizer->IsShown(1) == $show));
+
+ Slic3r::GUI::set_show_print_info($show);
+ return if (wxTheApp->{app_config}->get("view_mode") eq "simple");
+
+ if ($show) {
+ my $print_info_sizer = $self->{print_info_sizer};
+ $print_info_sizer->Clear(1);
+ my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
+ $grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
+ $grid_sizer->AddGrowableCol(1, 1);
+ $grid_sizer->AddGrowableCol(3, 1);
+ $print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
+ my @info = (
+ L("Used Filament (m)")
+ => sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
+ L("Used Filament (mm³)")
+ => sprintf("%.2f" , $self->{print}->total_extruded_volume),
+ L("Used Filament (g)"),
+ => sprintf("%.2f" , $self->{print}->total_weight),
+ L("Cost"),
+ => sprintf("%.2f" , $self->{print}->total_cost),
+ L("Estimated printing time (normal mode)")
+ => $self->{print}->estimated_normal_print_time,
+ L("Estimated printing time (silent mode)")
+ => $self->{print}->estimated_silent_print_time
+ );
+ while ( my $label = shift @info) {
+ my $value = shift @info;
+ next if $value eq "N/A";
+# my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
+ my $text = Wx::StaticText->new($panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
+ $text->SetFont($Slic3r::GUI::small_font);
+ $grid_sizer->Add($text, 0);
+# my $field = Wx::StaticText->new($scrolled_window_panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+ my $field = Wx::StaticText->new($panel, -1, $value, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+ $field->SetFont($Slic3r::GUI::small_font);
+ $grid_sizer->Add($field, 0);
+ }
+ }
+
+# $scrolled_window_sizer->Show(2, $show);
+# $scrolled_window_panel->Layout;
+ $sizer->Show(1, $show);
+
+ $self->Layout;
+ $panel->Refresh;
+}
+
sub do_print {
my ($self) = @_;
@@ -1362,7 +1689,7 @@ sub reload_from_disk {
my $model_object = $self->{model}->objects->[$obj_idx];
#FIXME convert to local file encoding
return if !$model_object->input_file
- || !-e $model_object->input_file;
+ || !-e Slic3r::encode_path($model_object->input_file);
my @new_obj_idx = $self->load_files([$model_object->input_file]);
return if !@new_obj_idx;
@@ -1395,12 +1722,40 @@ sub export_object_stl {
$self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
}
+sub fix_through_netfabb {
+ my ($self) = @_;
+ my ($obj_idx, $object) = $self->selected_object;
+ return if !defined $obj_idx;
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ my $model_fixed = Slic3r::Model->new;
+ Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed);
+
+ my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects});
+ return if !@new_obj_idx;
+
+ foreach my $new_obj_idx (@new_obj_idx) {
+ my $o = $self->{model}->objects->[$new_obj_idx];
+ $o->clear_instances;
+ $o->add_instance($_) for @{$model_object->instances};
+ #$o->invalidate_bounding_box;
+
+ if ($o->volumes_count == $model_object->volumes_count) {
+ for my $i (0..($o->volumes_count-1)) {
+ $o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
+ }
+ }
+ #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
+ }
+
+ $self->remove($obj_idx);
+}
+
sub export_amf {
my ($self) = @_;
return if !@{$self->{objects}};
# Ask user for a file name to write into.
my $output_file = $self->_get_export_file('AMF') or return;
- my $res = $self->{model}->store_amf($output_file, $self->{print});
+ my $res = $self->{model}->store_amf($output_file, $self->{print}, $self->{export_option});
if ($res)
{
$self->statusbar->SetStatusText(L("AMF file exported to ").$output_file);
@@ -1416,7 +1771,7 @@ sub export_3mf {
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});
+ my $res = $self->{model}->store_3mf($output_file, $self->{print}, $self->{export_option});
if ($res)
{
$self->statusbar->SetStatusText(L("3MF file exported to ").$output_file);
@@ -1453,24 +1808,28 @@ sub _get_export_file {
$suffix = '.3mf';
$wildcard = 'threemf';
}
+ # Copy the names of active presets into the placeholder parser.
+ wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser);
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/;
my $dlg = Wx::FileDialog->new($self, L("Save ").$format.L(" file as:"), dirname($output_file),
basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{$wildcard}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ Slic3r::GUI::add_export_option($dlg, $format);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return undef;
}
$output_file = $dlg->GetPath;
+ $self->{export_option} = Slic3r::GUI::get_export_option($dlg);
$dlg->Destroy;
return $output_file;
}
-sub reset_thumbnail {
- my ($self, $obj_idx) = @_;
- $self->{objects}[$obj_idx]->thumbnail(undef);
-}
+#sub reset_thumbnail {
+# my ($self, $obj_idx) = @_;
+# $self->{objects}[$obj_idx]->thumbnail(undef);
+#}
# this method gets called whenever print center is changed or the objects' bounding box changes
# (i.e. when an object is added/removed/moved/rotated/scaled)
@@ -1482,11 +1841,33 @@ sub update {
$self->stop_background_process;
$self->{print}->reload_model_instances();
$self->{canvas}->reload_scene if $self->{canvas};
- $self->{canvas3D}->reload_scene if $self->{canvas3D};
+# $self->{canvas}->reload_scene if $self->{canvas};
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
+ $self->{preview3D}->reset_gcode_preview_data if $self->{preview3D};
$self->{preview3D}->reload_print if $self->{preview3D};
$self->schedule_background_process;
}
+# When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes.
+sub show_preset_comboboxes{
+ my ($self, $showSLA) = @_; #if showSLA is oposite value to "ptFFF"
+
+ my $choices = $self->{preset_choosers}{filament};
+ my $print_filament_ctrls_cnt = 2 + 2 * ($#$choices+1);
+
+ foreach (0..$print_filament_ctrls_cnt-1){
+ $self->{presets_sizer}->Show($_, !$showSLA);
+ }
+ $self->{presets_sizer}->Show($print_filament_ctrls_cnt , $showSLA);
+ $self->{presets_sizer}->Show($print_filament_ctrls_cnt+1, $showSLA);
+
+ $self->{frequently_changed_parameters_sizer}->Show(0,!$showSLA);
+
+ $self->Layout;
+}
+
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
# Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly
# and some reasonable default has to be selected for the additional extruders.
@@ -1499,7 +1880,7 @@ sub on_extruders_change {
my @presets = $choices->[0]->GetStrings;
# initialize new choice
- my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
+ my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
my $extruder_idx = scalar @$choices;
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } );
push @$choices, $choice;
@@ -1536,38 +1917,39 @@ sub on_config_change {
foreach my $opt_key (@{$self->{config}->diff($config)}) {
$self->{config}->set($opt_key, $config->get($opt_key));
if ($opt_key eq 'bed_shape') {
- $self->{canvas}->update_bed_size;
- $self->{canvas3D}->update_bed_size if $self->{canvas3D};
- $self->{preview3D}->set_bed_shape($self->{config}->bed_shape)
- if $self->{preview3D};
+# $self->{canvas}->update_bed_size;
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D};
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D};
$update_scheduled = 1;
} elsif ($opt_key =~ '^wipe_tower' || $opt_key eq 'single_extruder_multi_material') {
$update_scheduled = 1;
} elsif ($opt_key eq 'serial_port') {
$self->{btn_print}->Show($config->get('serial_port'));
$self->Layout;
- } elsif ($opt_key eq 'octoprint_host') {
- $self->{btn_send_gcode}->Show($config->get('octoprint_host'));
+ } elsif ($opt_key eq 'print_host') {
+ $self->{btn_send_gcode}->Show($config->get('print_host'));
$self->Layout;
} elsif ($opt_key eq 'variable_layer_height') {
if ($config->get('variable_layer_height') != 1) {
- if ($self->{htoolbar}) {
- $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0);
- $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0);
- } else {
- $self->{"btn_layer_editing"}->Disable;
- $self->{"btn_layer_editing"}->SetValue(0);
- }
- $self->{canvas3D}->layer_editing_enabled(0);
+# if ($self->{htoolbar}) {
+# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0);
+# $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, 0);
+# } else {
+# $self->{"btn_layer_editing"}->Disable;
+# $self->{"btn_layer_editing"}->SetValue(0);
+# }
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 0);
+ Slic3r::GUI::_3DScene::enable_layers_editing($self->{canvas3D}, 0);
$self->{canvas3D}->Refresh;
$self->{canvas3D}->Update;
- } elsif ($self->{canvas3D}->layer_editing_allowed) {
+ } elsif (Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D})) {
# Want to allow the layer editing, but do it only if the OpenGL supports it.
- if ($self->{htoolbar}) {
- $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1);
- } else {
- $self->{"btn_layer_editing"}->Enable;
- }
+# if ($self->{htoolbar}) {
+# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 1);
+# } else {
+# $self->{"btn_layer_editing"}->Enable;
+# }
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", 1);
}
} elsif ($opt_key eq 'extruder_colour') {
$update_scheduled = 1;
@@ -1575,6 +1957,11 @@ sub on_config_change {
$self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors}));
} elsif ($opt_key eq 'max_print_height') {
$update_scheduled = 1;
+ } elsif ($opt_key eq 'printer_model') {
+ # update to force bed selection (for texturing)
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D};
+ Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D};
+ $update_scheduled = 1;
}
}
@@ -1586,27 +1973,31 @@ sub on_config_change {
$self->schedule_background_process;
}
-sub list_item_deselected {
- my ($self, $event) = @_;
- return if $PreventListEvents;
- if ($self->{list}->GetFirstSelected == -1) {
- $self->select_object(undef);
- $self->{canvas}->Refresh;
- #FIXME VBOs are being refreshed just to change a selection color?
- $self->{canvas3D}->reload_scene if $self->{canvas3D};
+sub item_changed_selection {
+ my ($self, $obj_idx) = @_;
+
+ if (($obj_idx >= 0) && ($obj_idx < 1000)) { # skip if wipe tower selected
+ if ($self->{canvas3D}) {
+ Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
+ if ($obj_idx >= 0) {
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
+ }
+# Slic3r::GUI::_3DScene::render($self->{canvas3D});
+ }
}
}
-sub list_item_selected {
- my ($self, $event) = @_;
- return if $PreventListEvents;
- my $obj_idx = $event->GetIndex;
- $self->select_object($obj_idx);
- $self->{canvas}->Refresh;
- #FIXME VBOs are being refreshed just to change a selection color?
- $self->{canvas3D}->reload_scene if $self->{canvas3D};
+sub collect_selections {
+ my ($self) = @_;
+ my $selections = [];
+ foreach my $o (@{$self->{objects}}) {
+ push(@$selections, $o->selected);
+ }
+ return $selections;
}
+# doesn't used now
sub list_item_activated {
my ($self, $event, $obj_idx) = @_;
@@ -1663,6 +2054,7 @@ sub object_cut_dialog {
$self->remove($obj_idx);
$self->load_model_objects(grep defined($_), @new_objects);
$self->arrange;
+ Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D};
}
}
@@ -1685,19 +2077,46 @@ sub object_settings_dialog {
$self->stop_background_process;
$dlg->ShowModal;
- # update thumbnail since parts may have changed
- if ($dlg->PartsChanged) {
- # recenter and re-align to Z = 0
- $model_object->center_around_origin;
- $self->reset_thumbnail($obj_idx);
- }
+# # update thumbnail since parts may have changed
+# if ($dlg->PartsChanged) {
+# # recenter and re-align to Z = 0
+# $model_object->center_around_origin;
+# $self->reset_thumbnail($obj_idx);
+# }
# update print
if ($dlg->PartsChanged || $dlg->PartSettingsChanged) {
$self->{print}->reload_object($obj_idx);
$self->schedule_background_process;
+# $self->{canvas}->reload_scene if $self->{canvas};
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
+ } else {
+ $self->resume_background_process;
+ }
+}
+
+sub changed_object_settings {
+ my ($self, $obj_idx, $parts_changed, $part_settings_changed) = @_;
+
+ # update thumbnail since parts may have changed
+ if ($parts_changed) {
+ # recenter and re-align to Z = 0
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ $model_object->center_around_origin;
+# $self->reset_thumbnail($obj_idx);
+ }
+
+ # update print
+ if ($parts_changed || $part_settings_changed) {
+ $self->stop_background_process;
+ $self->{print}->reload_object($obj_idx);
+ $self->schedule_background_process;
$self->{canvas}->reload_scene if $self->{canvas};
- $self->{canvas3D}->reload_scene if $self->{canvas3D};
+ my $selections = $self->collect_selections;
+ Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
+ Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
} else {
$self->schedule_background_process;
}
@@ -1710,22 +2129,25 @@ sub object_list_changed {
# Enable/disable buttons depending on whether there are any objects on the platter.
my $have_objects = @{$self->{objects}} ? 1 : 0;
- my $variable_layer_height_allowed = $self->{config}->variable_layer_height && $self->{canvas3D}->layer_editing_allowed;
- if ($self->{htoolbar}) {
- # On OSX or Linux
- $self->{htoolbar}->EnableTool($_, $have_objects)
- for (TB_RESET, TB_ARRANGE, TB_LAYER_EDITING);
- $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, 0) if (! $variable_layer_height_allowed);
- } else {
- # On MSW
- my $method = $have_objects ? 'Enable' : 'Disable';
- $self->{"btn_$_"}->$method
- for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
- $self->{"btn_layer_editing"}->Disable if (! $variable_layer_height_allowed);
- }
+# if ($self->{htoolbar}) {
+# # On OSX or Linux
+# $self->{htoolbar}->EnableTool($_, $have_objects)
+# for (TB_RESET, TB_ARRANGE);
+# } else {
+# # On MSW
+# my $method = $have_objects ? 'Enable' : 'Disable';
+# $self->{"btn_$_"}->$method
+# for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
+# }
+ for my $toolbar_item (qw(deleteall arrange)) {
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_objects);
+ }
+
my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};
- my $method = ($have_objects && ! $export_in_progress) ? 'Enable' : 'Disable';
+ my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1;
+ # $model_fits == 1 -> ModelInstance::PVS_Partly_Outside
+ my $method = ($have_objects && ! $export_in_progress && ($model_fits != 1)) ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
}
@@ -1735,18 +2157,70 @@ sub selection_changed {
my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
my $have_sel = defined $obj_idx;
+ my $layers_height_allowed = $self->{config}->variable_layer_height && Slic3r::GUI::_3DScene::is_layers_editing_allowed($self->{canvas3D}) && $have_sel;
+
+ $self->{right_panel}->Freeze;
+# if ($self->{htoolbar}) {
+# # On OSX or Linux
+# $self->{htoolbar}->EnableTool($_, $have_sel)
+# for (TB_REMOVE, TB_MORE, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_SETTINGS);
+#
+# $self->{htoolbar}->EnableTool(TB_LAYER_EDITING, $layers_height_allowed);
+#
+# if ($have_sel) {
+# my $model_object = $self->{model}->objects->[$obj_idx];
+# $self->{htoolbar}->EnableTool(TB_FEWER, $model_object->instances_count > 1);
+# } else {
+# $self->{htoolbar}->EnableTool(TB_FEWER, 0);
+# }
+#
+# } else {
+# # On MSW
+# my $method = $have_sel ? 'Enable' : 'Disable';
+# $self->{"btn_$_"}->$method
+# for grep $self->{"btn_$_"}, qw(remove increase rotate45cw rotate45ccw changescale split cut settings);
+#
+# if ($layers_height_allowed) {
+# $self->{"btn_layer_editing"}->Enable;
+# } else {
+# $self->{"btn_layer_editing"}->Disable;
+# }
+#
+# if ($have_sel) {
+# my $model_object = $self->{model}->objects->[$obj_idx];
+# if ($model_object->instances_count > 1) {
+# $self->{"btn_decrease"}->Enable;
+# } else {
+# $self->{"btn_decrease"}->Disable;
+# }
+# } else {
+# $self->{"btn_decrease"}->Disable;
+# }
+# }
+
+ for my $toolbar_item (qw(delete more fewer split cut settings)) {
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, $toolbar_item, $have_sel);
+ }
- if ($self->{htoolbar}) {
- # On OSX or Linux
- $self->{htoolbar}->EnableTool($_, $have_sel)
- for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_SETTINGS);
- } else {
- # On MSW
- my $method = $have_sel ? 'Enable' : 'Disable';
- $self->{"btn_$_"}->$method
- for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split cut settings);
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", $layers_height_allowed);
+
+ my $can_select_by_parts = 0;
+
+ if ($have_sel) {
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ $can_select_by_parts = ($obj_idx >= 0) && ($obj_idx < 1000) && ($model_object->volumes_count > 1);
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "fewer", $model_object->instances_count > 1);
}
+ if ($can_select_by_parts) {
+ # first disable to let the item in the toolbar to switch to the unpressed state
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0);
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 1);
+ } else {
+ Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0);
+ Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object');
+ }
+
if ($self->{object_info_size}) { # have we already loaded the info pane?
if ($have_sel) {
my $model_object = $self->{model}->objects->[$obj_idx];
@@ -1761,7 +2235,8 @@ sub selection_changed {
$self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $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(L("Auto-repaired (%d errors)"), $errors));
- $self->{object_info_manifold_warning_icon}->Show;
+ #$self->{object_info_manifold_warning_icon}->Show;
+ $self->{"object_info_manifold_warning_icon_show"}->(1);
# we don't show normals_fixed because we never provide normals
# to admesh, so it generates normals for all facets
@@ -1771,37 +2246,45 @@ sub selection_changed {
$self->{object_info_manifold_warning_icon}->SetToolTipString($message);
} else {
$self->{object_info_manifold}->SetLabel(L("Yes"));
- $self->{object_info_manifold_warning_icon}->Hide;
+ #$self->{object_info_manifold_warning_icon}->Hide;
+ $self->{"object_info_manifold_warning_icon_show"}->(0);
+ $self->{object_info_manifold}->SetToolTipString("");
+ $self->{object_info_manifold_warning_icon}->SetToolTipString("");
}
} else {
$self->{object_info_facets}->SetLabel($object->facets);
}
} else {
$self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold);
- $self->{object_info_manifold_warning_icon}->Hide;
+ #$self->{object_info_manifold_warning_icon}->Hide;
+ $self->{"object_info_manifold_warning_icon_show"}->(0);
$self->{object_info_manifold}->SetToolTipString("");
+ $self->{object_info_manifold_warning_icon}->SetToolTipString("");
}
$self->Layout;
}
# prepagate the event to the frame (a custom Wx event would be cleaner)
$self->GetFrame->on_plater_selection_changed($have_sel);
+ $self->{right_panel}->Thaw;
}
sub select_object {
- my ($self, $obj_idx) = @_;
-
- $_->selected(0) for @{ $self->{objects} };
+ my ($self, $obj_idx, $child) = @_;
+
+ # remove current selection
+ foreach my $o (0..$#{$self->{objects}}) {
+ $self->{objects}->[$o]->selected(0);
+ }
+
if (defined $obj_idx) {
$self->{objects}->[$obj_idx]->selected(1);
- # We use this flag to avoid circular event handling
- # Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows,
- # whose event handler calls this method again and again and again
- $PreventListEvents = 1;
- $self->{list}->Select($obj_idx, 1);
- $PreventListEvents = 0;
+ # Select current object in the list on c++ side, if item isn't child
+ if (!defined $child){
+ Slic3r::GUI::select_current_object($obj_idx);}
} else {
- # TODO: deselect all in list
+ # Unselect all objects in the list on c++ side
+ Slic3r::GUI::unselect_objects();
}
$self->selection_changed(1);
}
@@ -1917,6 +2400,11 @@ sub object_menu {
$frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub {
$self->export_object_stl;
}, undef, 'brick_go.png');
+ if (Slic3r::GUI::is_windows10) {
+ $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub {
+ $self->fix_through_netfabb;
+ }, undef, 'brick_go.png');
+ }
return $menu;
}
@@ -1927,11 +2415,11 @@ sub select_view {
my $idx_page = $self->{preview_notebook}->GetSelection;
my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page);
if ($page eq L('Preview')) {
- $self->{preview3D}->canvas->select_view($direction);
- $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
+ Slic3r::GUI::_3DScene::select_view($self->{preview3D}->canvas, $direction);
+ Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas);
} else {
- $self->{canvas3D}->select_view($direction);
- $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
+ Slic3r::GUI::_3DScene::select_view($self->{canvas3D}, $direction);
+ Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D});
}
}
@@ -1961,48 +2449,6 @@ package Slic3r::GUI::Plater::Object;
use Moo;
has 'name' => (is => 'rw', required => 1);
-has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms
-has 'transformed_thumbnail' => (is => 'rw');
-has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
has 'selected' => (is => 'rw', default => sub { 0 });
-sub make_thumbnail {
- my ($self, $model, $obj_idx) = @_;
- # make method idempotent
- $self->thumbnail->clear;
- # raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
- my $mesh = $model->objects->[$obj_idx]->raw_mesh;
-#FIXME The "correct" variant could be extremely slow.
-# if ($mesh->facets_count <= 5000) {
-# # remove polygons with area <= 1mm
-# my $area_threshold = Slic3r::Geometry::scale 1;
-# $self->thumbnail->append(
-# grep $_->area >= $area_threshold,
-# @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons
-# );
-# $self->thumbnail->simplify(0.5);
-# } else {
- my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
- $self->thumbnail->append($convex_hull);
-# }
- return $self->thumbnail;
-}
-
-sub transform_thumbnail {
- my ($self, $model, $obj_idx) = @_;
-
- return unless defined $self->thumbnail;
-
- my $model_object = $model->objects->[$obj_idx];
- my $model_instance = $model_object->instances->[0];
-
- # the order of these transformations MUST be the same everywhere, including
- # in Slic3r::Print->add_model_object()
- my $t = $self->thumbnail->clone;
- $t->rotate($model_instance->rotation, Slic3r::Point->new(0,0));
- $t->scale($model_instance->scaling_factor);
-
- $self->transformed_thumbnail($t);
-}
-
1;
diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm
index 7beba9299..88a05c292 100644
--- a/lib/Slic3r/GUI/Plater/2D.pm
+++ b/lib/Slic3r/GUI/Plater/2D.pm
@@ -222,7 +222,7 @@ sub mouse_event {
];
$self->{drag_object} = [ $obj_idx, $instance_idx ];
} elsif ($event->RightDown) {
- $self->{on_right_click}->($pos);
+ $self->{on_right_click}->($pos->x, $pos->y);
}
last OBJECTS;
@@ -231,8 +231,9 @@ sub mouse_event {
}
$self->Refresh;
} elsif ($event->LeftUp) {
- $self->{on_instances_moved}->()
- if $self->{drag_object};
+ if ($self->{drag_object}) {
+ $self->{on_instances_moved}->();
+ }
$self->{drag_start_pos} = undef;
$self->{drag_object} = undef;
$self->SetCursor(wxSTANDARD_CURSOR);
diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm
index f6edcbe24..bcd1aeed4 100644
--- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm
+++ b/lib/Slic3r/GUI/Plater/2DToolpaths.pm
@@ -193,11 +193,11 @@ sub new {
my $old_zoom = $self->_zoom;
# Calculate the zoom delta and apply it to the current zoom factor
- my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
+ my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta();
$zoom = max(min($zoom, 4), -4);
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
- $self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
+ $self->_zoom(1.25) if $self->_zoom > 1.25; # prevent from zooming out too much
{
# In order to zoom around the mouse point we need to translate
@@ -221,7 +221,6 @@ sub new {
}
$self->_dirty(1);
- $self->Refresh;
});
EVT_MOUSE_EVENTS($self, \&mouse_event);
@@ -249,8 +248,8 @@ sub mouse_event {
return if !$self->GetParent->enabled;
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
- if ($e->Entering && &Wx::wxMSW) {
- # wxMSW needs focus in order to catch mouse wheel events
+ if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) {
+ # wxMSW and Linux needs focus in order to catch key events
$self->SetFocus;
} elsif ($e->Dragging) {
if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
@@ -270,7 +269,6 @@ sub mouse_event {
);
$self->_dirty(1);
- $self->Refresh;
}
$self->_drag_start_xy($pos);
}
@@ -625,6 +623,27 @@ sub Resize {
glLoadIdentity();
my $bb = $self->bb->clone;
+
+ # rescale in dependence of window aspect ratio
+ my $bb_size = $bb->size;
+ my $ratio_x = ($x != 0.0) ? $bb_size->x / $x : 1.0;
+ my $ratio_y = ($y != 0.0) ? $bb_size->y / $y : 1.0;
+
+ if ($ratio_y < $ratio_x) {
+ if ($ratio_y != 0.0) {
+ my $new_size_y = $bb_size->y * $ratio_x / $ratio_y;
+ my $half_delta_size_y = 0.5 * ($new_size_y - $bb_size->y);
+ $bb->set_y_min($bb->y_min - $half_delta_size_y);
+ $bb->set_y_max($bb->y_max + $half_delta_size_y);
+ }
+ } elsif ($ratio_x < $ratio_y) {
+ if ($ratio_x != 0.0) {
+ my $new_size_x = $bb_size->x * $ratio_y / $ratio_x;
+ my $half_delta_size_x = 0.5 * ($new_size_x - $bb_size->x);
+ $bb->set_x_min($bb->x_min - $half_delta_size_x);
+ $bb->set_x_max($bb->x_max + $half_delta_size_x);
+ }
+ }
# center bounding box around origin before scaling it
my $bb_center = $bb->center;
@@ -639,25 +658,25 @@ sub Resize {
# translate camera
$bb->translate(@{$self->_camera_target});
- # keep camera_bb within total bb
- # (i.e. prevent user from panning outside the bounding box)
- {
- my @translate = (0,0);
- if ($bb->x_min < $self->bb->x_min) {
- $translate[X] += $self->bb->x_min - $bb->x_min;
- }
- if ($bb->y_min < $self->bb->y_min) {
- $translate[Y] += $self->bb->y_min - $bb->y_min;
- }
- if ($bb->x_max > $self->bb->x_max) {
- $translate[X] -= $bb->x_max - $self->bb->x_max;
- }
- if ($bb->y_max > $self->bb->y_max) {
- $translate[Y] -= $bb->y_max - $self->bb->y_max;
- }
- $self->_camera_target->translate(@translate);
- $bb->translate(@translate);
- }
+# # keep camera_bb within total bb
+# # (i.e. prevent user from panning outside the bounding box)
+# {
+# my @translate = (0,0);
+# if ($bb->x_min < $self->bb->x_min) {
+# $translate[X] += $self->bb->x_min - $bb->x_min;
+# }
+# if ($bb->y_min < $self->bb->y_min) {
+# $translate[Y] += $self->bb->y_min - $bb->y_min;
+# }
+# if ($bb->x_max > $self->bb->x_max) {
+# $translate[X] -= $bb->x_max - $self->bb->x_max;
+# }
+# if ($bb->y_max > $self->bb->y_max) {
+# $translate[Y] -= $bb->y_max - $self->bb->y_max;
+# }
+# $self->_camera_target->translate(@translate);
+# $bb->translate(@translate);
+# }
# save camera
$self->_camera_bb($bb);
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 37e1321ae..0b770b31c 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -5,235 +5,22 @@ use utf8;
use List::Util qw();
use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL);
-use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
use base qw(Slic3r::GUI::3DScene Class::Accessor);
-use Wx::Locale gettext => 'L';
-
-__PACKAGE__->mk_accessors(qw(
- on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
- on_remove_object on_increase_objects on_decrease_objects));
-
sub new {
my $class = shift;
my ($parent, $objects, $model, $print, $config) = @_;
my $self = $class->SUPER::new($parent);
- $self->enable_picking(1);
- $self->enable_moving(1);
- $self->select_by('object');
- $self->drag_by('instance');
-
- $self->{objects} = $objects;
- $self->{model} = $model;
- $self->{print} = $print;
- $self->{config} = $config;
- $self->{on_select_object} = sub {};
- $self->{on_instances_moved} = sub {};
- $self->{on_wipe_tower_moved} = sub {};
-
- $self->on_select(sub {
- my ($volume_idx) = @_;
- $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx)
- if ($self->{on_select_object});
- });
- $self->on_move(sub {
- my @volume_idxs = @_;
-
- my %done = (); # prevent moving instances twice
- my $object_moved;
- my $wipe_tower_moved;
- foreach my $volume_idx (@volume_idxs) {
- my $volume = $self->volumes->[$volume_idx];
- my $obj_idx = $volume->object_idx;
- my $instance_idx = $volume->instance_idx;
- next if $done{"${obj_idx}_${instance_idx}"};
- $done{"${obj_idx}_${instance_idx}"} = 1;
- if ($obj_idx < 1000) {
- # Move a regular object.
- my $model_object = $self->{model}->get_object($obj_idx);
- $model_object
- ->instances->[$instance_idx]
- ->offset
- ->translate($volume->origin->x, $volume->origin->y); #))
- $model_object->invalidate_bounding_box;
- $object_moved = 1;
- } elsif ($obj_idx == 1000) {
- # Move a wipe tower proxy.
- $wipe_tower_moved = $volume->origin;
- }
- }
-
- $self->{on_instances_moved}->()
- if $object_moved && $self->{on_instances_moved};
- $self->{on_wipe_tower_moved}->($wipe_tower_moved)
- if $wipe_tower_moved && $self->{on_wipe_tower_moved};
- });
-
- EVT_KEY_DOWN($self, sub {
- my ($s, $event) = @_;
- if ($event->HasModifiers) {
- $event->Skip;
- } else {
- my $key = $event->GetKeyCode;
- if ($key == WXK_DELETE) {
- $self->on_remove_object->() if $self->on_remove_object;
- } else {
- $event->Skip;
- }
- }
- });
-
- EVT_CHAR($self, sub {
- my ($s, $event) = @_;
- if ($event->HasModifiers) {
- $event->Skip;
- } else {
- my $key = $event->GetKeyCode;
- if ($key == ord('a')) {
- $self->on_arrange->() if $self->on_arrange;
- } elsif ($key == ord('l')) {
- $self->on_rotate_object_left->() if $self->on_rotate_object_left;
- } elsif ($key == ord('r')) {
- $self->on_rotate_object_right->() if $self->on_rotate_object_right;
- } elsif ($key == ord('s')) {
- $self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly;
- } elsif ($key == ord('+')) {
- $self->on_increase_objects->() if $self->on_increase_objects;
- } elsif ($key == ord('-')) {
- $self->on_decrease_objects->() if $self->on_decrease_objects;
- } else {
- $event->Skip;
- }
- }
- });
+ Slic3r::GUI::_3DScene::enable_picking($self, 1);
+ Slic3r::GUI::_3DScene::enable_moving($self, 1);
+ Slic3r::GUI::_3DScene::set_select_by($self, 'object');
+ Slic3r::GUI::_3DScene::set_drag_by($self, 'instance');
+ Slic3r::GUI::_3DScene::set_model($self, $model);
+ Slic3r::GUI::_3DScene::set_print($self, $print);
+ Slic3r::GUI::_3DScene::set_config($self, $config);
return $self;
}
-sub set_on_select_object {
- my ($self, $cb) = @_;
- $self->{on_select_object} = $cb;
-}
-
-sub set_on_double_click {
- my ($self, $cb) = @_;
- $self->on_double_click($cb);
-}
-
-sub set_on_right_click {
- my ($self, $cb) = @_;
- $self->on_right_click($cb);
-}
-
-sub set_on_arrange {
- my ($self, $cb) = @_;
- $self->on_arrange($cb);
-}
-
-sub set_on_rotate_object_left {
- my ($self, $cb) = @_;
- $self->on_rotate_object_left($cb);
-}
-
-sub set_on_rotate_object_right {
- my ($self, $cb) = @_;
- $self->on_rotate_object_right($cb);
-}
-
-sub set_on_scale_object_uniformly {
- my ($self, $cb) = @_;
- $self->on_scale_object_uniformly($cb);
-}
-
-sub set_on_increase_objects {
- my ($self, $cb) = @_;
- $self->on_increase_objects($cb);
-}
-
-sub set_on_decrease_objects {
- my ($self, $cb) = @_;
- $self->on_decrease_objects($cb);
-}
-
-sub set_on_remove_object {
- my ($self, $cb) = @_;
- $self->on_remove_object($cb);
-}
-
-sub set_on_instances_moved {
- my ($self, $cb) = @_;
- $self->{on_instances_moved} = $cb;
-}
-
-sub set_on_wipe_tower_moved {
- my ($self, $cb) = @_;
- $self->{on_wipe_tower_moved} = $cb;
-}
-
-sub set_on_model_update {
- my ($self, $cb) = @_;
- $self->on_model_update($cb);
-}
-
-sub reload_scene {
- my ($self, $force) = @_;
-
- $self->reset_objects;
- $self->update_bed_size;
-
- if (! $self->IsShown && ! $force) {
- $self->{reload_delayed} = 1;
- return;
- }
-
- $self->{reload_delayed} = 0;
-
- foreach my $obj_idx (0..$#{$self->{model}->objects}) {
- my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
- if ($self->{objects}[$obj_idx]->selected) {
- $self->select_volume($_) for @volume_idxs;
- }
- }
- if (defined $self->{config}->nozzle_diameter) {
- # Should the wipe tower be visualized?
- my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
- # Height of a print.
- my $height = $self->{model}->bounding_box->z_max;
- # Show at least a slab.
- $height = 10 if $height < 10;
- if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower &&
- ! $self->{config}->complete_objects) {
- $self->volumes->load_wipe_tower_preview(1000,
- $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y,
- $self->{config}->wipe_tower_width, $self->{config}->wipe_tower_per_color_wipe * ($extruders_count - 1),
- $self->{model}->bounding_box->z_max, $self->UseVBOs);
- }
- }
-
- # checks for geometry outside the print volume to render it accordingly
- if (scalar @{$self->volumes} > 0)
- {
- if (!$self->{model}->fits_print_volume($self->{config})) {
- $self->set_warning_enabled(1);
- Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume"));
- } else {
- $self->set_warning_enabled(0);
- $self->volumes->update_outside_state($self->{config}, 1);
- Slic3r::GUI::_3DScene::reset_warning_texture();
- }
- }
-}
-
-sub update_bed_size {
- my ($self) = @_;
- $self->set_bed_shape($self->{config}->bed_shape);
-}
-
-# Called by the Platter wxNotebook when this page is activated.
-sub OnActivate {
- my ($self) = @_;
- $self->reload_scene(1) if ($self->{reload_delayed});
-}
-
1;
diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm
index 8f3fa49f5..09c2f0b8c 100644
--- a/lib/Slic3r/GUI/Plater/3DPreview.pm
+++ b/lib/Slic3r/GUI/Plater/3DPreview.pm
@@ -10,7 +10,7 @@ use base qw(Wx::Panel Class::Accessor);
use Wx::Locale gettext => 'L';
-__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer auto_zoom));
+__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer));
sub new {
my $class = shift;
@@ -21,11 +21,11 @@ sub new {
$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);
- $canvas->use_plain_shader(1);
+ Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
+ Slic3r::GUI::_3DScene::set_config($canvas, $config);
$self->canvas($canvas);
my $slider_low = Wx::Slider->new(
$self, -1,
@@ -59,6 +59,13 @@ sub new {
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
$z_label_high->SetFont($Slic3r::GUI::small_font);
+ my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
+ [40,-1], wxALIGN_CENTRE_HORIZONTAL);
+ $z_label_low_idx->SetFont($Slic3r::GUI::small_font);
+ my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
+ [40,-1], wxALIGN_CENTRE_HORIZONTAL);
+ $z_label_high_idx->SetFont($Slic3r::GUI::small_font);
+
$self->single_layer(0);
my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
@@ -69,9 +76,13 @@ sub new {
$choice_view_type->Append(L("Height"));
$choice_view_type->Append(L("Width"));
$choice_view_type->Append(L("Speed"));
+ $choice_view_type->Append(L("Volumetric flow rate"));
$choice_view_type->Append(L("Tool"));
$choice_view_type->SetSelection(0);
+ # the following value needs to be changed if new items are added into $choice_view_type before "Tool"
+ $self->{tool_idx} = 5;
+
my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, L("Show"));
my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new();
@@ -100,11 +111,13 @@ sub new {
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
- $vsizer->Add($slider_low, 3, 0, 0);
- $vsizer->Add($z_label_low, 0, 0, 0);
+ $vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
+ $vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
+ $vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
$vsizer = Wx::BoxSizer->new(wxVERTICAL);
- $vsizer->Add($slider_high, 3, 0, 0);
+ $vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
+ $vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
$vsizer->Add($z_label_high, 0, 0, 0);
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
$vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
@@ -204,43 +217,31 @@ sub new {
});
EVT_CHOICE($self, $choice_view_type, sub {
my $selection = $choice_view_type->GetCurrentSelection();
- $self->{preferred_color_mode} = ($selection == 4) ? 'tool' : 'feature';
+ $self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? '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($main_sizer);
@@ -277,8 +278,8 @@ sub new {
sub reload_print {
my ($self, $force) = @_;
-
- $self->canvas->reset_objects;
+
+ Slic3r::GUI::_3DScene::reset_volumes($self->canvas);
$self->_loaded(0);
if (! $self->IsShown && ! $force) {
@@ -301,6 +302,12 @@ sub refresh_print {
$self->load_print;
}
+sub reset_gcode_preview_data {
+ my ($self) = @_;
+ $self->gcode_preview_data->reset;
+ Slic3r::GUI::_3DScene::reset_legend_texture();
+}
+
sub load_print {
my ($self) = @_;
@@ -322,25 +329,17 @@ sub load_print {
}
if ($n_layers == 0) {
- $self->enabled(0);
- $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->reset_sliders;
+ Slic3r::GUI::_3DScene::reset_legend_texture();
$self->canvas->Refresh; # clears canvas
return;
}
- # used to set the sliders to the extremes of the current zs range
- $self->{force_sliders_full_range} = 0;
-
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;
- my $type = (scalar(@{$extruders}) > 1) ? 4 : 0;
+ my $type = (scalar(@{$extruders}) > 1) ? $self->{tool_idx} : 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".
@@ -349,7 +348,7 @@ sub load_print {
# Collect colors per extruder.
my @colors = ();
- if (! $self->gcode_preview_data->empty() || $self->gcode_preview_data->type == 4) {
+ if (! $self->gcode_preview_data->empty() || $self->gcode_preview_data->type == $self->{tool_idx}) {
my @extruder_colors = @{$self->{config}->extruder_colour};
my @filament_colors = @{$self->{config}->filament_colour};
for (my $i = 0; $i <= $#extruder_colors; $i += 1) {
@@ -361,36 +360,47 @@ sub load_print {
}
if ($self->IsShown) {
+ # used to set the sliders to the extremes of the current zs range
+ $self->{force_sliders_full_range} = 0;
+
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;
- }
+ Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
+ Slic3r::GUI::_3DScene::load_preview($self->canvas, \@colors);
$self->show_hide_ui_elements('simple');
} else {
- $self->{force_sliders_full_range} = (scalar(@{$self->canvas->volumes}) == 0) && $self->auto_zoom;
- $self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors);
+ $self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0);
+ Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
+ Slic3r::GUI::_3DScene::load_gcode_preview($self->canvas, $self->gcode_preview_data, \@colors);
$self->show_hide_ui_elements('full');
# recalculates zs and update sliders accordingly
- $self->{layers_z} = $self->canvas->get_current_print_zs();
+ $self->{layers_z} = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 1);
$n_layers = scalar(@{$self->{layers_z}});
- }
+ if ($n_layers == 0) {
+ # all layers filtered out
+ $self->reset_sliders;
+ $self->canvas->Refresh; # clears canvas
+ }
+ }
- $self->update_sliders($n_layers);
-
- if ($self->auto_zoom) {
- $self->canvas->zoom_to_volumes;
- }
+ $self->update_sliders($n_layers) if ($n_layers > 0);
$self->_loaded(1);
}
}
+sub reset_sliders {
+ my ($self) = @_;
+ $self->enabled(0);
+ $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->{z_label_low_idx}->SetLabel("");
+ $self->{z_label_high_idx}->SetLabel("");
+}
+
sub update_sliders
{
my ($self, $n_layers) = @_;
@@ -405,18 +415,32 @@ sub update_sliders
$z_idx_low = 0;
$z_idx_high = $n_layers - 1;
} elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
- # use $z_idx
- } else {
+ # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
+ if (defined($self->{z_low})) {
+ for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
+ if ($self->{layers_z}[$i] <= $self->{z_low}) {
+ $z_idx_low = $i;
+ last;
+ }
+ }
+ }
+ if (defined($self->{z_high})) {
+ for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
+ if ($self->{layers_z}[$i] <= $self->{z_high}) {
+ $z_idx_high = $i;
+ last;
+ }
+ }
+ }
+ } elsif ($z_idx_high >= $n_layers) {
# Out of range. Disable 'single layer' view.
$self->single_layer(0);
$self->{checkbox_singlelayer}->SetValue(0);
$z_idx_low = 0;
$z_idx_high = $n_layers - 1;
- }
- if ($self->single_layer) {
- $z_idx_low = $z_idx_high;
- } elsif ($z_idx_low > $z_idx_high) {
+ } else {
$z_idx_low = 0;
+ $z_idx_high = $n_layers - 1;
}
$self->slider_low->SetValue($z_idx_low);
@@ -432,9 +456,26 @@ sub set_z_range
my ($self, $z_low, $z_high) = @_;
return if !$self->enabled;
+ $self->{z_low} = $z_low;
+ $self->{z_high} = $z_high;
$self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low);
$self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high);
- $self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6);
+
+ my $layers_z = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 0);
+ for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
+ if (($z_low - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_low + 1e-6)) {
+ $self->{z_label_low_idx}->SetLabel(sprintf '%d', $i + 1);
+ last;
+ }
+ }
+ for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
+ if (($z_high - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_high + 1e-6)) {
+ $self->{z_label_high_idx}->SetLabel(sprintf '%d', $i + 1);
+ last;
+ }
+ }
+
+ Slic3r::GUI::_3DScene::set_toolpaths_range($self->canvas, $z_low - 1e-6, $z_high + 1e-6);
$self->canvas->Refresh if $self->IsShown;
}
@@ -464,21 +505,16 @@ sub set_z_idx_high
}
}
-sub set_bed_shape {
- my ($self, $bed_shape) = @_;
- $self->canvas->set_bed_shape($bed_shape);
-}
-
sub set_number_extruders {
my ($self, $number_extruders) = @_;
if ($self->{number_extruders} != $number_extruders) {
$self->{number_extruders} = $number_extruders;
my $type = ($number_extruders > 1) ?
- 4 # color by a tool number
+ $self->{tool_idx} # 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';
+ $self->{preferred_color_mode} = ($type == $self->{tool_idx}) ? 'tool_or_feature' : 'feature';
}
}
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index 7712bd01d..26a6fdec3 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -9,6 +9,7 @@ use utf8;
use Slic3r::Geometry qw(PI X);
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
+use List::Util qw(max);
use base 'Wx::Dialog';
sub new {
@@ -60,7 +61,7 @@ sub new {
label => 'Z',
default => $self->{cut_options}{z},
min => 0,
- max => $self->{model_object}->bounding_box->size->z * $self->{model_object}->instances->[0]->scaling_factor,
+ max => $self->{model_object}->bounding_box->size->z,
full_width => 1,
));
{
@@ -112,11 +113,13 @@ sub new {
my $canvas;
if ($Slic3r::GUI::have_OpenGL) {
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
- $canvas->load_object($self->{model_object}, undef, undef, [0]);
- $canvas->set_auto_bed_shape;
+ Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
+ Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
+ Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
$canvas->SetSize([500,500]);
$canvas->SetMinSize($canvas->GetSize);
- $canvas->zoom_to_volumes;
+ Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config});
+ Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
}
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -134,7 +137,7 @@ sub new {
# Adjust position / orientation of the split object halves.
if ($self->{new_model_objects}{lower}) {
if ($self->{cut_options}{rotate_lower}) {
- $self->{new_model_objects}{lower}->rotate(PI, X);
+ $self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
$self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0
}
}
@@ -145,6 +148,7 @@ sub new {
# Note that the window was already closed, so a pending update will not be executed.
$self->{already_closed} = 1;
$self->EndModal(wxID_OK);
+ $self->{canvas}->Destroy;
$self->Destroy();
});
@@ -152,6 +156,7 @@ sub new {
# Note that the window was already closed, so a pending update will not be executed.
$self->{already_closed} = 1;
$self->EndModal(wxID_CANCEL);
+ $self->{canvas}->Destroy;
$self->Destroy();
});
@@ -227,12 +232,14 @@ sub _update {
push @objects, $self->{model_object};
}
+ my $z_cut = $z + $self->{model_object}->bounding_box->z_min;
+
# get section contour
my @expolygons = ();
foreach my $volume (@{$self->{model_object}->volumes}) {
next if !$volume->mesh;
next if $volume->modifier;
- my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0];
+ my $expp = $volume->mesh->slice([ $z_cut ])->[0];
push @expolygons, @$expp;
}
foreach my $expolygon (@expolygons) {
@@ -240,14 +247,12 @@ sub _update {
for @$expolygon;
$expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset });
}
-
- $self->{canvas}->reset_objects;
- $self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
- $self->{canvas}->SetCuttingPlane(
- $self->{cut_options}{z},
- [@expolygons],
- );
- $self->{canvas}->Render;
+
+ Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
+ Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
+ Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]);
+ Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
+ Slic3r::GUI::_3DScene::render($self->{canvas});
}
}
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 8a8e6064c..783c1a9f5 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -9,7 +9,8 @@ use utf8;
use File::Basename qw(basename);
use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL
wxTheApp);
-use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN);
+use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN);
+use List::Util qw(max);
use base 'Wx::Panel';
use constant ICON_OBJECT => 0;
@@ -88,7 +89,7 @@ sub new {
$self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
# part settings panel
- $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; });
+ $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
@@ -150,19 +151,19 @@ sub new {
my $canvas;
if ($Slic3r::GUI::have_OpenGL) {
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
- $canvas->enable_picking(1);
- $canvas->select_by('volume');
-
- $canvas->on_select(sub {
+ Slic3r::GUI::_3DScene::enable_picking($canvas, 1);
+ Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume');
+ Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub {
my ($volume_idx) = @_;
- # convert scene volume to model object volume
- $self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx);
+ $self->reload_tree($volume_idx);
});
-
- $canvas->load_object($self->{model_object}, undef, undef, [0]);
- $canvas->set_auto_bed_shape;
+ Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
+ Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
+ Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
$canvas->SetSize([500,700]);
- $canvas->zoom_to_volumes;
+ Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config});
+ Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas);
+ Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
}
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -190,6 +191,14 @@ sub new {
EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split);
EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up);
EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down);
+ EVT_KEY_DOWN($canvas, sub {
+ my ($canvas, $event) = @_;
+ if ($event->GetKeyCode == WXK_DELETE) {
+ $canvas->GetParent->on_btn_delete;
+ } else {
+ $event->Skip;
+ }
+ });
$self->reload_tree;
@@ -254,7 +263,7 @@ sub selection_changed {
# deselect all meshes
if ($self->{canvas}) {
- $_->set_selected(0) for @{$self->{canvas}->volumes};
+ Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas});
}
# disable things as if nothing is selected
@@ -282,7 +291,7 @@ sub selection_changed {
if ($itemData->{type} eq 'volume') {
# select volume in 3D preview
if ($self->{canvas}) {
- $self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1);
+ Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
}
$self->{btn_delete}->Enable;
$self->{btn_split}->Enable;
@@ -313,7 +322,13 @@ sub selection_changed {
}
# get default values
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
-
+
+ # decide which settings will be shown by default
+ if ($itemData->{type} eq 'object') {
+ $config->set_ifndef('wipe_into_objects', 0);
+ $config->set_ifndef('wipe_into_infill', 0);
+ }
+
# append default extruder
push @opt_keys, 'extruder';
$default_config->set('extruder', 0);
@@ -321,11 +336,18 @@ sub selection_changed {
$self->{settings_panel}->set_default_config($default_config);
$self->{settings_panel}->set_config($config);
$self->{settings_panel}->set_opt_keys(\@opt_keys);
- $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+
+ # disable minus icon to remove the settings
+ if ($itemData->{type} eq 'object') {
+ $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
+ } else {
+ $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+ }
+
$self->{settings_panel}->enable;
}
- $self->{canvas}->Render if $self->{canvas};
+ Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
}
sub on_btn_load {
@@ -355,7 +377,8 @@ sub on_btn_load {
}
}
}
-
+
+ $self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed;
}
@@ -400,14 +423,17 @@ sub on_tree_key_down {
my ($self, $event) = @_;
my $keycode = $event->GetKeyCode;
# Wx >= 0.9911
- if (defined(&Wx::TreeEvent::GetKeyEvent) &&
- ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL)) {
- if ($keycode == WXK_UP) {
- $event->Skip;
- $self->on_btn_move_up;
- } elsif ($keycode == WXK_DOWN) {
- $event->Skip;
- $self->on_btn_move_down;
+ if (defined(&Wx::TreeEvent::GetKeyEvent)) {
+ if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) {
+ if ($keycode == WXK_UP) {
+ $event->Skip;
+ $self->on_btn_move_up;
+ } elsif ($keycode == WXK_DOWN) {
+ $event->Skip;
+ $self->on_btn_move_down;
+ }
+ } elsif ($keycode == WXK_DELETE) {
+ $self->on_btn_delete;
}
}
}
@@ -418,7 +444,7 @@ sub on_btn_move_up {
if ($itemData && $itemData->{type} eq 'volume') {
my $volume_id = $itemData->{volume_id};
if ($self->{model_object}->move_volume_up($volume_id)) {
- $self->{canvas}->volumes->move_volume_up($volume_id);
+ Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id);
$self->{parts_changed} = 1;
$self->reload_tree($volume_id - 1);
}
@@ -431,7 +457,7 @@ sub on_btn_move_down {
if ($itemData && $itemData->{type} eq 'volume') {
my $volume_id = $itemData->{volume_id};
if ($self->{model_object}->move_volume_down($volume_id)) {
- $self->{canvas}->volumes->move_volume_down($volume_id);
+ Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id);
$self->{parts_changed} = 1;
$self->reload_tree($volume_id + 1);
}
@@ -454,7 +480,8 @@ sub on_btn_delete {
$self->{model_object}->delete_volume($itemData->{volume_id});
$self->{parts_changed} = 1;
}
-
+
+ $self->{model_object}->center_around_origin if $self->{parts_changed};
$self->_parts_changed;
}
@@ -464,7 +491,8 @@ sub on_btn_split {
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
- $self->{parts_changed} = 1 if $volume->split > 1;
+ my $nozzle_dmrs = $self->GetParent->GetParent->GetParent->{config}->get('nozzle_diameter');
+ $self->{parts_changed} = 1 if $volume->split(scalar(@$nozzle_dmrs)) > 1;
}
$self->_parts_changed;
@@ -475,10 +503,11 @@ sub _parts_changed {
$self->reload_tree;
if ($self->{canvas}) {
- $self->{canvas}->reset_objects;
- $self->{canvas}->load_object($self->{model_object});
- $self->{canvas}->zoom_to_volumes;
- $self->{canvas}->Render;
+ Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
+ Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
+ Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas});
+ Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
+ Slic3r::GUI::_3DScene::render($self->{canvas});
}
}
@@ -498,6 +527,11 @@ sub CanClose {
return ! Slic3r::GUI::catch_error($self);
}
+sub Destroy {
+ my ($self) = @_;
+ $self->{canvas}->Destroy if ($self->{canvas});
+}
+
sub PartsChanged {
my ($self) = @_;
return $self->{parts_changed};
@@ -508,6 +542,25 @@ sub PartSettingsChanged {
return $self->{part_settings_changed};
}
+sub _update_canvas {
+ my ($self) = @_;
+
+ if ($self->{canvas}) {
+ Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
+ Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
+
+ # restore selection, if any
+ if (my $itemData = $self->get_selection) {
+ if ($itemData->{type} eq 'volume') {
+ Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
+ }
+ }
+
+ Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
+ Slic3r::GUI::_3DScene::render($self->{canvas});
+ }
+}
+
sub _update {
my ($self) = @_;
my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
@@ -526,9 +579,10 @@ sub _update {
$self->{parts_changed} = 1;
my @objects = ();
push @objects, $self->{model_object};
- $self->{canvas}->reset_objects;
- $self->{canvas}->load_object($_, undef, [0]) for @objects;
- $self->{canvas}->Render;
+ Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
+ Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
+ Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
+ Slic3r::GUI::_3DScene::render($self->{canvas});
}
1;
diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
index 908d5eff7..3befba708 100644
--- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm
@@ -36,6 +36,7 @@ sub new {
wxTheApp->save_window_pos($self, "object_settings");
$self->EndModal(wxID_OK);
+ $self->{parts}->Destroy;
$self->Destroy;
});
@@ -46,6 +47,8 @@ sub new {
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
+ $self->Layout;
+
wxTheApp->restore_window_pos($self, "object_settings");
return $self;
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index 3b10ed99f..ea4ce7132 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -136,7 +136,7 @@ sub update_optgroup {
full_labels => 1,
label_font => $Slic3r::GUI::small_font,
sidetext_font => $Slic3r::GUI::small_font,
- label_width => 120,
+ label_width => 150,
on_change => sub { $self->{on_change}->() if $self->{on_change} },
extra_column => sub {
my ($line) = @_;
diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm
index 32fd52680..edc0c0ce3 100644
--- a/lib/Slic3r/GUI/ProgressStatusBar.pm
+++ b/lib/Slic3r/GUI/ProgressStatusBar.pm
@@ -1,144 +1,18 @@
# Status bar at the bottom of the main screen.
+# Now it just implements cancel cb on perl side, every other functionality is
+# in C++
-package Slic3r::GUI::ProgressStatusBar;
+package Slic3r::GUI::ProgressStatusBar;
use strict;
use warnings;
-use Wx qw(:gauge :misc);
-use base 'Wx::StatusBar';
-
-sub new {
- my $class = shift;
- my $self = $class->SUPER::new(@_);
-
- $self->{busy} = 0;
- $self->{timer} = Wx::Timer->new($self);
- $self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
- $self->{prog}->Hide;
- $self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize);
- $self->{cancelbutton}->Hide;
-
- $self->SetFieldsCount(3);
- $self->SetStatusWidths(-1, 150, 155);
-
- Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer});
- Wx::Event::EVT_SIZE($self, \&OnSize);
- Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub {
- $self->{cancel_cb}->();
- $self->{cancelbutton}->Hide;
- });
-
- return $self;
-}
-
-sub DESTROY {
- my $self = shift;
- $self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning;
-}
-
-sub OnSize {
- my ($self, $event) = @_;
-
- my %fields = (
- # 0 is reserved for status text
- 1 => $self->{cancelbutton},
- 2 => $self->{prog},
- );
-
- foreach (keys %fields) {
- my $rect = $self->GetFieldRect($_);
- my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK
- my $pos = [$rect->GetX + $offset, $rect->GetY + $offset];
- $fields{$_}->Move($pos);
- $fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight);
- }
-
- $event->Skip;
-}
-
-sub OnTimer {
- my ($self, $event) = @_;
-
- if ($self->{prog}->IsShown) {
- $self->{timer}->Stop;
- }
- $self->{prog}->Pulse if $self->{_busy};
-}
+our $cancel_cb;
sub SetCancelCallback {
my $self = shift;
my ($cb) = @_;
- $self->{cancel_cb} = $cb;
- $cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide;
-}
-
-sub Run {
- my $self = shift;
- my $rate = shift || 100;
- if (!$self->{timer}->IsRunning) {
- $self->{timer}->Start($rate);
- }
-}
-
-sub GetProgress {
- my $self = shift;
- return $self->{prog}->GetValue;
-}
-
-sub SetProgress {
- my $self = shift;
- my ($val) = @_;
- if (!$self->{prog}->IsShown) {
- $self->ShowProgress(1);
- }
- if ($val == $self->{prog}->GetRange) {
- $self->{prog}->SetValue(0);
- $self->ShowProgress(0);
- } else {
- $self->{prog}->SetValue($val);
- }
-}
-
-sub SetRange {
- my $self = shift;
- my ($val) = @_;
-
- if ($val != $self->{prog}->GetRange) {
- $self->{prog}->SetRange($val);
- }
-}
-
-sub ShowProgress {
- my $self = shift;
- my ($show) = @_;
-
- $self->{prog}->Show($show);
- $self->{prog}->Pulse;
-}
-
-sub StartBusy {
- my $self = shift;
- my $rate = shift || 100;
-
- $self->{_busy} = 1;
- $self->ShowProgress(1);
- if (!$self->{timer}->IsRunning) {
- $self->{timer}->Start($rate);
- }
-}
-
-sub StopBusy {
- my $self = shift;
-
- $self->{timer}->Stop;
- $self->ShowProgress(0);
- $self->{prog}->SetValue(0);
- $self->{_busy} = 0;
-}
-
-sub IsBusy {
- my $self = shift;
- return $self->{_busy};
+ $cancel_cb = $cb;
+ $cb ? $self->ShowCancelButton : $self->HideCancelButton;
}
1;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 5a0ddd38f..38aa318e6 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -35,7 +35,12 @@ sub run_post_process_scripts {
die "The configured post-processing script is not executable: check permissions. ($script)\n";
}
if ($^O eq 'MSWin32' && $script =~ /\.[pP][lL]/) {
- system($^X, $script, $output_file);
+ # The current process (^X) may be slic3r.exe or slic3r-console.exe.
+ # Replace it with the current perl interpreter.
+ my($filename, $directories, $suffix) = fileparse($^X);
+ $filename =~ s/^slic3r.*$/perl5\.24\.0\.exe/;
+ my $interpreter = $directories . $filename;
+ system($interpreter, $script, $output_file);
} else {
system($script, $output_file);
}
@@ -43,13 +48,37 @@ sub run_post_process_scripts {
}
}
+sub export_png {
+ my $self = shift;
+ my %params = @_;
+
+ my @sobjects = @{$self->objects};
+ my $objnum = scalar @sobjects;
+ for(my $oi = 0; $oi < $objnum; $oi++)
+ {
+ $sobjects[$oi]->slice;
+ $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing...");
+ }
+
+ my $fh = $params{output_file};
+ $self->status_cb->(90, "Exporting zipped archive...");
+ $self->print_to_png($fh);
+ $self->status_cb->(100, "Done.");
+}
+
# Export SVG slices for the offline SLA printing.
# The export_svg is expected to be executed inside an eval block.
sub export_svg {
my $self = shift;
my %params = @_;
- $_->slice for @{$self->objects};
+ my @sobjects = @{$self->objects};
+ my $objnum = scalar @sobjects;
+ for(my $oi = 0; $oi < $objnum; $oi++)
+ {
+ $sobjects[$oi]->slice;
+ $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing...");
+ }
my $fh = $params{output_fh};
if (!$fh) {
diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm
index 9dbc7fefa..3b18646d9 100644
--- a/lib/Slic3r/Print/Simple.pm
+++ b/lib/Slic3r/Print/Simple.pm
@@ -97,4 +97,14 @@ sub export_svg {
$self->_print->export_svg(output_file => $self->output_file);
}
+sub export_png {
+ my ($self) = @_;
+
+ $self->_before_export;
+
+ $self->_print->export_png(output_file => $self->output_file);
+
+ $self->_after_export;
+}
+
1;