Initial work for custom bed shape

This commit is contained in:
Alessandro Ranellucci 2014-06-16 01:49:49 +02:00
parent b142469cf3
commit 4fe2128fc4
8 changed files with 248 additions and 31 deletions

View File

@ -23,7 +23,7 @@ $Options->{threads}{readonly} = !$Slic3r::have_threads;
*{$opt_key} = sub { $_[0]->get($opt_key) };
}
}
sub bed_size { [200,200] }
sub new_from_defaults {
my $class = shift;
my (@opt_keys) = @_;

View File

@ -6,6 +6,7 @@ use utf8;
use File::Basename qw(basename);
use FindBin;
use Slic3r::GUI::AboutDialog;
use Slic3r::GUI::BedShapeDialog;
use Slic3r::GUI::ConfigWizard;
use Slic3r::GUI::MainFrame;
use Slic3r::GUI::Notifier;

View File

@ -0,0 +1,167 @@
package Slic3r::GUI::BedShapeDialog;
use strict;
use warnings;
use utf8;
use List::Util qw(min max);
use Slic3r::Geometry qw(PI X Y unscale);
use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_CHOICE);
use base 'Wx::Dialog';
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, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
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} = [];
$self->_init_shape_options_page('Rectangular', [
{
opt_key => 'rect_size',
type => 'point',
label => 'Size',
tooltip => 'Size in X and Y of the rectangular plate.',
default => [200,200],
},
{
opt_key => 'rect_origin',
type => 'select',
label => 'Origin',
tooltip => 'Position of the 0,0 point.',
labels => ['Front left corner','Center'],
values => ['corner','center'],
default => 'corner',
},
]);
# right pane with preview canvas
my $canvas;
# 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, 0) if $canvas;
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
$main_sizer->Add($top_sizer, 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;
});
$self->_set_shape($default);
$self->_update_preview;
return $self;
}
sub _set_shape {
my ($self, $points) = @_;
$self->{bed_shape} = $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
# let's check whether origin is at a known point
my $x_min = min(map $_->[X], @$points);
my $x_max = max(map $_->[X], @$points);
my $y_min = min(map $_->[Y], @$points);
my $y_max = max(map $_->[Y], @$points);
my $origin;
if ($x_min == 0 && $y_min == 0) {
$origin = 'corner';
} elsif (($x_min + $x_max)/2 == 0 && ($y_min + $y_max)/2 == 0) {
$origin = 'center';
}
if (defined $origin) {
$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);
return;
}
}
}
$self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
}
sub _update_shape {
my ($self) = @_;
my $page_idx = $self->{shape_options_book}->GetSelection;
if ($page_idx == SHAPE_RECTANGULAR) {
return if grep !defined($self->{"_$_"}), qw(rect_size rect_origin); # not loaded yet
my ($x, $y) = @{$self->{_rect_size}};
my ($x0, $y0) = (0,0);
my ($x1, $y1) = ($x,$y);
if ($self->{_rect_origin} eq 'center') {
$x0 -= $x/2;
$x1 -= $x/2;
$y0 -= $y/2;
$y1 -= $y/2;
}
$self->{bed_shape} = [
[$x0,$y0],
[$x1,$y0],
[$x1,$y1],
[$x0,$y1],
];
}
$self->_update_preview;
}
sub _update_preview {
my ($self) = @_;
}
sub _init_shape_options_page {
my ($self, $title, $options) = @_;
my $panel = Wx::Panel->new($self->{shape_options_book});
push @{$self->{optgroups}}, my $optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $panel,
title => 'Settings',
options => $options,
on_change => sub {
my ($opt_key, $value) = @_;
$self->{"_$opt_key"} = $value;
$self->_update_shape;
},
label_width => 100,
);
$panel->SetSizerAndFit($optgroup->sizer);
$self->{shape_options_book}->AddPage($panel, $title);
}
sub GetValue {
my ($self) = @_;
return $self->{bed_shape};
}
1;

View File

@ -48,6 +48,7 @@ has 'options' => (is => 'ro', required => 1, trigger => 1);
has 'lines' => (is => 'lazy');
has 'on_change' => (is => 'ro', default => sub { sub {} });
has 'no_labels' => (is => 'ro', default => sub { 0 });
has 'staticbox' => (is => 'ro', default => sub { 1 });
has 'label_width' => (is => 'ro', default => sub { 180 });
has 'extra_column' => (is => 'ro');
has 'label_font' => (is => 'ro');
@ -63,9 +64,11 @@ sub _trigger_options {}
sub BUILD {
my $self = shift;
{
if ($self->staticbox) {
my $box = Wx::StaticBox->new($self->parent, -1, $self->title);
$self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL));
} else {
$self->sizer(Wx::BoxSizer->new(wxVERTICAL));
}
my $num_columns = $self->extra_column ? 3 : 2;
@ -79,9 +82,9 @@ sub BUILD {
foreach my $line (@{$self->lines}) {
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);
$self->sizer->Add($window, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
} elsif ($line->{widget} && $line->{full_width}) {
my $sizer = $line->{widget}->($self->parent);
$self->sizer->Add($sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
} else {
$self->_build_line($line, $grid_sizer);
}
@ -143,7 +146,7 @@ sub _build_line {
push @fields, $self->_build_field($opt);
push @field_labels, $opt->{label};
}
if (@fields > 1 || $line->{sidetext}) {
if (@fields > 1 || $line->{widget} || $line->{sidetext}) {
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
for my $i (0 .. $#fields) {
if (@fields > 1 && $field_labels[$i]) {
@ -153,7 +156,10 @@ sub _build_line {
}
$sizer->Add($fields[$i], 0, wxALIGN_CENTER_VERTICAL, 0);
}
if ($line->{sidetext}) {
if ($line->{widget}) {
my $widget_sizer = $line->{widget}->($self->parent);
$sizer->Add($widget_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
} elsif ($line->{sidetext}) {
my $sidetext = Wx::StaticText->new($self->parent, -1, $line->{sidetext}, wxDefaultPosition, wxDefaultSize);
$sidetext->SetFont($self->sidetext_font);
$sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4);
@ -479,26 +485,24 @@ sub _config_methods {
}
package Slic3r::GUI::OptionsGroup::StaticTextLine;
use Moo;
use Wx qw(:misc :systemsettings);
use base 'Wx::StaticText';
sub GetWindow {
my $self = shift;
my ($parent) = @_;
sub new {
my ($class, $parent) = @_;
$self->{statictext} = Wx::StaticText->new($parent, -1, "foo", wxDefaultPosition, wxDefaultSize);
my $self = $class->SUPER::new($parent, -1, "", wxDefaultPosition, wxDefaultSize);
my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$self->{statictext}->SetFont($font);
return $self->{statictext};
$self->SetFont($font);
return $self;
}
sub SetText {
my $self = shift;
my ($value) = @_;
my ($self, $value) = @_;
$self->{statictext}->SetLabel($value);
$self->{statictext}->Wrap(400);
$self->{statictext}->GetParent->Layout;
$self->SetLabel($value);
$self->Wrap(400);
$self->GetParent->Layout;
}
1;

View File

@ -599,7 +599,11 @@ sub build {
Slic3r::GUI::OptionsGroup->single_option_line('cooling'),
{
label => '',
widget => ($self->{description_line} = Slic3r::GUI::OptionsGroup::StaticTextLine->new),
full_width => 1,
widget => sub {
my ($parent) = @_;
return $self->{description_line} = Slic3r::GUI::OptionsGroup::StaticTextLine->new($parent);
},
},
],
},
@ -662,6 +666,8 @@ sub on_value_change {
package Slic3r::GUI::Tab::Printer;
use base 'Slic3r::GUI::Tab';
use Wx qw(:sizer :button :bitmap :misc :id);
use Wx::Event qw(EVT_BUTTON);
sub name { 'printer' }
sub title { 'Printer Settings' }
@ -671,10 +677,42 @@ sub build {
$self->{extruders_count} = 1;
my $bed_shape_widget = sub {
my ($parent) = @_;
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$btn->SetFont($Slic3r::GUI::small_font);
if ($Slic3r::GUI::have_button_icons) {
$btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG));
}
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$sizer->Add($btn);
EVT_BUTTON($self, $btn, sub {
my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape);
if ($dlg->ShowModal == wxID_OK) {
my $value = $dlg->GetValue;
$self->{config}->set('bed_shape', $value);
$self->on_value_change('bed_shape', $value);
}
});
return $sizer;
};
$self->add_options_page('General', 'printer_empty.png', optgroups => [
{
title => 'Size and coordinates',
options => [qw(bed_size print_center z_offset)],
options => [qw(bed_shape print_center z_offset)],
lines => [
{
label => 'Bed shape',
widget => $bed_shape_widget,
},
Slic3r::GUI::OptionsGroup->single_option_line('print_center'),
Slic3r::GUI::OptionsGroup->single_option_line('z_offset'),
],
},
{
title => 'Firmware',

View File

@ -11,11 +11,8 @@ PrintConfigDef::build_def() {
Options["avoid_crossing_perimeters"].tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation.";
Options["avoid_crossing_perimeters"].cli = "avoid-crossing-perimeters!";
Options["bed_size"].type = coPoint;
Options["bed_size"].label = "Bed size";
Options["bed_size"].tooltip = "Size of your bed. This is used to adjust the preview in the plater and for auto-arranging parts in it.";
Options["bed_size"].sidetext = "mm";
Options["bed_size"].cli = "bed-size=s";
Options["bed_shape"].type = coPoints;
Options["bed_shape"].label = "Bed shape";
Options["bed_temperature"].type = coInt;
Options["bed_temperature"].label = "Other layers";

View File

@ -307,7 +307,7 @@ class PrintConfig : public virtual StaticPrintConfig
{
public:
ConfigOptionBool avoid_crossing_perimeters;
ConfigOptionPoint bed_size;
ConfigOptionPoints bed_shape;
ConfigOptionInt bed_temperature;
ConfigOptionFloat bridge_acceleration;
ConfigOptionInt bridge_fan_speed;
@ -378,7 +378,10 @@ class PrintConfig : public virtual StaticPrintConfig
PrintConfig() : StaticPrintConfig() {
this->avoid_crossing_perimeters.value = false;
this->bed_size.point = Pointf(200,200);
this->bed_shape.values.push_back(Pointf(0,0));
this->bed_shape.values.push_back(Pointf(200,0));
this->bed_shape.values.push_back(Pointf(200,200));
this->bed_shape.values.push_back(Pointf(0,200));
this->bed_temperature.value = 0;
this->bridge_acceleration.value = 0;
this->bridge_fan_speed.value = 100;
@ -466,7 +469,7 @@ class PrintConfig : public virtual StaticPrintConfig
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters;
if (opt_key == "bed_size") return &this->bed_size;
if (opt_key == "bed_shape") return &this->bed_shape;
if (opt_key == "bed_temperature") return &this->bed_temperature;
if (opt_key == "bridge_acceleration") return &this->bridge_acceleration;
if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed;

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 34;
use Test::More tests => 39;
use constant PI => 4 * atan2(1, 1);
use constant EPSILON => 1E-4;
@ -40,10 +40,17 @@ isa_ok $line->[0], 'Slic3r::Point::Ref', 'line point is blessed';
], 'translate';
}
{
ok +Slic3r::Line->new([0,0],[200,0])->parallel_to_line(Slic3r::Line->new([200,200],[0,200])), 'parallel_to';
}
foreach my $base_angle (0, PI/4, PI/2, PI) {
my $line = Slic3r::Line->new([0,0], [100,0]);
$line->rotate($base_angle, [0,0]);
ok $line->parallel_to_line($line->clone), 'line is parallel to self';
my $clone = $line->clone;
ok $line->parallel_to_line($clone), 'line is parallel to self';
$clone->reverse;
ok $line->parallel_to_line($clone), 'line is parallel to self + PI';
ok $line->parallel_to($line->direction), 'line is parallel to its direction';
ok $line->parallel_to($line->direction + PI), 'line is parallel to its direction + PI';
ok $line->parallel_to($line->direction - PI), 'line is parallel to its direction - PI';