From b2562a9f3122279a419628227ac85c373766fd87 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 19:55:43 -0600 Subject: [PATCH 01/19] Permit sliding modifer meshes around in the Settings dialog. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 104 +++++++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 4882233a67..05ed657a0d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -22,6 +22,18 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $object = $self->{model_object} = $params{model_object}; + + # Save state for sliders. + $self->{move_options} = { + x => 0, + y => 0, + z => 0, + }; + $self->{last_coords} = { + x => 0, + y => 0, + z => 0, + }; # create TreeCtrl my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], @@ -61,12 +73,61 @@ sub new { $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; }); my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); + + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Move', + on_change => sub { + my ($opt_id) = @_; + # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider + # genates tens of events for a single value change. + # Only trigger the recalculation if the value changes + # or a live preview was activated and the mesh cut is not valid yet. + if ($self->{move_options}{$opt_id} != $optgroup->get_value($opt_id)) { + $self->{move_options}{$opt_id} = $optgroup->get_value($opt_id); + wxTheApp->CallAfter(sub { + $self->_update; + }); + } + }, + label_width => 20, + ); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'x', + type => 'slider', + label => 'X', + default => 0, + min => -($self->{model_object}->bounding_box->size->x)*4, + max => $self->{model_object}->bounding_box->size->x*4, + full_width => 1, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'y', + type => 'slider', + label => 'Y', + default => 0, + min => -($self->{model_object}->bounding_box->size->y)*4, + max => $self->{model_object}->bounding_box->size->y*4, + full_width => 1, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z', + type => 'slider', + label => 'Z', + default => 0, + min => -($self->{model_object}->bounding_box->size->z)*4, + max => $self->{model_object}->bounding_box->size->z*4, + full_width => 1, + )); + # left pane with tree my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); $left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0); + $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); # right pane with preview canvas my $canvas; @@ -179,6 +240,21 @@ sub selection_changed { $self->{btn_delete}->Disable; $self->{settings_panel}->disable; $self->{settings_panel}->set_config(undef); + + # reset move sliders + $self->{optgroup}->set_value("x", 0); + $self->{optgroup}->set_value("y", 0); + $self->{optgroup}->set_value("z", 0); + $self->{move_options} = { + x => 0, + y => 0, + z => 0, + }; + $self->{last_coords} = { + x => 0, + y => 0, + z => 0, + }; if (my $itemData = $self->get_selection) { my ($config, @opt_keys); @@ -254,17 +330,17 @@ sub on_btn_load { sub on_btn_delete { my ($self) = @_; - + my $itemData = $self->get_selection; if ($itemData && $itemData->{type} eq 'volume') { my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - + # if user is deleting the last solid part, throw error if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) { Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object."); return; } - + $self->{model_object}->delete_volume($itemData->{volume_id}); $self->{parts_changed} = 1; } @@ -310,4 +386,26 @@ sub PartSettingsChanged { return $self->{part_settings_changed}; } +sub _update { + my ($self) = @_; + my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z}); + my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z}); + + my $itemData = $self->get_selection; + if ($itemData && $itemData->{type} eq 'volume') { + my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z); + my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; + $volume->mesh->translate(@{$d}); + $self->{last_coords}{x} = $m_x; + $self->{last_coords}{y} = $m_y; + $self->{last_coords}{z} = $m_z; + } + + my @objects = (); + push @objects, $self->{model_object}; + $self->{canvas}->reset_objects; + $self->{canvas}->load_object($_, undef, [0]) for @objects; + $self->{canvas}->Render; +} + 1; From 89dab6a19dbebb9885d8f456d98789db481d2ed4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 19:57:35 -0600 Subject: [PATCH 02/19] Anonymous object supports. Initial implementation provides for a box of arbitrary size. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 110 ++++++++++++++++++++ lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 29 +++++- lib/Slic3r/Test.pm | 8 ++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm new file mode 100644 index 0000000000..e0de073098 --- /dev/null +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -0,0 +1,110 @@ +# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings. +# + +package Slic3r::GUI::Plater::LambdaObjectDialog; +use strict; +use warnings; +use utf8; + +use Slic3r::Geometry qw(PI X); +use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB); +use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT); +use Scalar::Util qw(looks_like_number); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + # Note whether the window was already closed, so a pending update is not executed. + $self->{already_closed} = 0; + $self->{object_parameters} = { + type => "box", + dim => [1, 1, 1], + }; + + $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); + my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + my $buttons = $self->CreateStdDialogButtonSizer(wxOK); + EVT_BUTTON($self, wxID_OK, sub { + # validate user input + return if !$self->CanClose; + + $self->EndModal(wxID_OK); + $self->Destroy; + }); + $button_sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + my @options = ("box"); + $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); + + my $sbox = Wx::StaticBox->new($self, -1, '', wxDefaultPosition, wxDefaultSize, 0, 'sbox'); + my $cube_dim_sizer = Wx::StaticBoxSizer->new($sbox, wxVERTICAL); + { + my $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, 2, "X", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_x} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_x}); + + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, -1, "Y", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_y} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_y}); + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, -1, "Z", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_z} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_z}); + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + EVT_TEXT($self, 2, sub { + if (!looks_like_number($self->{dim_x}->GetValue)) { + return 0; + } + if (!looks_like_number($self->{dim_y}->GetValue)) { + return 0; + } + if (!looks_like_number($self->{dim_z}->GetValue)) { + return 0; + } + if ($self->{dim_x}->GetValue() > 0) { + $self->{object_parameters}->{dim}[0] = $self->{dim_x}->GetValue; + } + if ($self->{dim_y}->GetValue() > 0) { + $self->{object_parameters}->{dim}[1] = $self->{dim_y}->GetValue; + } + if ($self->{dim_z}->GetValue() > 0) { + $self->{object_parameters}->{dim}[2] = $self->{dim_z}->GetValue; + } + }); + } + EVT_COMBOBOX($self, 1, sub{ + $self->{object_parameters}->{type} = $self->{type}->GetValue(); + }); + $self->{sizer}->Add($self->{type}, 0, wxEXPAND, 3); + $self->{sizer}->Add($cube_dim_sizer, 0, wxEXPAND, 10); + $self->{sizer}->Add($button_sizer); + $self->SetSizer($self->{sizer}); + $self->{sizer}->Fit($self); + $self->{sizer}->SetSizeHints($self); + + + return $self; +} +sub CanClose { + return 1; +} +sub ObjectParameter { + my ($self) = @_; + return $self->{object_parameters}; +} +1; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 05ed657a0d..c4ee24f0b2 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -53,10 +53,12 @@ sub new { # buttons $self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); if ($Slic3r::GUI::have_button_icons) { $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); $self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG)); } @@ -64,9 +66,11 @@ sub new { my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); $buttons_sizer->Add($self->{btn_load_part}, 0); $buttons_sizer->Add($self->{btn_load_modifier}, 0); + $buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0); $buttons_sizer->Add($self->{btn_delete}, 0); $self->{btn_load_part}->SetFont($Slic3r::GUI::small_font); $self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font); + $self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font); $self->{btn_delete}->SetFont($Slic3r::GUI::small_font); # part settings panel @@ -145,7 +149,7 @@ sub new { $canvas->load_object($self->{model_object}, undef, [0]); $canvas->set_auto_bed_shape; - $canvas->SetSize([500,500]); + $canvas->SetSize([500,700]); $canvas->zoom_to_volumes; } @@ -168,6 +172,7 @@ sub new { }); 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; @@ -328,6 +333,28 @@ sub on_btn_load { $self->_parts_changed; } +sub on_btn_lambda { + my ($self, $is_modifier) = @_; + + my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); + $dlg->ShowModal(); + my $params = $dlg->ObjectParameter; + my $name = "lambda-".$params->{"type"}; + + my $new_volume = $self->{model_object}->add_volume(mesh => Slic3r::Test::mesh($params->{"type"}, dim=>$params->{"dim"}), material_id=>"generic"); + $new_volume->set_modifier($is_modifier); + $new_volume->set_name($name); + + # apply the same translation we applied to the object + $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); + + # set a default extruder value, since user can't add it manually + $new_volume->config->set_ifndef('extruder', 0); + + $self->{parts_changed} = 1; + $self->_parts_changed; +} + sub on_btn_delete { my ($self) = @_; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 1d8431b14e..4b4d35520d 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -27,6 +27,14 @@ sub mesh { $facets = [ [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], ], + } elsif ($name eq 'box') { + my ($x, $y, $z) = @{ $params{"dim"} }; + $vertices = [ + [$x,$y,0], [$x,0,0], [0,0,0], [0,$y,0], [$x,$y,$z], [0,$y,$z], [0,0,$z], [$x,0,$z], + ]; + $facets = [ + [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], + ], } elsif ($name eq 'cube_with_hole') { $vertices = [ [0,0,0],[0,0,10],[0,20,0],[0,20,10],[20,0,0],[20,0,10],[5,5,0],[15,5,0],[5,15,0],[20,20,0],[15,15,0],[20,20,10],[5,5,10],[5,15,10],[15,5,10],[15,15,10] From a41fcb90666d715a05c368df1349255a7457e4b4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 22:13:55 -0600 Subject: [PATCH 03/19] Update main plater view after moving mesh --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index c4ee24f0b2..ee812a5a6d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -428,6 +428,7 @@ sub _update { $self->{last_coords}{z} = $m_z; } + $self->{parts_changed} = 1; my @objects = (); push @objects, $self->{model_object}; $self->{canvas}->reset_objects; From 9abd5eecee8ac835d154e1d46826fac41365a733 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 23:03:51 -0600 Subject: [PATCH 04/19] Added a cancel button, and rewrote initial option code to reuse OptionGroup. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 116 ++++++++++---------- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 6 +- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index e0de073098..49d1a1888c 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -25,7 +25,10 @@ sub new { $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); + my $button_ok = $self->CreateStdDialogButtonSizer(wxOK); + my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL); + $button_sizer->Add($button_ok); + $button_sizer->Add($button_cancel); EVT_BUTTON($self, wxID_OK, sub { # validate user input return if !$self->CanClose; @@ -33,66 +36,67 @@ sub new { $self->EndModal(wxID_OK); $self->Destroy; }); - $button_sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - + EVT_BUTTON($self, wxID_CANCEL, sub { + # validate user input + return if !$self->CanClose; + + $self->EndModal(wxID_CANCEL); + $self->Destroy; + }); + + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Generic...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { + if (!looks_like_number($optgroup->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{dim}[$opt_id] = $optgroup->get_value($opt_id); + }, + label_width => 100, + ); my @options = ("box"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - - my $sbox = Wx::StaticBox->new($self, -1, '', wxDefaultPosition, wxDefaultSize, 0, 'sbox'); - my $cube_dim_sizer = Wx::StaticBoxSizer->new($sbox, wxVERTICAL); - { - my $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, 2, "X", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_x} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_x}); - - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, -1, "Y", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_y} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_y}); - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, -1, "Z", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_z} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_z}); - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - EVT_TEXT($self, 2, sub { - if (!looks_like_number($self->{dim_x}->GetValue)) { - return 0; - } - if (!looks_like_number($self->{dim_y}->GetValue)) { - return 0; - } - if (!looks_like_number($self->{dim_z}->GetValue)) { - return 0; - } - if ($self->{dim_x}->GetValue() > 0) { - $self->{object_parameters}->{dim}[0] = $self->{dim_x}->GetValue; - } - if ($self->{dim_y}->GetValue() > 0) { - $self->{object_parameters}->{dim}[1] = $self->{dim_y}->GetValue; - } - if ($self->{dim_z}->GetValue() > 0) { - $self->{object_parameters}->{dim}[2] = $self->{dim_z}->GetValue; - } - }); - } + #my @types = ("box"); + #$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + # opt_id => 'type', + # label => 'Type', + # type => 'select', + # values => \@types, + # default => 'box', + #)); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 0, + label => 'L', + type => 'f', + default => '1', + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 1, + label => 'W', + type => 'f', + default => '1', + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 2, + label => 'H', + type => 'f', + default => '1', + )); EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); }); - $self->{sizer}->Add($self->{type}, 0, wxEXPAND, 3); - $self->{sizer}->Add($cube_dim_sizer, 0, wxEXPAND, 10); - $self->{sizer}->Add($button_sizer); + + + $optgroup->sizer->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->SetSizer($self->{sizer}); $self->{sizer}->Fit($self); $self->{sizer}->SetSizeHints($self); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index ee812a5a6d..1fb0e3bd93 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -7,7 +7,7 @@ use warnings; use utf8; use File::Basename qw(basename); -use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG +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 base 'Wx::Panel'; @@ -337,7 +337,9 @@ sub on_btn_lambda { my ($self, $is_modifier) = @_; my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); - $dlg->ShowModal(); + if ($dlg->ShowModal() == wxID_CANCEL) { + return; + } my $params = $dlg->ObjectParameter; my $name = "lambda-".$params->{"type"}; From cc9bae90a48b52a62b88c74ff09c41a812a86db4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 23:16:50 -0600 Subject: [PATCH 05/19] Removed commented code. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 49d1a1888c..d6d4c3b093 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -62,14 +62,7 @@ sub new { ); my @options = ("box"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - #my @types = ("box"); - #$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - # opt_id => 'type', - # label => 'Type', - # type => 'select', - # values => \@types, - # default => 'box', - #)); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 0, label => 'L', From 3a70a448abe56aa1b473b928887af61ee483c9e6 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 16:05:28 -0600 Subject: [PATCH 06/19] Use LambdaObjectDialog in GUI.pm to avoid crash on Windows. --- lib/Slic3r/GUI.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c95e9afbbe..a298451892 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -24,6 +24,7 @@ use Slic3r::GUI::Plater::3DPreview; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; +use Slic3r::GUI::Plater::LambdaObjectDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::Preferences; use Slic3r::GUI::ProgressStatusBar; From efeb49e3a7831930a83d406850f4988690f3eb00 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 16:06:45 -0600 Subject: [PATCH 07/19] Added new constructor to TriangleMesh that accepts vectors of Points and make_cube function to make a new simple cube mesh. --- xs/src/libslic3r/TriangleMesh.cpp | 61 +++++++++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 3 ++ xs/xsp/TriangleMesh.xsp | 2 + 3 files changed, 66 insertions(+) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c1d7a9edf6..b43b1f0d6d 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -24,6 +24,48 @@ TriangleMesh::TriangleMesh() stl_initialize(&this->stl); } +TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets ) + : repaired(false) +{ + stl_initialize(&this->stl); + stl_file &stl = this->stl; + stl.error = 0; + stl.stats.type = inmemory; + + // count facets and allocate memory + stl.stats.number_of_facets = facets.size(); + stl.stats.original_num_facets = stl.stats.number_of_facets; + stl_allocate(&stl); + + for (int i = 0; i < stl.stats.number_of_facets; i++) { + stl_facet facet; + facet.normal.x = 0; + facet.normal.y = 0; + facet.normal.z = 0; + + const Pointf3& ref_f1 = points[facets[i].x]; + facet.vertex[0].x = ref_f1.x; + facet.vertex[0].y = ref_f1.y; + facet.vertex[0].z = ref_f1.z; + + const Pointf3& ref_f2 = points[facets[i].y]; + facet.vertex[1].x = ref_f2.x; + facet.vertex[1].y = ref_f2.y; + facet.vertex[1].z = ref_f2.z; + + const Pointf3& ref_f3 = points[facets[i].z]; + facet.vertex[2].x = ref_f3.x; + facet.vertex[2].y = ref_f3.y; + facet.vertex[2].z = ref_f3.z; + + facet.extra[0] = 0; + facet.extra[1] = 0; + + stl.facet_start[i] = facet; + } + stl_get_size(&stl); +} + TriangleMesh::TriangleMesh(const TriangleMesh &other) : stl(other.stl), repaired(other.repaired) { @@ -1120,4 +1162,23 @@ TriangleMeshSlicer::~TriangleMeshSlicer() if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } +TriangleMesh make_cube(double x, double y, double z) { + Pointf3 pv[8] = { + Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), + Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z), + Pointf3(0, 0, z), Pointf3(x, 0, z) + }; + Point3 fv[12] = { + Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6), + Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1), + Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5), + Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5) + }; + + std::vector facets(&fv[0], &fv[0]+12); + Pointf3s vertices(&pv[0], &pv[0]+8); + + TriangleMesh mesh(vertices ,facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 2c538c3f52..5a393738af 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -21,6 +21,7 @@ class TriangleMesh { public: TriangleMesh(); + TriangleMesh(const Pointf3s &points, const std::vector &facets); TriangleMesh(const TriangleMesh &other); TriangleMesh& operator= (TriangleMesh other); void swap(TriangleMesh &other); @@ -112,6 +113,8 @@ class TriangleMeshSlicer void make_expolygons(std::vector &lines, ExPolygons* slices) const; }; +TriangleMesh make_cube(double x, double y, double z); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3b8f1022c4..5f67b9561e 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -39,6 +39,8 @@ %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); + Clone cube(double x, double y, double z) + %code{% RETVAL = make_cube(x, y, z); %}; %{ void From 2171d6ab969d3ba807de99b16a8660fc1c595b5e Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 19:15:27 -0600 Subject: [PATCH 08/19] Added prototype make_cylinder() --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 13 ++++--- xs/src/libslic3r/TriangleMesh.cpp | 40 ++++++++++++++++++++- xs/src/libslic3r/TriangleMesh.hpp | 3 ++ xs/xsp/TriangleMesh.xsp | 2 ++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index d6d4c3b093..2f815c27eb 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -60,7 +60,7 @@ sub new { }, label_width => 100, ); - my @options = ("box"); + my @options = ("box", "cylinder"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 1fb0e3bd93..5049d7dda6 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -342,14 +342,19 @@ sub on_btn_lambda { } my $params = $dlg->ObjectParameter; my $name = "lambda-".$params->{"type"}; + my $mesh = Slic3r::TriangleMesh->new(); - my $new_volume = $self->{model_object}->add_volume(mesh => Slic3r::Test::mesh($params->{"type"}, dim=>$params->{"dim"}), material_id=>"generic"); + #TODO support non-boxes + if ($name eq "box") { + $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); + } elsif ($name eq "cylinder") { + $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + } + + my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); - # apply the same translation we applied to the object - $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); - # set a default extruder value, since user can't add it manually $new_volume->config->set_ifndef('extruder', 0); diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index b43b1f0d6d..b71f29215a 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1161,7 +1161,7 @@ TriangleMeshSlicer::~TriangleMeshSlicer() { if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } - +// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. TriangleMesh make_cube(double x, double y, double z) { Pointf3 pv[8] = { Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), @@ -1181,4 +1181,42 @@ TriangleMesh make_cube(double x, double y, double z) { TriangleMesh mesh(vertices ,facets); return mesh; } + +// Generate the mesh for a cylinder and return it, using +// the generated angle to calculate the top mesh triangles. +TriangleMesh make_cylinder(double r, double h, double fa) { + Pointf3s vertices; + std::vector facets; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.push_back(Pointf3(0.0, 0.0, 0.0)); + vertices.push_back(Pointf3(0.0, 0.0, h)); + + // adjust via rounding + double angle = (2*PI / floor(2*PI / fa)); + + // for each line along the polygon approximating the top/bottom of the + // circle, generate four points and four facets (2 for the wall, 2 for the + // top and bottom. + // Special case: Last line shares 2 vertices with the first line. + unsigned id = 3; + vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0)); + vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h)); + for (double i = angle; i < 2*PI; i+=angle) { + vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, 0)); + vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, h)); + id += 2; + facets.push_back(Point3(0, id - 1, id - 3)); + facets.push_back(Point3(1, id, id - 2)); + facets.push_back(Point3(id, id - 1, id - 3)); + facets.push_back(Point3(id - 3, id - 2, id)); + } + facets.push_back(Point3(0, 2, id -1)); + facets.push_back(Point3(1, 3, id)); + facets.push_back(Point3(id - 1, 2, 3)); + facets.push_back(Point3(id - 1, 3, id)); + + TriangleMesh mesh(vertices ,facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 5a393738af..586deb414d 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -115,6 +115,9 @@ class TriangleMeshSlicer TriangleMesh make_cube(double x, double y, double z); +// Generate a TriangleMesh of a cylinder +TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 5f67b9561e..69035ba376 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -41,6 +41,8 @@ void reset_repair_stats(); Clone cube(double x, double y, double z) %code{% RETVAL = make_cube(x, y, z); %}; + Clone cylinder(double r, double h) + %code{% RETVAL = make_cylinder(r, h); %}; %{ void From d961a88969740aaf33304af36a33975e7a5c28a2 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 22:34:10 -0600 Subject: [PATCH 09/19] Fixed mesh generation to generate cylinders. --- xs/src/libslic3r/TriangleMesh.cpp | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index b71f29215a..f7b26aa835 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1184,6 +1184,7 @@ TriangleMesh make_cube(double x, double y, double z) { // Generate the mesh for a cylinder and return it, using // the generated angle to calculate the top mesh triangles. +// Default is 360 sides, angle fa is in radians. TriangleMesh make_cylinder(double r, double h, double fa) { Pointf3s vertices; std::vector facets; @@ -1192,31 +1193,36 @@ TriangleMesh make_cylinder(double r, double h, double fa) { vertices.push_back(Pointf3(0.0, 0.0, 0.0)); vertices.push_back(Pointf3(0.0, 0.0, h)); - // adjust via rounding + // adjust via rounding to get an even multiple for any provided angle. double angle = (2*PI / floor(2*PI / fa)); // for each line along the polygon approximating the top/bottom of the // circle, generate four points and four facets (2 for the wall, 2 for the // top and bottom. // Special case: Last line shares 2 vertices with the first line. - unsigned id = 3; + unsigned id = vertices.size() - 1; vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0)); vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h)); - for (double i = angle; i < 2*PI; i+=angle) { - vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, 0)); - vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, h)); - id += 2; - facets.push_back(Point3(0, id - 1, id - 3)); - facets.push_back(Point3(1, id, id - 2)); - facets.push_back(Point3(id, id - 1, id - 3)); - facets.push_back(Point3(id - 3, id - 2, id)); + for (double i = 0; i < 2*PI; i+=angle) { + Pointf3 b(0, r, 0); + Pointf3 t(0, r, h); + b.rotate(i, Pointf3(0,0,0)); + t.rotate(i, Pointf3(0,0,h)); + vertices.push_back(b); + vertices.push_back(t); + id = vertices.size() - 1; + facets.push_back(Point3( 0, id - 1, id - 3)); // top + facets.push_back(Point3(id, 1, id - 2)); // bottom + facets.push_back(Point3(id, id - 2, id - 3)); // upper-right of side + facets.push_back(Point3(id, id - 3, id - 1)); // bottom-left of side } - facets.push_back(Point3(0, 2, id -1)); - facets.push_back(Point3(1, 3, id)); - facets.push_back(Point3(id - 1, 2, 3)); - facets.push_back(Point3(id - 1, 3, id)); + // Connect the last set of vertices with the first. + facets.push_back(Point3( 2, 0, id - 1)); + facets.push_back(Point3( 1, 3, id)); + facets.push_back(Point3(id, 3, 2)); + facets.push_back(Point3(id, 2, id - 1)); - TriangleMesh mesh(vertices ,facets); + TriangleMesh mesh(vertices, facets); return mesh; } } From 7aede7aa2891f1c4e587286b98cd8656773efee1 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 22:35:19 -0600 Subject: [PATCH 10/19] Menu now works to select cylinders (reusing the gui options) --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 5049d7dda6..a34a4d7f5c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -341,16 +341,18 @@ sub on_btn_lambda { return; } my $params = $dlg->ObjectParameter; + my $type = "".$params->{"type"}; my $name = "lambda-".$params->{"type"}; my $mesh = Slic3r::TriangleMesh->new(); #TODO support non-boxes - if ($name eq "box") { + if ($type eq "box") { $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); - } elsif ($name eq "cylinder") { + } elsif ($type eq "cylinder") { $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + } else { + return; } - my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); From 9e2e5079f0029f2e4ea04fde537825092fc93033 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 23:45:40 -0600 Subject: [PATCH 11/19] UI now changes its options based on the object type selected. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 71 +++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 2f815c27eb..17a3ad613f 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -19,8 +19,10 @@ sub new { # Note whether the window was already closed, so a pending update is not executed. $self->{already_closed} = 0; $self->{object_parameters} = { - type => "box", + type => "box", dim => [1, 1, 1], + cyl_r => 1, + cyl_h => 1, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -44,51 +46,84 @@ sub new { $self->Destroy; }); - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + my $optgroup_box; + $optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new( parent => $self, - title => 'Add Generic...', + title => 'Add Cube...', on_change => sub { # Do validation my ($opt_id) = @_; if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { - if (!looks_like_number($optgroup->get_value($opt_id))) { + if (!looks_like_number($optgroup_box->get_value($opt_id))) { return 0; } } - $self->{object_parameters}->{dim}[$opt_id] = $optgroup->get_value($opt_id); + $self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id); }, label_width => 100, ); my @options = ("box", "cylinder"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 0, label => 'L', type => 'f', default => '1', )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 1, label => 'W', type => 'f', default => '1', )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 2, label => 'H', type => 'f', default => '1', )); + + my $optgroup_cylinder; + $optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Cylinder...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') { + if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id); + }, + label_width => 100, + ); + + $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_r", + label => 'Radius', + type => 'f', + default => '1', + )); + $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_h", + label => 'Height', + type => 'f', + default => '1', + )); EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); + $self->_update_ui; }); - $optgroup->sizer->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->_update_ui; $self->SetSizer($self->{sizer}); $self->{sizer}->Fit($self); @@ -104,4 +139,18 @@ sub ObjectParameter { my ($self) = @_; return $self->{object_parameters}; } + +sub _update_ui { + my ($self) = @_; + $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); + $self->{sizer}->Hide($self->{optgroup_box}->sizer); + if ($self->{type}->GetValue eq "box") { + $self->{sizer}->Show($self->{optgroup_box}->sizer); + } elsif ($self->{type}->GetValue eq "cylinder") { + $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } + $self->{sizer}->Fit($self); + $self->{sizer}->SetSizeHints($self); + +} 1; From 9ffba8c4346b9f71dd86131f1c3936d72c591931 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 23:46:05 -0600 Subject: [PATCH 12/19] Disable sliders if a volume is not selected. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index a34a4d7f5c..2e8a57cf8b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -78,8 +78,8 @@ sub new { my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + my $optgroup_movers; + $optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new( parent => $self, title => 'Move', on_change => sub { @@ -88,8 +88,8 @@ sub new { # genates tens of events for a single value change. # Only trigger the recalculation if the value changes # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{move_options}{$opt_id} != $optgroup->get_value($opt_id)) { - $self->{move_options}{$opt_id} = $optgroup->get_value($opt_id); + if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) { + $self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id); wxTheApp->CallAfter(sub { $self->_update; }); @@ -97,7 +97,7 @@ sub new { }, label_width => 20, ); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'x', type => 'slider', label => 'X', @@ -106,7 +106,7 @@ sub new { max => $self->{model_object}->bounding_box->size->x*4, full_width => 1, )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'y', type => 'slider', label => 'Y', @@ -115,7 +115,7 @@ sub new { max => $self->{model_object}->bounding_box->size->y*4, full_width => 1, )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'z', type => 'slider', label => 'Z', @@ -124,14 +124,13 @@ sub new { max => $self->{model_object}->bounding_box->size->z*4, full_width => 1, )); - - + # left pane with tree my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); $left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0); - $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); # right pane with preview canvas my $canvas; @@ -247,9 +246,9 @@ sub selection_changed { $self->{settings_panel}->set_config(undef); # reset move sliders - $self->{optgroup}->set_value("x", 0); - $self->{optgroup}->set_value("y", 0); - $self->{optgroup}->set_value("z", 0); + $self->{optgroup_movers}->set_value("x", 0); + $self->{optgroup_movers}->set_value("y", 0); + $self->{optgroup_movers}->set_value("z", 0); $self->{move_options} = { x => 0, y => 0, @@ -269,6 +268,7 @@ sub selection_changed { $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1; } $self->{btn_delete}->Enable; + $self->{optgroup_movers}->enable; # attach volume config to settings panel my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; @@ -281,6 +281,7 @@ sub selection_changed { # select nothing in 3D preview # attach object config to settings panel + $self->{optgroup_movers}->disable; $self->{staticbox}->SetLabel('Object Settings'); @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); $config = $self->{model_object}->config; @@ -345,11 +346,10 @@ sub on_btn_lambda { my $name = "lambda-".$params->{"type"}; my $mesh = Slic3r::TriangleMesh->new(); - #TODO support non-boxes if ($type eq "box") { $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { - $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + $mesh = $mesh->cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } else { return; } From f0a45356c83a6e32fe254d789494e6ef828ea102 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 28 Nov 2016 02:46:43 -0600 Subject: [PATCH 13/19] Made cube and cylinder static functions of the package, not some specific TriangleMesh object. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 6 +++--- xs/xsp/TriangleMesh.xsp | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 2e8a57cf8b..579f3d5ffb 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -344,12 +344,12 @@ sub on_btn_lambda { my $params = $dlg->ObjectParameter; my $type = "".$params->{"type"}; my $name = "lambda-".$params->{"type"}; - my $mesh = Slic3r::TriangleMesh->new(); + my $mesh; if ($type eq "box") { - $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); + $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { - $mesh = $mesh->cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); + $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } else { return; } diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 69035ba376..8fdd3a5da7 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -39,10 +39,7 @@ %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); - Clone cube(double x, double y, double z) - %code{% RETVAL = make_cube(x, y, z); %}; - Clone cylinder(double r, double h) - %code{% RETVAL = make_cylinder(r, h); %}; + %{ void @@ -230,6 +227,21 @@ TriangleMesh::bb3() OUTPUT: RETVAL + +Clone +cube(double x, double y, double z) + CODE: + RETVAL = make_cube(x,y,z); + OUTPUT: + RETVAL + +Clone +cylinder(double r, double h) + CODE: + RETVAL = make_cylinder(r, h); + OUTPUT: + RETVAL + %} }; From bf0dd34a78dbf77755a85cfc37e15605b2ddccd9 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 22:43:55 +0000 Subject: [PATCH 14/19] Added make_sphere, generates a mesh with specified step angle and raidus rho. --- xs/src/libslic3r/TriangleMesh.cpp | 80 +++++++++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 2 + xs/xsp/TriangleMesh.xsp | 7 +++ 3 files changed, 89 insertions(+) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index f7b26aa835..cefe9b0424 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1225,4 +1225,84 @@ TriangleMesh make_cylinder(double r, double h, double fa) { TriangleMesh mesh(vertices, facets); return mesh; } + +// Generates mesh for a sphere centered about the origin, using the generated angle +// to determine the granularity. +// Default angle is 1 degree. +TriangleMesh make_sphere(double rho, double fa) { + Pointf3s vertices; + std::vector facets; + + // Algorithm: + // Add points one-by-one to the sphere grid and form facets using relative coordinates. + // Sphere is composed effectively of a mesh of stacked circles. + + // adjust via rounding to get an even multiple for any provided angle. + double angle = (2*PI / floor(2*PI / fa)); + + // Ring to be scaled to generate the steps of the sphere + std::vector ring; + for (double i = 0; i < 2*PI; i+=angle) { + ring.push_back(i); + } + const size_t steps = ring.size(); + const double increment = (double)(1.0 / (double)steps); + + // special case: first ring connects to 0,0,0 + // insert and form facets. + vertices.push_back(Pointf3(0.0, 0.0, -rho)); + size_t id = vertices.size(); + for (size_t i = 0; i < ring.size(); i++) { + // Fixed scaling + const double z = -rho + increment*rho*2.0; + // radius of the circle for this step. + const double r = sqrt(abs(rho*rho - z*z)); + Pointf3 b(0, r, z); + b.rotate(ring[i], Pointf3(0,0,z)); + vertices.push_back(b); + if (i == 0) { + facets.push_back(Point3(1, 0, ring.size())); + } else { + facets.push_back(Point3(id, 0, id - 1)); + } + id++; + } + + // General case: insert and form facets for each step, joining it to the ring below it. + for (size_t s = 2; s < steps - 1; s++) { + const double z = -rho + increment*(double)s*2.0*rho; + const double r = sqrt(abs(rho*rho - z*z)); + + for (size_t i = 0; i < ring.size(); i++) { + Pointf3 b(0, r, z); + b.rotate(ring[i], Pointf3(0,0,z)); + vertices.push_back(b); + if (i == 0) { + // wrap around + facets.push_back(Point3(id + ring.size() - 1 , id, id - 1)); + facets.push_back(Point3(id, id - ring.size(), id - 1)); + } else { + facets.push_back(Point3(id , id - ring.size(), (id - 1) - ring.size())); + facets.push_back(Point3(id, id - 1 - ring.size() , id - 1)); + } + id++; + } + } + + + // special case: last ring connects to 0,0,rho*2.0 + // only form facets. + vertices.push_back(Pointf3(0.0, 0.0, rho)); + for (size_t i = 0; i < ring.size(); i++) { + if (i == 0) { + // third vertex is on the other side of the ring. + facets.push_back(Point3(id, id - ring.size(), id - 1)); + } else { + facets.push_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1))); + } + } + id++; + TriangleMesh mesh(vertices, facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 586deb414d..f9337f6aa0 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -118,6 +118,8 @@ TriangleMesh make_cube(double x, double y, double z); // Generate a TriangleMesh of a cylinder TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); +TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 8fdd3a5da7..028bb586bf 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -242,6 +242,13 @@ cylinder(double r, double h) OUTPUT: RETVAL +Clone +sphere(double rho) + CODE: + RETVAL = make_sphere(rho); + OUTPUT: + RETVAL + %} }; From fbc954ac1e4a5dde974eb672083677b77b574eda Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 22:44:17 +0000 Subject: [PATCH 15/19] UI code to generate a generic sphere. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 32 ++++++++++++++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 17a3ad613f..3b96c0db93 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -23,6 +23,7 @@ sub new { dim => [1, 1, 1], cyl_r => 1, cyl_h => 1, + cyl_rho => 1.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -62,7 +63,7 @@ sub new { }, label_width => 100, ); - my @options = ("box", "cylinder"); + my @options = ("box", "cylinder", "sphere"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -113,6 +114,31 @@ sub new { type => 'f', default => '1', )); + + my $optgroup_sphere; + $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Cylinder...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'cyl_rho') { + if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id); + }, + label_width => 100, + ); + + $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_rho", + label => 'Rho', + type => 'f', + default => '1', + )); + EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); $self->_update_ui; @@ -122,6 +148,7 @@ sub new { $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->_update_ui; @@ -144,10 +171,13 @@ sub _update_ui { my ($self) = @_; $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); $self->{sizer}->Hide($self->{optgroup_box}->sizer); + $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); if ($self->{type}->GetValue eq "box") { $self->{sizer}->Show($self->{optgroup_box}->sizer); } elsif ($self->{type}->GetValue eq "cylinder") { $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } elsif ($self->{type}->GetValue eq "sphere") { + $self->{sizer}->Show($self->{optgroup_sphere}->sizer); } $self->{sizer}->Fit($self); $self->{sizer}->SetSizeHints($self); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 579f3d5ffb..f123e042b1 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -350,6 +350,8 @@ sub on_btn_lambda { $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); + } elsif ($type eq "sphere") { + $mesh = Slic3r::TriangleMesh::sphere($params->{"cyl_rho"}); } else { return; } From 6414c10e7e233750f21fefee4ad9502328f08768 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:08:12 +0000 Subject: [PATCH 16/19] UI: fixed naming sphere is not a cylinder. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 8 ++++---- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 3b96c0db93..cabaebb5bc 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -23,7 +23,7 @@ sub new { dim => [1, 1, 1], cyl_r => 1, cyl_h => 1, - cyl_rho => 1.0, + sph_rho => 1.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -118,11 +118,11 @@ sub new { my $optgroup_sphere; $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( parent => $self, - title => 'Add Cylinder...', + title => 'Add Sphere...', on_change => sub { # Do validation my ($opt_id) = @_; - if ($opt_id eq 'cyl_rho') { + if ($opt_id eq 'sph_rho') { if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { return 0; } @@ -133,7 +133,7 @@ sub new { ); $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_rho", + opt_id => "sph_rho", label => 'Rho', type => 'f', default => '1', diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index f123e042b1..8e2dcdeb77 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -351,7 +351,7 @@ sub on_btn_lambda { } elsif ($type eq "cylinder") { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } elsif ($type eq "sphere") { - $mesh = Slic3r::TriangleMesh::sphere($params->{"cyl_rho"}); + $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); } else { return; } From bbb84278d4baf11ca4efdae9cfafe728296717f6 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:08:36 +0000 Subject: [PATCH 17/19] Added UI options to make a slab, defaulting to the model object's bounding box * 1.5 --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 38 ++++++++++++++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 4 +++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index cabaebb5bc..683b990bd1 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -24,6 +24,8 @@ sub new { cyl_r => 1, cyl_h => 1, sph_rho => 1.0, + slab_h => 1.0, + slab_z => 0.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -63,7 +65,7 @@ sub new { }, label_width => 100, ); - my @options = ("box", "cylinder", "sphere"); + my @options = ("box", "slab", "cylinder", "sphere"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -139,6 +141,36 @@ sub new { default => '1', )); + my $optgroup_slab; + $optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Slab...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') { + if (!looks_like_number($optgroup_slab->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id); + }, + label_width => 100, + ); + $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "slab_h", + label => 'H', + type => 'f', + default => '1', + )); + $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "slab_z", + label => 'Initial Z', + type => 'f', + default => '0', + )); + + EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); $self->_update_ui; @@ -149,6 +181,7 @@ sub new { $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->_update_ui; @@ -170,12 +203,15 @@ sub ObjectParameter { sub _update_ui { my ($self) = @_; $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); + $self->{sizer}->Hide($self->{optgroup_slab}->sizer); $self->{sizer}->Hide($self->{optgroup_box}->sizer); $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); if ($self->{type}->GetValue eq "box") { $self->{sizer}->Show($self->{optgroup_box}->sizer); } elsif ($self->{type}->GetValue eq "cylinder") { $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } elsif ($self->{type}->GetValue eq "slab") { + $self->{sizer}->Show($self->{optgroup_slab}->sizer); } elsif ($self->{type}->GetValue eq "sphere") { $self->{sizer}->Show($self->{optgroup_sphere}->sizer); } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 8e2dcdeb77..fd93675c27 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -352,6 +352,10 @@ sub on_btn_lambda { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } elsif ($type eq "sphere") { $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); + } elsif ($type eq "slab") { + $mesh = Slic3r::TriangleMesh::cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); + # box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z + $mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); } else { return; } From 2875624eac2ac6dcd103b77f0c04756fc2db64d0 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:40:28 +0000 Subject: [PATCH 18/19] Repair the generic mesh so it can be exported. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index fd93675c27..4632635c56 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -359,6 +359,7 @@ sub on_btn_lambda { } else { return; } + $mesh->repair; my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); From a26a60f8dd7b4d373081dccf11d7df3b534f6ec4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 6 Dec 2016 00:57:16 -0600 Subject: [PATCH 19/19] Only enable movers if modifier mesh. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 4632635c56..013cec3d97 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -268,10 +268,15 @@ sub selection_changed { $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1; } $self->{btn_delete}->Enable; - $self->{optgroup_movers}->enable; # attach volume config to settings panel my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; + + if ($volume->modifier) { + $self->{optgroup_movers}->enable; + } else { + $self->{optgroup_movers}->disable; + } $config = $volume->config; $self->{staticbox}->SetLabel('Part Settings');