From 1167458acdd5ec1414a2f4fae9bc4996bd464482 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 16 Feb 2018 09:38:03 +0100 Subject: [PATCH 1/4] Fixed converting of Unicode codepoint (\uXXXX) into a character in Perl. --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4be5681a8..5b24a39d8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -431,7 +431,7 @@ sub new { $print_info_sizer->Add($grid_sizer, 0, wxEXPAND); my @info = ( fil_m => "Used Filament (m)", - fil_mm3 => "Used Filament (mm\u00B3)", + fil_mm3 => "Used Filament (mm\x{00B3})", fil_g => "Used Filament (g)", cost => "Cost", time => "Estimated printing time", From 53006384d582260f6f8bb1127fda49f0a9233635 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 16 Feb 2018 10:29:28 +0100 Subject: [PATCH 2/4] Fixed converting of Unicode codepoint (\uXXXX) into a character in Perl. --- utils/post-processing/flowrate.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/post-processing/flowrate.pl b/utils/post-processing/flowrate.pl index 4d742631e..f29d2312d 100755 --- a/utils/post-processing/flowrate.pl +++ b/utils/post-processing/flowrate.pl @@ -25,7 +25,7 @@ while (<>) { my $mm_per_mm = $e_length / $dist; # dE/dXY my $mm3_per_mm = ($filament_diameter[$T] ** 2) * PI/4 * $mm_per_mm; my $vol_speed = $F/60 * $mm3_per_mm; - my $comment = sprintf ' ; dXY = %.3fmm ; dE = %.5fmm ; dE/XY = %.5fmm/mm; volspeed = %.5fmm\u00B3/sec', + my $comment = sprintf ' ; dXY = %.3fmm ; dE = %.5fmm ; dE/XY = %.5fmm/mm; volspeed = %.5fmm\x{00B3}/sec', $dist, $e_length, $mm_per_mm, $vol_speed; s/(\R+)/$comment$1/; } From 8dd5fe83fcad2f46aedb1da29747c820a58ddb79 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 16 Feb 2018 17:20:34 +0100 Subject: [PATCH 3/4] Humbly re-added the BedShapeDialog.pm. Even though we have a C++ implementation now, the Perl BedShapeDialog.pm is used by the wizard. --- lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/BedShapeDialog.pm | 316 +++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 lib/Slic3r/GUI/BedShapeDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 0b4f9743a..54a27dc99 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -8,6 +8,7 @@ use FindBin; use List::Util qw(first); use Slic3r::GUI::2DBed; use Slic3r::GUI::AboutDialog; +use Slic3r::GUI::BedShapeDialog; use Slic3r::GUI::BonjourBrowser; use Slic3r::GUI::ConfigWizard; use Slic3r::GUI::Controller; diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm new file mode 100644 index 000000000..70c8e0256 --- /dev/null +++ b/lib/Slic3r/GUI/BedShapeDialog.pm @@ -0,0 +1,316 @@ +# The bed shape dialog. +# The dialog opens from Print Settins tab -> Bed Shape: Set... + +package Slic3r::GUI::BedShapeDialog; +use strict; +use warnings; +use utf8; + +use List::Util qw(min max); +use Slic3r::Geometry qw(X Y unscale); +use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_CLOSE); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent, $default) = @_; + my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + + $self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default); + + my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); + $main_sizer->Add($panel, 1, wxEXPAND); + $main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND); + + $self->SetSizer($main_sizer); + $self->SetMinSize($self->GetSize); + $main_sizer->SetSizeHints($self); + + # needed to actually free memory + EVT_CLOSE($self, sub { + $self->EndModal(wxID_OK); + $self->Destroy; + }); + + return $self; +} + +sub GetValue { + my ($self) = @_; + return $self->{panel}->GetValue; +} + +package Slic3r::GUI::BedShapePanel; + +use List::Util qw(min max sum first); +use Scalar::Util qw(looks_like_number); +use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon); +use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON); +use base 'Wx::Panel'; + +use constant SHAPE_RECTANGULAR => 0; +use constant SHAPE_CIRCULAR => 1; +use constant SHAPE_CUSTOM => 2; + +sub new { + my $class = shift; + my ($parent, $default) = @_; + my $self = $class->SUPER::new($parent, -1); + + $self->on_change(undef); + + my $box = Wx::StaticBox->new($self, -1, "Shape"); + my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); + + # shape options + $self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP); + $sbsizer->Add($self->{shape_options_book}); + + $self->{optgroups} = []; + { + my $optgroup = $self->_init_shape_options_page('Rectangular'); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'rect_size', + type => 'point', + label => 'Size', + tooltip => 'Size in X and Y of the rectangular plate.', + default => [200,200], + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'rect_origin', + type => 'point', + label => 'Origin', + tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.', + default => [0,0], + )); + } + { + my $optgroup = $self->_init_shape_options_page('Circular'); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'diameter', + type => 'f', + label => 'Diameter', + tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.', + sidetext => 'mm', + default => 200, + )); + } + { + my $optgroup = $self->_init_shape_options_page('Custom'); + $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( + full_width => 1, + widget => sub { + my ($parent) = @_; + + my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize); + EVT_BUTTON($self, $btn, sub { $self->_load_stl }); + return $btn; + } + )); + } + + EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub { + $self->_update_shape; + }); + + # right pane with preview canvas + my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self); + + # main sizer + my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); + $top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas; + + $self->SetSizerAndFit($top_sizer); + + $self->_set_shape($default); + $self->_update_preview; + + return $self; +} + +sub on_change { + my ($self, $cb) = @_; + $self->{on_change} = $cb // sub {}; +} + +# Called from the constructor. +# Set the initial bed shape from a list of points. +# Deduce the bed shape type (rect, circle, custom) +# This routine shall be smart enough if the user messes up +# with the list of points in the ini file directly. +sub _set_shape { + my ($self, $points) = @_; + + # is this a rectangle? + if (@$points == 4) { + my $polygon = Slic3r::Polygon->new_scale(@$points); + my $lines = $polygon->lines; + if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) { + # okay, it's a rectangle + + # find origin + # the || 0 hack prevents "-0" which might confuse the user + my $x_min = min(map $_->[X], @$points) || 0; + my $x_max = max(map $_->[X], @$points) || 0; + my $y_min = min(map $_->[Y], @$points) || 0; + my $y_max = max(map $_->[Y], @$points) || 0; + my $origin = [-$x_min, -$y_min]; + + $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); + my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; + $optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]); + $optgroup->set_value('rect_origin', $origin); + $self->_update_shape; + return; + } + } + + # is this a circle? + { + # Analyze the array of points. Do they reside on a circle? + my $polygon = Slic3r::Polygon->new_scale(@$points); + my $center = $polygon->bounding_box->center; + my @vertex_distances = map $center->distance_to($_), @$polygon; + my $avg_dist = sum(@vertex_distances)/@vertex_distances; + if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) { + # all vertices are equidistant to center + $self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR); + my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR]; + $optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2))); + $self->_update_shape; + return; + } + } + + if (@$points < 3) { + # Invalid polygon. Revert to default bed dimensions. + $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); + my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; + $optgroup->set_value('rect_size', [200, 200]); + $optgroup->set_value('rect_origin', [0, 0]); + $self->_update_shape; + return; + } + + # This is a custom bed shape, use the polygon provided. + $self->{shape_options_book}->SetSelection(SHAPE_CUSTOM); + # Copy the polygon to the canvas, make a copy of the array. + $self->{canvas}->bed_shape([@$points]); + $self->_update_shape; +} + +# Update the bed shape from the dialog fields. +sub _update_shape { + my ($self) = @_; + + my $page_idx = $self->{shape_options_book}->GetSelection; + if ($page_idx == SHAPE_RECTANGULAR) { + my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size'); + my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin'); + my ($x, $y) = @$rect_size; + return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things + return if !$x || !$y or $x == 0 or $y == 0; + my ($x0, $y0) = (0,0); + my ($x1, $y1) = ($x ,$y); + { + my ($dx, $dy) = @$rect_origin; + return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things + $x0 -= $dx; + $x1 -= $dx; + $y0 -= $dy; + $y1 -= $dy; + } + $self->{canvas}->bed_shape([ + [$x0,$y0], + [$x1,$y0], + [$x1,$y1], + [$x0,$y1], + ]); + } elsif ($page_idx == SHAPE_CIRCULAR) { + my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter'); + return if !$diameter or $diameter == 0; + my $r = $diameter/2; + my $twopi = 2*PI; + my $edges = 60; + my $polygon = Slic3r::Polygon->new_scale( + map [ $r * cos $_, $r * sin $_ ], + map { $twopi/$edges*$_ } 1..$edges + ); + $self->{canvas}->bed_shape([ + map [ unscale($_->x), unscale($_->y) ], @$polygon #)) + ]); + } + + $self->{on_change}->(); + $self->_update_preview; +} + +sub _update_preview { + my ($self) = @_; + $self->{canvas}->Refresh if $self->{canvas}; + $self->Refresh; +} + +# Called from the constructor. +# Create a panel for a rectangular / circular / custom bed shape. +sub _init_shape_options_page { + my ($self, $title) = @_; + + my $panel = Wx::Panel->new($self->{shape_options_book}); + my $optgroup; + push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new( + parent => $panel, + title => 'Settings', + label_width => 100, + on_change => sub { + my ($opt_id) = @_; + #$self->{"_$opt_id"} = $optgroup->get_value($opt_id); + $self->_update_shape; + }, + ); + $panel->SetSizerAndFit($optgroup->sizer); + $self->{shape_options_book}->AddPage($panel, $title); + + return $optgroup; +} + +# Loads an stl file, projects it to the XY plane and calculates a polygon. +sub _load_stl { + my ($self) = @_; + + my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if ($dialog->ShowModal != wxID_OK) { + $dialog->Destroy; + return; + } + my $input_file = $dialog->GetPaths; + $dialog->Destroy; + + my $model = Slic3r::Model->read_from_file($input_file); + my $mesh = $model->mesh; + my $expolygons = $mesh->horizontal_projection; + + if (@$expolygons == 0) { + Slic3r::GUI::show_error($self, "The selected file contains no geometry."); + return; + } + if (@$expolygons > 1) { + Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported."); + return; + } + + my $polygon = $expolygons->[0]->contour; + $self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]); + $self->_update_preview(); +} + +# Returns the resulting bed shape polygon. This value will be stored to the ini file. +sub GetValue { + my ($self) = @_; + return $self->{canvas}->bed_shape; +} + +1; From ac904b273182506892f57cc9a08312550e24d80c Mon Sep 17 00:00:00 2001 From: fsantini Date: Fri, 16 Feb 2018 17:27:50 +0100 Subject: [PATCH 4/4] Error messages in parsing variables (#722) Making error messages more clear when a vector or scalar is found in macro parsing, and the other type is expected. --- xs/src/libslic3r/PlaceholderParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index ba0efd6e4..62b516935 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -623,7 +623,7 @@ namespace client expr &output) { if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a scalar variable in a vector context", opt.it_range); + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); switch (opt.opt->type()) { case coFloat: output.set_d(opt.opt->getFloat()); break; case coInt: output.set_i(opt.opt->getInt()); break; @@ -648,7 +648,7 @@ namespace client expr &output) { if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing a vector variable in a scalar context", opt.it_range); + ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); const ConfigOptionVectorBase *vec = static_cast(opt.opt); if (vec->empty()) ctx->throw_exception("Indexing an empty vector variable", opt.it_range);