From 6bfa2cfaecd9629e75875938758279abb1b774f5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 15 Nov 2015 21:08:14 +0100 Subject: [PATCH 01/17] Projector for DLP --- lib/Slic3r/GUI.pm | 3 + .../GUI/Controller/ManualControlDialog.pm | 2 +- lib/Slic3r/GUI/MainFrame.pm | 7 + lib/Slic3r/GUI/OptionsGroup.pm | 8 +- lib/Slic3r/GUI/OptionsGroup/Field.pm | 4 +- lib/Slic3r/GUI/Projector.pm | 619 ++++++++++++++++++ var/film.png | Bin 0 -> 653 bytes 7 files changed, 636 insertions(+), 7 deletions(-) create mode 100644 lib/Slic3r/GUI/Projector.pm create mode 100644 var/film.png diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 782645f83..dca7bb6f4 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -26,6 +26,7 @@ use Slic3r::GUI::Plater::ObjectSettingsDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::Preferences; use Slic3r::GUI::ProgressStatusBar; +use Slic3r::GUI::Projector; use Slic3r::GUI::OptionsGroup; use Slic3r::GUI::OptionsGroup::Field; use Slic3r::GUI::SimpleTab; @@ -77,6 +78,8 @@ our $grey = Wx::Colour->new(200,200,200); our $VERSION_CHECK_EVENT : shared = Wx::NewEventType; +our $DLP_projection_screen; + sub OnInit { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index d22d86d79..b7f6160d4 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -60,7 +60,7 @@ sub new { my ($pos) = @_; # delete any pending commands to get a smoother movement - $self->purge_queue(1); + $self->sender->purge_queue(1); $self->abs_xy_move($pos); }); $bed_sizer->Add($canvas, 0, wxEXPAND | wxRIGHT, 3); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 9b9d18583..3ad443572 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -226,6 +226,13 @@ sub _init_menubar { $self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', sub { $plater->export_amf; }, undef, 'brick_go.png'); + $self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub { + my $projector = Slic3r::GUI::Projector->new($self); + + # this double invocation is needed for properly hiding the MainFrame + $projector->Show; + $projector->ShowModal; + }, undef, 'film.png'); $self->{object_menu} = $self->{plater}->object_menu; $self->on_plater_selection_changed(0); diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index 00b19180a..e980d44bb 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -247,8 +247,8 @@ sub set_value { } sub _on_change { - my ($self, $opt_id) = @_; - $self->on_change->($opt_id); + my ($self, $opt_id, $value) = @_; + $self->on_change->($opt_id, $value); } sub _on_kill_focus { @@ -408,7 +408,7 @@ sub _get_config_value { } sub _on_change { - my ($self, $opt_id) = @_; + my ($self, $opt_id, $value) = @_; if (exists $self->_opt_map->{$opt_id}) { my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} }; @@ -430,7 +430,7 @@ sub _on_change { } } - $self->SUPER::_on_change($opt_id); + $self->SUPER::_on_change($opt_id, $value); } sub _on_kill_focus { diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index 3a7d184db..ae6660067 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -35,7 +35,7 @@ sub toggle { sub _on_change { my ($self, $opt_id) = @_; - $self->on_change->($opt_id) + $self->on_change->($opt_id, $self->get_value) unless $self->disable_change_event; } @@ -219,7 +219,7 @@ sub BUILD { my ($self) = @_; my $style = 0; - $style |= wxCB_READONLY if $self->option->gui_type ne 'select_open'; + $style |= wxCB_READONLY if defined $self->option->gui_type && $self->option->gui_type ne 'select_open'; my $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, $self->_default_size, $self->option->labels || $self->option->values || [], $style); $self->wxWindow($field); diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm new file mode 100644 index 000000000..19afa8a52 --- /dev/null +++ b/lib/Slic3r/GUI/Projector.pm @@ -0,0 +1,619 @@ +package Slic3r::GUI::Projector; +use strict; +use warnings; +use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp); +use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); +use base qw(Wx::Dialog Class::Accessor); + +__PACKAGE__->mk_accessors(qw(config config2 screen controller)); + +sub new { + my ($class, $parent) = @_; + my $self = $class->SUPER::new($parent, -1, "Projector for DLP", wxDefaultPosition, wxDefaultSize); + $self->config2({ + display => 0, + show_bed => 1, + zoom => 100, + exposure_time => 4, + settle_time => 4, + z_lift => 15, + offset => [0,0], + }); + + my $ini = eval { Slic3r::Config->read_ini("$Slic3r::GUI::datadir/DLP.ini") }; + if ($ini) { + foreach my $opt_id (keys %{$ini->{_}}) { + my $value = $ini->{_}{$opt_id}; + if ($opt_id eq 'offset') { + $value = [ split /,/, $value ]; + } + $self->config2->{$opt_id} = $value; + } + } + + my $sizer = Wx::BoxSizer->new(wxVERTICAL); + + { + $self->config(Slic3r::Config->new_from_defaults(qw(serial_port serial_speed bed_shape))); + $self->config->apply(wxTheApp->{mainframe}->config); + + my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + parent => $self, + title => 'USB/Serial connection', + config => $self->config, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Serial port', + ); + my $serial_port = $optgroup->get_option('serial_port'); + $serial_port->side_widget(sub { + my ($parent) = @_; + + my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), + wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); + $btn->SetToolTipString("Rescan serial ports") + if $btn->can('SetToolTipString'); + EVT_BUTTON($self, $btn, sub { + $optgroup->get_field('serial_port')->set_values([ wxTheApp->scan_serial_ports ]); + }); + + return $btn; + }); + my $serial_test = sub { + my ($parent) = @_; + + my $btn = $self->{serial_test_btn} = Wx::Button->new($parent, -1, + "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + $btn->SetFont($Slic3r::GUI::small_font); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/wrench.png", wxBITMAP_TYPE_PNG)); + } + + EVT_BUTTON($self, $btn, sub { + my $sender = Slic3r::GCode::Sender->new; + my $res = $sender->connect( + $self->{config}->serial_port, + $self->{config}->serial_speed, + ); + if ($res && $sender->wait_connected) { + Slic3r::GUI::show_info($self, "Connection to printer works correctly.", "Success!"); + } else { + Slic3r::GUI::show_error($self, "Connection failed."); + } + }); + return $btn; + }; + $line->append_option($serial_port); + $line->append_option($optgroup->get_option('serial_speed')); + $line->append_widget($serial_test); + $optgroup->append_line($line); + } + + my $on_change = sub { + my ($opt_id, $value) = @_; + + $self->config2->{$opt_id} = $value; + $self->position_screen; + + my $serialized = {}; + foreach my $opt_id (keys %{$self->config2}) { + my $value = $self->config2->{$opt_id}; + if (ref($value) eq 'ARRAY') { + $value = join ',', @$value; + } + $serialized->{$opt_id} = $value; + } + Slic3r::Config->write_ini( + "$Slic3r::GUI::datadir/DLP.ini", + { _ => $serialized }); + }; + + { + my $optgroup = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Projection', + on_change => $on_change, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + my @displays = 0 .. (Wx::Display::GetCount()-1); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'display', + type => 'select', + label => 'Display', + tooltip => '', + labels => [@displays], + values => [@displays], + default => $self->config2->{display}, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'show_bed', + type => 'bool', + label => 'Show bed', + tooltip => '', + default => $self->config2->{show_bed}, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'zoom', + type => 'percent', + label => 'Zoom', + sidetext => '%', + tooltip => '', + default => $self->config2->{zoom}, + min => 0.1, + max => 100, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'offset', + type => 'point', + label => 'Offset', + tooltip => '', + default => $self->config2->{offset}, + )); + } + + { + my $optgroup = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Print', + on_change => $on_change, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'exposure_time', + type => 'i', + label => 'Exposure time', + sidetext => 'seconds', + tooltip => '', + default => $self->config2->{exposure_time}, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'settle_time', + type => 'i', + label => 'Settle time', + sidetext => 'seconds', + tooltip => '', + default => $self->config2->{settle_time}, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z_lift', + type => 'f', + label => 'Z Lift', + sidetext => 'mm', + tooltip => '', + default => $self->config2->{z_lift}, + )); + } + + { + my $buttons = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + { + my $btn = $self->{btn_print} = Wx::Button->new($self, -1, 'Print', wxDefaultPosition, wxDefaultSize); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/control_play.png", wxBITMAP_TYPE_PNG)); + } + $buttons->Add($btn, 0); + EVT_BUTTON($self, $btn, sub { + $self->controller->start_print; + $self->_update_buttons; + $self->_set_status(''); + }); + } + { + my $btn = $self->{btn_stop} = Wx::Button->new($self, -1, 'Stop', wxDefaultPosition, wxDefaultSize); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/control_stop.png", wxBITMAP_TYPE_PNG)); + } + $buttons->Add($btn, 0); + EVT_BUTTON($self, $btn, sub { + $self->controller->stop_print; + $self->_update_buttons; + $self->_set_status(''); + }); + } + + $self->{status_text} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, [200,-1]); + $buttons->Add($self->{status_text}, 0); + } + + { + my $buttons = $self->CreateStdDialogButtonSizer(wxCLOSE); + EVT_BUTTON($self, wxID_CLOSE, sub { + $self->_close; + }); + $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + } + + $self->SetSizer($sizer); + $sizer->SetSizeHints($self); + + # reuse existing screen if any + if ($Slic3r::GUI::DLP_projection_screen) { + $self->screen($Slic3r::GUI::DLP_projection_screen); + $self->screen->config($self->config); + $self->screen->config2($self->config2); + } else { + $self->screen(Slic3r::GUI::Projector::Screen->new($parent, $self->config, $self->config2)); + $Slic3r::GUI::DLP_projection_screen = $self->screen; + } + $self->position_screen; + $self->screen->Show; + wxTheApp->{mainframe}->Hide; + + # initialize controller + $self->controller(Slic3r::GUI::Projector::Controller->new( + config => $self->config, + config2 => $self->config2, + screen => $self->screen, + on_project_layer => sub { + my ($layer_num) = @_; + $self->_set_status(sprintf "Printing layer %d/%d (z = %.2f)", + $layer_num, $self->controller->layer_count, + $self->controller->current_layer_height); + }, + on_print_completed => sub { + $self->_update_buttons; + $self->_set_status(''); + }, + )); + + $self->_update_buttons; + + return $self; +} + +sub _update_buttons { + my ($self) = @_; + + my $is_printing = $self->controller->is_printing; + $self->{btn_print}->Show(!$is_printing); + $self->{btn_stop}->Show($is_printing); + $self->Layout; +} + +sub _set_status { + my ($self, $status) = @_; + $self->{status_text}->SetLabel($status // ''); + $self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth); + $self->{status_text}->Refresh; + $self->Layout; +} + +sub position_screen { + my ($self) = @_; + + my $display = Wx::Display->new($self->config2->{display}); + my $area = $display->GetGeometry; + $self->screen->Move($area->GetPosition); + # ShowFullScreen doesn't use the right screen + #$self->screen->ShowFullScreen($self->config2->{fullscreen}); + $self->screen->SetSize($area->GetSize); + $self->screen->_resize; + $self->screen->Refresh; +} + +sub _close { + my $self = shift; + + # if projection screen is not on the same display as our dialog, + # ask the user whether they want to keep it open + my $keep_screen = 0; + my $display_area = Wx::Display->new($self->config2->{display})->GetGeometry; + if (!$display_area->Contains($self->GetScreenPosition)) { + my $res = Wx::MessageDialog->new($self, "Do you want to keep the black screen open?", 'Black screen', wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal; + $keep_screen = ($res == wxID_YES); + } + + if ($keep_screen) { + $self->screen->config(undef); + $self->screen->config2(undef); + $self->screen->Refresh; + } else { + $self->screen->Destroy; + $self->screen(undef); + $Slic3r::GUI::DLP_projection_screen = undef; + } + wxTheApp->{mainframe}->Show; + + $self->EndModal(wxID_OK); +} + +package Slic3r::GUI::Projector::Controller; +use Moo; +use Wx qw(wxTheApp :id :timer); +use Wx::Event qw(EVT_TIMER); + +has 'config' => (is => 'ro', required => 1); +has 'config2' => (is => 'ro', required => 1); +has 'screen' => (is => 'ro', required => 1); +has 'on_project_layer' => (is => 'rw'); +has 'on_print_completed' => (is => 'rw'); +has 'sender' => (is => 'rw'); +has 'timer' => (is => 'rw'); +has '_print' => (is => 'rw'); +has '_layers' => (is => 'rw'); +has '_heights' => (is => 'rw'); +has '_layer_num' => (is => 'rw'); +has '_timer_cb' => (is => 'rw'); + +sub BUILD { + my ($self) = @_; + + $self->set_print(wxTheApp->{mainframe}->{plater}->{print}); + + # projection timer + my $timer_id = &Wx::NewId(); + $self->timer(Wx::Timer->new($self->screen, $timer_id)); + EVT_TIMER($self->screen, $timer_id, sub { + my $cb = $self->_timer_cb; + $self->_timer_cb(undef); + $cb->(); + }); +} + +sub delay { + my ($self, $wait, $cb) = @_; + + $self->_timer_cb($cb); + $self->timer->Start($wait * 1000, wxTIMER_ONE_SHOT); +} + +sub is_printing { + my ($self) = @_; + + return $self->timer->IsRunning; +} + +sub set_print { + my ($self, $print) = @_; + + $self->_print($print); + + # sort layers by Z + my %layers = (); + foreach my $layer (map { @{$_->layers}, @{$_->support_layers} } @{$print->objects}) { + my $height = $layer->print_z; + $layers{$height} //= []; + push @{$layers{$height}}, $layer; + } + $self->_layers({ %layers }); + $self->_heights([ sort { $a <=> $b } keys %layers ]); +} + +sub layer_count { + my ($self) = @_; + + return scalar @{$self->_heights}; +} + +sub current_layer_height { + my ($self) = @_; + + return $self->_heights->[$self->_layer_num]; +} + +sub start_print { + my ($self) = @_; + + if (0) { + $self->sender(Slic3r::GCode::Sender->new); + my $res = $self->sender->connect( + $self->config->serial_port, + $self->config->serial_speed, + ); + if (!$res || !$self->sender->wait_connected) { + Slic3r::GUI::show_error($self, "Connection failed. Check serial port and speed."); + return; + } + Slic3r::debugf "connected to " . $self->config->serial_port . "\n"; + + # TODO: replace this with customizable start G-code + $self->sender->send("G28 Z", 1); + $self->sender->send(sprintf("G1 Z%.5f F5000", $self->config2->{z_lift}), 1); + } + + # TODO: block until the G1 command has been performed + # we could do this with M400 + M115 but maybe it's not portable + $self->delay(2, sub { + # start with black + Slic3r::debugf "starting black projection\n"; + $self->_layer_num(-1); + $self->screen->project_layers(undef); + $self->delay($self->config2->{settle_time}, sub { + $self->project_next_layer; + }); + }); +} + +sub stop_print { + my ($self) = @_; + + $self->timer->Stop; + $self->_timer_cb(undef); + $self->screen->project_layers(undef); + $self->sender->disconnect if $self->sender; +} + +sub project_next_layer { + my ($self) = @_; + + Slic3r::debugf "projecting next layer\n"; + $self->_layer_num($self->_layer_num + 1); + if ($self->_layer_num >= $self->layer_count) { + $self->on_print_completed->() if $self->on_print_completed; + return; + } + + $self->on_project_layer->($self->_layer_num) if $self->on_project_layer; + + if ($self->sender) { + my $z = $self->current_layer_height; + $self->sender->send(sprintf("G1 Z%.5f F5000", $z + $self->config2->{z_lift}), 1); + $self->sender->send(sprintf("G1 Z%.5f F5000", $z), 1); + } + + # TODO: we should block until G1 commands have been performed, see note below + # TODO: subtract this waiting time from the settle_time + $self->delay(2, sub { + my @layers = @{ $self->_layers->{ $self->_heights->[$self->_layer_num] } }; + printf "id = %d, height = %s\n", $self->_layer_num, $self->_heights->[$self->_layer_num]; + $self->screen->project_layers([ @layers ]); + $self->delay($self->config2->{exposure_time}, sub { + $self->settle; + }); + }); +} + +sub settle { + my ($self) = @_; + + Slic3r::debugf "settling\n"; + $self->screen->project_layers(undef); + $self->delay($self->config2->{settle_time}, sub { + $self->project_next_layer; + }); +} + +sub DESTROY { + my ($self) = @_; + + $self->timer->Stop if $self->timer; +} + +package Slic3r::GUI::Projector::Screen; +use Wx qw(:dialog :id :misc :sizer :colour :pen :brush); +use Wx::Event qw(EVT_PAINT EVT_SIZE); +use base qw(Wx::Dialog Class::Accessor); + +use List::Util qw(min); +use Slic3r::Geometry qw(X Y unscale scale); + +__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layers)); + +sub new { + my ($class, $parent, $config, $config2) = @_; + my $self = $class->SUPER::new($parent, -1, "Projector", wxDefaultPosition, wxDefaultSize, 0); + + $self->config($config); + $self->config2($config2); + EVT_SIZE($self, \&_resize); + EVT_PAINT($self, \&_repaint); + $self->_resize; + + return $self; +} + +sub _resize { + my ($self) = @_; + + return if !$self->config; + my ($cw, $ch) = $self->GetSizeWH; + + # get bed shape polygon + my $bed_polygon = Slic3r::Polygon->new_scale(@{$self->config->bed_shape}); + my $bb = $bed_polygon->bounding_box; + my $size = $bb->size; + my $center = $bb->center; + + # calculate the scaling factor needed for constraining print bed area inside preview + # scaling_factor is expressed in pixel / mm + $self->scaling_factor(min($cw / unscale($size->x), $ch / unscale($size->y))); #) + + # apply zoom to scaling factor + if ($self->config2->{zoom} != 0) { + # TODO: make sure min and max in the option config are enforced + $self->scaling_factor($self->scaling_factor * ($self->config2->{zoom}/100)); + } + + # calculate the displacement needed for centering bed on screen + $self->bed_origin([ + $cw/2 - (unscale($center->x) - $self->config2->{offset}->[X]) * $self->scaling_factor, + $ch/2 - (unscale($center->y) - $self->config2->{offset}->[Y]) * $self->scaling_factor, #)) + ]); + + $self->Refresh; +} + +sub project_layers { + my ($self, $layers) = @_; + + $self->layers($layers); + $self->Refresh; +} + +sub _repaint { + my ($self) = @_; + + my $dc = Wx::PaintDC->new($self); + my ($cw, $ch) = $self->GetSizeWH; + return if $cw == 0; # when canvas is not rendered yet, size is 0,0 + + $dc->SetPen(Wx::Pen->new(wxBLACK, 1, wxSOLID)); + $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); + $dc->DrawRectangle(0, 0, $cw, $ch); + + return if !$self->config; + + # turn size into max visible coordinates + # TODO: or should we use ClientArea? + $cw--; + $ch--; + + # draw bed + if ($self->config2->{show_bed}) { + $dc->SetPen(Wx::Pen->new(wxRED, 2, wxSOLID)); + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxTRANSPARENT)); + my $bed_polygon = Slic3r::Polygon->new_scale(@{$self->config->bed_shape}); + $dc->DrawPolygon($self->scaled_points_to_pixel($bed_polygon), 0, 0); + } + + return if !defined $self->layers; + + # get layers at this height + # draw layers + $dc->SetPen(Wx::Pen->new(wxWHITE, 1, wxSOLID)); + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + foreach my $layer (@{$self->layers}) { + foreach my $copy (@{$layer->object->_shifted_copies}) { + foreach my $expolygon (@{ $layer->slices }) { + $expolygon = $expolygon->clone; + $expolygon->translate(@$copy); + foreach my $points (@{$expolygon->pp}) { + $dc->DrawPolygon($self->scaled_points_to_pixel($points), 0, 0); + } + } + } + } +} + +# convert a model coordinate into a pixel coordinate +sub unscaled_point_to_pixel { + my ($self, $point) = @_; + + my $ch = $self->GetSize->GetHeight; + my $zero = $self->bed_origin; + return [ + $point->[X] * $self->scaling_factor + $zero->[X], + $ch - ($point->[Y] * $self->scaling_factor + $zero->[Y]), + ]; +} + +sub scaled_points_to_pixel { + my ($self, $points) = @_; + + return [ + map $self->unscaled_point_to_pixel($_), + map Slic3r::Pointf->new_unscale(@$_), + @$points + ]; +} + +1; diff --git a/var/film.png b/var/film.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ce7bb198a3b268bd634d2b26e9b710f3797d37 GIT binary patch literal 653 zcmV;80&@L{P)WO3(`_cf+b25@DJ#zdQm}8GzWtq2-QnZ8W6mB^kfeK5f%S{ zUW%tGMCwrwic~ZrQcG=4f?5bkV+3dRk8hw6bk~y$KX#b!y*J4EJ~>;dRASqrSu;ZpM>?P}K~6AT zWv6Dmq?v&9LdXC(m%WCO6ma_di$R(v$@ad_>@R41N3N5lSJq9@6CGhX84-$%Xrd_6 z;){?{E|Ytt5$S-&Au>t4wDlIxdkfe-a22LMj``McG};r8@{GsRPm*+8fFey6C)@ifDBXVyTw(N@Xd41b45OFg6x_QA zpwLiigyy~cVoPxW^r~C7ZQpr%>1$*HKmv~AY-qJw4;gUecS--wnqslISSS=^KA&Ic n@BK|Onfz#3R%n{$a)0j^sqv5F(1NTL00000NkvXXu0mjf3S}fX literal 0 HcmV?d00001 From be0ba4d5a22cef88feef1486b01de09d1d14367e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 15 Nov 2015 21:08:47 +0100 Subject: [PATCH 02/17] Re-enable serial connection for DLP projector --- lib/Slic3r/GUI/Projector.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 19afa8a52..a9f3ec827 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -403,7 +403,7 @@ sub current_layer_height { sub start_print { my ($self) = @_; - if (0) { + { $self->sender(Slic3r::GCode::Sender->new); my $res = $self->sender->connect( $self->config->serial_port, From 5ba2f723242ec375783bf45ac5a85fd815233b06 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 15 Nov 2015 22:42:56 +0100 Subject: [PATCH 03/17] More customizable options for DLP projector --- lib/Slic3r/GUI/Projector.pm | 295 +++++++++++++++++++++++------------- 1 file changed, 190 insertions(+), 105 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index a9f3ec827..68635dbe1 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -11,13 +11,16 @@ sub new { my ($class, $parent) = @_; my $self = $class->SUPER::new($parent, -1, "Projector for DLP", wxDefaultPosition, wxDefaultSize); $self->config2({ - display => 0, - show_bed => 1, - zoom => 100, - exposure_time => 4, - settle_time => 4, - z_lift => 15, - offset => [0,0], + display => 0, + show_bed => 1, + zoom => 100, + exposure_time => 4, + bottom_exposure_time => 10, + settle_time => 4, + bottom_layers => 3, + z_lift => 15, + z_lift_speed => 50, + offset => [0,0], }); my $ini = eval { Slic3r::Config->read_ini("$Slic3r::GUI::datadir/DLP.ini") }; @@ -33,9 +36,12 @@ sub new { my $sizer = Wx::BoxSizer->new(wxVERTICAL); + $self->config(Slic3r::Config->new_from_defaults( + qw(serial_port serial_speed bed_shape start_gcode end_gcode) + )); + $self->config->apply(wxTheApp->{mainframe}->config); + { - $self->config(Slic3r::Config->new_from_defaults(qw(serial_port serial_speed bed_shape))); - $self->config->apply(wxTheApp->{mainframe}->config); my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( parent => $self, @@ -45,51 +51,76 @@ sub new { ); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => 'Serial port', - ); - my $serial_port = $optgroup->get_option('serial_port'); - $serial_port->side_widget(sub { - my ($parent) = @_; + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Serial port', + ); + my $serial_port = $optgroup->get_option('serial_port'); + $serial_port->side_widget(sub { + my ($parent) = @_; - my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); - $btn->SetToolTipString("Rescan serial ports") - if $btn->can('SetToolTipString'); - EVT_BUTTON($self, $btn, sub { - $optgroup->get_field('serial_port')->set_values([ wxTheApp->scan_serial_ports ]); + my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), + wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); + $btn->SetToolTipString("Rescan serial ports") + if $btn->can('SetToolTipString'); + EVT_BUTTON($self, $btn, sub { + $optgroup->get_field('serial_port')->set_values([ wxTheApp->scan_serial_ports ]); + }); + + return $btn; }); + my $serial_test = sub { + my ($parent) = @_; - return $btn; - }); - my $serial_test = sub { - my ($parent) = @_; - - my $btn = $self->{serial_test_btn} = Wx::Button->new($parent, -1, - "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - $btn->SetFont($Slic3r::GUI::small_font); - if ($Slic3r::GUI::have_button_icons) { - $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/wrench.png", wxBITMAP_TYPE_PNG)); - } - - EVT_BUTTON($self, $btn, sub { - my $sender = Slic3r::GCode::Sender->new; - my $res = $sender->connect( - $self->{config}->serial_port, - $self->{config}->serial_speed, - ); - if ($res && $sender->wait_connected) { - Slic3r::GUI::show_info($self, "Connection to printer works correctly.", "Success!"); - } else { - Slic3r::GUI::show_error($self, "Connection failed."); + my $btn = $self->{serial_test_btn} = Wx::Button->new($parent, -1, + "Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + $btn->SetFont($Slic3r::GUI::small_font); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/wrench.png", wxBITMAP_TYPE_PNG)); } - }); - return $btn; - }; - $line->append_option($serial_port); - $line->append_option($optgroup->get_option('serial_speed')); - $line->append_widget($serial_test); - $optgroup->append_line($line); + + EVT_BUTTON($self, $btn, sub { + my $sender = Slic3r::GCode::Sender->new; + my $res = $sender->connect( + $self->{config}->serial_port, + $self->{config}->serial_speed, + ); + if ($res && $sender->wait_connected) { + Slic3r::GUI::show_info($self, "Connection to printer works correctly.", "Success!"); + } else { + Slic3r::GUI::show_error($self, "Connection failed."); + } + }); + return $btn; + }; + $line->append_option($serial_port); + $line->append_option($optgroup->get_option('serial_speed')); + $line->append_widget($serial_test); + $optgroup->append_line($line); + } + } + + { + my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + parent => $self, + title => 'G-code', + config => $self->config, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + { + my $option = $optgroup->get_option('start_gcode'); + $option->height(50); + $option->full_width(1); + $optgroup->append_single_option_line($option); + } + { + my $option = $optgroup->get_option('end_gcode'); + $option->height(50); + $option->full_width(1); + $optgroup->append_single_option_line($option); + } } my $on_change = sub { @@ -120,16 +151,40 @@ sub new { ); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - my @displays = 0 .. (Wx::Display::GetCount()-1); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'display', - type => 'select', - label => 'Display', - tooltip => '', - labels => [@displays], - values => [@displays], - default => $self->config2->{display}, - )); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Display', + ); + + my @displays = 0 .. (Wx::Display::GetCount()-1); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'display', + type => 'select', + label => 'Display', + tooltip => '', + labels => [@displays], + values => [@displays], + default => $self->config2->{display}, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'zoom', + type => 'percent', + label => 'Zoom %', + tooltip => '', + default => $self->config2->{zoom}, + min => 0.1, + max => 100, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'offset', + type => 'point', + label => 'Offset', + tooltip => '', + default => $self->config2->{offset}, + )); + $optgroup->append_line($line); + } + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'show_bed', type => 'bool', @@ -137,23 +192,6 @@ sub new { tooltip => '', default => $self->config2->{show_bed}, )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'zoom', - type => 'percent', - label => 'Zoom', - sidetext => '%', - tooltip => '', - default => $self->config2->{zoom}, - min => 0.1, - max => 100, - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'offset', - type => 'point', - label => 'Offset', - tooltip => '', - default => $self->config2->{offset}, - )); } { @@ -165,30 +203,64 @@ sub new { ); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Time (seconds)', + ); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'bottom_exposure_time', + type => 'i', + label => 'Bottom exposure', + tooltip => '', + default => $self->config2->{bottom_exposure_time}, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'exposure_time', + type => 'i', + label => 'Exposure', + tooltip => '', + default => $self->config2->{exposure_time}, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'settle_time', + type => 'i', + label => 'Settle', + tooltip => '', + default => $self->config2->{settle_time}, + )); + $optgroup->append_line($line); + } + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'exposure_time', + opt_id => 'bottom_layers', type => 'i', - label => 'Exposure time', - sidetext => 'seconds', + label => 'Bottom layers', tooltip => '', - default => $self->config2->{exposure_time}, - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'settle_time', - type => 'i', - label => 'Settle time', - sidetext => 'seconds', - tooltip => '', - default => $self->config2->{settle_time}, - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z_lift', - type => 'f', - label => 'Z Lift', - sidetext => 'mm', - tooltip => '', - default => $self->config2->{z_lift}, + default => $self->config2->{bottom_layers}, )); + + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Z Lift', + ); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z_lift', + type => 'f', + label => 'Distance', + sidetext => 'mm', + tooltip => '', + default => $self->config2->{z_lift}, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z_lift_speed', + type => 'f', + label => 'Speed', + sidetext => 'mm/s', + tooltip => '', + default => $self->config2->{z_lift_speed}, + )); + $optgroup->append_line($line); + } } { @@ -410,14 +482,15 @@ sub start_print { $self->config->serial_speed, ); if (!$res || !$self->sender->wait_connected) { - Slic3r::GUI::show_error($self, "Connection failed. Check serial port and speed."); + Slic3r::GUI::show_error(undef, "Connection failed. Check serial port and speed."); return; } Slic3r::debugf "connected to " . $self->config->serial_port . "\n"; - # TODO: replace this with customizable start G-code - $self->sender->send("G28 Z", 1); - $self->sender->send(sprintf("G1 Z%.5f F5000", $self->config2->{z_lift}), 1); + # send custom start G-code + $self->sender->send($_, 1) for grep !/^;/, split /\n/, $self->config->start_gcode; + $self->sender->send(sprintf("G1 Z%.5f F%d", + $self->config2->{z_lift}, $self->config2->{z_lift_speed}*60), 1); } # TODO: block until the G1 command has been performed @@ -439,6 +512,9 @@ sub stop_print { $self->timer->Stop; $self->_timer_cb(undef); $self->screen->project_layers(undef); + + # send custom end G-code + $self->sender->send($_, 1) for grep !/^;/, split /\n/, $self->config->end_gcode; $self->sender->disconnect if $self->sender; } @@ -456,17 +532,26 @@ sub project_next_layer { if ($self->sender) { my $z = $self->current_layer_height; - $self->sender->send(sprintf("G1 Z%.5f F5000", $z + $self->config2->{z_lift}), 1); - $self->sender->send(sprintf("G1 Z%.5f F5000", $z), 1); + my $F = $self->config2->{z_lift_speed} * 60; + if ($self->config2->{z_lift} != 0) { + $self->sender->send(sprintf("G1 Z%.5f F%d", $z + $self->config2->{z_lift}, $F), 1); + } + $self->sender->send(sprintf("G1 Z%.5f F%d", $z, $F), 1); } # TODO: we should block until G1 commands have been performed, see note below # TODO: subtract this waiting time from the settle_time $self->delay(2, sub { my @layers = @{ $self->_layers->{ $self->_heights->[$self->_layer_num] } }; - printf "id = %d, height = %s\n", $self->_layer_num, $self->_heights->[$self->_layer_num]; $self->screen->project_layers([ @layers ]); - $self->delay($self->config2->{exposure_time}, sub { + + # get exposure time + my $time = $self->config2->{exposure_time}; + if ($self->_layer_num < $self->config2->{bottom_layers}) { + $time = $self->config2->{bottom_exposure_time}; + } + + $self->delay($time, sub { $self->settle; }); }); From c1f95ac173b1459778d80af876613609e142dff8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 15 Nov 2015 23:16:14 +0100 Subject: [PATCH 04/17] Slice objects even if background processing is disabled --- lib/Slic3r/GUI/Projector.pm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 68635dbe1..7bc12f3f9 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -402,6 +402,7 @@ package Slic3r::GUI::Projector::Controller; use Moo; use Wx qw(wxTheApp :id :timer); use Wx::Event qw(EVT_TIMER); +use Slic3r::Print::State ':steps'; has 'config' => (is => 'ro', required => 1); has 'config2' => (is => 'ro', required => 1); @@ -447,6 +448,18 @@ sub is_printing { sub set_print { my ($self, $print) = @_; + # make sure layers were sliced + { + my $progress_dialog; + foreach my $object (@{$print->objects}) { + next if $object->step_done(STEP_SLICE); + $progress_dialog //= Wx::ProgressDialog->new('Slicing…', "Processing layers…", 100, undef, 0); + $progress_dialog->Pulse; + $object->slice; + } + $progress_dialog->Destroy if $progress_dialog; + } + $self->_print($print); # sort layers by Z From 624c5e78db7007285bb2edb955af0402b0aec344 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 19 Nov 2015 15:04:50 +0100 Subject: [PATCH 05/17] Changed default settings for DLP projector and changed time options from integer to decimal --- lib/Slic3r/GUI/Projector.pm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 7bc12f3f9..10a17d351 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -14,12 +14,12 @@ sub new { display => 0, show_bed => 1, zoom => 100, - exposure_time => 4, - bottom_exposure_time => 10, - settle_time => 4, + exposure_time => 2, + bottom_exposure_time => 7, + settle_time => 1.5, bottom_layers => 3, - z_lift => 15, - z_lift_speed => 50, + z_lift => 5, + z_lift_speed => 8, offset => [0,0], }); @@ -209,21 +209,21 @@ sub new { ); $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'bottom_exposure_time', - type => 'i', + type => 'f', label => 'Bottom exposure', tooltip => '', default => $self->config2->{bottom_exposure_time}, )); $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'exposure_time', - type => 'i', + type => 'f', label => 'Exposure', tooltip => '', default => $self->config2->{exposure_time}, )); $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'settle_time', - type => 'i', + type => 'f', label => 'Settle', tooltip => '', default => $self->config2->{settle_time}, From c149420d235ea94aa82bbb1b32bd43ead990c8cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 19 Nov 2015 15:31:33 +0100 Subject: [PATCH 06/17] Further improvements for compilation (Ubuntu) Conflicts: .travis.yml --- .travis.yml | 5 ++++- xs/Build.PL | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cbb89cac..3b8f049d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,7 @@ branches: only: - master - stable - +before_script: + - sudo add-apt-repository ppa:boost-latest/ppa + - sudo apt-get update -qq + - sudo apt-get install libboost-thread1.55-dev libboost-system1.55-dev diff --git a/xs/Build.PL b/xs/Build.PL index 8b88ce88a..44066c28d 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -43,25 +43,36 @@ if (defined $ENV{BOOST_DIR}) { # In order to generate the -l switches we need to know how Boost libraries are named my $have_boost = 0; -foreach my $path (@boost_libs) { - my @files = glob "$path/libboost_system*"; - next if !@files; + +# check without explicit lib path (works on Linux) +$have_boost = 1 + if check_lib( + lib => "boost_system", + INC => join(' ', map "-I$_", @INC, @boost_include), + LIBS => join(' ', map "-L$_", @INC, @boost_libs), + ); + +if (!$have_boost) { + foreach my $path (@boost_libs) { + my @files = glob "$path/libboost_system*"; + next if !@files; - if ($files[0] =~ /libboost_system([^.]+)/) { - my $suffix = $1; - check_lib( - lib => "boost_system$suffix", - INC => join(' ', map "-I$_", @INC, @boost_include), - LIBS => "-L$path", - ) or next; + if ($files[0] =~ /libboost_system([^.]+)/) { + my $suffix = $1; + check_lib( + lib => "boost_system$suffix", + INC => join(' ', map "-I$_", @INC, @boost_include), + LIBS => "-L$path", + ) or next; - push @INC, (map " -I$_", @boost_include); # TODO: only use the one related to the chosen lib path - push @LIBS, " -L$path", (map " -lboost_$_$suffix", qw(thread system)); # we need these - push @cflags, '-DBOOST_LIBS'; - $have_boost = 1; - last; + push @INC, (map " -I$_", @boost_include); # TODO: only use the one related to the chosen lib path + push @LIBS, " -L$path", (map " -lboost_$_$suffix", qw(thread system)); # we need these + $have_boost = 1; + last; + } } } +push @cflags, '-DBOOST_LIBS' if $have_boost; die <<'EOF' if !$have_boost; Slic3r requires the Boost libraries. Please make sure they are installed. From 6fde339164a3f25247ebf47ba5738d91493d6000 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 19 Nov 2015 17:00:25 +0100 Subject: [PATCH 07/17] One more fix for Travis CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3b8f049d4..5252939de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,6 @@ branches: - master - stable before_script: - - sudo add-apt-repository ppa:boost-latest/ppa + - sudo add-apt-repository -y ppa:boost-latest/ppa - sudo apt-get update -qq - sudo apt-get install libboost-thread1.55-dev libboost-system1.55-dev From 128b2623cca8d66df77cfe0121be236959c1e2ac Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 19 Nov 2015 17:02:01 +0100 Subject: [PATCH 08/17] More small fixes for compilation on Linux --- xs/Build.PL | 4 +++- xs/src/libslic3r/GCodeSender.cpp | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/xs/Build.PL b/xs/Build.PL index 44066c28d..08bb259d1 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -52,7 +52,9 @@ $have_boost = 1 LIBS => join(' ', map "-L$_", @INC, @boost_libs), ); -if (!$have_boost) { +if ($have_boost) { + push @LIBS, '-lboost_system', '-lboost_thread'; +} else { foreach my $path (@boost_libs) { my @files = glob "$path/libboost_system*"; next if !@files; diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index 27a2d34c5..56b256a06 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -105,8 +105,8 @@ GCodeSender::set_baud_rate(unsigned int baud_rate) long closestSpeed = ss.baud_base / ss.custom_divisor; //cout << " Closest speed " << closestSpeed << endl; ss.reserved_char[0] = 0; - if (closestSpeed < baud * 98 / 100 || closestSpeed > baud_rate * 102 / 100) { - throw std::exception("Failed to set baud rate"); + if (closestSpeed < baud_rate * 98 / 100 || closestSpeed > baud_rate * 102 / 100) { + printf("Failed to set baud rate\n"); } ioctl(handle, TIOCSSERIAL, &ss); @@ -316,7 +316,7 @@ GCodeSender::on_read(const boost::system::error_code& error, } this->send(); } else { - printf("Cannot resend %lu (last was %lu)\n", toresend, this->sent); + printf("Cannot resend %zu (last was %zu)\n", toresend, this->sent); } } else if (boost::starts_with(line, "wait")) { // ignore From 2efc759a74e4ff60b2d7d377ac7b733d4f9c8e06 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 19 Nov 2015 17:31:47 +0100 Subject: [PATCH 09/17] Add manual control to DLP projector too --- .../GUI/Controller/ManualControlDialog.pm | 8 +++--- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 3 ++- lib/Slic3r/GUI/Projector.pm | 25 +++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index 84c61deb1..dd3009eed 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -14,11 +14,11 @@ __PACKAGE__->mk_accessors(qw(sender)); use constant TRAVEL_SPEED => 130*60; # TODO: make customizable? sub new { - my ($class, $printer_panel) = @_; + my ($class, $parent, $config, $sender) = @_; - my $self = $class->SUPER::new($printer_panel, -1, "Manual Control", wxDefaultPosition, + my $self = $class->SUPER::new($parent, -1, "Manual Control", wxDefaultPosition, [430,380], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->sender($printer_panel->sender); + $self->sender($sender); my $bed_sizer = Wx::FlexGridSizer->new(2, 3, 1, 1); $bed_sizer->AddGrowableCol(1, 1); @@ -53,7 +53,7 @@ sub new { # Bed canvas { - my $bed_shape = $printer_panel->config->bed_shape; + my $bed_shape = $config->bed_shape; $self->{canvas} = my $canvas = Slic3r::GUI::2DBed->new($self, $bed_shape); $canvas->interactive(1); $canvas->on_move(sub { diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm index eff99dc4d..290067cba 100644 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -170,7 +170,8 @@ sub new { $btn->Hide; $left_sizer->Add($btn, 0, wxTOP, 15); EVT_BUTTON($self, $btn, sub { - my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new($self); + my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new + ($self, $self->config, $self->sender); $dlg->ShowModal; }); } diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 10a17d351..b06817e11 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -267,6 +267,30 @@ sub new { my $buttons = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + { + my $btn = $self->{btn_manual_control} = Wx::Button->new($self, -1, 'Manual Control', wxDefaultPosition, wxDefaultSize); + if ($Slic3r::GUI::have_button_icons) { + $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG)); + } + $buttons->Add($btn, 0); + EVT_BUTTON($self, $btn, sub { + my $sender = Slic3r::GCode::Sender->new; + my $res = $sender->connect( + $self->config->serial_port, + $self->config->serial_speed, + ); + if (!$res || !$sender->wait_connected) { + Slic3r::GUI::show_error(undef, "Connection failed. Check serial port and speed."); + return; + } + my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new + ($self, $self->config, $sender); + $dlg->ShowModal; + $sender->disconnect; + }); + + + } { my $btn = $self->{btn_print} = Wx::Button->new($self, -1, 'Print', wxDefaultPosition, wxDefaultSize); if ($Slic3r::GUI::have_button_icons) { @@ -346,6 +370,7 @@ sub _update_buttons { my ($self) = @_; my $is_printing = $self->controller->is_printing; + $self->{btn_manual_control}->Show(!$is_printing); $self->{btn_print}->Show(!$is_printing); $self->{btn_stop}->Show($is_printing); $self->Layout; From 0af289ed78e801d99ed2009f1a9c7934ed90e31b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 09:18:41 +0100 Subject: [PATCH 10/17] Added manual projection control --- lib/Slic3r/GUI/Projector.pm | 107 ++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index b06817e11..9633e92cb 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -2,7 +2,7 @@ package Slic3r::GUI::Projector; use strict; use warnings; use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); +use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER); use base qw(Wx::Dialog Class::Accessor); __PACKAGE__->mk_accessors(qw(config config2 screen controller)); @@ -264,15 +264,15 @@ sub new { } { - my $buttons = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + my $sizer1 = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($sizer1, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); { my $btn = $self->{btn_manual_control} = Wx::Button->new($self, -1, 'Manual Control', wxDefaultPosition, wxDefaultSize); if ($Slic3r::GUI::have_button_icons) { $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG)); } - $buttons->Add($btn, 0); + $sizer1->Add($btn, 0); EVT_BUTTON($self, $btn, sub { my $sender = Slic3r::GCode::Sender->new; my $res = $sender->connect( @@ -296,7 +296,7 @@ sub new { if ($Slic3r::GUI::have_button_icons) { $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/control_play.png", wxBITMAP_TYPE_PNG)); } - $buttons->Add($btn, 0); + $sizer1->Add($btn, 0); EVT_BUTTON($self, $btn, sub { $self->controller->start_print; $self->_update_buttons; @@ -304,11 +304,11 @@ sub new { }); } { - my $btn = $self->{btn_stop} = Wx::Button->new($self, -1, 'Stop', wxDefaultPosition, wxDefaultSize); + my $btn = $self->{btn_stop} = Wx::Button->new($self, -1, 'Stop/Black', wxDefaultPosition, wxDefaultSize); if ($Slic3r::GUI::have_button_icons) { $btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/control_stop.png", wxBITMAP_TYPE_PNG)); } - $buttons->Add($btn, 0); + $sizer1->Add($btn, 0); EVT_BUTTON($self, $btn, sub { $self->controller->stop_print; $self->_update_buttons; @@ -316,8 +316,50 @@ sub new { }); } - $self->{status_text} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, [200,-1]); - $buttons->Add($self->{status_text}, 0); + { + { + my $text = Wx::StaticText->new($self, -1, "Layer:", wxDefaultPosition, wxDefaultSize); + $text->SetFont($Slic3r::GUI::small_font); + $sizer1->Add($text, 0, wxEXPAND | wxLEFT, 10); + } + { + my $spin = $self->{layers_spinctrl} = Wx::SpinCtrl->new($self, -1, 0, wxDefaultPosition, [60,-1], + 0, 0, 300, 0); + $sizer1->Add($spin, 0); + EVT_SPINCTRL($self, $spin, sub { + my $value = $spin->GetValue; + $self->{layers_slider}->SetValue($value); + $self->controller->project_layer($value); + $self->_update_buttons; + }); + } + { + my $slider = $self->{layers_slider} = Wx::Slider->new( + $self, -1, + 0, # default + 0, # min + 300, # max + wxDefaultPosition, + wxDefaultSize, + ); + $sizer1->Add($slider, 1); + EVT_SLIDER($self, $slider, sub { + my $value = $slider->GetValue; + $self->{layers_spinctrl}->SetValue($value); + $self->controller->project_layer($value); + $self->_update_buttons; + }); + } + } + + my $sizer2 = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($sizer2, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + { + $self->{status_text} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize); + $self->{status_text}->SetFont($Slic3r::GUI::small_font); + $sizer2->Add($self->{status_text}, 1 | wxEXPAND); + } } { @@ -351,6 +393,8 @@ sub new { screen => $self->screen, on_project_layer => sub { my ($layer_num) = @_; + + $self->{layers_spinctrl}->SetValue($layer_num); $self->_set_status(sprintf "Printing layer %d/%d (z = %.2f)", $layer_num, $self->controller->layer_count, $self->controller->current_layer_height); @@ -360,6 +404,7 @@ sub new { $self->_set_status(''); }, )); + #$self->{layers_spinctrl}->SetMax($self->controller->layer_count); $self->_update_buttons; @@ -370,9 +415,11 @@ sub _update_buttons { my ($self) = @_; my $is_printing = $self->controller->is_printing; + my $is_projecting = $self->controller->is_projecting; $self->{btn_manual_control}->Show(!$is_printing); - $self->{btn_print}->Show(!$is_printing); - $self->{btn_stop}->Show($is_printing); + $self->{btn_print}->Show(!$is_printing && !$is_projecting); + $self->{btn_stop}->Show($is_printing || $is_projecting); + $self->{layers_spinctrl}->Enable(!$is_printing); $self->Layout; } @@ -436,6 +483,7 @@ has 'on_project_layer' => (is => 'rw'); has 'on_print_completed' => (is => 'rw'); has 'sender' => (is => 'rw'); has 'timer' => (is => 'rw'); +has 'is_printing' => (is => 'rw', default => sub { 0 }); has '_print' => (is => 'rw'); has '_layers' => (is => 'rw'); has '_heights' => (is => 'rw'); @@ -464,12 +512,6 @@ sub delay { $self->timer->Start($wait * 1000, wxTIMER_ONE_SHOT); } -sub is_printing { - my ($self) = @_; - - return $self->timer->IsRunning; -} - sub set_print { my ($self, $print) = @_; @@ -531,9 +573,11 @@ sub start_print { $self->config2->{z_lift}, $self->config2->{z_lift_speed}*60), 1); } + $self->is_printing(1); + # TODO: block until the G1 command has been performed # we could do this with M400 + M115 but maybe it's not portable - $self->delay(2, sub { + $self->delay(5, sub { # start with black Slic3r::debugf "starting black projection\n"; $self->_layer_num(-1); @@ -547,6 +591,7 @@ sub start_print { sub stop_print { my ($self) = @_; + $self->is_printing(0); $self->timer->Stop; $self->_timer_cb(undef); $self->screen->project_layers(undef); @@ -556,13 +601,32 @@ sub stop_print { $self->sender->disconnect if $self->sender; } +sub is_projecting { + my ($self) = @_; + + return defined $self->screen->layers; +} + +sub project_layer { + my ($self, $layer_num) = @_; + + if (!defined $layer_num || $layer_num >= $self->layer_count) { + $self->screen->project_layers(undef); + return; + } + + my @layers = @{ $self->_layers->{ $self->_heights->[$layer_num] } }; + $self->screen->project_layers([ @layers ]); +} + sub project_next_layer { my ($self) = @_; - Slic3r::debugf "projecting next layer\n"; $self->_layer_num($self->_layer_num + 1); + Slic3r::debugf "projecting layer %d\n", $self->_layer_num; if ($self->_layer_num >= $self->layer_count) { - $self->on_print_completed->() if $self->on_print_completed; + $self->on_print_completed->() + if $self->is_printing && $self->on_print_completed; return; } @@ -580,8 +644,7 @@ sub project_next_layer { # TODO: we should block until G1 commands have been performed, see note below # TODO: subtract this waiting time from the settle_time $self->delay(2, sub { - my @layers = @{ $self->_layers->{ $self->_heights->[$self->_layer_num] } }; - $self->screen->project_layers([ @layers ]); + $self->project_layer($self->_layer_num); # get exposure time my $time = $self->config2->{exposure_time}; From cc57432be4d41752831397f299b43524250a56f4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 09:32:02 +0100 Subject: [PATCH 11/17] Let user configure travel speed in manual control dialog --- .../GUI/Controller/ManualControlDialog.pm | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index dd3009eed..2a444d2b4 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -9,17 +9,20 @@ use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap use Wx::Event qw(EVT_CLOSE EVT_BUTTON); use base qw(Wx::Dialog Class::Accessor); -__PACKAGE__->mk_accessors(qw(sender)); - -use constant TRAVEL_SPEED => 130*60; # TODO: make customizable? +__PACKAGE__->mk_accessors(qw(sender config2)); sub new { my ($class, $parent, $config, $sender) = @_; my $self = $class->SUPER::new($parent, -1, "Manual Control", wxDefaultPosition, - [430,380], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + [500,380], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); $self->sender($sender); + $self->config2({ + xy_travel_speed => 130, + z_travel_speed => 10, + }); + my $bed_sizer = Wx::FlexGridSizer->new(2, 3, 1, 1); $bed_sizer->AddGrowableCol(1, 1); $bed_sizer->AddGrowableRow(0, 1); @@ -94,10 +97,40 @@ sub new { $bed_sizer->Add($sizer, 1, wxEXPAND, 0); } + my $optgroup = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Settings', + on_change => sub { + my ($opt_id, $value) = @_; + $self->config2->{$opt_id} = $value; + }, + ); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Speed (mm/s)', + ); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'xy_travel_speed', + type => 'f', + label => 'X/Y', + tooltip => '', + default => $self->config2->{xy_travel_speed}, + )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z_travel_speed', + type => 'f', + label => 'Z', + tooltip => '', + default => $self->config2->{z_travel_speed}, + )); + $optgroup->append_line($line); + } + my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); $main_sizer->Add($bed_sizer, 1, wxEXPAND | wxALL, 10); - $main_sizer->Add($self->CreateButtonSizer(wxCLOSE), 0, wxEXPAND); - EVT_BUTTON($self, wxID_CLOSE, sub { $self->Close }); + $main_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10); + #$main_sizer->Add($self->CreateButtonSizer(wxCLOSE), 0, wxEXPAND); + #EVT_BUTTON($self, wxID_CLOSE, sub { $self->Close }); $self->SetSizer($main_sizer); $self->SetMinSize($self->GetSize); @@ -117,15 +150,16 @@ sub abs_xy_move { my ($self, $pos) = @_; $self->sender->send("G90", 1); # set absolute positioning - $self->sender->send(sprintf("G1 X%.1f Y%.1f F%d", @$pos, TRAVEL_SPEED), 1); + $self->sender->send(sprintf("G1 X%.1f Y%.1f F%d", @$pos, $self->config2->{xy_travel_speed}*60), 1); $self->{canvas}->set_pos($pos); } sub rel_move { my ($self, $axis, $distance) = @_; + my $speed = ($axis eq 'Z') ? $self->config2->{z_travel_speed} : $self->config2->{xy_travel_speed}; $self->sender->send("G91", 1); # set relative positioning - $self->sender->send(sprintf("G1 %s%.1f F%d", $axis, $distance, TRAVEL_SPEED), 1); + $self->sender->send(sprintf("G1 %s%.1f F%d", $axis, $distance, $speed*60), 1); $self->sender->send("G90", 1); # set absolute positioning if (my $pos = $self->{canvas}->pos) { From 4a65671f64a7f6f12fd11e5fbd87b03a6b4edc41 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 09:36:17 +0100 Subject: [PATCH 12/17] Prevent absolute movement if user hasn't homed both X and Y --- lib/Slic3r/GUI/Controller/ManualControlDialog.pm | 9 ++++++++- lib/Slic3r/GUI/Projector.pm | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index 2a444d2b4..2214b3eef 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -9,7 +9,7 @@ use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap use Wx::Event qw(EVT_CLOSE EVT_BUTTON); use base qw(Wx::Dialog Class::Accessor); -__PACKAGE__->mk_accessors(qw(sender config2)); +__PACKAGE__->mk_accessors(qw(sender config2 x_homed y_homed)); sub new { my ($class, $parent, $config, $sender) = @_; @@ -62,6 +62,11 @@ sub new { $canvas->on_move(sub { my ($pos) = @_; + if (!($self->x_homed && $self->y_homed)) { + Slic3r::GUI::show_error($self, "Please home both X and Y before moving."); + return ; + } + # delete any pending commands to get a smoother movement $self->sender->purge_queue(1); $self->abs_xy_move($pos); @@ -177,6 +182,8 @@ sub home { $self->sender->send(sprintf("G28 %s", $axis), 1); $self->{canvas}->set_pos(undef); + $self->x_homed if $axis eq 'X'; + $self->y_homed if $axis eq 'Y'; } 1; diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 9633e92cb..b012efd8e 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -281,7 +281,7 @@ sub new { ); if (!$res || !$sender->wait_connected) { Slic3r::GUI::show_error(undef, "Connection failed. Check serial port and speed."); - return; + #return; } my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new ($self, $self->config, $sender); From 78d2240d710b615795a0d16a4a306bea5f140369 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 09:41:13 +0100 Subject: [PATCH 13/17] Limit slider to number of layers --- lib/Slic3r/GUI/Projector.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index b012efd8e..386da0b29 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -395,6 +395,7 @@ sub new { my ($layer_num) = @_; $self->{layers_spinctrl}->SetValue($layer_num); + $self->{layers_slider}->SetValue($layer_num); $self->_set_status(sprintf "Printing layer %d/%d (z = %.2f)", $layer_num, $self->controller->layer_count, $self->controller->current_layer_height); @@ -404,7 +405,11 @@ sub new { $self->_set_status(''); }, )); - #$self->{layers_spinctrl}->SetMax($self->controller->layer_count); + { + my $max = $self->controller->layer_count-1; + $self->{layers_spinctrl}->SetRange(0, $max); + $self->{layers_slider}->SetRange(0, $max); + } $self->_update_buttons; From bd00e7c584289a3bc0fd95f7c155971da8029d0c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 09:57:51 +0100 Subject: [PATCH 14/17] Fix projection of slices with holes because wxDC is not honoring the fill rule --- lib/Slic3r/GUI/Projector.pm | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 386da0b29..515718a03 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -4,6 +4,7 @@ use warnings; use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER); use base qw(Wx::Dialog Class::Accessor); +use utf8; __PACKAGE__->mk_accessors(qw(config config2 screen controller)); @@ -771,15 +772,19 @@ sub _repaint { # get layers at this height # draw layers $dc->SetPen(Wx::Pen->new(wxWHITE, 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); foreach my $layer (@{$self->layers}) { + my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{ $layer->slices }; foreach my $copy (@{$layer->object->_shifted_copies}) { - foreach my $expolygon (@{ $layer->slices }) { - $expolygon = $expolygon->clone; - $expolygon->translate(@$copy); - foreach my $points (@{$expolygon->pp}) { - $dc->DrawPolygon($self->scaled_points_to_pixel($points), 0, 0); + foreach my $polygon (@polygons) { + $polygon = $polygon->clone; + $polygon->translate(@$copy); + + if ($polygon->is_counter_clockwise) { + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + } else { + $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); } + $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); } } } From f9d1ca837340a1abeea3af7267f34e752d61dc58 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 10:04:17 +0100 Subject: [PATCH 15/17] Project grid --- lib/Slic3r/GUI/Projector.pm | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 515718a03..73255b22f 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -687,6 +687,7 @@ use base qw(Wx::Dialog Class::Accessor); use List::Util qw(min); use Slic3r::Geometry qw(X Y unscale scale); +use Slic3r::Geometry::Clipper qw(intersection_pl); __PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layers)); @@ -763,8 +764,27 @@ sub _repaint { if ($self->config2->{show_bed}) { $dc->SetPen(Wx::Pen->new(wxRED, 2, wxSOLID)); $dc->SetBrush(Wx::Brush->new(wxWHITE, wxTRANSPARENT)); + + # draw contour my $bed_polygon = Slic3r::Polygon->new_scale(@{$self->config->bed_shape}); $dc->DrawPolygon($self->scaled_points_to_pixel($bed_polygon), 0, 0); + + # draw grid + $dc->SetPen(Wx::Pen->new(wxRED, 1, wxSOLID)); + { + my $bb = $bed_polygon->bounding_box; + my $step = scale 10; # 1cm grid + my @polylines = (); + for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) { + push @polylines, Slic3r::Polyline->new([$x, $bb->y_min], [$x, $bb->y_max]); + } + for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) { + push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); + } + $dc->DrawLine(map @$_, @$_) + for map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), + @{intersection_pl(\@polylines, [$bed_polygon])}; + } } return if !defined $self->layers; From ad4940a1d6b07f3b049978ef9997dd783dbd2af1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 20 Nov 2015 10:30:56 +0100 Subject: [PATCH 16/17] New option for inverting the Y axis in projection --- lib/Slic3r/GUI/Projector.pm | 40 +++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 73255b22f..3f7d43a90 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -14,6 +14,7 @@ sub new { $self->config2({ display => 0, show_bed => 1, + invert_y => 0, zoom => 100, exposure_time => 2, bottom_exposure_time => 7, @@ -43,7 +44,6 @@ sub new { $self->config->apply(wxTheApp->{mainframe}->config); { - my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( parent => $self, title => 'USB/Serial connection', @@ -183,6 +183,13 @@ sub new { tooltip => '', default => $self->config2->{offset}, )); + $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'invert_y', + type => 'bool', + label => 'Invert Y', + tooltip => '', + default => $self->config2->{invert_y}, + )); $optgroup->append_line($line); } @@ -681,7 +688,7 @@ sub DESTROY { } package Slic3r::GUI::Projector::Screen; -use Wx qw(:dialog :id :misc :sizer :colour :pen :brush); +use Wx qw(:dialog :id :misc :sizer :colour :pen :brush :font); use Wx::Event qw(EVT_PAINT EVT_SIZE); use base qw(Wx::Dialog Class::Accessor); @@ -782,9 +789,24 @@ sub _repaint { push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); } $dc->DrawLine(map @$_, @$_) - for map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), + for map $self->scaled_points_to_pixel([ @$_[0,-1] ]), @{intersection_pl(\@polylines, [$bed_polygon])}; } + + # draw axes orientation + $dc->SetPen(Wx::Pen->new(wxWHITE, 4, wxSOLID)); + { + foreach my $endpoint ([10, 0], [0, 10]) { + $dc->DrawLine( + map @{$self->unscaled_point_to_pixel($_)}, [0,0], $endpoint + ); + } + + $dc->SetTextForeground(wxWHITE); + $dc->SetFont(Wx::Font->new(20, wxDEFAULT, wxNORMAL, wxNORMAL)); + $dc->DrawText("X", @{$self->unscaled_point_to_pixel([10, -2])}); + $dc->DrawText("Y", @{$self->unscaled_point_to_pixel([-2, 10])}); + } } return if !defined $self->layers; @@ -814,12 +836,18 @@ sub _repaint { sub unscaled_point_to_pixel { my ($self, $point) = @_; - my $ch = $self->GetSize->GetHeight; my $zero = $self->bed_origin; - return [ + my $p = [ $point->[X] * $self->scaling_factor + $zero->[X], - $ch - ($point->[Y] * $self->scaling_factor + $zero->[Y]), + $point->[Y] * $self->scaling_factor + $zero->[Y], ]; + + if (!$self->config2->{invert_y}) { + my $ch = $self->GetSize->GetHeight; + $p->[Y] = $ch - $p->[Y]; + } + + return $p; } sub scaled_points_to_pixel { From 017ed0591106e02dd702646e8e351acef218111b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 22 Nov 2015 10:13:58 +0100 Subject: [PATCH 17/17] Removed debugging comment --- lib/Slic3r/GUI/Projector.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 3f7d43a90..d2cef6eda 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -289,7 +289,7 @@ sub new { ); if (!$res || !$sender->wait_connected) { Slic3r::GUI::show_error(undef, "Connection failed. Check serial port and speed."); - #return; + return; } my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new ($self, $self->config, $sender);