mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-11-01 05:01:10 -06:00
Merge branch 'master' into sender
Conflicts: Build.PL lib/Slic3r/GUI/MainFrame.pm
This commit is contained in:
commit
13b7316807
74 changed files with 1260 additions and 553 deletions
|
|
@ -593,7 +593,7 @@ sub Resize {
|
|||
-$x/2, $x/2, -$y/2, $y/2,
|
||||
-$depth, 2*$depth,
|
||||
);
|
||||
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
|
|
@ -937,7 +937,7 @@ package Slic3r::GUI::3DScene;
|
|||
use base qw(Slic3r::GUI::3DScene::Base);
|
||||
|
||||
use OpenGL qw(:glconstants :gluconstants :glufunctions);
|
||||
use List::Util qw(first);
|
||||
use List::Util qw(first min max);
|
||||
use Slic3r::Geometry qw(scale unscale epsilon);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
|
|
@ -1079,6 +1079,59 @@ sub load_print_object_slices {
|
|||
);
|
||||
}
|
||||
|
||||
sub load_print_toolpaths {
|
||||
my ($self, $print) = @_;
|
||||
|
||||
return if !$print->step_done(STEP_SKIRT);
|
||||
return if !$print->step_done(STEP_BRIM);
|
||||
return if !$print->has_skirt && $print->config->brim_width == 0;
|
||||
|
||||
my $qverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||
my $tverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||
my %offsets = (); # print_z => [ qverts, tverts ]
|
||||
|
||||
my $skirt_height = 0; # number of layers
|
||||
if ($print->has_infinite_skirt) {
|
||||
$skirt_height = $print->total_layer_count;
|
||||
} else {
|
||||
$skirt_height = min($print->config->skirt_height, $print->total_layer_count);
|
||||
}
|
||||
$skirt_height ||= 1 if $print->config->brim_width > 0;
|
||||
|
||||
# get first $skirt_height layers (maybe this should be moved to a PrintObject method?)
|
||||
my $object0 = $print->get_object(0);
|
||||
my @layers = ();
|
||||
push @layers, map $object0->get_layer($_-1), 1..min($skirt_height, $object0->layer_count);
|
||||
push @layers, map $object0->get_support_layer($_-1), 1..min($skirt_height, $object0->support_layer_count);
|
||||
@layers = sort { $a->print_z <=> $b->print_z } @layers;
|
||||
@layers = @layers[0..($skirt_height-1)];
|
||||
|
||||
foreach my $i (0..($skirt_height-1)) {
|
||||
my $top_z = $layers[$i]->print_z;
|
||||
$offsets{$top_z} = [$qverts->size, $tverts->size];
|
||||
|
||||
if ($i == 0) {
|
||||
$self->_extrusionentity_to_verts($print->brim, $top_z, Slic3r::Point->new(0,0), $qverts, $tverts);
|
||||
}
|
||||
|
||||
$self->_extrusionentity_to_verts($print->skirt, $top_z, Slic3r::Point->new(0,0), $qverts, $tverts);
|
||||
}
|
||||
|
||||
my $bb = Slic3r::Geometry::BoundingBoxf3->new;
|
||||
{
|
||||
my $pbb = $print->bounding_box;
|
||||
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$pbb->min_point}));
|
||||
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$pbb->max_point}));
|
||||
}
|
||||
push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new(
|
||||
bounding_box => $bb,
|
||||
color => COLORS->[2],
|
||||
qverts => $qverts,
|
||||
tverts => $tverts,
|
||||
offsets => { %offsets },
|
||||
);
|
||||
}
|
||||
|
||||
sub load_print_object_toolpaths {
|
||||
my ($self, $object) = @_;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use strict;
|
|||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:font :html :misc :sizer :systemsettings);
|
||||
use Wx qw(:font :html :misc :dialog :sizer :systemsettings);
|
||||
use Wx::Event qw(EVT_HTML_LINK_CLICKED);
|
||||
use Wx::Print;
|
||||
use Wx::Html;
|
||||
|
|
@ -12,7 +12,7 @@ use base 'Wx::Dialog';
|
|||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, 'About Slic3r', wxDefaultPosition, [600, 300]);
|
||||
my $self = $class->SUPER::new($parent, -1, 'About Slic3r', wxDefaultPosition, [600, 300], &Wx::wxCLOSE_BOX);
|
||||
|
||||
$self->SetBackgroundColour(Wx::wxWHITE);
|
||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ sub _load_stl {
|
|||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
my $input_file = $dialog->GetPaths;
|
||||
my $input_file = Slic3r::decode_path($dialog->GetPaths);
|
||||
$dialog->Destroy;
|
||||
|
||||
my $model = Slic3r::Model->read_from_file($input_file);
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ sub _init_tabpanel {
|
|||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
my ($group, $preset) = @_;
|
||||
$self->{options_tabs}{$group}->select_preset($preset);
|
||||
my ($group, $i) = @_;
|
||||
$self->{options_tabs}{$group}->select_preset($i);
|
||||
});
|
||||
|
||||
# load initial config
|
||||
|
|
@ -158,38 +158,44 @@ sub _init_menubar {
|
|||
{
|
||||
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
|
||||
$self->load_config_file;
|
||||
});
|
||||
}, undef, 'plugin_add.png');
|
||||
$self->_append_menu_item($fileMenu, "&Export Config…\tCtrl+E", 'Export current configuration to file', sub {
|
||||
$self->export_config;
|
||||
});
|
||||
}, undef, 'plugin_go.png');
|
||||
$self->_append_menu_item($fileMenu, "&Load Config Bundle…", 'Load presets from a bundle', sub {
|
||||
$self->load_configbundle;
|
||||
});
|
||||
}, undef, 'lorry_add.png');
|
||||
$self->_append_menu_item($fileMenu, "&Export Config Bundle…", 'Export all presets to file', sub {
|
||||
$self->export_configbundle;
|
||||
});
|
||||
}, undef, 'lorry_go.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
my $repeat;
|
||||
$self->_append_menu_item($fileMenu, "Q&uick Slice…\tCtrl+U", 'Slice file', sub {
|
||||
$self->quick_slice;
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice;
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$self->_append_menu_item($fileMenu, "Quick Slice and Save &As…\tCtrl+Alt+U", 'Slice file and save as', sub {
|
||||
$self->quick_slice(save_as => 1);
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice(save_as => 1);
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$repeat = $self->_append_menu_item($fileMenu, "&Repeat Last Quick Slice\tCtrl+Shift+U", 'Repeat last quick slice', sub {
|
||||
$self->quick_slice(reslice => 1);
|
||||
});
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice(reslice => 1);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$repeat->Enable(0);
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub {
|
||||
$self->quick_slice(save_as => 1, export_svg => 1);
|
||||
});
|
||||
}, undef, 'shape_handles.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub {
|
||||
$self->repair_stl;
|
||||
});
|
||||
}, undef, 'wrench.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, "Preferences…", 'Application preferences', sub {
|
||||
Slic3r::GUI::Preferences->new($self)->ShowModal;
|
||||
|
|
@ -207,13 +213,13 @@ sub _init_menubar {
|
|||
$self->{plater_menu} = Wx::Menu->new;
|
||||
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
|
||||
$plater->export_gcode;
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as STL...", 'Export current plate as STL', sub {
|
||||
$plater->export_stl;
|
||||
});
|
||||
}, undef, 'brick_go.png');
|
||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', sub {
|
||||
$plater->export_amf;
|
||||
});
|
||||
}, undef, 'brick_go.png');
|
||||
|
||||
$self->{object_menu} = $self->{plater}->object_menu;
|
||||
$self->on_plater_selection_changed(0);
|
||||
|
|
@ -226,22 +232,22 @@ sub _init_menubar {
|
|||
if (!$self->{no_plater}) {
|
||||
$self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub {
|
||||
$self->select_tab(0);
|
||||
});
|
||||
}, undef, 'application_view_tile.png');
|
||||
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
|
||||
$self->select_tab(1);
|
||||
});
|
||||
}, undef, 'printer_empty.png');
|
||||
$windowMenu->AppendSeparator();
|
||||
$tab_offset += 2;
|
||||
}
|
||||
$self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub {
|
||||
$self->select_tab($tab_offset+0);
|
||||
});
|
||||
}, undef, 'cog.png');
|
||||
$self->_append_menu_item($windowMenu, "Select &Filament Settings Tab\tCtrl+3", 'Show the filament settings', sub {
|
||||
$self->select_tab($tab_offset+1);
|
||||
});
|
||||
}, undef, 'spool.png');
|
||||
$self->_append_menu_item($windowMenu, "Select Print&er Settings Tab\tCtrl+4", 'Show the printer settings', sub {
|
||||
$self->select_tab($tab_offset+2);
|
||||
});
|
||||
}, undef, 'printer_empty.png');
|
||||
}
|
||||
|
||||
# Help menu
|
||||
|
|
@ -313,7 +319,7 @@ sub quick_slice {
|
|||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
$input_file = $dialog->GetPaths;
|
||||
$input_file = Slic3r::decode_path($dialog->GetPaths);
|
||||
$dialog->Destroy;
|
||||
$last_input_file = $input_file unless $params{export_svg};
|
||||
} else {
|
||||
|
|
@ -373,7 +379,7 @@ sub quick_slice {
|
|||
$dlg->Destroy;
|
||||
return;
|
||||
}
|
||||
$output_file = $dlg->GetPath;
|
||||
$output_file = Slic3r::decode_path($dlg->GetPath);
|
||||
$last_output_file = $output_file unless $params{export_svg};
|
||||
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file);
|
||||
wxTheApp->save_settings;
|
||||
|
|
@ -420,7 +426,7 @@ sub repair_stl {
|
|||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
$input_file = $dialog->GetPaths;
|
||||
$input_file = Slic3r::decode_path($dialog->GetPaths);
|
||||
$dialog->Destroy;
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +439,7 @@ sub repair_stl {
|
|||
$dlg->Destroy;
|
||||
return undef;
|
||||
}
|
||||
$output_file = $dlg->GetPath;
|
||||
$output_file = Slic3r::decode_path($dlg->GetPath);
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +476,7 @@ sub export_config {
|
|||
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $file = $dlg->GetPath;
|
||||
my $file = Slic3r::decode_path($dlg->GetPath);
|
||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||
wxTheApp->save_settings;
|
||||
$last_config = $file;
|
||||
|
|
@ -489,7 +495,7 @@ sub load_config_file {
|
|||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
return unless $dlg->ShowModal == wxID_OK;
|
||||
($file) = $dlg->GetPaths;
|
||||
$file = Slic3r::decode_path($dlg->GetPaths);
|
||||
$dlg->Destroy;
|
||||
}
|
||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||
|
|
@ -514,7 +520,7 @@ sub export_configbundle {
|
|||
my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename,
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $file = $dlg->GetPath;
|
||||
my $file = Slic3r::decode_path($dlg->GetPath);
|
||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||
wxTheApp->save_settings;
|
||||
|
||||
|
|
@ -547,7 +553,7 @@ sub load_configbundle {
|
|||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
return unless $dlg->ShowModal == wxID_OK;
|
||||
my ($file) = $dlg->GetPaths;
|
||||
my $file = Slic3r::decode_path($dlg->GetPaths);
|
||||
$dlg->Destroy;
|
||||
|
||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||
|
|
@ -601,6 +607,9 @@ sub load_config {
|
|||
foreach my $tab (values %{$self->{options_tabs}}) {
|
||||
$tab->load_config($config);
|
||||
}
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_config_change($config);
|
||||
}
|
||||
}
|
||||
|
||||
sub config_wizard {
|
||||
|
|
@ -643,13 +652,19 @@ sub config {
|
|||
if (!$self->{plater} || $self->{plater}->filament_presets == 1 || $self->{mode} eq 'simple') {
|
||||
$filament_config = $self->{options_tabs}{filament}->config;
|
||||
} else {
|
||||
# TODO: handle dirty presets.
|
||||
# perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
|
||||
my $i = -1;
|
||||
foreach my $preset_idx ($self->{plater}->filament_presets) {
|
||||
$i++;
|
||||
my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
|
||||
my $config = $self->{options_tabs}{filament}->get_preset_config($preset);
|
||||
my $config;
|
||||
if ($preset_idx == $self->{options_tabs}{filament}->current_preset) {
|
||||
# the selected preset for this extruder is the one in the tab
|
||||
# use the tab's config instead of the preset in case it is dirty
|
||||
# perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
|
||||
$config = $self->{options_tabs}{filament}->config;
|
||||
} else {
|
||||
my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
|
||||
$config = $self->{options_tabs}{filament}->get_preset_config($preset);
|
||||
}
|
||||
if (!$filament_config) {
|
||||
$filament_config = $config->clone;
|
||||
next;
|
||||
|
|
@ -719,12 +734,23 @@ sub select_tab {
|
|||
}
|
||||
|
||||
sub _append_menu_item {
|
||||
my ($self, $menu, $string, $description, $cb, $id) = @_;
|
||||
my ($self, $menu, $string, $description, $cb, $id, $icon) = @_;
|
||||
|
||||
$id //= &Wx::NewId();
|
||||
my $item = $menu->Append($id, $string, $description);
|
||||
$self->_set_menu_item_icon($item, $icon);
|
||||
|
||||
EVT_MENU($self, $id, $cb);
|
||||
return $item;
|
||||
}
|
||||
|
||||
sub _set_menu_item_icon {
|
||||
my ($self, $menuItem, $icon) = @_;
|
||||
|
||||
# SetBitmap was not available on OS X before Wx 0.9927
|
||||
if ($icon && $menuItem->can('SetBitmap')) {
|
||||
$menuItem->SetBitmap(Wx::Bitmap->new("$Slic3r::var/$icon", wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ sub _build_field {
|
|||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'color') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::ColourPicker->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type =~ /^(f|s|s@|percent)$/) {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::TextCtrl->new(
|
||||
parent => $self->parent,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ has 'parent' => (is => 'ro', required => 1);
|
|||
has 'option' => (is => 'ro', required => 1); # Slic3r::GUI::OptionsGroup::Option
|
||||
has 'on_change' => (is => 'rw', default => sub { sub {} });
|
||||
has 'on_kill_focus' => (is => 'rw', default => sub { sub {} });
|
||||
has 'wxSsizer' => (is => 'rw'); # alternatively, wxSizer object
|
||||
has 'disable_change_event' => (is => 'rw', default => sub { 0 });
|
||||
|
||||
# This method should not fire the on_change event
|
||||
|
|
@ -128,6 +127,8 @@ extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
|||
use Wx qw(:misc);
|
||||
use Wx::Event qw(EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS);
|
||||
|
||||
has 'tmp_value' => (is => 'rw');
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
|
|
@ -139,13 +140,27 @@ sub BUILD {
|
|||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
EVT_TEXT($self->parent, $field, sub {
|
||||
my ($s, $event) = @_;
|
||||
|
||||
# On OSX/Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||
# when it was changed from the text control, so the on_change callback
|
||||
# gets the old one, and on_kill_focus resets the control to the old value.
|
||||
# As a workaround, we get the new value from $event->GetString and store
|
||||
# here temporarily so that we can return it from $self->get_value
|
||||
$self->tmp_value($event->GetString);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
$self->tmp_value(undef);
|
||||
});
|
||||
EVT_KILL_FOCUS($field, sub {
|
||||
$self->_on_kill_focus($self->option->opt_id, @_);
|
||||
});
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->tmp_value // $self->wxWindow->GetValue;
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::TextCtrl;
|
||||
use Moo;
|
||||
|
|
@ -261,6 +276,9 @@ use List::Util qw(first);
|
|||
use Wx qw(wxTheApp :misc :combobox);
|
||||
use Wx::Event qw(EVT_COMBOBOX EVT_TEXT);
|
||||
|
||||
# if option has no 'values', indices are values
|
||||
# if option has no 'labels', values are labels
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
|
|
@ -274,15 +292,15 @@ sub BUILD {
|
|||
my $disable_change_event = $self->disable_change_event;
|
||||
$self->disable_change_event(1);
|
||||
|
||||
my $value = $field->GetSelection;
|
||||
my $idx = $field->GetSelection; # get index of selected value
|
||||
my $label;
|
||||
|
||||
if ($self->option->values) {
|
||||
$label = $value = $self->option->values->[$value];
|
||||
} elsif ($value <= $#{$self->option->labels}) {
|
||||
$label = $self->option->labels->[$value];
|
||||
if ($self->option->labels && $idx <= $#{$self->option->labels}) {
|
||||
$label = $self->option->labels->[$idx];
|
||||
} elsif ($self->option->values && $idx <= $#{$self->option->values}) {
|
||||
$label = $self->option->values->[$idx];
|
||||
} else {
|
||||
$label = $value;
|
||||
$label = $idx;
|
||||
}
|
||||
|
||||
# The MSW implementation of wxComboBox will leave the field blank if we call
|
||||
|
|
@ -322,8 +340,8 @@ sub set_value {
|
|||
$self->disable_change_event(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($self->option->labels && $value <= $#{$self->option->labels}) {
|
||||
} elsif ($self->option->labels && $value <= $#{$self->option->labels}) {
|
||||
# if we have no values, we expect value to be an index
|
||||
$field->SetValue($self->option->labels->[$value]);
|
||||
$self->disable_change_event(0);
|
||||
return;
|
||||
|
|
@ -351,6 +369,47 @@ sub get_value {
|
|||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::ColourPicker;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use Wx qw(:misc :colour);
|
||||
use Wx::Event qw(EVT_COLOURPICKER_CHANGED);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $field = Wx::ColourPickerCtrl->new($self->parent, -1,
|
||||
$self->_string_to_colour($self->option->default), wxDefaultPosition,
|
||||
$self->_default_size);
|
||||
$self->wxWindow($field);
|
||||
|
||||
EVT_COLOURPICKER_CHANGED($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
$self->wxWindow->SetColour($self->_string_to_colour($value));
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->wxWindow->GetColour->GetAsString(wxC2S_HTML_SYNTAX);
|
||||
}
|
||||
|
||||
sub _string_to_colour {
|
||||
my ($self, $string) = @_;
|
||||
|
||||
$string =~ s/^#//;
|
||||
return Wx::Colour->new(unpack 'C*', pack 'H*', $string);
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::wxSizer;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field';
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ use warnings;
|
|||
use utf8;
|
||||
|
||||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first);
|
||||
use List::Util qw(sum first max);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook);
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
EVT_CHOICE EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use constant TB_ADD => &Wx::NewId;
|
||||
|
|
@ -36,7 +36,7 @@ our $ERROR_EVENT : shared = Wx::NewEventType;
|
|||
our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType;
|
||||
our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType;
|
||||
|
||||
use constant FILAMENT_CHOOSERS_SPACING => 3;
|
||||
use constant FILAMENT_CHOOSERS_SPACING => 0;
|
||||
use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
|
||||
|
||||
my $PreventListEvents = 0;
|
||||
|
|
@ -335,12 +335,11 @@ sub new {
|
|||
for my $group (qw(print filament printer)) {
|
||||
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
|
||||
$choice->SetFont($Slic3r::GUI::small_font);
|
||||
my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
||||
$self->{preset_choosers}{$group} = [$choice];
|
||||
EVT_CHOICE($choice, $choice, sub { $self->_on_select_preset($group, @_) });
|
||||
EVT_COMBOBOX($choice, $choice, sub { $self->_on_select_preset($group, @_) });
|
||||
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
$presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 8);
|
||||
$presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,13 +445,33 @@ sub GetFrame {
|
|||
|
||||
sub update_presets {
|
||||
my $self = shift;
|
||||
my ($group, $items, $selected) = @_;
|
||||
my ($group, $presets, $selected) = @_;
|
||||
|
||||
foreach my $choice (@{ $self->{preset_choosers}{$group} }) {
|
||||
my $sel = $choice->GetSelection;
|
||||
$choice->Clear;
|
||||
$choice->Append($_) for @$items;
|
||||
$choice->SetSelection($sel) if $sel <= $#$items;
|
||||
foreach my $preset (@$presets) {
|
||||
my $bitmap;
|
||||
if ($group eq 'filament') {
|
||||
my $config = $preset->config(['filament_colour']);
|
||||
my $rgb_hex = $config->filament_colour->[0];
|
||||
if ($preset->default) {
|
||||
$bitmap = Wx::Bitmap->new("$Slic3r::var/spool.png", wxBITMAP_TYPE_PNG);
|
||||
} else {
|
||||
$rgb_hex =~ s/^#//;
|
||||
my @rgb = unpack 'C*', pack 'H*', $rgb_hex;
|
||||
my $image = Wx::Image->new(16,16);
|
||||
$image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb);
|
||||
$bitmap = Wx::Bitmap->new($image);
|
||||
}
|
||||
} elsif ($group eq 'print') {
|
||||
$bitmap = Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG);
|
||||
} elsif ($group eq 'printer') {
|
||||
$bitmap = Wx::Bitmap->new("$Slic3r::var/printer_empty.png", wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
$choice->AppendString($preset->name, $bitmap);
|
||||
}
|
||||
$choice->SetSelection($sel) if $sel <= $#$presets;
|
||||
}
|
||||
$self->{preset_choosers}{$group}[0]->SetSelection($selected);
|
||||
}
|
||||
|
|
@ -497,8 +516,11 @@ sub load_model_objects {
|
|||
my ($self, @model_objects) = @_;
|
||||
|
||||
my $bed_centerf = $self->bed_centerf;
|
||||
my $bed_shape = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape});
|
||||
my $bed_size = $bed_shape->bounding_box->size;
|
||||
|
||||
my $need_arrange = 0;
|
||||
my $scaled_down = 0;
|
||||
my @obj_idx = ();
|
||||
foreach my $model_object (@model_objects) {
|
||||
my $o = $self->{model}->add_object($model_object);
|
||||
|
|
@ -516,6 +538,16 @@ sub load_model_objects {
|
|||
$o->center_around_origin; # also aligns object to Z = 0
|
||||
$o->add_instance(offset => $bed_centerf);
|
||||
}
|
||||
|
||||
{
|
||||
# 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) {
|
||||
$_->set_scaling_factor(1/$ratio) for @{$o->instances};
|
||||
$scaled_down = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$self->{print}->auto_assign_extruders($o);
|
||||
$self->{print}->add_model_object($o);
|
||||
|
|
@ -526,14 +558,15 @@ sub load_model_objects {
|
|||
$need_arrange = 0;
|
||||
}
|
||||
|
||||
$self->objects_loaded(\@obj_idx, no_arrange => !$need_arrange);
|
||||
}
|
||||
|
||||
sub objects_loaded {
|
||||
my $self = shift;
|
||||
my ($obj_idxs, %params) = @_;
|
||||
if ($scaled_down) {
|
||||
Slic3r::GUI::show_info(
|
||||
$self,
|
||||
'Your object appears to be too large, so it was automatically scaled down to fit your print bed.',
|
||||
'Object too large?',
|
||||
);
|
||||
}
|
||||
|
||||
foreach my $obj_idx (@$obj_idxs) {
|
||||
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);
|
||||
|
|
@ -545,7 +578,7 @@ sub objects_loaded {
|
|||
|
||||
$self->make_thumbnail($obj_idx);
|
||||
}
|
||||
$self->arrange unless $params{no_arrange};
|
||||
$self->arrange if $need_arrange;
|
||||
$self->update;
|
||||
|
||||
# zoom to objects
|
||||
|
|
@ -553,7 +586,7 @@ sub objects_loaded {
|
|||
if $self->{canvas3D};
|
||||
|
||||
$self->{list}->Update;
|
||||
$self->{list}->Select($obj_idxs->[-1], 1);
|
||||
$self->{list}->Select($obj_idx[-1], 1);
|
||||
$self->object_list_changed;
|
||||
|
||||
$self->schedule_background_process;
|
||||
|
|
@ -691,6 +724,7 @@ sub rotate {
|
|||
my $self = shift;
|
||||
my ($angle, $axis) = @_;
|
||||
|
||||
# angle is in degrees
|
||||
$axis //= Z;
|
||||
|
||||
my ($obj_idx, $object) = $self->selected_object;
|
||||
|
|
@ -712,14 +746,14 @@ sub rotate {
|
|||
$self->stop_background_process;
|
||||
|
||||
if ($axis == Z) {
|
||||
my $new_angle = $model_instance->rotation + $angle;
|
||||
my $new_angle = $model_instance->rotation + deg2rad($angle);
|
||||
$_->set_rotation($new_angle) for @{ $model_object->instances };
|
||||
$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(deg2rad($model_instance->rotation), Z);
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
$model_object->rotate(deg2rad($angle), $axis);
|
||||
|
|
@ -749,7 +783,7 @@ sub flip {
|
|||
|
||||
# apply Z rotation before flipping
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate(deg2rad($model_instance->rotation), Z);
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
|
||||
|
|
@ -788,7 +822,7 @@ sub changescale {
|
|||
|
||||
# apply Z rotation before scaling
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate(deg2rad($model_instance->rotation), Z);
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
|
||||
|
|
@ -1054,9 +1088,10 @@ sub export_gcode {
|
|||
$dlg->Destroy;
|
||||
return;
|
||||
}
|
||||
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($dlg->GetPath);
|
||||
my $path = Slic3r::decode_path($dlg->GetPath);
|
||||
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path);
|
||||
wxTheApp->save_settings;
|
||||
$self->{export_gcode_output_file} = $Slic3r::GUI::MainFrame::last_output_file = $dlg->GetPath;
|
||||
$self->{export_gcode_output_file} = $Slic3r::GUI::MainFrame::last_output_file = $path;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
|
|
@ -1066,6 +1101,11 @@ sub export_gcode {
|
|||
$self->statusbar->SetCancelCallback(sub {
|
||||
$self->stop_background_process;
|
||||
$self->statusbar->SetStatusText("Export cancelled");
|
||||
$self->{export_gcode_output_file} = undef;
|
||||
$self->{send_gcode_file} = undef;
|
||||
|
||||
# this updates buttons status
|
||||
$self->object_list_changed;
|
||||
});
|
||||
|
||||
# start background process, whose completion event handler
|
||||
|
|
@ -1275,7 +1315,7 @@ sub _get_export_file {
|
|||
$dlg->Destroy;
|
||||
return undef;
|
||||
}
|
||||
$output_file = $Slic3r::GUI::MainFrame::last_output_file = $dlg->GetPath;
|
||||
$output_file = $Slic3r::GUI::MainFrame::last_output_file = Slic3r::decode_path($dlg->GetPath);
|
||||
$dlg->Destroy;
|
||||
}
|
||||
return $output_file;
|
||||
|
|
@ -1344,11 +1384,10 @@ sub on_extruders_change {
|
|||
my $choices = $self->{preset_choosers}{filament};
|
||||
while (@$choices < $num_extruders) {
|
||||
my @presets = $choices->[0]->GetStrings;
|
||||
push @$choices, Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [@presets]);
|
||||
$choices->[-1]->SetFont($Slic3r::GUI::small_font);
|
||||
push @$choices, Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
|
||||
$self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0);
|
||||
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choices->[-1], 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
|
||||
EVT_CHOICE($choices->[-1], $choices->[-1], sub { $self->_on_select_preset('filament', @_) });
|
||||
EVT_COMBOBOX($choices->[-1], $choices->[-1], sub { $self->_on_select_preset('filament', @_) });
|
||||
my $i = first { $choices->[-1]->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets;
|
||||
$choices->[-1]->SetSelection($i || 0);
|
||||
}
|
||||
|
|
@ -1616,26 +1655,27 @@ sub object_menu {
|
|||
my $menu = Wx::Menu->new;
|
||||
$frame->_append_menu_item($menu, "Delete\tCtrl+Del", 'Remove the selected object', sub {
|
||||
$self->remove;
|
||||
});
|
||||
}, undef, 'brick_delete.png');
|
||||
$frame->_append_menu_item($menu, "Increase copies\tCtrl++", 'Place one more copy of the selected object', sub {
|
||||
$self->increase;
|
||||
});
|
||||
}, undef, 'add.png');
|
||||
$frame->_append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub {
|
||||
$self->decrease;
|
||||
});
|
||||
}, undef, 'delete.png');
|
||||
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
|
||||
$self->set_number_of_copies;
|
||||
});
|
||||
}, undef, 'textfield.png');
|
||||
$menu->AppendSeparator();
|
||||
$frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
|
||||
$self->rotate(-45);
|
||||
});
|
||||
}, undef, 'arrow_rotate_clockwise.png');
|
||||
$frame->_append_menu_item($menu, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub {
|
||||
$self->rotate(+45);
|
||||
});
|
||||
}, undef, 'arrow_rotate_anticlockwise.png');
|
||||
|
||||
my $rotateMenu = Wx::Menu->new;
|
||||
$menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
|
||||
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
|
||||
$frame->_set_menu_item_icon($rotateMenuItem, 'textfield.png');
|
||||
$frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub {
|
||||
$self->rotate(undef, X);
|
||||
});
|
||||
|
|
@ -1647,7 +1687,8 @@ sub object_menu {
|
|||
});
|
||||
|
||||
my $flipMenu = Wx::Menu->new;
|
||||
$menu->AppendSubMenu($flipMenu, "Flip", 'Mirror the selected object');
|
||||
my $flipMenuItem = $menu->AppendSubMenu($flipMenu, "Flip", 'Mirror the selected object');
|
||||
$frame->_set_menu_item_icon($flipMenuItem, 'shape_flip_horizontal.png');
|
||||
$frame->_append_menu_item($flipMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
|
||||
$self->flip(X);
|
||||
});
|
||||
|
|
@ -1659,7 +1700,8 @@ sub object_menu {
|
|||
});
|
||||
|
||||
my $scaleMenu = Wx::Menu->new;
|
||||
$menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
|
||||
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
|
||||
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
|
||||
$frame->_append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
|
||||
$self->changescale(undef);
|
||||
});
|
||||
|
|
@ -1675,18 +1717,18 @@ sub object_menu {
|
|||
|
||||
$frame->_append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub {
|
||||
$self->split_object;
|
||||
});
|
||||
}, undef, 'shape_ungroup.png');
|
||||
$frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub {
|
||||
$self->object_cut_dialog;
|
||||
});
|
||||
}, undef, 'package.png');
|
||||
$menu->AppendSeparator();
|
||||
$frame->_append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub {
|
||||
$self->object_settings_dialog;
|
||||
});
|
||||
}, undef, 'cog.png');
|
||||
$menu->AppendSeparator();
|
||||
$frame->_append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub {
|
||||
$self->export_object_stl;
|
||||
});
|
||||
}, undef, 'brick_go.png');
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
|
@ -1763,7 +1805,7 @@ sub transform_thumbnail {
|
|||
# the order of these transformations MUST be the same everywhere, including
|
||||
# in Slic3r::Print->add_model_object()
|
||||
my $t = $self->thumbnail->clone;
|
||||
$t->rotate(deg2rad($model_instance->rotation), Slic3r::Point->new(0,0));
|
||||
$t->rotate($model_instance->rotation, Slic3r::Point->new(0,0));
|
||||
$t->scale($model_instance->scaling_factor);
|
||||
|
||||
$self->transformed_thumbnail($t);
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ sub reload_print {
|
|||
}
|
||||
|
||||
$self->{canvas}->bb($self->print->total_bounding_box);
|
||||
$self->{canvas}->_dirty(1);
|
||||
|
||||
my %z = (); # z => 1
|
||||
foreach my $object (@{$self->{print}->objects}) {
|
||||
|
|
@ -115,15 +116,23 @@ sub set_z {
|
|||
|
||||
package Slic3r::GUI::Plater::2DToolpaths::Canvas;
|
||||
|
||||
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
|
||||
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
|
||||
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
|
||||
use base qw(Wx::GLCanvas Class::Accessor);
|
||||
use Wx::GLCanvas qw(:all);
|
||||
use List::Util qw(min first);
|
||||
use Slic3r::Geometry qw(scale unscale epsilon);
|
||||
use List::Util qw(min max first);
|
||||
use Slic3r::Geometry qw(scale unscale epsilon X Y);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(print z layers color init bb));
|
||||
__PACKAGE__->mk_accessors(qw(
|
||||
print z layers color init
|
||||
bb
|
||||
_camera_bb
|
||||
_dirty
|
||||
_zoom
|
||||
_camera_target
|
||||
_drag_start_xy
|
||||
));
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
{
|
||||
|
|
@ -136,20 +145,99 @@ sub new {
|
|||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
$self->print($print);
|
||||
$self->_zoom(1);
|
||||
|
||||
# 2D point in model space
|
||||
$self->_camera_target(Slic3r::Pointf->new(0,0));
|
||||
|
||||
EVT_PAINT($self, sub {
|
||||
my $dc = Wx::PaintDC->new($self);
|
||||
$self->Render($dc);
|
||||
});
|
||||
EVT_SIZE($self, sub {
|
||||
EVT_SIZE($self, sub { $self->_dirty(1) });
|
||||
EVT_IDLE($self, sub {
|
||||
return unless $self->_dirty;
|
||||
return if !$self->IsShownOnScreen;
|
||||
$self->Resize( $self->GetSizeWH );
|
||||
$self->Resize;
|
||||
$self->Refresh;
|
||||
});
|
||||
EVT_MOUSEWHEEL($self, sub {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
my $old_zoom = $self->_zoom;
|
||||
|
||||
# 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;
|
||||
$self->_zoom($self->_zoom / (1-$zoom));
|
||||
$self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
|
||||
|
||||
{
|
||||
# In order to zoom around the mouse point we need to translate
|
||||
# the camera target. This math is almost there but not perfect yet...
|
||||
my $camera_bb_size = $self->_camera_bb->size;
|
||||
my $size = Slic3r::Pointf->new($self->GetSizeWH);
|
||||
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
|
||||
|
||||
# calculate the zooming center in pixel coordinates relative to the viewport center
|
||||
my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #-
|
||||
|
||||
# calculate where this point will end up after applying the new zoom
|
||||
my $vec2 = $vec->clone;
|
||||
$vec2->scale($old_zoom / $self->_zoom);
|
||||
|
||||
# move the camera target by the difference of the two positions
|
||||
$self->_camera_target->translate(
|
||||
-($vec->x - $vec2->x) * $camera_bb_size->x / $size->x,
|
||||
($vec->y - $vec2->y) * $camera_bb_size->y / $size->y, #//
|
||||
);
|
||||
}
|
||||
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
});
|
||||
EVT_MOUSE_EVENTS($self, \&mouse_event);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub mouse_event {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
|
||||
if ($e->Entering && &Wx::wxMSW) {
|
||||
# wxMSW needs focus in order to catch mouse wheel events
|
||||
$self->SetFocus;
|
||||
} elsif ($e->Dragging) {
|
||||
if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
|
||||
# if dragging, translate view
|
||||
|
||||
if (defined $self->_drag_start_xy) {
|
||||
my $move = $self->_drag_start_xy->vector_to($pos); # in pixels
|
||||
|
||||
# get viewport and camera size in order to convert pixel to model units
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
my $camera_bb_size = $self->_camera_bb->size;
|
||||
|
||||
# compute translation in model units
|
||||
$self->_camera_target->translate(
|
||||
-$move->x * $camera_bb_size->x / $x,
|
||||
$move->y * $camera_bb_size->y / $y, # /**
|
||||
);
|
||||
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
}
|
||||
$self->_drag_start_xy($pos);
|
||||
}
|
||||
} elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
|
||||
$self->_drag_start_xy(undef);
|
||||
} else {
|
||||
$e->Skip();
|
||||
}
|
||||
}
|
||||
|
||||
sub set_z {
|
||||
my ($self, $z) = @_;
|
||||
|
||||
|
|
@ -197,22 +285,6 @@ sub Render {
|
|||
return;
|
||||
}
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
my $bb = $self->bb;
|
||||
my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
|
||||
# adjust Y
|
||||
my $new_y = $y * ($x2 - $x1) / $x;
|
||||
$y1 = ($y2 + $y1)/2 - $new_y/2;
|
||||
$y2 = $y1 + $new_y;
|
||||
} else {
|
||||
my $new_x = $x * ($y2 - $y1) / $y;
|
||||
$x1 = ($x2 + $x1)/2 - $new_x/2;
|
||||
$x2 = $x1 + $new_x;
|
||||
}
|
||||
glOrtho($x1, $x2, $y1, $y2, 0, 1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
|
@ -398,12 +470,71 @@ sub SetCurrent {
|
|||
}
|
||||
|
||||
sub Resize {
|
||||
my ($self, $x, $y) = @_;
|
||||
my ($self) = @_;
|
||||
|
||||
return unless $self->GetContext;
|
||||
return unless $self->bb;
|
||||
$self->_dirty(0);
|
||||
|
||||
$self->SetCurrent($self->GetContext);
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
glViewport(0, 0, $x, $y);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
my $bb = $self->bb->clone;
|
||||
|
||||
# center bounding box around origin before scaling it
|
||||
my $bb_center = $bb->center;
|
||||
$bb->translate(@{$bb_center->negative});
|
||||
|
||||
# scale bounding box according to zoom factor
|
||||
$bb->scale($self->_zoom);
|
||||
|
||||
# reposition bounding box around original center
|
||||
$bb->translate(@{$bb_center});
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
# save camera
|
||||
$self->_camera_bb($bb);
|
||||
|
||||
my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
|
||||
if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
|
||||
# adjust Y
|
||||
my $new_y = $y * ($x2 - $x1) / $x;
|
||||
$y1 = ($y2 + $y1)/2 - $new_y/2;
|
||||
$y2 = $y1 + $new_y;
|
||||
} else {
|
||||
my $new_x = $x * ($y2 - $y1) / $y;
|
||||
$x1 = ($x2 + $x1)/2 - $new_x/2;
|
||||
$x2 = $x1 + $new_x;
|
||||
}
|
||||
glOrtho($x1, $x2, $y1, $y2, 0, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
sub line {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ sub load_print {
|
|||
}
|
||||
|
||||
if ($self->IsShown) {
|
||||
# load skirt and brim
|
||||
$self->canvas->load_print_toolpaths($self->print);
|
||||
|
||||
foreach my $object (@{$self->print->objects}) {
|
||||
$self->canvas->load_print_object_toolpaths($object);
|
||||
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ sub perform_cut {
|
|||
push @{$self->{new_model_objects}}, $lower_object;
|
||||
if ($self->{cut_options}{rotate_lower}) {
|
||||
$lower_object->rotate(PI, X);
|
||||
$lower_object->center_around_origin; # align to Z = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ sub new {
|
|||
});
|
||||
EVT_TREE_SEL_CHANGED($self, $tree, sub {
|
||||
my ($self, $event) = @_;
|
||||
return if $self->{disable_tree_sel_changed_event};
|
||||
$self->selection_changed;
|
||||
});
|
||||
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
|
||||
|
|
@ -118,7 +119,14 @@ sub reload_tree {
|
|||
my $tree = $self->{tree};
|
||||
my $rootId = $tree->GetRootItem;
|
||||
|
||||
# despite wxWidgets states that DeleteChildren "will not generate any events unlike Delete() method",
|
||||
# the MSW implementation of DeleteChildren actually calls Delete() for each item, so
|
||||
# EVT_TREE_SEL_CHANGED is being called, with bad effects (the event handler is called; this
|
||||
# subroutine is never continued; an invisible EndModal is called on the dialog causing Plater
|
||||
# to continue its logic and rescheduling the background process etc. GH #2774)
|
||||
$self->{disable_tree_sel_changed_event} = 1;
|
||||
$tree->DeleteChildren($rootId);
|
||||
$self->{disable_tree_sel_changed_event} = 0;
|
||||
|
||||
my $selectedId = $rootId;
|
||||
foreach my $volume_id (0..$#{$object->volumes}) {
|
||||
|
|
@ -136,9 +144,13 @@ sub reload_tree {
|
|||
}
|
||||
$tree->ExpandAll;
|
||||
|
||||
# This will trigger the selection_changed() event
|
||||
Slic3r::GUI->CallAfter(sub {
|
||||
$self->{tree}->SelectItem($selectedId);
|
||||
|
||||
# SelectItem() should trigger EVT_TREE_SEL_CHANGED as per wxWidgets docs,
|
||||
# but in fact it doesn't if the given item is already selected (this happens
|
||||
# on first load)
|
||||
$self->selection_changed;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ sub _update {}
|
|||
sub _on_presets_changed {
|
||||
my $self = shift;
|
||||
|
||||
$self->{on_presets_changed}->([$self->{presets_choice}->GetStrings], $self->{presets_choice}->GetSelection)
|
||||
$self->{on_presets_changed}->($self->{presets}, $self->{presets_choice}->GetSelection)
|
||||
if $self->{on_presets_changed};
|
||||
}
|
||||
|
||||
|
|
@ -360,10 +360,10 @@ sub load_presets {
|
|||
|
||||
$self->current_preset(undef);
|
||||
$self->{presets_choice}->Clear;
|
||||
$self->{presets_choice}->Append($_->{name}) for @{$self->{presets}};
|
||||
$self->{presets_choice}->Append($_->name) for @{$self->{presets}};
|
||||
{
|
||||
# load last used preset
|
||||
my $i = first { basename($self->{presets}[$_]{file}) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}};
|
||||
my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}};
|
||||
$self->select_preset($i || 0);
|
||||
}
|
||||
$self->_on_presets_changed;
|
||||
|
|
@ -813,7 +813,7 @@ sub build {
|
|||
my $self = shift;
|
||||
|
||||
$self->init_config_options(qw(
|
||||
filament_diameter extrusion_multiplier
|
||||
filament_colour filament_diameter extrusion_multiplier
|
||||
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
|
||||
fan_always_on cooling
|
||||
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
|
||||
|
|
@ -824,6 +824,7 @@ sub build {
|
|||
my $page = $self->add_options_page('Filament', 'spool.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Filament');
|
||||
$optgroup->append_single_option_line('filament_colour', 0);
|
||||
$optgroup->append_single_option_line('filament_diameter', 0);
|
||||
$optgroup->append_single_option_line('extrusion_multiplier', 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue