Rotation around X and Y axes from plater
This commit is contained in:
parent
51cf78534c
commit
ec7bb40da9
4 changed files with 51 additions and 21 deletions
|
@ -4,6 +4,7 @@ use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use List::Util qw(min);
|
use List::Util qw(min);
|
||||||
|
use Slic3r::Geometry qw(X Y Z);
|
||||||
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
|
||||||
:font :icon wxTheApp);
|
:font :icon wxTheApp);
|
||||||
use Wx::Event qw(EVT_CLOSE EVT_MENU);
|
use Wx::Event qw(EVT_CLOSE EVT_MENU);
|
||||||
|
@ -192,9 +193,19 @@ sub _init_menubar {
|
||||||
$self->_append_menu_item($self->{object_menu}, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub {
|
$self->_append_menu_item($self->{object_menu}, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub {
|
||||||
$plater->rotate(+45);
|
$plater->rotate(+45);
|
||||||
});
|
});
|
||||||
$self->_append_menu_item($self->{object_menu}, "Rotate…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
|
|
||||||
$plater->rotate(undef);
|
my $rotateMenu = Wx::Menu->new;
|
||||||
|
$self->{object_menu}->AppendSubMenu($rotateMenu, "Rotate…", 'Rotate the selected object by an arbitrary angle');
|
||||||
|
$self->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub {
|
||||||
|
$plater->rotate(undef, X);
|
||||||
});
|
});
|
||||||
|
$self->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub {
|
||||||
|
$plater->rotate(undef, Y);
|
||||||
|
});
|
||||||
|
$self->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
|
||||||
|
$plater->rotate(undef, Z);
|
||||||
|
});
|
||||||
|
|
||||||
$self->_append_menu_item($self->{object_menu}, "Scale…", 'Scale the selected object by an arbitrary factor', sub {
|
$self->_append_menu_item($self->{object_menu}, "Scale…", 'Scale the selected object by an arbitrary factor', sub {
|
||||||
$plater->changescale;
|
$plater->changescale;
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ use utf8;
|
||||||
|
|
||||||
use File::Basename qw(basename dirname);
|
use File::Basename qw(basename dirname);
|
||||||
use List::Util qw(sum first);
|
use List::Util qw(sum first);
|
||||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale);
|
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
|
||||||
use threads::shared qw(shared_clone);
|
use threads::shared qw(shared_clone);
|
||||||
use Thread::Semaphore;
|
use Thread::Semaphore;
|
||||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||||
|
@ -25,7 +25,6 @@ use constant TB_MORE => &Wx::NewId;
|
||||||
use constant TB_FEWER => &Wx::NewId;
|
use constant TB_FEWER => &Wx::NewId;
|
||||||
use constant TB_45CW => &Wx::NewId;
|
use constant TB_45CW => &Wx::NewId;
|
||||||
use constant TB_45CCW => &Wx::NewId;
|
use constant TB_45CCW => &Wx::NewId;
|
||||||
use constant TB_ROTATE => &Wx::NewId;
|
|
||||||
use constant TB_SCALE => &Wx::NewId;
|
use constant TB_SCALE => &Wx::NewId;
|
||||||
use constant TB_SPLIT => &Wx::NewId;
|
use constant TB_SPLIT => &Wx::NewId;
|
||||||
use constant TB_VIEW => &Wx::NewId;
|
use constant TB_VIEW => &Wx::NewId;
|
||||||
|
@ -96,7 +95,6 @@ sub new {
|
||||||
$self->{htoolbar}->AddSeparator;
|
$self->{htoolbar}->AddSeparator;
|
||||||
$self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_ROTATE, "Rotate…", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), '');
|
|
||||||
$self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new("$Slic3r::var/arrow_out.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new("$Slic3r::var/arrow_out.png", wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddSeparator;
|
$self->{htoolbar}->AddSeparator;
|
||||||
|
@ -160,7 +158,6 @@ sub new {
|
||||||
decrease delete.png
|
decrease delete.png
|
||||||
rotate45cw arrow_rotate_clockwise.png
|
rotate45cw arrow_rotate_clockwise.png
|
||||||
rotate45ccw arrow_rotate_anticlockwise.png
|
rotate45ccw arrow_rotate_anticlockwise.png
|
||||||
rotate arrow_rotate_clockwise.png
|
|
||||||
changescale arrow_out.png
|
changescale arrow_out.png
|
||||||
split shape_ungroup.png
|
split shape_ungroup.png
|
||||||
view package.png
|
view package.png
|
||||||
|
@ -187,7 +184,6 @@ sub new {
|
||||||
EVT_TOOL($self, TB_FEWER, \&decrease);
|
EVT_TOOL($self, TB_FEWER, \&decrease);
|
||||||
EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45) });
|
EVT_TOOL($self, TB_45CW, sub { $_[0]->rotate(-45) });
|
||||||
EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45) });
|
EVT_TOOL($self, TB_45CCW, sub { $_[0]->rotate(45) });
|
||||||
EVT_TOOL($self, TB_ROTATE, sub { $_[0]->rotate(undef) });
|
|
||||||
EVT_TOOL($self, TB_SCALE, \&changescale);
|
EVT_TOOL($self, TB_SCALE, \&changescale);
|
||||||
EVT_TOOL($self, TB_SPLIT, \&split_object);
|
EVT_TOOL($self, TB_SPLIT, \&split_object);
|
||||||
EVT_TOOL($self, TB_VIEW, sub { $_[0]->object_cut_dialog });
|
EVT_TOOL($self, TB_VIEW, sub { $_[0]->object_cut_dialog });
|
||||||
|
@ -202,7 +198,6 @@ sub new {
|
||||||
EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45) });
|
EVT_BUTTON($self, $self->{btn_rotate45cw}, sub { $_[0]->rotate(-45) });
|
||||||
EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45) });
|
EVT_BUTTON($self, $self->{btn_rotate45ccw}, sub { $_[0]->rotate(45) });
|
||||||
EVT_BUTTON($self, $self->{btn_changescale}, \&changescale);
|
EVT_BUTTON($self, $self->{btn_changescale}, \&changescale);
|
||||||
EVT_BUTTON($self, $self->{btn_rotate}, sub { $_[0]->rotate(undef) });
|
|
||||||
EVT_BUTTON($self, $self->{btn_split}, \&split_object);
|
EVT_BUTTON($self, $self->{btn_split}, \&split_object);
|
||||||
EVT_BUTTON($self, $self->{btn_view}, sub { $_[0]->object_cut_dialog });
|
EVT_BUTTON($self, $self->{btn_view}, sub { $_[0]->object_cut_dialog });
|
||||||
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
|
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
|
||||||
|
@ -555,9 +550,13 @@ sub decrease {
|
||||||
|
|
||||||
sub rotate {
|
sub rotate {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($angle) = @_;
|
my ($angle, $axis) = @_;
|
||||||
|
|
||||||
|
$axis //= Z;
|
||||||
|
|
||||||
my ($obj_idx, $object) = $self->selected_object;
|
my ($obj_idx, $object) = $self->selected_object;
|
||||||
|
return if !defined $obj_idx;
|
||||||
|
|
||||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||||
my $model_instance = $model_object->instances->[0];
|
my $model_instance = $model_object->instances->[0];
|
||||||
|
|
||||||
|
@ -565,14 +564,27 @@ sub rotate {
|
||||||
return if !$object->thumbnail;
|
return if !$object->thumbnail;
|
||||||
|
|
||||||
if (!defined $angle) {
|
if (!defined $angle) {
|
||||||
$angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", $model_instance->rotation, -364, 364, $self);
|
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
|
||||||
|
$angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate around $axis_name axis", $model_instance->rotation, -364, 364, $self);
|
||||||
return if !$angle || $angle == -1;
|
return if !$angle || $angle == -1;
|
||||||
$angle = 0 - $angle; # rotate clockwise (be consistent with button icon)
|
$angle = 0 - $angle; # rotate clockwise (be consistent with button icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $new_angle = $model_instance->rotation + $angle;
|
if ($axis == Z) {
|
||||||
$_->set_rotation($new_angle) for @{ $model_object->instances };
|
my $new_angle = $model_instance->rotation + $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_object->instances->[0]->rotation != 0) {
|
||||||
|
$model_object->rotate(deg2rad($model_object->instances->[0]->rotation), Z);
|
||||||
|
$_->set_rotation(0) for @{ $model_object->instances };
|
||||||
|
}
|
||||||
|
$model_object->rotate(deg2rad($angle), $axis);
|
||||||
|
$self->make_thumbnail($obj_idx);
|
||||||
|
}
|
||||||
$model_object->update_bounding_box;
|
$model_object->update_bounding_box;
|
||||||
|
|
||||||
# update print and start background processing
|
# update print and start background processing
|
||||||
|
@ -580,7 +592,6 @@ sub rotate {
|
||||||
$self->{print}->add_model_object($model_object, $obj_idx);
|
$self->{print}->add_model_object($model_object, $obj_idx);
|
||||||
$self->schedule_background_process;
|
$self->schedule_background_process;
|
||||||
|
|
||||||
$object->transform_thumbnail($self->{model}, $obj_idx);
|
|
||||||
}
|
}
|
||||||
$self->selection_changed; # refresh info (size etc.)
|
$self->selection_changed; # refresh info (size etc.)
|
||||||
$self->update;
|
$self->update;
|
||||||
|
@ -1210,11 +1221,11 @@ sub selection_changed {
|
||||||
|
|
||||||
my $method = $have_sel ? 'Enable' : 'Disable';
|
my $method = $have_sel ? 'Enable' : 'Disable';
|
||||||
$self->{"btn_$_"}->$method
|
$self->{"btn_$_"}->$method
|
||||||
for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw rotate changescale split view settings);
|
for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split view settings);
|
||||||
|
|
||||||
if ($self->{htoolbar}) {
|
if ($self->{htoolbar}) {
|
||||||
$self->{htoolbar}->EnableTool($_, $have_sel)
|
$self->{htoolbar}->EnableTool($_, $have_sel)
|
||||||
for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_ROTATE, TB_SCALE, TB_SPLIT, TB_VIEW, TB_SETTINGS);
|
for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_VIEW, TB_SETTINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
||||||
|
@ -1338,8 +1349,10 @@ has 'selected' => (is => 'rw', default => sub { 0 });
|
||||||
sub make_thumbnail {
|
sub make_thumbnail {
|
||||||
my ($self, $model, $obj_idx) = @_;
|
my ($self, $model, $obj_idx) = @_;
|
||||||
|
|
||||||
my $mesh = $model->objects->[$obj_idx]->raw_mesh;
|
# make method idempotent
|
||||||
|
$self->thumbnail->clear;
|
||||||
|
|
||||||
|
my $mesh = $model->objects->[$obj_idx]->raw_mesh;
|
||||||
if ($mesh->facets_count <= 5000) {
|
if ($mesh->facets_count <= 5000) {
|
||||||
# remove polygons with area <= 1mm
|
# remove polygons with area <= 1mm
|
||||||
my $area_threshold = Slic3r::Geometry::scale 1;
|
my $area_threshold = Slic3r::Geometry::scale 1;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use Slic3r::Geometry qw(PI);
|
use Slic3r::Geometry qw(PI X);
|
||||||
use Wx qw(:dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
use Wx qw(:dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
||||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||||
use base 'Wx::Dialog';
|
use base 'Wx::Dialog';
|
||||||
|
@ -133,7 +133,7 @@ sub perform_cut {
|
||||||
if ($self->{cut_options}{keep_lower} && defined $lower_object) {
|
if ($self->{cut_options}{keep_lower} && defined $lower_object) {
|
||||||
push @{$self->{new_model_objects}}, $lower_object;
|
push @{$self->{new_model_objects}}, $lower_object;
|
||||||
if ($self->{cut_options}{rotate_lower}) {
|
if ($self->{cut_options}{rotate_lower}) {
|
||||||
$lower_object->rotate_x(PI);
|
$lower_object->rotate(PI, X);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -463,13 +463,19 @@ sub translate {
|
||||||
$self->_bounding_box->translate(@shift) if defined $self->_bounding_box;
|
$self->_bounding_box->translate(@shift) if defined $self->_bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rotate_x {
|
sub rotate {
|
||||||
my ($self, $angle) = @_;
|
my ($self, $angle, $axis) = @_;
|
||||||
|
|
||||||
# we accept angle in radians but mesh currently uses degrees
|
# we accept angle in radians but mesh currently uses degrees
|
||||||
$angle = rad2deg($angle);
|
$angle = rad2deg($angle);
|
||||||
|
|
||||||
$_->mesh->rotate_x($angle) for @{$self->volumes};
|
if ($axis == X) {
|
||||||
|
$_->mesh->rotate_x($angle) for @{$self->volumes};
|
||||||
|
} elsif ($axis == Y) {
|
||||||
|
$_->mesh->rotate_y($angle) for @{$self->volumes};
|
||||||
|
} elsif ($axis == Z) {
|
||||||
|
$_->mesh->rotate_z($angle) for @{$self->volumes};
|
||||||
|
}
|
||||||
$self->invalidate_bounding_box;
|
$self->invalidate_bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue