Resize & Rotate modifier mesh (#3879)

* Prototype scaling-to-size for modifier meshes. Currently the rescale doesn't seem to operate as expected.

* Added scaling to size for modifier meshes.

* Added rotation for modifier meshes.

* Used correct UTF8 encoded ellipse.
This commit is contained in:
Joseph Lenox 2017-04-15 10:39:11 -05:00 committed by GitHub
parent b78ccc6d4f
commit 075adca8bb
3 changed files with 140 additions and 4 deletions

View File

@ -9,7 +9,9 @@ use utf8;
use File::Basename qw(basename);
use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL
wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED);
use List::Util qw(max);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_ITEM_RIGHT_CLICK);
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg);
use base 'Wx::Panel';
use constant ICON_OBJECT => 0;
@ -156,11 +158,47 @@ sub new {
return if $self->{disable_tree_sel_changed_event};
$self->selection_changed;
});
EVT_TREE_ITEM_RIGHT_CLICK($self, $tree, sub {
my ($self, $event) = @_;
my $item = $event->GetItem;
my $frame = $self->GetFrame;
my $menu = Wx::Menu->new;
my $scaleToSizeMenu = Wx::Menu->new;
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis');
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef, 1);
});
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X, 1);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y, 1);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$self->changescale(Z, 1);
}, undef, 'bullet_blue.png');
my $rotateMenu = Wx::Menu->new;
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
wxTheApp->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);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub {
$self->rotate(undef, Y);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
$self->rotate(undef, Z);
}, undef, 'bullet_blue.png');
$frame->PopupMenu($menu, $event->GetPoint);
});
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) });
EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) });
EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete);
$self->reload_tree;
return $self;
@ -450,4 +488,82 @@ sub _update {
$self->{canvas}->Render;
}
sub changescale {
my ($self, $axis, $tosize) = @_;
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
my $object_size = $volume->bounding_box->size;
if (defined $axis) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $scale;
if (defined $tosize) {
my $cursize = $object_size->[$axis];
# Wx::GetNumberFromUser() does not support decimal numbers
my $newsize = Wx::GetTextFromUser(
sprintf("Enter the new size for the selected mesh:"),
"Scale along $axis_name",
$cursize, $self);
return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0;
$scale = $newsize / $cursize * 100;
} else {
# Wx::GetNumberFromUser() does not support decimal numbers
$scale = Wx::GetTextFromUser("Enter the scale % for the selected object:",
"Scale along $axis_name", 100, $self);
$scale =~ s/%$//;
return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0;
}
my $versor = [1,1,1];
$versor->[$axis] = $scale/100;
$volume->mesh->scale_xyz(Slic3r::Pointf3->new(@$versor));
} else {
my $scale;
if ($tosize) {
my $cursize = max(@$object_size);
# Wx::GetNumberFromUser() does not support decimal numbers
my $newsize = Wx::GetTextFromUser("Enter the new max size for the selected object:",
"Scale", $cursize, $self);
return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0;
$scale = $newsize / $cursize;
} else {
# max scale factor should be above 2540 to allow importing files exported in inches
# Wx::GetNumberFromUser() does not support decimal numbers
$scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", 'Scale',
100, $self);
return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0;
}
return if !$scale || $scale < 0;
$volume->mesh->scale($scale);
}
$self->_parts_changed;
}
}
sub rotate {
my $self = shift;
my ($angle, $axis) = @_;
# angle is in degrees
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
if (!defined $angle) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $default = $axis == Z ? 0 : 0;
# Wx::GetNumberFromUser() does not support decimal numbers
$angle = Wx::GetTextFromUser("Enter the rotation angle:", "Rotate around $axis_name axis",
$default, $self);
return if !$angle || $angle !~ /^-?\d*(?:\.\d*)?$/ || $angle == -1;
}
if ($axis == X) { $volume->mesh->rotate_x(deg2rad($angle)); }
if ($axis == Y) { $volume->mesh->rotate_y(deg2rad($angle)); }
if ($axis == Z) { $volume->mesh->rotate_z(deg2rad($angle)); }
$self->_parts_changed;
}
}
sub GetFrame {
my ($self) = @_;
return &Wx::GetTopLevelParent($self);
}
1;

View File

@ -7,8 +7,8 @@ use strict;
use warnings;
use utf8;
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_BUTTON);
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_MENU);
use base 'Wx::Dialog';
sub new {
@ -53,6 +53,17 @@ sub PartSettingsChanged {
my ($self) = @_;
return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged;
}
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description, $kind);
wxTheApp->set_menu_item_icon($item, $icon);
EVT_MENU($self, $id, $cb);
return $item;
}
package Slic3r::GUI::Plater::ObjectDialog::BaseTab;
use base 'Wx::Panel';

View File

@ -248,6 +248,15 @@ ModelMaterial::attributes()
void set_material_id(t_model_material_id material_id)
%code%{ THIS->material_id(material_id); %};
Ref<ModelMaterial> material();
Clone<BoundingBoxf3> bounding_box()
%code%{
try {
RETVAL = THIS->mesh.bounding_box();
} catch (std::exception& e) {
croak("%s", e.what());
}
%};
Ref<DynamicPrintConfig> config()
%code%{ RETVAL = &THIS->config; %};