Initial work for custom bed shape
This commit is contained in:
parent
b142469cf3
commit
4fe2128fc4
@ -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) = @_;
|
||||
|
@ -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;
|
||||
|
167
lib/Slic3r/GUI/BedShapeDialog.pm
Normal file
167
lib/Slic3r/GUI/BedShapeDialog.pm
Normal 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;
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user