diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index bee7e54f5..842ff4407 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -13,6 +13,7 @@ use Slic3r::GUI::Notifier; use Slic3r::GUI::Plater; use Slic3r::GUI::Plater::2D; use Slic3r::GUI::Plater::2DToolpaths; +use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; use Slic3r::GUI::Plater::ObjectPreviewDialog; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9b3f92eea..600008e32 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -8,7 +8,7 @@ use List::Util qw(sum first); 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); + :panel :sizer :toolbar :window wxTheApp :notebook); 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); @@ -65,7 +65,12 @@ sub new { } }); - $self->{canvas} = Slic3r::GUI::Plater::2D->new($self, [335,335], $self->{objects}, $self->{model}, $self->{config}); + # Initialize preview notebook + $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM); + + # Initialize 2D preview canvas + $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); + $self->{preview_notebook}->AddPage($self->{canvas}, '2D'); $self->{canvas}->on_select_object(sub { my ($obj_idx) = @_; $self->select_object($obj_idx); @@ -87,6 +92,12 @@ sub new { $self->update; }); + # Initialize 3D preview canvas + if ($Slic3r::GUI::have_OpenGL) { + $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); + $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); + } + # toolbar for object manipulation if (!&Wx::wxMSW) { Wx::ToolTip::Enable(1); @@ -103,8 +114,8 @@ sub new { $self->{htoolbar}->AddTool(TB_45CW, "45° cw", 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_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_VIEW, "Cut…", Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; - $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), ''); } else { my %tbar_buttons = ( @@ -337,7 +348,7 @@ sub new { $right_sizer->Add($object_info_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); - $hsizer->Add($self->{canvas}, 1, wxEXPAND | wxTOP, 1); + $hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1); $hsizer->Add($right_sizer, 0, wxEXPAND | wxBOTTOM, 0); my $sizer = Wx::BoxSizer->new(wxVERTICAL); @@ -558,7 +569,7 @@ sub increase { if ($Slic3r::GUI::Settings->{_}{autocenter}) { $self->arrange; } else { - $self->{canvas}->Refresh; + $self->update; } } @@ -580,7 +591,6 @@ sub decrease { $self->{list}->Select($obj_idx, 1); } $self->update; - $self->{canvas}->Refresh; } sub rotate { @@ -1151,6 +1161,7 @@ sub update { } $self->{canvas}->Refresh; + $self->{canvas3D}->update if $self->{canvas3D}; } sub on_extruders_change { @@ -1466,7 +1477,7 @@ sub object_menu { $frame->_append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub { $self->split_object; }); - $frame->_append_menu_item($menu, "View/Cut…", 'Open the 3D cutting tool', sub { + $frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub { $self->object_cut_dialog; }); $menu->AppendSeparator(); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm new file mode 100644 index 000000000..adc89b2f8 --- /dev/null +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -0,0 +1,45 @@ +package Slic3r::GUI::Plater::3D; +use strict; +use warnings; +use utf8; + +use List::Util qw(); +use Slic3r::Geometry qw(); +use Slic3r::Geometry::Clipper qw(); +use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); +use Wx::Event qw(); +use base 'Slic3r::GUI::PreviewCanvas'; + +sub new { + my $class = shift; + my ($parent, $objects, $model, $config) = @_; + + my $self = $class->SUPER::new($parent); + + $self->{objects} = $objects; + $self->{model} = $model; + $self->{config} = $config; + $self->{on_select_object} = sub {}; + $self->{on_double_click} = sub {}; + $self->{on_right_click} = sub {}; + $self->{on_instance_moved} = sub {}; + + + + return $self; +} + +sub update { + my ($self) = @_; + + $self->reset_objects; + return if $self->{model}->objects_count == 0; + + $self->set_bounding_box($self->{model}->bounding_box); + + foreach my $model_object (@{$self->{model}->objects}) { + $self->load_object($model_object, 1); + } +} + +1; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 4e2d13561..bacd2fc8b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -86,7 +86,8 @@ sub new { # right pane with preview canvas my $canvas; if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object}); + $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self); + $canvas->load_object($self->{model_object}); $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index c38c218c3..1162bffb6 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -68,7 +68,8 @@ sub new { # right pane with preview canvas my $canvas; if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object}); + $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self); + $canvas->load_object($self->{model_object}); $canvas->SetSize([500,500]); } diff --git a/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm b/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm index 57edcedb4..71cf7cb5b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPreviewDialog.pm @@ -13,8 +13,11 @@ sub new { my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); $self->{model_object} = $params{model_object}; + my $canvas = Slic3r::GUI::PreviewCanvas->new($self); + $canvas->load_object($self->{model_object}); + my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add(Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object}), 1, wxEXPAND, 0); + $sizer->Add($canvas, 1, wxEXPAND, 0); $self->SetSizer($sizer); $self->SetMinSize($self->GetSize); diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm index c0638ddb6..297d6d643 100644 --- a/lib/Slic3r/GUI/PreviewCanvas.pm +++ b/lib/Slic3r/GUI/PreviewCanvas.pm @@ -32,23 +32,25 @@ use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; } sub new { - my ($class, $parent, $object) = @_; + my ($class, $parent) = @_; my $self = $class->SUPER::new($parent); $self->quat((0, 0, 0, 1)); $self->sphi(45); $self->stheta(-45); - - $self->load_object($object); + + $self->reset_objects; EVT_PAINT($self, sub { my $dc = Wx::PaintDC->new($self); + return if !@{$self->volumes}; $self->Render($dc); }); EVT_SIZE($self, sub { $self->dirty(1) }); EVT_IDLE($self, sub { return unless $self->dirty; return if !$self->IsShownOnScreen; + return if !@{$self->volumes}; $self->Resize( $self->GetSizeWH ); $self->Refresh; }); @@ -79,51 +81,69 @@ sub new { return $self; } -sub load_object { - my ($self, $object) = @_; +sub reset_objects { + my ($self) = @_; + + $self->volumes([]); + $self->dirty(1); +} + +# this method accepts a Slic3r::BoudingBox3f object +sub set_bounding_box { + my ($self, $bb) = @_; - my $bb = $object->instance_bounding_box; my $center = $bb->center; $self->object_shift(Slic3r::Pointf3->new(-$center->x, -$center->y, -$bb->z_min)); #,, $bb->translate(@{ $self->object_shift }); $self->object_bounding_box($bb); + $self->dirty(1); +} + +sub load_object { + my ($self, $object, $all_instances) = @_; + + $self->set_bounding_box($object->instance_bounding_box) + if !$all_instances; # group mesh(es) by material my @materials = (); - $self->volumes([]); # sort volumes: non-modifiers first my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes}; foreach my $volume (@volumes) { - my $mesh = $volume->mesh->clone; - $object->instances->[0]->transform_mesh($mesh); - $mesh->translate(@{ $self->object_shift }); + my @instances = $all_instances ? @{$object->instances} : $object->instances->[0]; + foreach my $instance (@instances) { + my $mesh = $volume->mesh->clone; + $instance->transform_mesh($mesh); + $mesh->translate(@{ $self->object_shift }); - my $material_id = $volume->material_id // '_'; - my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; - if (!defined $color_idx) { - push @materials, $material_id; - $color_idx = $#materials; - } + my $material_id = $volume->material_id // '_'; + my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; + if (!defined $color_idx) { + push @materials, $material_id; + $color_idx = $#materials; + } - my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; - push @$color, $volume->modifier ? 0.5 : 1; - push @{$self->volumes}, my $v = { - mesh => $mesh, - color => $color, - }; + my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; + push @$color, $volume->modifier ? 0.5 : 1; + push @{$self->volumes}, my $v = { + mesh => $mesh, + color => $color, + }; - { - my $vertices = $mesh->vertices; - my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; - $v->{verts} = OpenGL::Array->new_list(GL_FLOAT, @verts); - } + { + my $vertices = $mesh->vertices; + my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; + $v->{verts} = OpenGL::Array->new_list(GL_FLOAT, @verts); + } - { - my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; - $v->{norms} = OpenGL::Array->new_list(GL_FLOAT, @norms); + { + my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; + $v->{norms} = OpenGL::Array->new_list(GL_FLOAT, @norms); + } } } + $self->dirty(1); } sub SetCuttingPlane { diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 66f83930b..105c72ecd 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -446,6 +446,8 @@ sub bounding_box { sub instance_bounding_box { my ($self, $instance_idx) = @_; + $instance_idx //= 0; + my $mesh = $self->raw_mesh; $self->instances->[$instance_idx]->transform_mesh($mesh); return $mesh->bounding_box; diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 680102405..4847da7fb 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -31,8 +31,8 @@ my %opt = (); # make sure all objects have at least one defined instance $model->add_default_instances; - $Slic3r::ViewMesh::object = $model->objects->[0]; my $app = Slic3r::ViewMesh->new; + $app->{canvas}->load_object($model->objects->[0]); $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; $app->MainLoop; } @@ -55,16 +55,16 @@ package Slic3r::ViewMesh; use Wx qw(:sizer); use base qw(Wx::App); -our $object; - sub OnInit { my $self = shift; my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]); my $panel = Wx::Panel->new($frame, -1); + $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($panel); + my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{canvas} = Slic3r::GUI::PreviewCanvas->new($panel, $object), 1, wxEXPAND, 0); + $sizer->Add($self->{canvas}, 1, wxEXPAND, 0); $panel->SetSizer($sizer); $sizer->SetSizeHints($panel); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 1038b804e..aaf5c1e75 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -18,6 +18,8 @@ %code%{ RETVAL = THIS->add_object(*other); %}; void delete_object(size_t idx); void clear_objects(); + size_t objects_count() + %code%{ RETVAL = THIS->objects.size(); %}; Ref get_material(t_model_material_id material_id) %code%{