Rotation around X and Y axes from plater

This commit is contained in:
Alessandro Ranellucci 2014-06-14 20:52:21 +02:00
parent 51cf78534c
commit ec7bb40da9
4 changed files with 51 additions and 21 deletions

View file

@ -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;
}); });

View file

@ -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;

View file

@ -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);
} }
} }

View file

@ -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;
} }