Initial work for a GUI for the cut feature
This commit is contained in:
parent
334bc4c581
commit
882a98ed44
6 changed files with 215 additions and 10 deletions
|
@ -9,6 +9,7 @@ use Slic3r::GUI::AboutDialog;
|
||||||
use Slic3r::GUI::ConfigWizard;
|
use Slic3r::GUI::ConfigWizard;
|
||||||
use Slic3r::GUI::Plater;
|
use Slic3r::GUI::Plater;
|
||||||
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||||
|
use Slic3r::GUI::Plater::ObjectCutDialog;
|
||||||
use Slic3r::GUI::Plater::ObjectPreviewDialog;
|
use Slic3r::GUI::Plater::ObjectPreviewDialog;
|
||||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||||
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||||
|
|
|
@ -77,7 +77,9 @@ sub BUILD {
|
||||||
$self->sizer->Add($grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5);
|
$self->sizer->Add($grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5);
|
||||||
|
|
||||||
foreach my $line (@{$self->lines}) {
|
foreach my $line (@{$self->lines}) {
|
||||||
if ($line->{widget}) {
|
if ($line->{sizer}) {
|
||||||
|
$self->sizer->Add($line->{sizer}, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
|
||||||
|
} elsif ($line->{widget}) {
|
||||||
my $window = $line->{widget}->GetWindow($self->parent);
|
my $window = $line->{widget}->GetWindow($self->parent);
|
||||||
$self->sizer->Add($window, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
|
$self->sizer->Add($window, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -86,7 +86,7 @@ sub new {
|
||||||
$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;
|
||||||
$self->{htoolbar}->AddTool(TB_VIEW, "View", Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_VIEW, "View/Cut…", Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG), '');
|
||||||
} else {
|
} else {
|
||||||
my %tbar_buttons = (
|
my %tbar_buttons = (
|
||||||
|
@ -101,7 +101,7 @@ sub new {
|
||||||
rotate => "Rotate…",
|
rotate => "Rotate…",
|
||||||
changescale => "Scale…",
|
changescale => "Scale…",
|
||||||
split => "Split",
|
split => "Split",
|
||||||
view => "View",
|
view => "View/Cut…",
|
||||||
settings => "Settings…",
|
settings => "Settings…",
|
||||||
);
|
);
|
||||||
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
|
@ -173,7 +173,7 @@ sub new {
|
||||||
EVT_TOOL($self, TB_ROTATE, sub { $_[0]->rotate(undef) });
|
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_preview_dialog });
|
EVT_TOOL($self, TB_VIEW, sub { $_[0]->object_cut_dialog });
|
||||||
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
|
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
|
||||||
} else {
|
} else {
|
||||||
EVT_BUTTON($self, $self->{btn_add}, \&add);
|
EVT_BUTTON($self, $self->{btn_add}, \&add);
|
||||||
|
@ -187,7 +187,7 @@ sub new {
|
||||||
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_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_preview_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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,7 +1149,7 @@ sub list_item_activated {
|
||||||
$self->object_preview_dialog($obj_idx);
|
$self->object_preview_dialog($obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub object_preview_dialog {
|
sub object_cut_dialog {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($obj_idx) = @_;
|
my ($obj_idx) = @_;
|
||||||
|
|
||||||
|
@ -1162,11 +1162,16 @@ sub object_preview_dialog {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dlg = Slic3r::GUI::Plater::ObjectPreviewDialog->new($self,
|
my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self,
|
||||||
object => $self->{objects}[$obj_idx],
|
object => $self->{objects}[$obj_idx],
|
||||||
model_object => $self->{model}->objects->[$obj_idx],
|
model_object => $self->{model}->objects->[$obj_idx],
|
||||||
);
|
);
|
||||||
$dlg->ShowModal;
|
$dlg->ShowModal;
|
||||||
|
|
||||||
|
if (my @new_objects = $dlg->NewModelObjects) {
|
||||||
|
$self->remove($obj_idx);
|
||||||
|
$self->load_model_objects(@new_objects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub object_settings_dialog {
|
sub object_settings_dialog {
|
||||||
|
|
126
lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
Normal file
126
lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package Slic3r::GUI::Plater::ObjectCutDialog;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use Wx qw(:dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
||||||
|
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||||
|
use base 'Wx::Dialog';
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $class = shift;
|
||||||
|
my ($parent, %params) = @_;
|
||||||
|
my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||||
|
$self->{model_object_idx} = $params{model_object_idx};
|
||||||
|
$self->{model_object} = $params{model_object};
|
||||||
|
$self->{new_model_objects} = [];
|
||||||
|
|
||||||
|
# cut options
|
||||||
|
$self->{cut_options} = {
|
||||||
|
z => 0,
|
||||||
|
keep_upper => 1,
|
||||||
|
keep_lower => 1,
|
||||||
|
};
|
||||||
|
my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
{
|
||||||
|
$self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize);
|
||||||
|
$cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10);
|
||||||
|
}
|
||||||
|
my $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||||
|
parent => $self,
|
||||||
|
title => 'Cut',
|
||||||
|
options => [
|
||||||
|
{
|
||||||
|
opt_key => 'z',
|
||||||
|
type => 'f',
|
||||||
|
label => 'Z',
|
||||||
|
default => $self->{cut_options}{z},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opt_key => 'keep_upper',
|
||||||
|
type => 'bool',
|
||||||
|
label => 'Upper part',
|
||||||
|
default => $self->{cut_options}{keep_upper},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opt_key => 'keep_lower',
|
||||||
|
type => 'bool',
|
||||||
|
label => 'Lower part',
|
||||||
|
default => $self->{cut_options}{keep_lower},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
lines => [
|
||||||
|
{
|
||||||
|
label => 'Z',
|
||||||
|
options => [qw(z)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label => 'Keep',
|
||||||
|
options => [qw(keep_upper keep_lower)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sizer => $cut_button_sizer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
on_change => sub {
|
||||||
|
my ($opt_key, $value) = @_;
|
||||||
|
|
||||||
|
$self->{cut_options}{$opt_key} = $value;
|
||||||
|
if ($opt_key eq 'z') {
|
||||||
|
if ($self->{canvas}) {
|
||||||
|
$self->{canvas}->SetCuttingPlane($value);
|
||||||
|
$self->{canvas}->Render;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label_width => 50,
|
||||||
|
);
|
||||||
|
|
||||||
|
# left pane with tree
|
||||||
|
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
$left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||||
|
|
||||||
|
# right pane with preview canvas
|
||||||
|
my $canvas;
|
||||||
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
|
$canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object});
|
||||||
|
$canvas->SetSize([500,500]);
|
||||||
|
$canvas->SetMinSize($canvas->GetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
|
$self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||||
|
$self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas;
|
||||||
|
|
||||||
|
$self->SetSizer($self->{sizer});
|
||||||
|
$self->SetMinSize($self->GetSize);
|
||||||
|
$self->{sizer}->SetSizeHints($self);
|
||||||
|
|
||||||
|
# needed to actually free memory
|
||||||
|
EVT_CLOSE($self, sub {
|
||||||
|
$self->EndModal(wxID_OK);
|
||||||
|
$self->Destroy;
|
||||||
|
});
|
||||||
|
|
||||||
|
EVT_BUTTON($self, $self->{btn_cut}, sub { $self->perform_cut });
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub perform_cut {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my ($upper_object, $lower_object) = $self->{model_object}->cut($self->{cut_options}{z});
|
||||||
|
$self->{new_model_objects} = [];
|
||||||
|
push @{$self->{new_model_objects}}, $upper_object
|
||||||
|
if $self->{cut_options}{keep_upper} && defined $upper_object;
|
||||||
|
push @{$self->{new_model_objects}}, $lower_object
|
||||||
|
if $self->{cut_options}{keep_lower} && defined $lower_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub NewModelObjects {
|
||||||
|
my ($self) = @_;
|
||||||
|
return @{ $self->{new_model_objects} };
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -14,7 +14,9 @@ use Wx::GLCanvas qw(:all);
|
||||||
__PACKAGE__->mk_accessors( qw(quat dirty init mview_init
|
__PACKAGE__->mk_accessors( qw(quat dirty init mview_init
|
||||||
object_bounding_box object_shift
|
object_bounding_box object_shift
|
||||||
volumes initpos
|
volumes initpos
|
||||||
sphi stheta) );
|
sphi stheta
|
||||||
|
cutting_plane_z
|
||||||
|
) );
|
||||||
|
|
||||||
use constant TRACKBALLSIZE => 0.8;
|
use constant TRACKBALLSIZE => 0.8;
|
||||||
use constant TURNTABLE_MODE => 1;
|
use constant TURNTABLE_MODE => 1;
|
||||||
|
@ -114,6 +116,11 @@ sub load_object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub SetCuttingPlane {
|
||||||
|
my ($self, $z) = @_;
|
||||||
|
$self->cutting_plane_z($z);
|
||||||
|
}
|
||||||
|
|
||||||
# Given an axis and angle, compute quaternion.
|
# Given an axis and angle, compute quaternion.
|
||||||
sub axis_to_quat {
|
sub axis_to_quat {
|
||||||
my ($ax, $phi) = @_;
|
my ($ax, $phi) = @_;
|
||||||
|
@ -440,6 +447,23 @@ sub Render {
|
||||||
glVertex3f($axis_len, $y, $ground_z);
|
glVertex3f($axis_len, $y, $ground_z);
|
||||||
}
|
}
|
||||||
glEnd();
|
glEnd();
|
||||||
|
|
||||||
|
# draw cutting plane
|
||||||
|
if (defined $self->cutting_plane_z) {
|
||||||
|
my $plane_z = $z0 + $self->cutting_plane_z;
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glColor4f(1, 0.8, 0.8, 0.5);
|
||||||
|
glVertex3f(-$axis_len, -$axis_len, $plane_z);
|
||||||
|
glVertex3f($axis_len, -$axis_len, $plane_z);
|
||||||
|
glVertex3f($axis_len, $axis_len, $plane_z);
|
||||||
|
glVertex3f(-$axis_len, $axis_len, $plane_z);
|
||||||
|
glEnd();
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
|
@ -562,6 +562,53 @@ sub print_info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cut {
|
||||||
|
my ($self, $z) = @_;
|
||||||
|
|
||||||
|
# clone this one
|
||||||
|
my $upper = Slic3r::Model::Object->new(
|
||||||
|
input_file => $self->input_file,
|
||||||
|
model => $self->model,
|
||||||
|
config => $self->config->clone,
|
||||||
|
layer_height_ranges => $self->layer_height_ranges,
|
||||||
|
);
|
||||||
|
my $lower = Slic3r::Model::Object->new(
|
||||||
|
input_file => $self->input_file,
|
||||||
|
model => $self->model,
|
||||||
|
config => $self->config->clone,
|
||||||
|
layer_height_ranges => $self->layer_height_ranges,
|
||||||
|
);
|
||||||
|
$upper->add_instance(offset => Slic3r::Point->new(0,0));
|
||||||
|
$lower->add_instance(offset => Slic3r::Point->new(0,0));
|
||||||
|
|
||||||
|
foreach my $volume (@{$self->volumes}) {
|
||||||
|
if ($volume->modifier) {
|
||||||
|
# don't cut modifiers
|
||||||
|
$upper->add_volume($volume);
|
||||||
|
$lower->add_volume($volume);
|
||||||
|
} else {
|
||||||
|
my $upper_mesh = Slic3r::TriangleMesh->new;
|
||||||
|
my $lower_mesh = Slic3r::TriangleMesh->new;
|
||||||
|
$volume->mesh->cut($z, $upper_mesh, $lower_mesh);
|
||||||
|
$upper_mesh->repair;
|
||||||
|
$lower_mesh->repair;
|
||||||
|
|
||||||
|
$upper->add_volume(
|
||||||
|
material_id => $volume->material_id,
|
||||||
|
mesh => $upper_mesh,
|
||||||
|
modifier => $volume->modifier,
|
||||||
|
);
|
||||||
|
$lower->add_volume(
|
||||||
|
material_id => $volume->material_id,
|
||||||
|
mesh => $lower_mesh,
|
||||||
|
modifier => $volume->modifier,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($upper, $lower);
|
||||||
|
}
|
||||||
|
|
||||||
package Slic3r::Model::Volume;
|
package Slic3r::Model::Volume;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue