diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 483fd36f9..d25f1a8af 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -6,25 +6,11 @@ use utf8; use File::Basename qw(basename); use FindBin; use List::Util qw(first); -use Slic3r::GUI::2DBed; -use Slic3r::GUI::Controller; -use Slic3r::GUI::Controller::ManualControlDialog; -use Slic3r::GUI::Controller::PrinterPanel; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Plater; -use Slic3r::GUI::Plater::2D; -use Slic3r::GUI::Plater::2DToolpaths; use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::3DPreview; -use Slic3r::GUI::Plater::ObjectPartsPanel; -use Slic3r::GUI::Plater::ObjectCutDialog; -use Slic3r::GUI::Plater::ObjectSettingsDialog; -use Slic3r::GUI::Plater::LambdaObjectDialog; -use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::ProgressStatusBar; -use Slic3r::GUI::OptionsGroup; -use Slic3r::GUI::OptionsGroup::Field; -use Slic3r::GUI::SystemInfo; use Wx::Locale gettext => 'L'; @@ -226,16 +212,15 @@ sub system_info { $opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1); $opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1); } - my $about = Slic3r::GUI::SystemInfo->new( - parent => undef, - slic3r_info => $slic3r_info, -# copyright_info => $copyright_info, - system_info => $system_info, - opengl_info => $opengl_info, - text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt, - ); - $about->ShowModal; - $about->Destroy; +# my $about = Slic3r::GUI::SystemInfo->new( +# parent => undef, +# slic3r_info => $slic3r_info, +# system_info => $system_info, +# opengl_info => $opengl_info, +# text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt, +# ); +# $about->ShowModal; +# $about->Destroy; } # static method accepting a wxWindow object as first parameter diff --git a/lib/Slic3r/GUI/2DBed.pm b/lib/Slic3r/GUI/2DBed.pm deleted file mode 100644 index 0891a4836..000000000 --- a/lib/Slic3r/GUI/2DBed.pm +++ /dev/null @@ -1,217 +0,0 @@ -# Bed shape dialog -# still used by the Slic3r::GUI::Controller::ManualControlDialog Perl module. - -package Slic3r::GUI::2DBed; -use strict; -use warnings; - -use List::Util qw(min max); -use Slic3r::Geometry qw(X Y unscale deg2rad); -use Slic3r::Geometry::Clipper qw(intersection_pl); -use Wx qw(:misc :pen :brush :font :systemsettings wxTAB_TRAVERSAL wxSOLID); -use Wx::Event qw(EVT_PAINT EVT_ERASE_BACKGROUND EVT_MOUSE_EVENTS EVT_SIZE); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(bed_shape interactive pos _scale_factor _shift on_move _painted)); - -sub new { - my ($class, $parent, $bed_shape) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [250,-1], wxTAB_TRAVERSAL); - $self->{user_drawn_background} = $^O ne 'darwin'; - $self->bed_shape($bed_shape // []); - EVT_PAINT($self, \&_repaint); - EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; - EVT_MOUSE_EVENTS($self, \&_mouse_event); - EVT_SIZE($self, sub { $self->Refresh; }); - return $self; -} - -sub _repaint { - my ($self, $event) = @_; - - my $dc = Wx::AutoBufferedPaintDC->new($self); - my ($cw, $ch) = $self->GetSizeWH; - return if $cw == 0; # when canvas is not rendered yet, size is 0,0 - - if ($self->{user_drawn_background}) { - # On all systems the AutoBufferedPaintDC() achieves double buffering. - # On MacOS the background is erased, on Windows the background is not erased - # and on Linux/GTK the background is erased to gray color. - # Fill DC with the background on Windows & Linux/GTK. - my $color = Wx::SystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT); - $dc->SetPen(Wx::Pen->new($color, 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new($color, wxSOLID)); - my $rect = $self->GetUpdateRegion()->GetBox(); - $dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight()); - } - - # turn $cw and $ch from sizes to max coordinates - $cw--; - $ch--; - - my $cbb = Slic3r::Geometry::BoundingBoxf->new_from_points([ - Slic3r::Pointf->new(0, 0), - Slic3r::Pointf->new($cw, $ch), - ]); - - # leave space for origin point - $cbb->set_x_min($cbb->x_min + 4); - $cbb->set_x_max($cbb->x_max - 4); - $cbb->set_y_max($cbb->y_max - 4); - - # leave space for origin label - $cbb->set_y_max($cbb->y_max - 13); - - # read new size - ($cw, $ch) = @{$cbb->size}; - my $ccenter = $cbb->center; - - # get bounding box of bed shape in G-code coordinates - my $bed_shape = $self->bed_shape; - my $bed_polygon = Slic3r::Polygon->new_scale(@$bed_shape); - my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($bed_shape); - $bb->merge_point(Slic3r::Pointf->new(0,0)); # origin needs to be in the visible area - my ($bw, $bh) = @{$bb->size}; - my $bcenter = $bb->center; - - # calculate the scaling factor for fitting bed shape in canvas area - my $sfactor = min($cw/$bw, $ch/$bh); - my $shift = Slic3r::Pointf->new( - $ccenter->x - $bcenter->x * $sfactor, - $ccenter->y - $bcenter->y * $sfactor, #- - ); - $self->_scale_factor($sfactor); - $self->_shift(Slic3r::Pointf->new( - $shift->x + $cbb->x_min, - $shift->y - ($cbb->y_max-$self->GetSize->GetHeight), #++ - )); - - # draw bed fill - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxSOLID)); - $dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0); - } - - # draw grid - { - my $step = 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_scale([$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_scale([$bb->x_min, $y], [$bb->x_max, $y]); - } - @polylines = @{intersection_pl(\@polylines, [$bed_polygon])}; - - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID)); - $dc->DrawLine(map @{$self->to_pixels([map unscale($_), @$_])}, @$_[0,-1]) for @polylines; - } - - # draw bed contour - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxTRANSPARENT)); - $dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0); - } - - my $origin_px = $self->to_pixels(Slic3r::Pointf->new(0,0)); - - # draw axes - { - my $axes_len = 50; - my $arrow_len = 6; - my $arrow_angle = deg2rad(45); - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(255,0,0), 2, wxSOLID)); # red - my $x_end = Slic3r::Pointf->new($origin_px->[X] + $axes_len, $origin_px->[Y]); - $dc->DrawLine(@$origin_px, @$x_end); - foreach my $angle (-$arrow_angle, +$arrow_angle) { - my $end = $x_end->clone; - $end->translate(-$arrow_len, 0); - $end->rotate($angle, $x_end); - $dc->DrawLine(@$x_end, @$end); - } - - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,255,0), 2, wxSOLID)); # green - my $y_end = Slic3r::Pointf->new($origin_px->[X], $origin_px->[Y] - $axes_len); - $dc->DrawLine(@$origin_px, @$y_end); - foreach my $angle (-$arrow_angle, +$arrow_angle) { - my $end = $y_end->clone; - $end->translate(0, +$arrow_len); - $end->rotate($angle, $y_end); - $dc->DrawLine(@$y_end, @$end); - } - } - - # draw origin - { - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(0,0,0), wxSOLID)); - $dc->DrawCircle(@$origin_px, 3); - - $dc->SetTextForeground(Wx::Colour->new(0,0,0)); - $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawText("(0,0)", $origin_px->[X] + 1, $origin_px->[Y] + 2); - } - - # draw current position - if (defined $self->pos) { - my $pos_px = $self->to_pixels($self->pos); - $dc->SetPen(Wx::Pen->new(Wx::Colour->new(200,0,0), 2, wxSOLID)); - $dc->SetBrush(Wx::Brush->new(Wx::Colour->new(200,0,0), wxTRANSPARENT)); - $dc->DrawCircle(@$pos_px, 5); - - $dc->DrawLine($pos_px->[X]-15, $pos_px->[Y], $pos_px->[X]+15, $pos_px->[Y]); - $dc->DrawLine($pos_px->[X], $pos_px->[Y]-15, $pos_px->[X], $pos_px->[Y]+15); - } - - $self->_painted(1); -} - -sub _mouse_event { - my ($self, $event) = @_; - - return if !$self->interactive; - return if !$self->_painted; - - my $pos = $event->GetPosition; - my $point = $self->to_units([ $pos->x, $pos->y ]); #]] - if ($event->LeftDown || $event->Dragging) { - $self->on_move->($point) if $self->on_move; - $self->Refresh; - } -} - -# convert G-code coordinates into pixels -sub to_pixels { - my ($self, $point) = @_; - - my $p = Slic3r::Pointf->new(@$point); - $p->scale($self->_scale_factor); - $p->translate(@{$self->_shift}); - return [$p->x, $self->GetSize->GetHeight - $p->y]; #]] -} - -# convert pixels into G-code coordinates -sub to_units { - my ($self, $point) = @_; - - my $p = Slic3r::Pointf->new( - $point->[X], - $self->GetSize->GetHeight - $point->[Y], - ); - $p->translate(@{$self->_shift->negative}); - $p->scale(1/$self->_scale_factor); - return $p; -} - -sub set_pos { - my ($self, $pos) = @_; - - $self->pos($pos); - $self->Refresh; -} - -1; diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm deleted file mode 100644 index f7d90c796..000000000 --- a/lib/Slic3r/GUI/Controller.pm +++ /dev/null @@ -1,190 +0,0 @@ -# The "Controller" tab to control the printer using serial / USB. -# This feature is rarely used. Much more often, the firmware reads the G-codes from a SD card. -# May there be multiple subtabs per each printer connected? - -package Slic3r::GUI::Controller; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog wxBORDER_NONE); -use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU); -use base qw(Wx::ScrolledWindow Class::Accessor); -use List::Util qw(first); - -__PACKAGE__->mk_accessors(qw(_selected_printer_preset)); - -our @ConfigOptions = qw(bed_shape serial_port serial_speed); - -sub new { - my ($class, $parent) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [600,350]); - - $self->SetScrollbars(0, 1, 0, 1); - $self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL); - - # warning to show when there are no printers configured - { - $self->{text_no_printers} = Wx::StaticText->new($self, -1, - "No printers were configured for USB/serial control.", - wxDefaultPosition, wxDefaultSize); - $self->{sizer}->Add($self->{text_no_printers}, 0, wxTOP | wxLEFT, 30); - } - - # button for adding new printer panels - { - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - $btn->SetToolTipString("Add printer…") - if $btn->can('SetToolTipString'); - - EVT_LEFT_DOWN($btn, sub { - my $menu = Wx::Menu->new; - my @panels = $self->print_panels; - # remove printers that already exist - # update configs of currently loaded print panels - foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) { - my $preset_name = $preset->name; - next if ! $preset->config->serial_port || - defined first { defined $_ && $_->printer_name eq $preset_name } @panels; - my $myconfig = $preset->config->clone_only(\@ConfigOptions); - my $id = &Wx::NewId(); - $menu->Append($id, $preset_name); - EVT_MENU($menu, $id, sub { - $self->add_printer($preset_name, $myconfig); - }); - } - $self->PopupMenu($menu, $btn->GetPosition); - $menu->Destroy; - }); - $self->{sizer}->Add($btn, 0, wxTOP | wxLEFT, 10); - } - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - #$sizer->SetSizeHints($self); - - EVT_CLOSE($self, sub { - my (undef, $event) = @_; - - if ($event->CanVeto) { - foreach my $panel ($self->print_panels) { - if ($panel->printing) { - my $confirm = Wx::MessageDialog->new( - $self, "Printer '" . $panel->printer_name . "' is printing.\n\nDo you want to stop printing?", - 'Unfinished Print', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION, - ); - if ($confirm->ShowModal == wxID_NO) { - $event->Veto; - return; - } - } - } - } - foreach my $panel ($self->print_panels) { - $panel->disconnect; - } - - $event->Skip; - }); - - $self->Layout; - - return $self; -} - -sub OnActivate { - my ($self) = @_; - - # get all available presets - my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) } - grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer}; - - # decide which ones we want to keep - my %active = (); - - # keep the ones that are currently connected or have jobs in queue - $active{$_} = 1 for map $_->printer_name, - grep { $_->is_connected || @{$_->jobs} > 0 } - $self->print_panels; - - if (%presets) { - # if there are no active panels, use sensible defaults - if (!%active && keys %presets <= 2) { - # if only one or two presets exist, load them - $active{$_} = 1 for keys %presets; - } - if (!%active) { - # enable printers whose port is available - my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports; - $active{$_} = 1 - for grep exists $ports{$presets{$_}->serial_port}, keys %presets; - } - if (!%active && $self->_selected_printer_preset) { - # enable currently selected printer if it is configured - $active{$self->_selected_printer_preset} = 1 - if $presets{$self->_selected_printer_preset}; - } - } - - # apply changes - for my $panel ($self->print_panels) { - next if $active{$panel->printer_name}; - - $self->{sizer}->DetachWindow($panel); - $panel->Destroy; - } - $self->add_printer($_, $presets{$_}) for sort keys %active; - - # show/hide the warning about no printers - $self->{text_no_printers}->Show(!%presets); - - # show/hide the Add button - $self->{btn_add}->Show(keys %presets != keys %active); - - $self->Layout; - - # we need this in order to trigger the OnSize event of wxScrolledWindow which - # recalculates the virtual size - Wx::GetTopLevelParent($self)->SendSizeEvent; -} - -sub add_printer { - my ($self, $printer_name, $config) = @_; - - # check that printer doesn't exist already - foreach my $panel ($self->print_panels) { - if ($panel->printer_name eq $printer_name) { - return $panel; - } - } - - my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $printer_name, $config); - $self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); - $self->Layout; - - return $printer_panel; -} - -sub print_panels { - my ($self) = @_; - return grep $_->isa('Slic3r::GUI::Controller::PrinterPanel'), - map $_->GetWindow, $self->{sizer}->GetChildren; -} - -# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed -# when the presets are loaded or the user selects another preset. -sub update_presets { - my ($self, $presets) = @_; - # update configs of currently loaded print panels - my @presets = @$presets; - foreach my $panel ($self->print_panels) { - my $preset = $presets->find_preset($panel->printer_name, 0); - $panel->config($preset->config->clone_only(\@ConfigOptions)) - if defined $preset; - } - - $self->_selected_printer_preset($presets->get_selected_preset->name); -} - -1; diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm deleted file mode 100644 index de0565255..000000000 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ /dev/null @@ -1,190 +0,0 @@ -# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected? - -package Slic3r::GUI::Controller::ManualControlDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap - wxBORDER_NONE wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); -use base qw(Wx::Dialog Class::Accessor); - -__PACKAGE__->mk_accessors(qw(sender config2 x_homed y_homed)); - -sub new { - my ($class, $parent, $config, $sender) = @_; - - my $self = $class->SUPER::new($parent, -1, "Manual Control", wxDefaultPosition, - [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); - - my $move_button = sub { - my ($sizer, $label, $icon, $bold, $pos, $handler) = @_; - - my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, - wxBU_LEFT | wxBU_EXACTFIT); - $btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapPosition($pos); - EVT_BUTTON($self, $btn, $handler); - $sizer->Add($btn, 1, wxEXPAND | wxALL, 0); - }; - - # Y buttons - { - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - for my $d (qw(+10 +1 +0.1)) { - $move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Y', $d) }); - } - $move_button->($sizer, 'Y', 'house', 1, wxLEFT, sub { $self->home('Y') }); - for my $d (qw(-0.1 -1 -10)) { - $move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Y', $d) }); - }; - $bed_sizer->Add($sizer, 1, wxEXPAND, 0); - } - - # Bed canvas - { - my $bed_shape = $config->bed_shape; - $self->{canvas} = my $canvas = Slic3r::GUI::2DBed->new($self, $bed_shape); - $canvas->interactive(1); - $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); - }); - $bed_sizer->Add($canvas, 0, wxEXPAND | wxRIGHT, 3); - } - - # Z buttons - { - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - for my $d (qw(+10 +1 +0.1)) { - $move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Z', $d) }); - } - $move_button->($sizer, 'Z', 'house', 1, wxLEFT, sub { $self->home('Z') }); - for my $d (qw(-0.1 -1 -10)) { - $move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Z', $d) }); - }; - $bed_sizer->Add($sizer, 1, wxEXPAND, 0); - } - - # XYZ home button - $move_button->($bed_sizer, 'XYZ', 'house', 1, wxTOP, sub { $self->home(undef) }); - - # X buttons - { - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - for my $d (qw(-10 -1 -0.1)) { - $move_button->($sizer, $d, 'arrow_left', 0, wxTOP, sub { $self->rel_move('X', $d) }); - } - $move_button->($sizer, 'X', 'house', 1, wxTOP, sub { $self->home('X') }); - for my $d (qw(+0.1 +1 +10)) { - $move_button->($sizer, $d, 'arrow_right', 0, wxTOP, sub { $self->rel_move('X', $d) }); - } - $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($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); - #$main_sizer->SetSizeHints($self); - $self->Layout; - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -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, $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, $speed*60), 1); - $self->sender->send("G90", 1); # set absolute positioning - - if (my $pos = $self->{canvas}->pos) { - if ($axis eq 'X') { - $pos->translate($distance, 0); - } elsif ($axis eq 'Y') { - $pos->translate(0, $distance); - } - $self->{canvas}->set_pos($pos); - } -} - -sub home { - my ($self, $axis) = @_; - - $axis //= ''; - $self->sender->send(sprintf("G28 %s", $axis), 1); - $self->{canvas}->set_pos(undef); - $self->x_homed(1) if $axis eq 'X'; - $self->y_homed(1) if $axis eq 'Y'; -} - -1; diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm deleted file mode 100644 index 794ea4e4e..000000000 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ /dev/null @@ -1,707 +0,0 @@ -package Slic3r::GUI::Controller::PrinterPanel; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer - :textctrl :font :systemsettings); -use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER EVT_SCROLLWIN); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(printer_name config sender jobs - printing status_timer temp_timer)); - -use constant CONNECTION_TIMEOUT => 3; # seconds -use constant STATUS_TIMER_INTERVAL => 1000; # milliseconds -use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds - -sub new { - my ($class, $parent, $printer_name, $config) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]); - - $self->printer_name($printer_name || 'Printer'); - $self->config($config); - $self->jobs([]); - - # set up the timer that polls for updates - { - my $timer_id = &Wx::NewId(); - $self->status_timer(Wx::Timer->new($self, $timer_id)); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - - if ($self->printing) { - my $queue_size = $self->sender->queue_size; - $self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size); - if ($queue_size == 0) { - $self->print_completed; - } - } - $self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log}; - { - my $temp = $self->sender->getT; - if ($temp eq '') { - $self->{temp_panel}->Hide; - } else { - if (!$self->{temp_panel}->IsShown) { - $self->{temp_panel}->Show; - $self->Layout; - } - $self->{temp_text}->SetLabel($temp . "°C"); - - $temp = $self->sender->getB; - if ($temp eq '') { - $self->{bed_temp_text}->SetLabel('n.a.'); - } else { - $self->{bed_temp_text}->SetLabel($temp . "°C"); - } - } - } - }); - } - - # set up the timer that sends temperature requests - # (responses are handled by status_timer) - { - my $timer_id = &Wx::NewId(); - $self->temp_timer(Wx::Timer->new($self, $timer_id)); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - $self->sender->send("M105", 1); # send it through priority queue - }); - } - - my $box = Wx::StaticBox->new($self, -1, ""); - my $sizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL); - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - - # printer name - { - my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [220,-1]); - my $font = $text->GetFont; - $font->SetPointSize(20); - $text->SetFont($font); - $left_sizer->Add($text, 0, wxEXPAND, 0); - } - - # connection info - { - my $conn_sizer = Wx::FlexGridSizer->new(2, 2, 1, 0); - $conn_sizer->SetFlexibleDirection(wxHORIZONTAL); - $conn_sizer->AddGrowableCol(1, 1); - $left_sizer->Add($conn_sizer, 0, wxEXPAND | wxTOP, 5); - { - my $text = Wx::StaticText->new($box, -1, "Port:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - $self->{serial_port_combobox} = Wx::ComboBox->new($box, -1, $config->serial_port, wxDefaultPosition, wxDefaultSize, []); - $self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font); - $self->update_serial_ports; - $serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1); - } - { - $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -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'); - $serial_port_sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL, 0); - EVT_BUTTON($self, $btn, sub { $self->update_serial_ports }); - } - $conn_sizer->Add($serial_port_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - - { - my $text = Wx::StaticText->new($box, -1, "Speed:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - $self->{serial_speed_combobox} = Wx::ComboBox->new($box, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize, - ["115200", "250000"]); - $self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font); - $serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0); - } - { - $self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); - $serial_speed_sizer->Add($btn, 0, wxLEFT, 5); - EVT_BUTTON($self, $btn, \&disconnect); - } - $conn_sizer->Add($serial_speed_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); - } - - # buttons - { - $self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect to printer", wxDefaultPosition, [-1, 40]); - my $font = $btn->GetFont; - $font->SetPointSize($font->GetPointSize + 2); - $btn->SetFont($font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("arrow_up.png"), wxBITMAP_TYPE_PNG)); - $left_sizer->Add($btn, 0, wxTOP, 15); - EVT_BUTTON($self, $btn, \&connect); - } - - # print progress bar - { - my $gauge = $self->{gauge} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize); - $left_sizer->Add($self->{gauge}, 0, wxEXPAND | wxTOP, 15); - $gauge->Hide; - } - - # status - $self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [200,-1]); - $left_sizer->Add($self->{status_text}, 1, wxEXPAND | wxTOP, 15); - - # manual control - { - $self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); - $btn->Hide; - $left_sizer->Add($btn, 0, wxTOP, 15); - EVT_BUTTON($self, $btn, sub { - my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new - ($self, $self->config, $self->sender); - $dlg->ShowModal; - }); - } - - # temperature - { - my $temp_panel = $self->{temp_panel} = Wx::Panel->new($box, -1); - my $temp_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - - my $temp_font = Wx::Font->new($Slic3r::GUI::small_font); - $temp_font->SetWeight(wxFONTWEIGHT_BOLD); - { - my $text = Wx::StaticText->new($temp_panel, -1, "Temperature:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); - - $self->{temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); - $self->{temp_text}->SetFont($temp_font); - $self->{temp_text}->SetForegroundColour(Wx::wxRED); - $temp_sizer->Add($self->{temp_text}, 1, wxALIGN_CENTER_VERTICAL); - } - { - my $text = Wx::StaticText->new($temp_panel, -1, "Bed:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); - - $self->{bed_temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); - $self->{bed_temp_text}->SetFont($temp_font); - $self->{bed_temp_text}->SetForegroundColour(Wx::wxRED); - $temp_sizer->Add($self->{bed_temp_text}, 1, wxALIGN_CENTER_VERTICAL); - } - $temp_panel->SetSizer($temp_sizer); - $temp_panel->Hide; - $left_sizer->Add($temp_panel, 0, wxEXPAND | wxTOP | wxBOTTOM, 4); - } - - # print jobs panel - $self->{print_jobs_sizer} = my $print_jobs_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - my $text = Wx::StaticText->new($box, -1, "Queue:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $print_jobs_sizer->Add($text, 0, wxEXPAND, 0); - - $self->{jobs_panel} = Wx::ScrolledWindow->new($box, -1, wxDefaultPosition, wxDefaultSize, - wxVSCROLL | wxBORDER_NONE); - $self->{jobs_panel}->SetScrollbars(0, 1, 0, 1); - $self->{jobs_panel_sizer} = Wx::BoxSizer->new(wxVERTICAL); - $self->{jobs_panel}->SetSizer($self->{jobs_panel_sizer}); - $print_jobs_sizer->Add($self->{jobs_panel}, 1, wxEXPAND, 0); - - # TODO: fix this. We're trying to pass the scroll event to the parent but it - # doesn't work. - EVT_SCROLLWIN($self->{jobs_panel}, sub { - my ($panel, $event) = @_; - - my $controller = $self->GetParent; - my $new_event = Wx::ScrollWinEvent->new( - $event->GetEventType, - $event->GetPosition, - $event->GetOrientation, - ); - $controller->ProcessEvent($new_event); - }) if 0; - } - - my $log_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - my $text = Wx::StaticText->new($box, -1, "Log:", wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - $log_sizer->Add($text, 0, wxEXPAND, 0); - - my $log = $self->{log_textctrl} = Wx::TextCtrl->new($box, -1, "", wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxBORDER_NONE); - $log->SetBackgroundColour($box->GetBackgroundColour); - $log->SetFont($Slic3r::GUI::small_font); - $log->SetEditable(0); - $log_sizer->Add($self->{log_textctrl}, 1, wxEXPAND, 0); - } - - $sizer->Add($left_sizer, 0, wxEXPAND | wxALL, 0); - $sizer->Add($print_jobs_sizer, 2, wxEXPAND | wxALL, 0); - $sizer->Add($log_sizer, 1, wxEXPAND | wxLEFT, 15); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - $self->_update_connection_controls; - - return $self; -} - -sub is_connected { - my ($self) = @_; - return $self->sender && $self->sender->is_connected; -} - -sub _update_connection_controls { - my ($self) = @_; - - $self->{btn_connect}->Show; - $self->{btn_disconnect}->Hide; - $self->{serial_port_combobox}->Enable; - $self->{serial_speed_combobox}->Enable; - $self->{btn_rescan_serial}->Enable; - $self->{btn_manual_control}->Hide; - $self->{btn_manual_control}->Disable; - - if ($self->is_connected) { - $self->{btn_connect}->Hide; - $self->{btn_manual_control}->Show; - if (!$self->printing || $self->printing->paused) { - $self->{btn_disconnect}->Show; - $self->{btn_manual_control}->Enable; - } - $self->{serial_port_combobox}->Disable; - $self->{serial_speed_combobox}->Disable; - $self->{btn_rescan_serial}->Disable; - } - - $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 connect { - my ($self) = @_; - - return if $self->is_connected; - - $self->set_status("Connecting..."); - $self->sender(Slic3r::GCode::Sender->new); - my $res = $self->sender->connect( - $self->{serial_port_combobox}->GetValue, - $self->{serial_speed_combobox}->GetValue, - ); - if (!$res) { - $self->set_status("Connection failed. Check serial port and speed."); - } else { - if ($self->sender->wait_connected) { - $self->set_status("Printer is online. You can now start printing from the queue on the right."); - $self->status_timer->Start(STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS); - $self->temp_timer->Start(TEMP_TIMER_INTERVAL, wxTIMER_CONTINUOUS); - - # request temperature now, without waiting for the timer - $self->sender->send("M105", 1); - } else { - $self->set_status("Connection failed. Check serial port and speed."); - } - } - $self->_update_connection_controls; - $self->reload_jobs; -} - -sub disconnect { - my ($self) = @_; - - $self->status_timer->Stop; - $self->temp_timer->Stop; - return if !$self->is_connected; - - $self->printing->printing(0) if $self->printing; - $self->printing(undef); - $self->{gauge}->Hide; - $self->{temp_panel}->Hide; - $self->sender->disconnect; - $self->set_status(""); - $self->_update_connection_controls; - $self->reload_jobs; -} - -sub update_serial_ports { - my ($self) = @_; - - my $cb = $self->{serial_port_combobox}; - my $current = $cb->GetValue; - $cb->Clear; - $cb->Append($_) for Slic3r::GUI::scan_serial_ports; - $cb->SetValue($current); -} - -sub load_print_job { - my ($self, $gcode_file, $filament_stats) = @_; - - push @{$self->jobs}, my $job = Slic3r::GUI::Controller::PrinterPanel::PrintJob->new( - id => time() . $gcode_file . rand(1000), - gcode_file => $gcode_file, - filament_stats => $filament_stats, - ); - $self->reload_jobs; - return $job; -} - -sub delete_job { - my ($self, $job) = @_; - - $self->jobs([ grep $_->id ne $job->id, @{$self->jobs} ]); - $self->reload_jobs; -} - -sub print_job { - my ($self, $job) = @_; - - $self->printing($job); - $job->printing(1); - $self->reload_jobs; - - open my $fh, '<', $job->gcode_file; - my $line_count = 0; - while (my $row = <$fh>) { - $self->sender->send($row); - $line_count++; - } - close $fh; - - $self->_update_connection_controls; - $self->{gauge}->SetRange($line_count); - $self->{gauge}->SetValue(0); - $self->{gauge}->Enable; - $self->{gauge}->Show; - $self->Layout; - - $self->set_status('Printing...'); - $self->{log_textctrl}->AppendText(sprintf "=====\n"); - $self->{log_textctrl}->AppendText(sprintf "Printing %s\n", $job->name); - $self->{log_textctrl}->AppendText(sprintf "Print started at %s\n", $self->_timestamp); -} - -sub print_completed { - my ($self) = @_; - - my $job = $self->printing; - $self->printing(undef); - $job->printing(0); - $job->printed(1); - $self->_update_connection_controls; - $self->{gauge}->Hide; - $self->Layout; - - $self->set_status('Print completed.'); - $self->{log_textctrl}->AppendText(sprintf "Print completed at %s\n", $self->_timestamp); - - $self->reload_jobs; -} - -sub reload_jobs { - my ($self) = @_; - - # reorder jobs - @{$self->jobs} = sort { ($a->printed <=> $b->printed) || ($a->timestamp <=> $b->timestamp) } - @{$self->jobs}; - - # remove all panels - foreach my $child ($self->{jobs_panel_sizer}->GetChildren) { - my $window = $child->GetWindow; - $self->{jobs_panel_sizer}->Detach($window); - # now $child does not exist anymore - $window->Destroy; - } - - # re-add all panels - foreach my $job (@{$self->jobs}) { - my $panel = Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel->new($self->{jobs_panel}, $job); - $self->{jobs_panel_sizer}->Add($panel, 0, wxEXPAND | wxBOTTOM, 5); - - $panel->on_delete_job(sub { - my ($job) = @_; - $self->delete_job($job); - }); - $panel->on_print_job(sub { - my ($job) = @_; - $self->print_job($job); - }); - $panel->on_pause_print(sub { - my ($job) = @_; - $self->sender->pause_queue; - $job->paused(1); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Disable; - $self->set_status('Print is paused. Click on Resume to continue.'); - }); - $panel->on_abort_print(sub { - my ($job) = @_; - $self->sender->purge_queue; - $self->printing(undef); - $job->printing(0); - $job->paused(0); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Disable; - $self->{gauge}->Hide; - $self->set_status('Print was aborted.'); - $self->{log_textctrl}->AppendText(sprintf "Print aborted at %s\n", $self->_timestamp); - }); - $panel->on_resume_print(sub { - my ($job) = @_; - $self->sender->resume_queue; - $job->paused(0); - $self->reload_jobs; - $self->_update_connection_controls; - $self->{gauge}->Enable; - $self->set_status('Printing...'); - }); - $panel->enable_print if $self->is_connected && !$self->printing; - - EVT_MOUSEWHEEL($panel, sub { - my (undef, $event) = @_; - Wx::PostEvent($self->{jobs_panel}, $event); - $event->Skip; - }); - } - - $self->{jobs_panel}->Layout; - $self->{print_jobs_sizer}->Layout; -} - -sub _timestamp { - my ($self) = @_; - - my @time = localtime(time); - return sprintf '%02d:%02d:%02d', @time[2,1,0]; -} - -package Slic3r::GUI::Controller::PrinterPanel::PrintJob; -use Moo; - -use File::Basename qw(basename); - -has 'id' => (is => 'ro', required => 1); -has 'timestamp' => (is => 'ro', default => sub { time }); -has 'gcode_file' => (is => 'ro', required => 1); -has 'filament_stats' => (is => 'rw'); -has 'printing' => (is => 'rw', default => sub { 0 }); -has 'paused' => (is => 'rw', default => sub { 0 }); -has 'printed' => (is => 'rw', default => sub { 0 }); - -sub name { - my ($self) = @_; - return basename($self->gcode_file); -} - -package Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :font :dialog :icon :timer - :colour :brush :pen); -use Wx::Event qw(EVT_BUTTON EVT_TIMER EVT_ERASE_BACKGROUND); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print - on_abort_print blink_timer)); - -sub new { - my ($class, $parent, $job) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); - - $self->job($job); - $self->SetBackgroundColour(wxWHITE); - - { - my $white_brush = Wx::Brush->new(wxWHITE, wxSOLID); - my $pen = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); - EVT_ERASE_BACKGROUND($self, sub { - my ($self, $event) = @_; - my $dc = $event->GetDC; - my $size = $self->GetSize; - $dc->SetBrush($white_brush); - $dc->SetPen($pen); - $dc->DrawRoundedRectangle(0, 0, $size->GetWidth,$size->GetHeight, 6); - }); - } - - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - { - $self->{job_name_textctrl} = my $text = Wx::StaticText->new($self, -1, $job->name, wxDefaultPosition, wxDefaultSize); - my $font = $text->GetFont; - $font->SetWeight(wxFONTWEIGHT_BOLD); - $text->SetFont($font); - if ($job->printed) { - $text->SetForegroundColour($Slic3r::GUI::grey); - } - $left_sizer->Add($text, 0, wxEXPAND, 0); - } - { - my $filament_stats = join "\n", - map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)", - sort keys %{$job->filament_stats}; - my $text = Wx::StaticText->new($self, -1, $filament_stats, wxDefaultPosition, wxDefaultSize); - $text->SetFont($Slic3r::GUI::small_font); - if ($job->printed && !$job->printing) { - $text->SetForegroundColour($Slic3r::GUI::grey); - } - $left_sizer->Add($text, 0, wxEXPAND | wxTOP, 6); - } - - my $buttons_sizer = Wx::BoxSizer->new(wxVERTICAL); - my $button_style = Wx::wxBORDER_NONE | wxBU_EXACTFIT; - { - my $btn = $self->{btn_delete} = Wx::Button->new($self, -1, 'Delete', - wxDefaultPosition, wxDefaultSize, $button_style); - $btn->SetToolTipString("Delete this job from print queue") - if $btn->can('SetToolTipString'); - $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG)); - if ($job->printing) { - $btn->Hide; - } - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete this print job?", 'Delete Job', wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal; - return unless $res == wxID_YES; - - wxTheApp->CallAfter(sub { - $self->on_delete_job->($job); - }); - }); - } - { - my $label = $job->printed ? 'Print Again' : 'Print This'; - my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_bold_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); - #$btn->SetBitmapPosition(wxRIGHT); - $btn->Hide; - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_print_job->($job); - }); - }); - } - { - my $btn = $self->{btn_pause} = Wx::Button->new($self, -1, "Pause", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing || $job->paused) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_pause_print->($job); - }); - }); - } - { - my $btn = $self->{btn_resume} = Wx::Button->new($self, -1, "Resume", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing || !$job->paused) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_resume_print->($job); - }); - }); - } - { - my $btn = $self->{btn_abort} = Wx::Button->new($self, -1, "Abort", wxDefaultPosition, wxDefaultSize, - $button_style); - $btn->SetFont($Slic3r::GUI::small_font); - if (!$job->printing) { - $btn->Hide; - } - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG)); - $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG)); - $buttons_sizer->Add($btn, 0, wxBOTTOM, 2); - - EVT_BUTTON($self, $btn, sub { - wxTheApp->CallAfter(sub { - $self->on_abort_print->($job); - }); - }); - } - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($left_sizer, 1, wxEXPAND | wxALL, 6); - $sizer->Add($buttons_sizer, 0, wxEXPAND | wxALL, 6); - $self->SetSizer($sizer); - - # set-up the timer that changes the job name color while printing - if ($self->job->printing && !$self->job->paused) { - my $timer_id = &Wx::NewId(); - $self->blink_timer(Wx::Timer->new($self, $timer_id)); - my $blink = 0; # closure - my $colour = Wx::Colour->new(0, 190, 0); - EVT_TIMER($self, $timer_id, sub { - my ($self, $event) = @_; - - $self->{job_name_textctrl}->SetForegroundColour($blink ? Wx::wxBLACK : $colour); - $blink = !$blink; - }); - $self->blink_timer->Start(1000, wxTIMER_CONTINUOUS); - } - - return $self; -} - -sub enable_print { - my ($self) = @_; - - if (!$self->job->printing) { - $self->{btn_print}->Show; - } - $self->Layout; -} - -sub Destroy { - my ($self) = @_; - - # There's a gap between the time Perl destroys the wxPanel object and - # the blink_timer member, so the wxTimer might still fire an event which - # isn't handled properly, causing a crash. So we ensure that blink_timer - # is stopped before we destroy the wxPanel. - $self->blink_timer->Stop if $self->blink_timer; - return $self->SUPER::Destroy; -} - -1; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 59c39dc8b..e2a25a7ab 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -150,9 +150,6 @@ sub _init_tabpanel { event_remove_object => $OBJECT_REMOVE_EVENT, event_update_scene => $UPDATE_SCENE_EVENT, ), L("Plater")); - if (!$self->{no_controller}) { - $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller")); - } } #TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view. diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm deleted file mode 100644 index 962e6ffc0..000000000 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ /dev/null @@ -1,498 +0,0 @@ -# A dialog group object. Used by the Tab, Preferences dialog, ManualControlDialog etc. - -package Slic3r::GUI::OptionsGroup; -use Moo; - -use List::Util qw(first); -use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl wxTheApp); -use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS EVT_SLIDER); - -has 'parent' => (is => 'ro', required => 1); -has 'title' => (is => 'ro', required => 1); -has 'on_change' => (is => 'rw', default => sub { sub {} }); -has 'staticbox' => (is => 'ro', default => sub { 1 }); -has 'label_width' => (is => 'rw', default => sub { 180 }); -has 'extra_column' => (is => 'rw', default => sub { undef }); -has 'label_font' => (is => 'rw'); -has 'sidetext_font' => (is => 'rw', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }); -has 'sizer' => (is => 'rw'); -has '_disabled' => (is => 'rw', default => sub { 0 }); -has '_grid_sizer' => (is => 'rw'); -has '_options' => (is => 'ro', default => sub { {} }); -has '_fields' => (is => 'ro', default => sub { {} }); - -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 = 1; - ++$num_columns if $self->label_width != 0; - ++$num_columns if $self->extra_column; - $self->_grid_sizer(Wx::FlexGridSizer->new(0, $num_columns, 0, 0)); - $self->_grid_sizer->SetFlexibleDirection(wxHORIZONTAL); - $self->_grid_sizer->AddGrowableCol($self->label_width != 0); - - # TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific - $self->sizer->Add($self->_grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5); -} - -# this method accepts a Slic3r::GUI::OptionsGroup::Line object -sub append_line { - my ($self, $line) = @_; - - if ($line->sizer || ($line->widget && $line->full_width)) { - # full-width widgets are appended *after* the grid sizer, so after all the non-full-width lines - my $sizer = $line->sizer // $line->widget->($self->parent); - $self->sizer->Add($sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15); - return; - } - - my $grid_sizer = $self->_grid_sizer; - - # if we have an extra column, build it - if ($self->extra_column) { - if (defined (my $item = $self->extra_column->($line))) { - $grid_sizer->Add($item, 0, wxALIGN_CENTER_VERTICAL, 0); - } else { - # if the callback provides no sizer for the extra cell, put a spacer - $grid_sizer->AddSpacer(1); - } - } - - # build label if we have it - my $label; - if ($self->label_width != 0) { - $label = Wx::StaticText->new($self->parent, -1, $line->label ? $line->label . ":" : "", wxDefaultPosition, [$self->label_width, -1]); - $label->SetFont($self->label_font) if $self->label_font; - $label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug - $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0); - $label->SetToolTipString($line->label_tooltip) if $line->label_tooltip; - } - - # if we have a widget, add it to the sizer - if ($line->widget) { - my $widget_sizer = $line->widget->($self->parent); - $grid_sizer->Add($widget_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15); - return; - } - - # if we have a single option with no sidetext just add it directly to the grid sizer - my @options = @{$line->get_options}; - $self->_options->{$_->opt_id} = $_ for @options; - if (@options == 1 && !$options[0]->sidetext && !$options[0]->side_widget && !@{$line->get_extra_widgets}) { - my $option = $options[0]; - my $field = $self->_build_field($option); - $grid_sizer->Add($field, 0, ($option->full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); - return; - } - - # if we're here, we have more than one option or a single option with sidetext - # so we need a horizontal sizer to arrange these things - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $grid_sizer->Add($sizer, 0, 0, 0); - - foreach my $i (0..$#options) { - my $option = $options[$i]; - - # add label if any - if ($option->label) { - my $field_label = Wx::StaticText->new($self->parent, -1, $option->label . ":", wxDefaultPosition, wxDefaultSize); - $field_label->SetFont($self->sidetext_font); - $sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0); - } - - # add field - my $field = $self->_build_field($option); - $sizer->Add($field, 0, wxALIGN_CENTER_VERTICAL, 0); - - # add sidetext if any - if ($option->sidetext) { - my $sidetext = Wx::StaticText->new($self->parent, -1, $option->sidetext, wxDefaultPosition, wxDefaultSize); - $sidetext->SetFont($self->sidetext_font); - $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); - } - - # add side widget if any - if ($option->side_widget) { - $sizer->Add($option->side_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); - } - - if ($option != $#options) { - $sizer->AddSpacer(4); - } - } - - # add extra sizers if any - foreach my $extra_widget (@{$line->get_extra_widgets}) { - $sizer->Add($extra_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); - } -} - -sub create_single_option_line { - my ($self, $option) = @_; - - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => $option->label, - label_tooltip => $option->tooltip, - ); - $option->label(""); - $line->append_option($option); - - return $line; -} - -sub append_single_option_line { - my ($self, $option) = @_; - return $self->append_line($self->create_single_option_line($option)); -} - -sub _build_field { - my $self = shift; - my ($opt) = @_; - - my $opt_id = $opt->opt_id; - my $on_change = sub { - #! This function will be called from Field. - my ($opt_id, $value) = @_; - #! Call OptionGroup._on_change(...) - $self->_on_change($opt_id, $value) - unless $self->_disabled; - }; - my $on_kill_focus = sub { - my ($opt_id) = @_; - $self->_on_kill_focus($opt_id); - }; - - my $type = $opt->{gui_type} || $opt->{type}; - - my $field; - if ($type eq 'bool') { - $field = Slic3r::GUI::OptionsGroup::Field::Checkbox->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'i') { - $field = Slic3r::GUI::OptionsGroup::Field::SpinCtrl->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'color') { - $field = Slic3r::GUI::OptionsGroup::Field::ColourPicker->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type =~ /^(f|s|s@|percent)$/) { - $field = Slic3r::GUI::OptionsGroup::Field::TextCtrl->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'select' || $type eq 'select_open') { - $field = Slic3r::GUI::OptionsGroup::Field::Choice->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'f_enum_open' || $type eq 'i_enum_open' || $type eq 'i_enum_closed') { - $field = Slic3r::GUI::OptionsGroup::Field::NumericChoice->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'point') { - $field = Slic3r::GUI::OptionsGroup::Field::Point->new( - parent => $self->parent, - option => $opt, - ); - } elsif ($type eq 'slider') { - $field = Slic3r::GUI::OptionsGroup::Field::Slider->new( - parent => $self->parent, - option => $opt, - ); - } - return undef if !$field; - - #! setting up a function that will be triggered when the field changes - #! think of it as $field->on_change = ($on_change) - $field->on_change($on_change); - $field->on_kill_focus($on_kill_focus); - $self->_fields->{$opt_id} = $field; - - return $field->isa('Slic3r::GUI::OptionsGroup::Field::wxWindow') - ? $field->wxWindow - : $field->wxSizer; -} - -sub get_option { - my ($self, $opt_id) = @_; - return undef if !exists $self->_options->{$opt_id}; - return $self->_options->{$opt_id}; -} - -sub get_field { - my ($self, $opt_id) = @_; - return undef if !exists $self->_fields->{$opt_id}; - return $self->_fields->{$opt_id}; -} - -sub get_value { - my ($self, $opt_id) = @_; - - return if !exists $self->_fields->{$opt_id}; - return $self->_fields->{$opt_id}->get_value; -} - -sub set_value { - my ($self, $opt_id, $value) = @_; - - return if !exists $self->_fields->{$opt_id}; - $self->_fields->{$opt_id}->set_value($value); -} - -sub _on_change { - my ($self, $opt_id, $value) = @_; - $self->on_change->($opt_id, $value); -} - -sub enable { - my ($self) = @_; - - $_->enable for values %{$self->_fields}; -} - -sub disable { - my ($self) = @_; - - $_->disable for values %{$self->_fields}; -} - -sub _on_kill_focus { - my ($self, $opt_id) = @_; - # nothing -} - - -package Slic3r::GUI::OptionsGroup::Line; -use Moo; - -has 'label' => (is => 'rw', default => sub { "" }); -has 'full_width' => (is => 'rw', default => sub { 0 }); -has 'label_tooltip' => (is => 'rw', default => sub { "" }); -has 'sizer' => (is => 'rw'); -has 'widget' => (is => 'rw'); -has '_options' => (is => 'ro', default => sub { [] }); -# Extra UI components after the label and the edit widget of the option. -has '_extra_widgets' => (is => 'ro', default => sub { [] }); - -# this method accepts a Slic3r::GUI::OptionsGroup::Option object -sub append_option { - my ($self, $option) = @_; - push @{$self->_options}, $option; -} - -sub append_widget { - my ($self, $widget) = @_; - push @{$self->_extra_widgets}, $widget; -} - -sub get_options { - my ($self) = @_; - return [ @{$self->_options} ]; -} - -sub get_extra_widgets { - my ($self) = @_; - return [ @{$self->_extra_widgets} ]; -} - - -# Configuration of an option. -# This very much reflects the content of the C++ ConfigOptionDef class. -package Slic3r::GUI::OptionsGroup::Option; -use Moo; - -has 'opt_id' => (is => 'rw', required => 1); -has 'type' => (is => 'rw', required => 1); -has 'default' => (is => 'rw', required => 1); -has 'gui_type' => (is => 'rw', default => sub { undef }); -has 'gui_flags' => (is => 'rw', default => sub { "" }); -has 'label' => (is => 'rw', default => sub { "" }); -has 'sidetext' => (is => 'rw', default => sub { "" }); -has 'tooltip' => (is => 'rw', default => sub { "" }); -has 'multiline' => (is => 'rw', default => sub { 0 }); -has 'full_width' => (is => 'rw', default => sub { 0 }); -has 'width' => (is => 'rw', default => sub { undef }); -has 'height' => (is => 'rw', default => sub { undef }); -has 'min' => (is => 'rw', default => sub { undef }); -has 'max' => (is => 'rw', default => sub { undef }); -has 'labels' => (is => 'rw', default => sub { [] }); -has 'values' => (is => 'rw', default => sub { [] }); -has 'readonly' => (is => 'rw', default => sub { 0 }); -has 'side_widget' => (is => 'rw', default => sub { undef }); - - -package Slic3r::GUI::ConfigOptionsGroup; -use Moo; - -use List::Util qw(first); - -extends 'Slic3r::GUI::OptionsGroup'; -has 'config' => (is => 'ro', required => 1); -has 'full_labels' => (is => 'ro', default => sub { 0 }); -has '_opt_map' => (is => 'ro', default => sub { {} }); - -sub get_option { - my ($self, $opt_key, $opt_index) = @_; - - $opt_index //= -1; - - if (!$self->config->has($opt_key)) { - die "No $opt_key in ConfigOptionsGroup config"; - } - - my $opt_id = ($opt_index == -1 ? $opt_key : "${opt_key}#${opt_index}"); - $self->_opt_map->{$opt_id} = [ $opt_key, $opt_index ]; - - # Slic3r::Config::Options is a C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes. - # The C++ counterpart is a constant singleton. - my $optdef = $Slic3r::Config::Options->{$opt_key}; # we should access this from $self->config - my $default_value = $self->_get_config_value($opt_key, $opt_index, $optdef->{gui_flags} =~ /\bserialized\b/); - - return Slic3r::GUI::OptionsGroup::Option->new( - opt_id => $opt_id, - type => $optdef->{type}, - default => $default_value, - gui_type => $optdef->{gui_type}, - gui_flags => $optdef->{gui_flags}, - label => ($self->full_labels && defined $optdef->{full_label}) ? $optdef->{full_label} : $optdef->{label}, - sidetext => $optdef->{sidetext}, - # calling serialize() ensures we get a stringified value - tooltip => $optdef->{tooltip} . " (default: " . $self->config->serialize($opt_key) . ")", - multiline => $optdef->{multiline}, - width => $optdef->{width}, - min => $optdef->{min}, - max => $optdef->{max}, - labels => $optdef->{labels}, - values => $optdef->{values}, - readonly => $optdef->{readonly}, - ); -} - -sub create_single_option_line { - my ($self, $opt_key, $opt_index) = @_; - - my $option; - if (ref($opt_key)) { - $option = $opt_key; - } else { - $option = $self->get_option($opt_key, $opt_index); - } - return $self->SUPER::create_single_option_line($option); -} - -sub append_single_option_line { - my ($self, $option, $opt_index) = @_; - return $self->append_line($self->create_single_option_line($option, $opt_index)); -} - -# Initialize UI components with the config values. -sub reload_config { - my ($self) = @_; - - foreach my $opt_id (keys %{ $self->_opt_map }) { - my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} }; - my $option = $self->_options->{$opt_id}; - $self->set_value($opt_id, $self->_get_config_value($opt_key, $opt_index, $option->gui_flags =~ /\bserialized\b/)); - } -} - -sub get_fieldc { - my ($self, $opt_key, $opt_index) = @_; - - $opt_index //= -1; - my $opt_id = first { $self->_opt_map->{$_}[0] eq $opt_key && $self->_opt_map->{$_}[1] == $opt_index } - keys %{$self->_opt_map}; - return defined($opt_id) ? $self->get_field($opt_id) : undef; -} - -sub _get_config_value { - my ($self, $opt_key, $opt_index, $deserialize) = @_; - - if ($deserialize) { - # Want to edit a vector value (currently only multi-strings) in a single edit box. - # Aggregate the strings the old way. - # Currently used for the post_process config value only. - die "Can't deserialize option indexed value" if $opt_index != -1; - return join(';', @{$self->config->get($opt_key)}); - } else { - return $opt_index == -1 - ? $self->config->get($opt_key) - : $self->config->get_at($opt_key, $opt_index); - } -} - -sub _on_change { - my ($self, $opt_id, $value) = @_; - - if (exists $self->_opt_map->{$opt_id}) { - my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} }; - my $option = $self->_options->{$opt_id}; - - # get value - my $field_value = $self->get_value($opt_id); - if ($option->gui_flags =~ /\bserialized\b/) { - die "Can't set serialized option indexed value" if $opt_index != -1; - # Split a string to multiple strings by a semi-colon. This is the old way of storing multi-string values. - # Currently used for the post_process config value only. - my @values = split /;/, $field_value; - $self->config->set($opt_key, \@values); - } else { - if ($opt_index == -1) { - $self->config->set($opt_key, $field_value); - } else { - my $value = $self->config->get($opt_key); - $value->[$opt_index] = $field_value; - $self->config->set($opt_key, $value); - } - } - } - - $self->SUPER::_on_change($opt_id, $value); -} - -sub _on_kill_focus { - my ($self, $opt_id) = @_; - - # when a field loses focus, reapply the config value to it - # (thus discarding any invalid input and reverting to the last - # accepted value) - $self->reload_config; -} - -# Static text shown among the options. -# Currently used for the filament cooling legend only. -package Slic3r::GUI::OptionsGroup::StaticText; -use Wx qw(:misc :systemsettings); -use base 'Wx::StaticText'; - -sub new { - my ($class, $parent) = @_; - - my $self = $class->SUPER::new($parent, -1, "", wxDefaultPosition, wxDefaultSize); - my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - $self->SetFont($font); - return $self; -} - -sub SetText { - my ($self, $value) = @_; - - $self->SetLabel($value); - $self->Wrap(400); - $self->GetParent->Layout; -} - -1; diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm deleted file mode 100644 index 1a53daeb5..000000000 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ /dev/null @@ -1,605 +0,0 @@ -# An input field class prototype. -package Slic3r::GUI::OptionsGroup::Field; -use Moo; - -# This is a base class for option fields. - -has 'parent' => (is => 'ro', required => 1); -# Slic3r::GUI::OptionsGroup::Option -has 'option' => (is => 'ro', required => 1); -# On change callback -has 'on_change' => (is => 'rw', default => sub { sub {} }); -has 'on_kill_focus' => (is => 'rw', default => sub { sub {} }); -# If set, the callback $self->on_change is not called. -# This is used to avoid recursive invocation of the field change/update by wxWidgets. -has 'disable_change_event' => (is => 'rw', default => sub { 0 }); - -# This method should not fire the on_change event -sub set_value { - my ($self, $value) = @_; - die "Method not implemented"; -} - -sub get_value { - my ($self) = @_; - die "Method not implemented"; -} - -sub set_tooltip { - my ($self, $tooltip) = @_; - - $self->SetToolTipString($tooltip) - if $tooltip && $self->can('SetToolTipString'); -} - -sub toggle { - my ($self, $enable) = @_; - $enable ? $self->enable : $self->disable; -} - -sub _on_change { - my ($self, $opt_id) = @_; - - $self->on_change->($opt_id, $self->get_value) - unless $self->disable_change_event; -} - -sub _on_kill_focus { - my ($self, $opt_id, $s, $event) = @_; - - # Without this, there will be nasty focus bugs on Windows. - # Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all - # non-command events to allow the default handling to take place." - $event->Skip(1); - - $self->on_kill_focus->($opt_id); -} - - -package Slic3r::GUI::OptionsGroup::Field::wxWindow; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field'; - -has 'wxWindow' => (is => 'rw', trigger => 1); # wxWindow object - -sub _default_size { - my ($self) = @_; - - # default width on Windows is too large - return Wx::Size->new($self->option->width || 60, $self->option->height || -1); -} - -sub _trigger_wxWindow { - my ($self) = @_; - - $self->wxWindow->SetToolTipString($self->option->tooltip) - if $self->option->tooltip && $self->wxWindow->can('SetToolTipString'); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->wxWindow->SetValue($value); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetValue; -} - -sub enable { - my ($self) = @_; - - $self->wxWindow->Enable; - $self->wxWindow->Refresh; -} - -sub disable { - my ($self) = @_; - - $self->wxWindow->Disable; - $self->wxWindow->Refresh; -} - - -package Slic3r::GUI::OptionsGroup::Field::Checkbox; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc); -use Wx::Event qw(EVT_CHECKBOX); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::CheckBox->new($self->parent, -1, ""); - $self->wxWindow($field); - $field->SetValue($self->option->default); - $field->Disable if $self->option->readonly; - - EVT_CHECKBOX($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetValue ? 1 : 0; -} - -package Slic3r::GUI::OptionsGroup::Field::SpinCtrl; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc); -use Wx::Event qw(EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS); - -has 'tmp_value' => (is => 'rw'); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::SpinCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size, - 0, $self->option->min || 0, $self->option->max || 2147483647, $self->option->default); - $self->wxWindow($field); - - EVT_SPINCTRL($self->parent, $field, sub { - $self->tmp_value(undef); - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - my ($s, $event) = @_; - - # On OSX/Cocoa, wxSpinCtrl::GetValue() doesn't return the new value - # when it was changed from the text control, so the on_change callback - # gets the old one, and on_kill_focus resets the control to the old value. - # As a workaround, we get the new value from $event->GetString and store - # here temporarily so that we can return it from $self->get_value - $self->tmp_value($event->GetString) if $event->GetString =~ /^\d+$/; - $self->_on_change($self->option->opt_id); - # We don't reset tmp_value here because _on_change might put callbacks - # in the CallAfter queue, and we want the tmp value to be available from - # them as well. - }); - EVT_KILL_FOCUS($field, sub { - $self->tmp_value(undef); - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub get_value { - my ($self) = @_; - return $self->tmp_value // $self->wxWindow->GetValue; -} - - -package Slic3r::GUI::OptionsGroup::Field::TextCtrl; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc :textctrl); -use Wx::Event qw(EVT_TEXT EVT_KILL_FOCUS); - -sub BUILD { - my ($self) = @_; - - my $style = 0; - $style = wxTE_MULTILINE if $self->option->multiline; - my $field = Wx::TextCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition, - $self->_default_size, $style); - $self->wxWindow($field); - - # TODO: test loading a config that has empty string for multi-value options like 'wipe' - - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); - EVT_KILL_FOCUS($field, sub { - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub enable { - my ($self) = @_; - - $self->wxWindow->Enable; - $self->wxWindow->SetEditable(1); -} - -sub disable { - my ($self) = @_; - - $self->wxWindow->Disable; - $self->wxWindow->SetEditable(0); -} - - -package Slic3r::GUI::OptionsGroup::Field::Choice; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use List::Util qw(first); -use Wx qw(:misc :combobox); -use Wx::Event qw(EVT_COMBOBOX EVT_TEXT); - -sub BUILD { - my ($self) = @_; - - my $style = 0; - $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); - - $self->set_value($self->option->default); - - EVT_COMBOBOX($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - - my $idx; - if ($self->option->values) { - $idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values}; - # if value is not among indexes values we use SetValue() - } - - if (defined $idx) { - $self->wxWindow->SetSelection($idx); - } else { - $self->wxWindow->SetValue($value); - } - - $self->disable_change_event(0); -} - -sub set_values { - my ($self, $values) = @_; - - $self->disable_change_event(1); - - # it looks that Clear() also clears the text field in recent wxWidgets versions, - # but we want to preserve it - my $ww = $self->wxWindow; - my $value = $ww->GetValue; - $ww->Clear; - $ww->Append($_) for @$values; - $ww->SetValue($value); - - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - if ($self->option->values) { - my $idx = $self->wxWindow->GetSelection; - if ($idx != &Wx::wxNOT_FOUND) { - return $self->option->values->[$idx]; - } - } - return $self->wxWindow->GetValue; -} - -package Slic3r::GUI::OptionsGroup::Field::NumericChoice; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use List::Util qw(first); -use Wx qw(wxTheApp :misc :combobox); -use Wx::Event qw(EVT_COMBOBOX EVT_TEXT); - -# if option has no 'values', indices are values -# if option has no 'labels', values are labels - -sub BUILD { - my ($self) = @_; - - my $field = Wx::ComboBox->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size, - $self->option->labels || $self->option->values); - $self->wxWindow($field); - - $self->set_value($self->option->default); - - EVT_COMBOBOX($self->parent, $field, sub { - my $disable_change_event = $self->disable_change_event; - $self->disable_change_event(1); - - my $idx = $field->GetSelection; # get index of selected value - my $label; - - if ($self->option->labels && $idx <= $#{$self->option->labels}) { - $label = $self->option->labels->[$idx]; - } elsif ($self->option->values && $idx <= $#{$self->option->values}) { - $label = $self->option->values->[$idx]; - } else { - $label = $idx; - } - - # The MSW implementation of wxComboBox will leave the field blank if we call - # SetValue() in the EVT_COMBOBOX event handler, so we postpone the call. - wxTheApp->CallAfter(sub { - my $dce = $self->disable_change_event; - $self->disable_change_event(1); - - # ChangeValue() is not exported in wxPerl - $field->SetValue($label); - - $self->disable_change_event($dce); - }); - - $self->disable_change_event($disable_change_event); - $self->_on_change($self->option->opt_id); - }); - EVT_TEXT($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - - my $field = $self->wxWindow; - if ($self->option->gui_flags =~ /\bshow_value\b/) { - $field->SetValue($value); - } else { - if ($self->option->values) { - # check whether we have a value index - my $value_idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values}; - if (defined $value_idx) { - $field->SetSelection($value_idx); - $self->disable_change_event(0); - return; - } - } elsif ($self->option->labels && $value <= $#{$self->option->labels}) { - # if we have no values, we expect value to be an index - $field->SetValue($self->option->labels->[$value]); - $self->disable_change_event(0); - return; - } - $field->SetValue($value); - } - - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - my $label = $self->wxWindow->GetValue; - if ($self->option->labels) { - my $value_idx = first { $self->option->labels->[$_] eq $label } 0..$#{$self->option->labels}; - if (defined $value_idx) { - if ($self->option->values) { - return $self->option->values->[$value_idx]; - } - return $value_idx; - } - } - return $label; -} - - -package Slic3r::GUI::OptionsGroup::Field::ColourPicker; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow'; - -use Wx qw(:misc :colour); -use Wx::Event qw(EVT_COLOURPICKER_CHANGED); - -sub BUILD { - my ($self) = @_; - - my $field = Wx::ColourPickerCtrl->new($self->parent, -1, - $self->_string_to_colour($self->option->default), wxDefaultPosition, - $self->_default_size); - $self->wxWindow($field); - - EVT_COLOURPICKER_CHANGED($self->parent, $field, sub { - $self->_on_change($self->option->opt_id); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->wxWindow->SetColour($self->_string_to_colour($value)); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->wxWindow->GetColour->GetAsString(wxC2S_HTML_SYNTAX); -} - -sub _string_to_colour { - my ($self, $string) = @_; - - $string =~ s/^#//; - # If the color is in an invalid format, set it to white. - $string = 'FFFFFF' if ($string !~ m/^[[:xdigit:]]{6}/); - return Wx::Colour->new(unpack 'C*', pack 'H*', $string); -} - - -package Slic3r::GUI::OptionsGroup::Field::wxSizer; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field'; - -has 'wxSizer' => (is => 'rw'); # wxSizer object - - -package Slic3r::GUI::OptionsGroup::Field::Point; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer'; - -has 'x_textctrl' => (is => 'rw'); -has 'y_textctrl' => (is => 'rw'); - -use Slic3r::Geometry qw(X Y); -use Wx qw(:misc :sizer); -use Wx::Event qw(EVT_TEXT); - -sub BUILD { - my ($self) = @_; - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $self->wxSizer($sizer); - - my $field_size = Wx::Size->new(40, -1); - - $self->x_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[X], wxDefaultPosition, $field_size)); - $self->y_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[Y], wxDefaultPosition, $field_size)); - - my @items = ( - Wx::StaticText->new($self->parent, -1, "x:"), - $self->x_textctrl, - Wx::StaticText->new($self->parent, -1, " y:"), - $self->y_textctrl, - ); - $sizer->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items; - - if ($self->option->tooltip) { - foreach my $item (@items) { - $item->SetToolTipString($self->option->tooltip) - if $item->can('SetToolTipString'); - } - } - - EVT_TEXT($self->parent, $_, sub { - $self->_on_change($self->option->opt_id); - }) for $self->x_textctrl, $self->y_textctrl; -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->x_textctrl->SetValue($value->[X]); - $self->y_textctrl->SetValue($value->[Y]); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - - return [ - $self->x_textctrl->GetValue, - $self->y_textctrl->GetValue, - ]; -} - -sub enable { - my ($self) = @_; - - $self->x_textctrl->Enable; - $self->y_textctrl->Enable; -} - -sub disable { - my ($self) = @_; - - $self->x_textctrl->Disable; - $self->y_textctrl->Disable; -} - - -package Slic3r::GUI::OptionsGroup::Field::Slider; -use Moo; -extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer'; - -has 'scale' => (is => 'rw', default => sub { 10 }); -has 'slider' => (is => 'rw'); -has 'textctrl' => (is => 'rw'); - -use Wx qw(:misc :sizer); -use Wx::Event qw(EVT_SLIDER EVT_TEXT EVT_KILL_FOCUS); - -sub BUILD { - my ($self) = @_; - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $self->wxSizer($sizer); - - my $slider = Wx::Slider->new( - $self->parent, -1, - ($self->option->default // $self->option->min) * $self->scale, - ($self->option->min // 0) * $self->scale, - ($self->option->max // 100) * $self->scale, - wxDefaultPosition, - [ $self->option->width // -1, $self->option->height // -1 ], - ); - $self->slider($slider); - - my $textctrl = Wx::TextCtrl->new($self->parent, -1, $slider->GetValue/$self->scale, - wxDefaultPosition, [50,-1]); - $self->textctrl($textctrl); - - $sizer->Add($slider, 1, wxALIGN_CENTER_VERTICAL, 0); - $sizer->Add($textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); - - EVT_SLIDER($self->parent, $slider, sub { - if (! $self->disable_change_event) { - # wxTextCtrl::SetLabel() does not work on Linux, use wxTextCtrl::SetValue() instead - $self->textctrl->SetValue($self->get_value); - $self->_on_change($self->option->opt_id); - } - }); - EVT_TEXT($self->parent, $textctrl, sub { - my $value = $textctrl->GetValue; - if ($value =~ /^-?\d+(\.\d*)?$/) { - $self->disable_change_event(1); - $self->slider->SetValue($value*$self->scale); - $self->disable_change_event(0); - $self->_on_change($self->option->opt_id); - } - }); - EVT_KILL_FOCUS($textctrl, sub { - $self->_on_kill_focus($self->option->opt_id, @_); - }); -} - -sub set_value { - my ($self, $value) = @_; - - $self->disable_change_event(1); - $self->slider->SetValue($value*$self->scale); - $self->textctrl->SetLabel($self->get_value); - $self->disable_change_event(0); -} - -sub get_value { - my ($self) = @_; - return $self->slider->GetValue/$self->scale; -} - -sub enable { - my ($self) = @_; - - $self->slider->Enable; - $self->textctrl->Enable; - $self->textctrl->SetEditable(1); -} - -sub disable { - my ($self) = @_; - - $self->slider->Disable; - $self->textctrl->Disable; - $self->textctrl->SetEditable(0); -} - -1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index de2657894..d3cca7e53 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -296,14 +296,6 @@ sub new { Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; }); -# # Initialize 2D preview canvas -# $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); -# $self->{preview_notebook}->AddPage($self->{canvas}, L('2D')); -# $self->{canvas}->on_select_object($on_select_object); -# $self->{canvas}->on_double_click($on_double_click); -# $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); -# $self->{canvas}->on_instances_moved($on_instances_moved); - # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config}); @@ -314,12 +306,6 @@ sub new { $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } - # Initialize toolpaths preview - if ($Slic3r::GUI::have_OpenGL) { - $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); - $self->{preview_notebook}->AddPage($self->{toolpaths2D}, L('Layers')); - } - EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { my $preview = $self->{preview_notebook}->GetCurrentPage; if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) { @@ -994,7 +980,6 @@ sub remove { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; $self->{preview3D}->enabled(0) if $self->{preview3D}; # If no object index is supplied, remove the selected one. @@ -1020,7 +1005,6 @@ sub reset { $self->stop_background_process; # Prevent toolpaths preview from rendering while we modify the Print object - $self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D}; $self->{preview3D}->enabled(0) if $self->{preview3D}; @{$self->{objects}} = (); @@ -1382,7 +1366,6 @@ sub async_apply_config { # Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. # Otherwise they will be just refreshed. $self->{gcode_preview_data}->reset; - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; # We also need to reload 3D scene because of the wipe tower preview box if ($self->{config}->wipe_tower) { @@ -1418,7 +1401,6 @@ sub start_background_process { sub stop_background_process { my ($self) = @_; $self->{background_slicing_process}->stop(); - $self->{toolpaths2D}->reload_print if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -1525,7 +1507,6 @@ sub export_gcode { # This message should be called by the background process synchronously. sub on_update_print_preview { my ($self) = @_; - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; # in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: @@ -1607,7 +1588,6 @@ sub on_process_completed { $self->object_list_changed; # refresh preview - $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; } @@ -2043,31 +2023,31 @@ sub filament_color_box_lmouse_down } } -sub object_cut_dialog { - my ($self, $obj_idx) = @_; - - if (!defined $obj_idx) { - ($obj_idx, undef) = $self->selected_object; - } - - if (!$Slic3r::GUI::have_OpenGL) { - Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); - return; - } - - my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $self->{model}->objects->[$obj_idx], - ); - return unless $dlg->ShowModal == wxID_OK; - - if (my @new_objects = $dlg->NewModelObjects) { - $self->remove($obj_idx); - $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; - Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; - } -} +#sub object_cut_dialog { +# my ($self, $obj_idx) = @_; +# +# if (!defined $obj_idx) { +# ($obj_idx, undef) = $self->selected_object; +# } +# +# if (!$Slic3r::GUI::have_OpenGL) { +# Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions).")); +# return; +# } +# +# my $dlg = Slic3r::GUI::Plater::ObjectCutDialog->new($self, +# object => $self->{objects}[$obj_idx], +# model_object => $self->{model}->objects->[$obj_idx], +# ); +# return unless $dlg->ShowModal == wxID_OK; +# +# if (my @new_objects = $dlg->NewModelObjects) { +# $self->remove($obj_idx); +# $self->load_model_objects(grep defined($_), @new_objects); +# $self->arrange; +# Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas3D}) if $self->{canvas3D}; +# } +#} sub object_settings_dialog { my ($self, $obj_idx) = @_; diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm deleted file mode 100644 index 88a05c292..000000000 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ /dev/null @@ -1,372 +0,0 @@ -# 2D preview on the platter. -# 3D objects are visualized by their convex hulls. - -package Slic3r::GUI::Plater::2D; -use strict; -use warnings; -use utf8; - -use List::Util qw(min max first); -use Slic3r::Geometry qw(X Y scale unscale convex_hull); -use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl); -use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE); -use base 'Wx::Panel'; - -use Wx::Locale gettext => 'L'; - -sub new { - my $class = shift; - my ($parent, $size, $objects, $model, $config) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL); - # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). - $self->SetBackgroundColour(Wx::wxWHITE); - - $self->{objects} = $objects; - $self->{model} = $model; - $self->{config} = $config; - $self->{on_select_object} = sub {}; - $self->{on_double_click} = sub {}; - $self->{on_right_click} = sub {}; - $self->{on_instances_moved} = sub {}; - - $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); - $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); - $self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID); - $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT); - $self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID); - $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); - $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID); - $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID); - - $self->{user_drawn_background} = $^O ne 'darwin'; - - EVT_PAINT($self, \&repaint); - EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; - EVT_MOUSE_EVENTS($self, \&mouse_event); - EVT_SIZE($self, sub { - $self->update_bed_size; - $self->Refresh; - }); - - return $self; -} - -sub on_select_object { - my ($self, $cb) = @_; - $self->{on_select_object} = $cb; -} - -sub on_double_click { - my ($self, $cb) = @_; - $self->{on_double_click} = $cb; -} - -sub on_right_click { - my ($self, $cb) = @_; - $self->{on_right_click} = $cb; -} - -sub on_instances_moved { - my ($self, $cb) = @_; - $self->{on_instances_moved} = $cb; -} - -sub repaint { - my ($self, $event) = @_; - - my $dc = Wx::AutoBufferedPaintDC->new($self); - my $size = $self->GetSize; - my @size = ($size->GetWidth, $size->GetHeight); - - if ($self->{user_drawn_background}) { - # On all systems the AutoBufferedPaintDC() achieves double buffering. - # On MacOS the background is erased, on Windows the background is not erased - # and on Linux/GTK the background is erased to gray color. - # Fill DC with the background on Windows & Linux/GTK. - my $brush_background = Wx::Brush->new(Wx::wxWHITE, wxSOLID); - $dc->SetPen(wxWHITE_PEN); - $dc->SetBrush($brush_background); - my $rect = $self->GetUpdateRegion()->GetBox(); - $dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight()); - } - - # draw grid - $dc->SetPen($self->{grid_pen}); - $dc->DrawLine(map @$_, @$_) for @{$self->{grid}}; - - # draw bed - { - $dc->SetPen($self->{print_center_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($self->{bed_polygon}, 1), 0, 0); - } - - # draw print center - if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) { - my $center = $self->unscaled_point_to_pixel($self->{print_center}); - $dc->SetPen($self->{print_center_pen}); - $dc->DrawLine($center->[X], 0, $center->[X], $size[Y]); - $dc->DrawLine(0, $center->[Y], $size[X], $center->[Y]); - $dc->SetTextForeground(Wx::Colour->new(0,0,0)); - $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel("X = " . sprintf('%.0f', $self->{print_center}->[X]), Wx::Rect->new(0, 0, $center->[X]*2, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); - $dc->DrawRotatedText("Y = " . sprintf('%.0f', $self->{print_center}->[Y]), 0, $center->[Y]+15, 90); - } - - # draw frame - if (0) { - $dc->SetPen(wxBLACK_PEN); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawRectangle(0, 0, @size); - } - - # draw text if plate is empty - if (!@{$self->{objects}}) { - $dc->SetTextForeground(Wx::Colour->new(150,50,50)); - $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel( - join('-', +(localtime)[3,4]) eq '13-8' - ? L('What do you want to print today? ™') # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap. - : L('Drag your objects here'), - Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); - } - - # draw thumbnails - $dc->SetPen(wxBLACK_PEN); - $self->clean_instance_thumbnails; - for my $obj_idx (0 .. $#{$self->{objects}}) { - my $object = $self->{objects}[$obj_idx]; - my $model_object = $self->{model}->objects->[$obj_idx]; - next unless defined $object->thumbnail; - for my $instance_idx (0 .. $#{$model_object->instances}) { - my $instance = $model_object->instances->[$instance_idx]; - next if !defined $object->transformed_thumbnail; - - my $thumbnail = $object->transformed_thumbnail->clone; # in scaled model coordinates - $thumbnail->translate(map scale($_), @{$instance->offset}); - - $object->instance_thumbnails->[$instance_idx] = $thumbnail; - - if (defined $self->{drag_object} && $self->{drag_object}[0] == $obj_idx && $self->{drag_object}[1] == $instance_idx) { - $dc->SetBrush($self->{dragged_brush}); - } elsif ($object->selected) { - $dc->SetBrush($self->{selected_brush}); - } else { - $dc->SetBrush($self->{objects_brush}); - } - foreach my $expolygon (@$thumbnail) { - foreach my $points (@{$expolygon->pp}) { - $dc->DrawPolygon($self->scaled_points_to_pixel($points, 1), 0, 0); - } - } - - if (0) { - # draw bounding box for debugging purposes - my $bb = $model_object->instance_bounding_box($instance_idx); - $bb->scale($self->{scaling_factor}); - # no need to translate by instance offset because instance_bounding_box() does that - my $points = $bb->polygon->pp; - $dc->SetPen($self->{clearance_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->_y($points), 0, 0); - } - - # if sequential printing is enabled and we have more than one object, draw clearance area - if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) { - my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))}; - $dc->SetPen($self->{clearance_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); - } - } - } - - # draw skirt - if (@{$self->{objects}} && $self->{config}->skirts) { - my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; - if (@points >= 3) { - my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))}; - $dc->SetPen($self->{skirt_pen}); - $dc->SetBrush($self->{transparent_brush}); - $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0); - } - } - - $event->Skip; -} - -sub mouse_event { - my ($self, $event) = @_; - my $pos = $event->GetPosition; - my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] - if ($event->ButtonDown) { - $self->{on_select_object}->(undef); - # traverse objects and instances in reverse order, so that if they're overlapping - # we get the one that gets drawn last, thus on top (as user expects that to move) - OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) { - my $object = $self->{objects}->[$obj_idx]; - for my $instance_idx (reverse 0 .. $#{ $object->instance_thumbnails }) { - my $thumbnail = $object->instance_thumbnails->[$instance_idx]; - if (defined first { $_->contour->contains_point($point) } @$thumbnail) { - $self->{on_select_object}->($obj_idx); - - if ($event->LeftDown) { - # start dragging - my $instance = $self->{model}->objects->[$obj_idx]->instances->[$instance_idx]; - my $instance_origin = [ map scale($_), @{$instance->offset} ]; - $self->{drag_start_pos} = [ # displacement between the click and the instance origin in scaled model units - $point->x - $instance_origin->[X], - $point->y - $instance_origin->[Y], #- - ]; - $self->{drag_object} = [ $obj_idx, $instance_idx ]; - } elsif ($event->RightDown) { - $self->{on_right_click}->($pos->x, $pos->y); - } - - last OBJECTS; - } - } - } - $self->Refresh; - } elsif ($event->LeftUp) { - if ($self->{drag_object}) { - $self->{on_instances_moved}->(); - } - $self->{drag_start_pos} = undef; - $self->{drag_object} = undef; - $self->SetCursor(wxSTANDARD_CURSOR); - } elsif ($event->LeftDClick) { - $self->{on_double_click}->(); - } elsif ($event->Dragging) { - return if !$self->{drag_start_pos}; # concurrency problems - my ($obj_idx, $instance_idx) = @{ $self->{drag_object} }; - my $model_object = $self->{model}->objects->[$obj_idx]; - $model_object->instances->[$instance_idx]->set_offset( - Slic3r::Pointf->new( - unscale($point->[X] - $self->{drag_start_pos}[X]), - unscale($point->[Y] - $self->{drag_start_pos}[Y]), - )); - $self->Refresh; - } elsif ($event->Moving) { - my $cursor = wxSTANDARD_CURSOR; - if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { - $cursor = Wx::Cursor->new(wxCURSOR_HAND); - } - $self->SetCursor($cursor); - } -} - -sub update_bed_size { - my ($self) = @_; - - # when the canvas is not rendered yet, its GetSize() method returns 0,0 - my $canvas_size = $self->GetSize; - my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); - return if $canvas_w == 0; - - # get bed shape polygon - $self->{bed_polygon} = my $polygon = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape}); - my $bb = $polygon->bounding_box; - my $size = $bb->size; - - # calculate the scaling factor needed for constraining print bed area inside preview - # scaling_factor is expressed in pixel / mm - $self->{scaling_factor} = min($canvas_w / unscale($size->x), $canvas_h / unscale($size->y)); #) - - # calculate the displacement needed to center bed - $self->{bed_origin} = [ - $canvas_w/2 - (unscale($bb->x_max + $bb->x_min)/2 * $self->{scaling_factor}), - $canvas_h - ($canvas_h/2 - (unscale($bb->y_max + $bb->y_min)/2 * $self->{scaling_factor})), - ]; - - # calculate print center - my $center = $bb->center; - $self->{print_center} = [ unscale($center->x), unscale($center->y) ]; #)) - - # cache bed contours and grid - { - 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]); - } - @polylines = @{intersection_pl(\@polylines, [$polygon])}; - $self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ]; - } -} - -sub clean_instance_thumbnails { - my ($self) = @_; - - foreach my $object (@{ $self->{objects} }) { - @{ $object->instance_thumbnails } = (); - } -} - -# convert a model coordinate into a pixel coordinate -sub unscaled_point_to_pixel { - my ($self, $point) = @_; - - my $canvas_height = $self->GetSize->GetHeight; - my $zero = $self->{bed_origin}; - return [ - $point->[X] * $self->{scaling_factor} + $zero->[X], - $canvas_height - $point->[Y] * $self->{scaling_factor} + ($zero->[Y] - $canvas_height), - ]; -} - -sub scaled_points_to_pixel { - my ($self, $points, $unscale) = @_; - - my $result = []; - foreach my $point (@$points) { - $point = [ map unscale($_), @$point ] if $unscale; - push @$result, $self->unscaled_point_to_pixel($point); - } - return $result; -} - -sub point_to_model_units { - my ($self, $point) = @_; - - my $zero = $self->{bed_origin}; - return Slic3r::Point->new( - scale ($point->[X] - $zero->[X]) / $self->{scaling_factor}, - scale ($zero->[Y] - $point->[Y]) / $self->{scaling_factor}, - ); -} - -sub reload_scene { - my ($self, $force) = @_; - - if (! $self->IsShown && ! $force) { - $self->{reload_delayed} = 1; - return; - } - - $self->{reload_delayed} = 0; - - foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my $plater_object = $self->{objects}[$obj_idx]; - next if $plater_object->thumbnail; - # The thumbnail is not valid, update it with a convex hull of an object. - $plater_object->thumbnail(Slic3r::ExPolygon::Collection->new); - $plater_object->make_thumbnail($self->{model}, $obj_idx); - $plater_object->transform_thumbnail($self->{model}, $obj_idx); - } - - $self->Refresh; -} - -# Called by the Platter wxNotebook when this page is activated. -sub OnActivate { - my ($self) = @_; - $self->reload_scene(1) if ($self->{reload_delayed}); -} - -1; diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm deleted file mode 100644 index bcd1aeed4..000000000 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ /dev/null @@ -1,910 +0,0 @@ -# 2D preview of the tool paths of a single layer, using a thin line. -# OpenGL is used to render the paths. -# Vojtech also added a 2D simulation of under/over extrusion in a single layer. - -package Slic3r::GUI::Plater::2DToolpaths; -use strict; -use warnings; -use utf8; - -use Slic3r::Print::State ':steps'; -use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxWANTS_CHARS); -use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN); -use base qw(Wx::Panel Class::Accessor); - -__PACKAGE__->mk_accessors(qw(print enabled)); - -sub new { - my $class = shift; - my ($parent, $print) = @_; - - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); - $self->SetBackgroundColour(wxWHITE); - - # init GUI elements - my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print); - my $slider = $self->{slider} = Wx::Slider->new( - $self, -1, - 0, # default - 0, # min - # we set max to a bogus non-zero value because the MSW implementation of wxSlider - # will skip drawing the slider if max <= min: - 1, # max - wxDefaultPosition, - wxDefaultSize, - wxVERTICAL | wxSL_INVERSE, - ); - my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [40,-1], wxALIGN_CENTRE_HORIZONTAL); - $z_label->SetFont($Slic3r::GUI::small_font); - - my $vsizer = Wx::BoxSizer->new(wxVERTICAL); - $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3); - $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3); - - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); - $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); - - EVT_SLIDER($self, $slider, sub { - $self->set_z($self->{layers_z}[$slider->GetValue]) - if $self->enabled; - }); - EVT_KEY_DOWN($canvas, sub { - my ($s, $event) = @_; - if ($event->HasModifiers) { - $event->Skip; - } else { - my $key = $event->GetKeyCode; - if ($key == ord('D') || $key == WXK_LEFT) { - # Keys: 'D' or WXK_LEFT - $slider->SetValue($slider->GetValue - 1); - $self->set_z($self->{layers_z}[$slider->GetValue]); - } elsif ($key == ord('U') || $key == WXK_RIGHT) { - # Keys: 'U' or WXK_RIGHT - $slider->SetValue($slider->GetValue + 1); - $self->set_z($self->{layers_z}[$slider->GetValue]); - } elsif ($key >= ord('1') && $key <= ord('3')) { - # Keys: '1' to '3' - $canvas->set_simulation_mode($key - ord('1')); - } else { - $event->Skip; - } - } - }); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - $sizer->SetSizeHints($self); - - # init print - $self->{print} = $print; - $self->reload_print; - - return $self; -} - -sub reload_print { - my ($self) = @_; - - # we require that there's at least one object and the posSlice step - # is performed on all of them (this ensures that _shifted_copies was - # populated and we know the number of layers) - if (!$self->print->object_step_done(STEP_SLICE)) { - $self->enabled(0); - $self->{slider}->Hide; - $self->{canvas}->Refresh; # clears canvas - return; - } - - $self->{canvas}->bb($self->print->total_bounding_box); - $self->{canvas}->_dirty(1); - - my %z = (); # z => 1 - foreach my $object (@{$self->{print}->objects}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { - $z{$layer->print_z} = 1; - } - } - $self->enabled(1); - $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; - $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1); - if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) { - $self->set_z($self->{layers_z}[$z_idx]); - } else { - $self->{slider}->SetValue(0); - $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}}; - } - $self->{slider}->Show; - $self->Layout; -} - -sub set_z { - my ($self, $z) = @_; - - return if !$self->enabled; - $self->{z_label}->SetLabel(sprintf '%.2f', $z); - $self->{canvas}->set_z($z); -} - - -package Slic3r::GUI::Plater::2DToolpaths::Canvas; - -use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS); -use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); -use base qw(Wx::GLCanvas Class::Accessor); -use Wx::GLCanvas qw(:all); -use List::Util qw(min max first); -use Slic3r::Geometry qw(scale epsilon X Y); -use Slic3r::Print::State ':steps'; - -__PACKAGE__->mk_accessors(qw( - print z layers color init - bb - _camera_bb - _dirty - _zoom - _camera_target - _drag_start_xy - _texture_name - _texture_size - _extrusion_simulator - _simulation_mode -)); - -sub new { - my ($class, $parent, $print) = @_; - - my $self = (Wx::wxVERSION >= 3.000003) ? - # The wxWidgets 3.0.3-beta have a bug, they crash with NULL attribute list. - $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", - [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0]) : - $class->SUPER::new($parent); - # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs. - $self->GetContext(); - $self->print($print); - $self->_zoom(1); - - # 2D point in model space - $self->_camera_target(Slic3r::Pointf->new(0,0)); - - # Texture for the extrusion simulator. The texture will be allocated / reallocated on Resize. - $self->_texture_name(0); - $self->_texture_size(Slic3r::Point->new(0,0)); - $self->_extrusion_simulator(Slic3r::ExtrusionSimulator->new()); - $self->_simulation_mode(0); - - EVT_PAINT($self, sub { - my $dc = Wx::PaintDC->new($self); - $self->Render($dc); - }); - EVT_SIZE($self, sub { $self->_dirty(1) }); - EVT_IDLE($self, sub { - return unless $self->_dirty; - return if !$self->IsShownOnScreen; - $self->Resize; - $self->Refresh; - }); - EVT_MOUSEWHEEL($self, sub { - my ($self, $e) = @_; - - return if !$self->GetParent->enabled; - - my $old_zoom = $self->_zoom; - - # Calculate the zoom delta and apply it to the current zoom factor - my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta(); - $zoom = max(min($zoom, 4), -4); - $zoom /= 10; - $self->_zoom($self->_zoom / (1-$zoom)); - $self->_zoom(1.25) if $self->_zoom > 1.25; # prevent from zooming out too much - - { - # In order to zoom around the mouse point we need to translate - # the camera target. This math is almost there but not perfect yet... - my $camera_bb_size = $self->_camera_bb->size; - my $size = Slic3r::Pointf->new($self->GetSizeWH); - my $pos = Slic3r::Pointf->new($e->GetPositionXY); - - # calculate the zooming center in pixel coordinates relative to the viewport center - my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #- - - # calculate where this point will end up after applying the new zoom - my $vec2 = $vec->clone; - $vec2->scale($old_zoom / $self->_zoom); - - # move the camera target by the difference of the two positions - $self->_camera_target->translate( - -($vec->x - $vec2->x) * $camera_bb_size->x / $size->x, - ($vec->y - $vec2->y) * $camera_bb_size->y / $size->y, #// - ); - } - - $self->_dirty(1); - }); - EVT_MOUSE_EVENTS($self, \&mouse_event); - - return $self; -} - -sub Destroy { - my ($self) = @_; - - # Deallocate the OpenGL resources. - my $context = $self->GetContext; - if ($context and $self->texture_id) { - $self->SetCurrent($context); - glDeleteTextures(1, ($self->texture_id)); - $self->SetCurrent(0); - $self->texture_id(0); - $self->texture_size(new Slic3r::Point(0, 0)); - } - return $self->SUPER::Destroy; -} - -sub mouse_event { - my ($self, $e) = @_; - - return if !$self->GetParent->enabled; - - my $pos = Slic3r::Pointf->new($e->GetPositionXY); - if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) { - # wxMSW and Linux needs focus in order to catch key events - $self->SetFocus; - } elsif ($e->Dragging) { - if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) { - # if dragging, translate view - - if (defined $self->_drag_start_xy) { - my $move = $self->_drag_start_xy->vector_to($pos); # in pixels - - # get viewport and camera size in order to convert pixel to model units - my ($x, $y) = $self->GetSizeWH; - my $camera_bb_size = $self->_camera_bb->size; - - # compute translation in model units - $self->_camera_target->translate( - -$move->x * $camera_bb_size->x / $x, - $move->y * $camera_bb_size->y / $y, # /** - ); - - $self->_dirty(1); - } - $self->_drag_start_xy($pos); - } - } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { - $self->_drag_start_xy(undef); - } else { - $e->Skip(); - } -} - -sub set_z { - my ($self, $z) = @_; - - my $print = $self->print; - - # can we have interlaced layers? - my $interlaced = (defined first { $_->config->support_material } @{$print->objects}) - || (defined first { $_->config->infill_every_layers > 1 } @{$print->regions}); - - my $max_layer_height = $print->max_allowed_layer_height; - - my @layers = (); - foreach my $object (@{$print->objects}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { - if ($interlaced) { - push @layers, $layer - if $z > ($layer->print_z - $max_layer_height - epsilon) - && $z <= $layer->print_z + epsilon; - } else { - push @layers, $layer if abs($layer->print_z - $z) < epsilon; - } - } - } - - # reverse layers so that we draw the lowermost (i.e. current) on top - $self->z($z); - $self->layers([ reverse @layers ]); - $self->Refresh; -} - -sub set_simulation_mode -{ - my ($self, $mode) = @_; - $self->_simulation_mode($mode); - $self->_dirty(1); - $self->Refresh; -} - -sub Render { - my ($self, $dc) = @_; - - # prevent calling SetCurrent() when window is not shown yet - return unless $self->IsShownOnScreen; - return unless my $context = $self->GetContext; - $self->SetCurrent($context); - $self->InitGL; - - glClearColor(1, 1, 1, 0); - glClear(GL_COLOR_BUFFER_BIT); - - if (!$self->GetParent->enabled || !$self->layers) { - $self->SwapBuffers; - return; - } - - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if ($self->_simulation_mode and $self->_texture_name and $self->_texture_size->x() > 0 and $self->_texture_size->y() > 0) { - $self->_simulate_extrusion(); - my ($x, $y) = $self->GetSizeWH; - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE); - glBindTexture(GL_TEXTURE_2D, $self->_texture_name); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexImage2D_c(GL_TEXTURE_2D, - 0, # level (0 normal, heighr is form mip-mapping) - GL_RGBA, # internal format - $self->_texture_size->x(), $self->_texture_size->y(), - 0, # border - GL_RGBA, # format RGBA color data - GL_UNSIGNED_BYTE, # unsigned byte data - $self->_extrusion_simulator->image_ptr()); # ptr to texture data - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, 1, 0, 1, 0, 1); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(0, 0); - glTexCoord2f($x/$self->_texture_size->x(), 0); - glVertex2f(1, 0); - glTexCoord2f($x/$self->_texture_size->x(), $y/$self->_texture_size->y()); - glVertex2f(1, 1); - glTexCoord2f(0, $y/$self->_texture_size->y()); - glVertex2f(0, 1); - glEnd(); - glPopMatrix(); - glBindTexture(GL_TEXTURE_2D, 0); - } - - # anti-alias - if (0) { - glEnable(GL_LINE_SMOOTH); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE); - } - - # Tesselator triangulates polygons with holes on the fly for the rendering purposes only. - my $tess; - if ($self->_simulation_mode() == 0 and !(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) { - # We can't use the GLU tesselator on MSW with older OpenGL versions - # because of an upstream bug: - # http://sourceforge.net/p/pogl/bugs/16/ - $tess = gluNewTess(); - gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_END, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT'); - gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT'); - } - - foreach my $layer (@{$self->layers}) { - my $object = $layer->object; - - # only draw the slice for the current layer - next unless abs($layer->print_z - $self->z) < epsilon; - - # draw slice contour - glLineWidth(1); - foreach my $copy (@{ $object->_shifted_copies }) { - glPushMatrix(); - glTranslatef(@$copy, 0); - - foreach my $slice (@{$layer->slices}) { - glColor3f(0.95, 0.95, 0.95); - - if ($tess) { - gluTessBeginPolygon($tess); - foreach my $polygon (@$slice) { - gluTessBeginContour($tess); - gluTessVertex_p($tess, @$_, 0) for @$polygon; - gluTessEndContour($tess); - } - gluTessEndPolygon($tess); - } - - glColor3f(0.9, 0.9, 0.9); - foreach my $polygon (@$slice) { - foreach my $line (@{$polygon->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - } - } - glPopMatrix(); - } - } - - my $skirt_drawn = 0; - my $brim_drawn = 0; - foreach my $layer (@{$self->layers}) { - my $object = $layer->object; - my $print_z = $layer->print_z; - - # draw brim - if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) { - $self->color([0, 0, 0]); - $self->_draw(undef, $print_z, $_) for @{$self->print->brim}; - $brim_drawn = 1; - } - if ($self->print->step_done(STEP_SKIRT) - && ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id) - && !$skirt_drawn) { - $self->color([0, 0, 0]); - $self->_draw(undef, $print_z, $_) for @{$self->print->skirt}; - $skirt_drawn = 1; - } - - foreach my $layerm (@{$layer->regions}) { - if ($object->step_done(STEP_PERIMETERS)) { - $self->color([0.7, 0, 0]); - $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->perimeters}; - } - - if ($object->step_done(STEP_INFILL)) { - $self->color([0, 0, 0.7]); - $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills}; - } - } - - if ($object->step_done(STEP_SUPPORTMATERIAL)) { - if ($layer->isa('Slic3r::Layer::Support')) { - $self->color([0, 0, 0]); - $self->_draw($object, $print_z, $_) for @{$layer->support_fills}; - } - } - } - - gluDeleteTess($tess) if $tess; - $self->SwapBuffers; -} - -sub _draw { - my ($self, $object, $print_z, $path) = @_; - - my @paths = ($path->isa('Slic3r::ExtrusionLoop') || $path->isa('Slic3r::ExtrusionMultiPath')) - ? @$path - : ($path); - - $self->_draw_path($object, $print_z, $_) for @paths; -} - -sub _draw_path { - my ($self, $object, $print_z, $path) = @_; - - return if $print_z - $path->height > $self->z - epsilon; - - if (abs($print_z - $self->z) < epsilon) { - glColor3f(@{$self->color}); - } else { - glColor3f(0.8, 0.8, 0.8); - } - - glLineWidth(1); - - if (defined $object) { - foreach my $copy (@{ $object->_shifted_copies }) { - glPushMatrix(); - glTranslatef(@$copy, 0); - foreach my $line (@{$path->polyline->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - glPopMatrix(); - } - } else { - foreach my $line (@{$path->polyline->lines}) { - glBegin(GL_LINES); - glVertex2f(@{$line->a}); - glVertex2f(@{$line->b}); - glEnd(); - } - } -} - -sub _simulate_extrusion { - my ($self) = @_; - $self->_extrusion_simulator->reset_accumulator(); - foreach my $layer (@{$self->layers}) { - if (abs($layer->print_z - $self->z) < epsilon) { - my $object = $layer->object; - my @shifts = (defined $object) ? @{$object->_shifted_copies} : (Slic3r::Point->new(0, 0)); - foreach my $layerm (@{$layer->regions}) { - my @extrusions = (); - if ($object->step_done(STEP_PERIMETERS)) { - push @extrusions, @$_ for @{$layerm->perimeters}; - } - if ($object->step_done(STEP_INFILL)) { - push @extrusions, @$_ for @{$layerm->fills}; - } - foreach my $extrusion_entity (@extrusions) { - my @paths = ($extrusion_entity->isa('Slic3r::ExtrusionLoop') || $extrusion_entity->isa('Slic3r::ExtrusionMultiPath')) - ? @$extrusion_entity - : ($extrusion_entity); - foreach my $path (@paths) { - print "width: ", $path->width, - " height: ", $path->height, - " mm3_per_mm: ", $path->mm3_per_mm, - " height2: ", $path->mm3_per_mm / $path->height, - "\n"; - $self->_extrusion_simulator->extrude_to_accumulator($path, $_, $self->_simulation_mode()) for @shifts; - } - } - } - } - } - $self->_extrusion_simulator->evaluate_accumulator($self->_simulation_mode()); -} - -sub InitGL { - my $self = shift; - - return if $self->init; - return unless $self->GetContext; - - my $texture_id = 0; - ($texture_id) = glGenTextures_p(1); - $self->_texture_name($texture_id); - $self->init(1); -} - -sub GetContext { - my ($self) = @_; - return $self->{context} ||= Wx::GLContext->new($self); -} - -sub SetCurrent { - my ($self, $context) = @_; - return $self->SUPER::SetCurrent($context); -} - -sub Resize { - my ($self) = @_; - - return unless $self->GetContext; - return unless $self->bb; - $self->_dirty(0); - - $self->SetCurrent($self->GetContext); - my ($x, $y) = $self->GetSizeWH; - - if ($self->_texture_size->x() < $x or $self->_texture_size->y() < $y) { - # Allocate a large enough OpenGL texture with power of 2 dimensions. - $self->_texture_size->set_x(1) if ($self->_texture_size->x() == 0); - $self->_texture_size->set_y(1) if ($self->_texture_size->y() == 0); - $self->_texture_size->set_x($self->_texture_size->x() * 2) while ($self->_texture_size->x() < $x); - $self->_texture_size->set_y($self->_texture_size->y() * 2) while ($self->_texture_size->y() < $y); - #print "screen size ", $x, "x", $y; - #print "texture size ", $self->_texture_size->x(), "x", $self->_texture_size->y(); - # Initialize an empty texture. - glBindTexture(GL_TEXTURE_2D, $self->_texture_name); - if (1) { - glTexImage2D_c(GL_TEXTURE_2D, - 0, # level (0 normal, heighr is form mip-mapping) - GL_RGBA, # internal format - $self->_texture_size->x(), $self->_texture_size->y(), - 0, # border - GL_RGBA, # format RGBA color data - GL_UNSIGNED_BYTE, # unsigned byte data - 0); # ptr to texture data - } - glBindTexture(GL_TEXTURE_2D, 0); - $self->_extrusion_simulator->set_image_size($self->_texture_size); - } - $self->_extrusion_simulator->set_viewport(Slic3r::Geometry::BoundingBox->new_from_points( - [Slic3r::Point->new(0, 0), Slic3r::Point->new($x, $y)])); - - glViewport(0, 0, $x, $y); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - my $bb = $self->bb->clone; - - # rescale in dependence of window aspect ratio - my $bb_size = $bb->size; - my $ratio_x = ($x != 0.0) ? $bb_size->x / $x : 1.0; - my $ratio_y = ($y != 0.0) ? $bb_size->y / $y : 1.0; - - if ($ratio_y < $ratio_x) { - if ($ratio_y != 0.0) { - my $new_size_y = $bb_size->y * $ratio_x / $ratio_y; - my $half_delta_size_y = 0.5 * ($new_size_y - $bb_size->y); - $bb->set_y_min($bb->y_min - $half_delta_size_y); - $bb->set_y_max($bb->y_max + $half_delta_size_y); - } - } elsif ($ratio_x < $ratio_y) { - if ($ratio_x != 0.0) { - my $new_size_x = $bb_size->x * $ratio_y / $ratio_x; - my $half_delta_size_x = 0.5 * ($new_size_x - $bb_size->x); - $bb->set_x_min($bb->x_min - $half_delta_size_x); - $bb->set_x_max($bb->x_max + $half_delta_size_x); - } - } - - # center bounding box around origin before scaling it - my $bb_center = $bb->center; - $bb->translate(@{$bb_center->negative}); - - # scale bounding box according to zoom factor - $bb->scale($self->_zoom); - - # reposition bounding box around original center - $bb->translate(@{$bb_center}); - - # translate camera - $bb->translate(@{$self->_camera_target}); - -# # keep camera_bb within total bb -# # (i.e. prevent user from panning outside the bounding box) -# { -# my @translate = (0,0); -# if ($bb->x_min < $self->bb->x_min) { -# $translate[X] += $self->bb->x_min - $bb->x_min; -# } -# if ($bb->y_min < $self->bb->y_min) { -# $translate[Y] += $self->bb->y_min - $bb->y_min; -# } -# if ($bb->x_max > $self->bb->x_max) { -# $translate[X] -= $bb->x_max - $self->bb->x_max; -# } -# if ($bb->y_max > $self->bb->y_max) { -# $translate[Y] -= $bb->y_max - $self->bb->y_max; -# } -# $self->_camera_target->translate(@translate); -# $bb->translate(@translate); -# } - - # save camera - $self->_camera_bb($bb); - - my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max); - if (($x2 - $x1)/($y2 - $y1) > $x/$y) { - # adjust Y - my $new_y = $y * ($x2 - $x1) / $x; - $y1 = ($y2 + $y1)/2 - $new_y/2; - $y2 = $y1 + $new_y; - } else { - my $new_x = $x * ($y2 - $y1) / $y; - $x1 = ($x2 + $x1)/2 - $new_x/2; - $x2 = $x1 + $new_x; - } - glOrtho($x1, $x2, $y1, $y2, 0, 1); - - # Set the adjusted bounding box at the extrusion simulator. - #print "Scene bbox ", $bb->x_min, ",", $bb->y_min, " ", $bb->x_max, ",", $bb->y_max, "\n"; - #print "Setting simulator bbox ", $x1, ",", $y1, " ", $x2, ",", $y2, "\n"; - $self->_extrusion_simulator->set_bounding_box( - Slic3r::Geometry::BoundingBox->new_from_points( - [Slic3r::Point->new($x1, $y1), Slic3r::Point->new($x2, $y2)])); - - glMatrixMode(GL_MODELVIEW); -} - -# Thick line drawing is not used anywhere. Probably not tested? -sub line { - my ( - $x1, $y1, $x2, $y2, # coordinates of the line - $w, # width/thickness of the line in pixel - $Cr, $Cg, $Cb, # RGB color components - $Br, $Bg, $Bb, # color of background when alphablend=false - # Br=alpha of color when alphablend=true - $alphablend, # use alpha blend or not - ) = @_; - - my $t; - my $R; - my $f = $w - int($w); - my $A; - - if ($alphablend) { - $A = $Br; - } else { - $A = 1; - } - - # determine parameters t,R - if ($w >= 0 && $w < 1) { - $t = 0.05; $R = 0.48 + 0.32 * $f; - if (!$alphablend) { - $Cr += 0.88 * (1-$f); - $Cg += 0.88 * (1-$f); - $Cb += 0.88 * (1-$f); - $Cr = 1.0 if ($Cr > 1.0); - $Cg = 1.0 if ($Cg > 1.0); - $Cb = 1.0 if ($Cb > 1.0); - } else { - $A *= $f; - } - } elsif ($w >= 1.0 && $w < 2.0) { - $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f; - } elsif ($w >= 2.0 && $w < 3.0) { - $t = 0.38 + $f*0.58; $R = 1.08; - } elsif ($w >= 3.0 && $w < 4.0) { - $t = 0.96 + $f*0.48; $R = 1.08; - } elsif ($w >= 4.0 && $w < 5.0) { - $t= 1.44 + $f*0.46; $R = 1.08; - } elsif ($w >= 5.0 && $w < 6.0) { - $t= 1.9 + $f*0.6; $R = 1.08; - } elsif ($w >= 6.0) { - my $ff = $w - 6.0; - $t = 2.5 + $ff*0.50; $R = 1.08; - } - #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C); - - # determine angle of the line to horizontal - my $tx = 0; my $ty = 0; # core thinkness of a line - my $Rx = 0; my $Ry = 0; # fading edge of a line - my $cx = 0; my $cy = 0; # cap of a line - my $ALW = 0.01; - my $dx = $x2 - $x1; - my $dy = $y2 - $y1; - if (abs($dx) < $ALW) { - # vertical - $tx = $t; $ty = 0; - $Rx = $R; $Ry = 0; - if ($w > 0.0 && $w < 1.0) { - $tx *= 8; - } elsif ($w == 1.0) { - $tx *= 10; - } - } elsif (abs($dy) < $ALW) { - #horizontal - $tx = 0; $ty = $t; - $Rx = 0; $Ry = $R; - if ($w > 0.0 && $w < 1.0) { - $ty *= 8; - } elsif ($w == 1.0) { - $ty *= 10; - } - } else { - if ($w < 3) { # approximate to make things even faster - my $m = $dy/$dx; - # and calculate tx,ty,Rx,Ry - if ($m > -0.4142 && $m <= 0.4142) { - # -22.5 < $angle <= 22.5, approximate to 0 (degree) - $tx = $t * 0.1; $ty = $t; - $Rx = $R * 0.6; $Ry = $R; - } elsif ($m > 0.4142 && $m <= 2.4142) { - # 22.5 < $angle <= 67.5, approximate to 45 (degree) - $tx = $t * -0.7071; $ty = $t * 0.7071; - $Rx = $R * -0.7071; $Ry = $R * 0.7071; - } elsif ($m > 2.4142 || $m <= -2.4142) { - # 67.5 < $angle <= 112.5, approximate to 90 (degree) - $tx = $t; $ty = $t*0.1; - $Rx = $R; $Ry = $R*0.6; - } elsif ($m > -2.4142 && $m < -0.4142) { - # 112.5 < angle < 157.5, approximate to 135 (degree) - $tx = $t * 0.7071; $ty = $t * 0.7071; - $Rx = $R * 0.7071; $Ry = $R * 0.7071; - } else { - # error in determining angle - printf("error in determining angle: m=%.4f\n", $m); - } - } else { # calculate to exact - $dx= $y1 - $y2; - $dy= $x2 - $x1; - my $L = sqrt($dx*$dx + $dy*$dy); - $dx /= $L; - $dy /= $L; - $cx = -0.6*$dy; $cy=0.6*$dx; - $tx = $t*$dx; $ty = $t*$dy; - $Rx = $R*$dx; $Ry = $R*$dy; - } - } - - # draw the line by triangle strip - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge - glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry); - - if (!$alphablend) { - glColor3f($Cr, $Cg, $Cb); - } else { - glColor4f($Cr, $Cg, $Cb, $A); - } - glVertex2f($x1 - $tx, $y1 - $ty); # core - glVertex2f($x2 - $tx, $y2 - $ty); - glVertex2f($x1 + $tx, $y1 + $ty); - glVertex2f($x2 + $tx, $y2 + $ty); - - if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) { - # printf("skipped one fading edge\n"); - } else { - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge - glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry); - } - glEnd(); - - # cap - if ($w < 3) { - # do not draw cap - } else { - # draw cap - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy); - glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy); - glColor3f($Cr, $Cg, $Cb); - glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); - glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry); - glEnd(); - glBegin(GL_TRIANGLE_STRIP); - if (!$alphablend) { - glColor3f($Br, $Bg, $Bb); - } else { - glColor4f($Cr, $Cg, $Cb, 0); - } - glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy); - glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy); - glColor3f($Cr, $Cg, $Cb); - glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry); - glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry); - glEnd(); - } -} - - -package Slic3r::GUI::Plater::2DToolpaths::Dialog; - -use Wx qw(:dialog :id :misc :sizer); -use Wx::Event qw(EVT_CLOSE); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, $print) = @_; - my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0); - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm deleted file mode 100644 index 0685b4100..000000000 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ /dev/null @@ -1,221 +0,0 @@ -# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings. -# - -package Slic3r::GUI::Plater::LambdaObjectDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT); -use Scalar::Util qw(looks_like_number); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - # Note whether the window was already closed, so a pending update is not executed. - $self->{already_closed} = 0; - $self->{object_parameters} = { - type => "box", - dim => [1, 1, 1], - cyl_r => 1, - cyl_h => 1, - sph_rho => 1.0, - slab_h => 1.0, - slab_z => 0.0, - }; - - $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); - my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $button_ok = $self->CreateStdDialogButtonSizer(wxOK); - my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL); - $button_sizer->Add($button_ok); - $button_sizer->Add($button_cancel); - EVT_BUTTON($self, wxID_OK, sub { - # validate user input - return if !$self->CanClose; - - $self->EndModal(wxID_OK); - $self->Destroy; - }); - EVT_BUTTON($self, wxID_CANCEL, sub { - # validate user input - return if !$self->CanClose; - - $self->EndModal(wxID_CANCEL); - $self->Destroy; - }); - - my $optgroup_box; - $optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Cube...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { - if (!looks_like_number($optgroup_box->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id); - }, - label_width => 100, - ); - my @options = ("box", "slab", "cylinder", "sphere"); - $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 0, - label => 'L', - type => 'f', - default => '1', - )); - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 1, - label => 'W', - type => 'f', - default => '1', - )); - $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 2, - label => 'H', - type => 'f', - default => '1', - )); - - my $optgroup_cylinder; - $optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Cylinder...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') { - if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id); - }, - label_width => 100, - ); - - $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_r", - label => 'Radius', - type => 'f', - default => '1', - )); - $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_h", - label => 'Height', - type => 'f', - default => '1', - )); - - my $optgroup_sphere; - $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Sphere...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'sph_rho') { - if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id); - }, - label_width => 100, - ); - - $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "sph_rho", - label => 'Rho', - type => 'f', - default => '1', - )); - - my $optgroup_slab; - $optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Add Slab...', - on_change => sub { - # Do validation - my ($opt_id) = @_; - if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') { - if (!looks_like_number($optgroup_slab->get_value($opt_id))) { - return 0; - } - } - $self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id); - }, - label_width => 100, - ); - $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "slab_h", - label => 'H', - type => 'f', - default => '1', - )); - $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "slab_z", - label => 'Initial Z', - type => 'f', - default => '0', - )); - - - EVT_COMBOBOX($self, 1, sub{ - $self->{object_parameters}->{type} = $self->{type}->GetValue(); - $self->_update_ui; - }); - - - $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->_update_ui; - - $self->SetSizer($self->{sizer}); - $self->{sizer}->Fit($self); - $self->{sizer}->SetSizeHints($self); - - - return $self; -} -sub CanClose { - return 1; -} -sub ObjectParameter { - my ($self) = @_; - return $self->{object_parameters}; -} - -sub _update_ui { - my ($self) = @_; - $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); - $self->{sizer}->Hide($self->{optgroup_slab}->sizer); - $self->{sizer}->Hide($self->{optgroup_box}->sizer); - $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); - if ($self->{type}->GetValue eq "box") { - $self->{sizer}->Show($self->{optgroup_box}->sizer); - } elsif ($self->{type}->GetValue eq "cylinder") { - $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); - } elsif ($self->{type}->GetValue eq "slab") { - $self->{sizer}->Show($self->{optgroup_slab}->sizer); - } elsif ($self->{type}->GetValue eq "sphere") { - $self->{sizer}->Show($self->{optgroup_sphere}->sizer); - } - $self->{sizer}->Fit($self); - $self->{sizer}->SetSizeHints($self); - -} -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm deleted file mode 100644 index 26a6fdec3..000000000 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ /dev/null @@ -1,284 +0,0 @@ -# Cut an object at a Z position, keep either the top or the bottom of the object. -# This dialog gets opened with the "Cut..." button above the platter. - -package Slic3r::GUI::Plater::ObjectCutDialog; -use strict; -use warnings; -use utf8; - -use Slic3r::Geometry qw(PI X); -use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); -use List::Util qw(max); -use base 'Wx::Dialog'; - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{model_object} = $params{model_object}; - $self->{new_model_objects} = []; - # Mark whether the mesh cut is valid. - # If not, it needs to be recalculated by _update() on wxTheApp->CallAfter() or on exit of the dialog. - $self->{mesh_cut_valid} = 0; - # Note whether the window was already closed, so a pending update is not executed. - $self->{already_closed} = 0; - - # cut options - $self->{cut_options} = { - z => 0, - keep_upper => 1, - keep_lower => 1, - rotate_lower => 1, -# preview => 1, -# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. - preview => 0, - }; - - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Cut', - on_change => sub { - my ($opt_id) = @_; - # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider - # genates tens of events for a single value change. - # Only trigger the recalculation if the value changes - # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{cut_options}{$opt_id} != $optgroup->get_value($opt_id) || - ! $self->{mesh_cut_valid} && $self->_life_preview_active()) { - $self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id); - $self->{mesh_cut_valid} = 0; - wxTheApp->CallAfter(sub { - $self->_update; - }); - } - }, - label_width => 120, - ); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z', - type => 'slider', - label => 'Z', - default => $self->{cut_options}{z}, - min => 0, - max => $self->{model_object}->bounding_box->size->z, - full_width => 1, - )); - { - my $line = Slic3r::GUI::OptionsGroup::Line->new( - label => 'Keep', - ); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'keep_upper', - type => 'bool', - label => 'Upper part', - default => $self->{cut_options}{keep_upper}, - )); - $line->append_option(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'keep_lower', - type => 'bool', - label => 'Lower part', - default => $self->{cut_options}{keep_lower}, - )); - $optgroup->append_line($line); - } - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rotate_lower', - label => 'Rotate lower part upwards', - type => 'bool', - tooltip => 'If enabled, the lower part will be rotated by 180° so that the flat cut surface lies on the print bed.', - default => $self->{cut_options}{rotate_lower}, - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'preview', - label => 'Show preview', - type => 'bool', - tooltip => 'If enabled, object will be cut in real time.', - default => $self->{cut_options}{preview}, - )); - { - my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL); - $self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize); - $cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10); - $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( - sizer => $cut_button_sizer, - )); - } - - # left pane with tree - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - # right pane with preview canvas - my $canvas; - if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - $canvas->SetSize([500,500]); - $canvas->SetMinSize($canvas->GetSize); - Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config}); - Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); - } - - $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); - $self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas; - - $self->SetSizer($self->{sizer}); - $self->SetMinSize($self->GetSize); - $self->{sizer}->SetSizeHints($self); - - EVT_BUTTON($self, $self->{btn_cut}, sub { - # Recalculate the cut if the preview was not active. - $self->_perform_cut() unless $self->{mesh_cut_valid}; - - # Adjust position / orientation of the split object halves. - if ($self->{new_model_objects}{lower}) { - if ($self->{cut_options}{rotate_lower}) { - $self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0)); - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 - } - } - if ($self->{new_model_objects}{upper}) { - $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 - } - - # Note that the window was already closed, so a pending update will not be executed. - $self->{already_closed} = 1; - $self->EndModal(wxID_OK); - $self->{canvas}->Destroy; - $self->Destroy(); - }); - - EVT_CLOSE($self, sub { - # Note that the window was already closed, so a pending update will not be executed. - $self->{already_closed} = 1; - $self->EndModal(wxID_CANCEL); - $self->{canvas}->Destroy; - $self->Destroy(); - }); - - $self->_update; - - return $self; -} - -# scale Z down to original size since we're using the transformed mesh for 3D preview -# and cut dialog but ModelObject::cut() needs Z without any instance transformation -sub _mesh_slice_z_pos -{ - my ($self) = @_; - return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; -} - -# Only perform live preview if just a single part of the object shall survive. -sub _life_preview_active -{ - my ($self) = @_; - return $self->{cut_options}{preview} && ($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); -} - -# Slice the mesh, keep the top / bottom part. -sub _perform_cut -{ - my ($self) = @_; - - # Early exit. If the cut is valid, don't recalculate it. - return if $self->{mesh_cut_valid}; - - my $z = $self->_mesh_slice_z_pos(); - - my ($new_model) = $self->{model_object}->cut($z); - my ($upper_object, $lower_object) = @{$new_model->objects}; - $self->{new_model} = $new_model; - $self->{new_model_objects} = {}; - if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { - $self->{new_model_objects}{upper} = $upper_object; - } - if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { - $self->{new_model_objects}{lower} = $lower_object; - } - - $self->{mesh_cut_valid} = 1; -} - -sub _update { - my ($self) = @_; - - # Don't update if the window was already closed. - # We are not sure whether the action planned by wxTheApp->CallAfter() may be triggered after the window is closed. - # Probably not, but better be safe than sorry, which is espetially true on multiple platforms. - return if $self->{already_closed}; - - # Only recalculate the cut, if the live cut preview is active. - my $life_preview_active = $self->_life_preview_active(); - $self->_perform_cut() if $life_preview_active; - - { - # scale Z down to original size since we're using the transformed mesh for 3D preview - # and cut dialog but ModelObject::cut() needs Z without any instance transformation - my $z = $self->_mesh_slice_z_pos(); - - - # update canvas - if ($self->{canvas}) { - # get volumes to render - my @objects = (); - if ($life_preview_active) { - push @objects, values %{$self->{new_model_objects}}; - } else { - push @objects, $self->{model_object}; - } - - my $z_cut = $z + $self->{model_object}->bounding_box->z_min; - - # get section contour - my @expolygons = (); - foreach my $volume (@{$self->{model_object}->volumes}) { - next if !$volume->mesh; - next if $volume->modifier; - my $expp = $volume->mesh->slice([ $z_cut ])->[0]; - push @expolygons, @$expp; - } - foreach my $expolygon (@expolygons) { - $self->{model_object}->instances->[0]->transform_polygon($_) - for @$expolygon; - $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); - } - - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; - Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } - } - - # update controls - { - my $z = $self->{cut_options}{z}; - my $optgroup = $self->{optgroup}; - $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1); - $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1); - $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower}); -# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage. -# $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); - - # update cut button - if (($self->{cut_options}{keep_upper} && $have_upper) - || ($self->{cut_options}{keep_lower} && $have_lower)) { - $self->{btn_cut}->Enable; - } else { - $self->{btn_cut}->Disable; - } - } -} - -sub NewModelObjects { - my ($self) = @_; - return values %{ $self->{new_model_objects} }; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm deleted file mode 100644 index 783c1a9f5..000000000 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ /dev/null @@ -1,588 +0,0 @@ -# Configuration of mesh modifiers and their parameters. -# This panel is inserted into ObjectSettingsDialog. - -package Slic3r::GUI::Plater::ObjectPartsPanel; -use strict; -use warnings; -use utf8; - -use File::Basename qw(basename); -use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL - wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN); -use List::Util qw(max); -use base 'Wx::Panel'; - -use constant ICON_OBJECT => 0; -use constant ICON_SOLIDMESH => 1; -use constant ICON_MODIFIERMESH => 2; - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - - # C++ type Slic3r::ModelObject - my $object = $self->{model_object} = $params{model_object}; - - # Save state for sliders. - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; - - # create TreeCtrl - my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], - wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT - | wxTR_SINGLE); - { - $self->{tree_icons} = Wx::ImageList->new(16, 16, 1); - $tree->AssignImageList($self->{tree_icons}); - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH - $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH - - my $rootId = $tree->AddRoot("Object", ICON_OBJECT); - $tree->SetPlData($rootId, { type => 'object' }); - } - - # buttons - $self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); - $self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT); - $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG)); - $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG)); - - # buttons sizer - my $buttons_sizer = Wx::GridSizer->new(2, 3); - $buttons_sizer->Add($self->{btn_load_part}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_load_modifier}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0, wxEXPAND | wxBOTTOM, 5); - $buttons_sizer->Add($self->{btn_delete}, 0, wxEXPAND | wxRIGHT, 5); - $buttons_sizer->Add($self->{btn_split}, 0, wxEXPAND | wxRIGHT, 5); - { - my $up_down_sizer = Wx::GridSizer->new(1, 2); - $up_down_sizer->Add($self->{btn_move_up}, 0, wxEXPAND | wxRIGHT, 5); - $up_down_sizer->Add($self->{btn_move_down}, 0, wxEXPAND, 5); - $buttons_sizer->Add($up_down_sizer, 0, wxEXPAND, 5); - } - $self->{btn_load_part}->SetFont($Slic3r::GUI::small_font); - $self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font); - $self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font); - $self->{btn_delete}->SetFont($Slic3r::GUI::small_font); - $self->{btn_split}->SetFont($Slic3r::GUI::small_font); - $self->{btn_move_up}->SetFont($Slic3r::GUI::small_font); - $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font); - - # part settings panel - $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; }); - my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); - $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); - - my $optgroup_movers; - $optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Move', - on_change => sub { - my ($opt_id) = @_; - # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider - # genates tens of events for a single value change. - # Only trigger the recalculation if the value changes - # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) { - $self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id); - wxTheApp->CallAfter(sub { - $self->_update; - }); - } - }, - label_width => 20, - ); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'x', - type => 'slider', - label => 'X', - default => 0, - min => -($self->{model_object}->bounding_box->size->x)*4, - max => $self->{model_object}->bounding_box->size->x*4, - full_width => 1, - )); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'y', - type => 'slider', - label => 'Y', - default => 0, - min => -($self->{model_object}->bounding_box->size->y)*4, - max => $self->{model_object}->bounding_box->size->y*4, - full_width => 1, - )); - $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'z', - type => 'slider', - label => 'Z', - default => 0, - min => -($self->{model_object}->bounding_box->size->z)*4, - max => $self->{model_object}->bounding_box->size->z*4, - full_width => 1, - )); - - # left pane with tree - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); - $left_sizer->Add($tree, 3, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); - $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); - $left_sizer->Add($settings_sizer, 5, wxEXPAND | wxALL, 0); - $left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - # right pane with preview canvas - my $canvas; - if ($Slic3r::GUI::have_OpenGL) { - $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - Slic3r::GUI::_3DScene::enable_picking($canvas, 1); - Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume'); - Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub { - my ($volume_idx) = @_; - $self->reload_tree($volume_idx); - }); - Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas); - Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size })); - $canvas->SetSize([500,700]); - Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config}); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas); - Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1); - } - - $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxALL, 0); - $self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas; - - $self->SetSizer($self->{sizer}); - $self->{sizer}->SetSizeHints($self); - - # attach events - EVT_TREE_ITEM_COLLAPSING($self, $tree, sub { - my ($self, $event) = @_; - $event->Veto; - }); - EVT_TREE_SEL_CHANGED($self, $tree, sub { - my ($self, $event) = @_; - return if $self->{disable_tree_sel_changed_event}; - $self->selection_changed; - }); - EVT_TREE_KEY_DOWN($self, $tree, \&on_tree_key_down); - EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) }); - EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) }); - EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) }); - EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete); - EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split); - EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up); - EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down); - EVT_KEY_DOWN($canvas, sub { - my ($canvas, $event) = @_; - if ($event->GetKeyCode == WXK_DELETE) { - $canvas->GetParent->on_btn_delete; - } else { - $event->Skip; - } - }); - - $self->reload_tree; - - return $self; -} - -sub reload_tree { - my ($self, $selected_volume_idx) = @_; - - $selected_volume_idx //= -1; - my $object = $self->{model_object}; - my $tree = $self->{tree}; - my $rootId = $tree->GetRootItem; - - # despite wxWidgets states that DeleteChildren "will not generate any events unlike Delete() method", - # the MSW implementation of DeleteChildren actually calls Delete() for each item, so - # EVT_TREE_SEL_CHANGED is being called, with bad effects (the event handler is called; this - # subroutine is never continued; an invisible EndModal is called on the dialog causing Plater - # to continue its logic and rescheduling the background process etc. GH #2774) - $self->{disable_tree_sel_changed_event} = 1; - $tree->DeleteChildren($rootId); - $self->{disable_tree_sel_changed_event} = 0; - - my $selectedId = $rootId; - foreach my $volume_id (0..$#{$object->volumes}) { - my $volume = $object->volumes->[$volume_id]; - - my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH; - my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon); - if ($volume_id == $selected_volume_idx) { - $selectedId = $itemId; - } - $tree->SetPlData($itemId, { - type => 'volume', - volume_id => $volume_id, - }); - } - $tree->ExpandAll; - - Slic3r::GUI->CallAfter(sub { - $self->{tree}->SelectItem($selectedId); - - # SelectItem() should trigger EVT_TREE_SEL_CHANGED as per wxWidgets docs, - # but in fact it doesn't if the given item is already selected (this happens - # on first load) - $self->selection_changed; - }); -} - -sub get_selection { - my ($self) = @_; - - my $nodeId = $self->{tree}->GetSelection; - if ($nodeId->IsOk) { - return $self->{tree}->GetPlData($nodeId); - } - return undef; -} - -sub selection_changed { - my ($self) = @_; - - # deselect all meshes - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas}); - } - - # disable things as if nothing is selected - $self->{'btn_' . $_}->Disable for (qw(delete move_up move_down split)); - $self->{settings_panel}->disable; - $self->{settings_panel}->set_config(undef); - - # reset move sliders - $self->{optgroup_movers}->set_value("x", 0); - $self->{optgroup_movers}->set_value("y", 0); - $self->{optgroup_movers}->set_value("z", 0); - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; - - if (my $itemData = $self->get_selection) { - my ($config, @opt_keys); - if ($itemData->{type} eq 'volume') { - # select volume in 3D preview - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); - } - $self->{btn_delete}->Enable; - $self->{btn_split}->Enable; - $self->{btn_move_up}->Enable if $itemData->{volume_id} > 0; - $self->{btn_move_down}->Enable if $itemData->{volume_id} + 1 < $self->{model_object}->volumes_count; - - # attach volume config to settings panel - my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; - - if ($volume->modifier) { - $self->{optgroup_movers}->enable; - } else { - $self->{optgroup_movers}->disable; - } - $config = $volume->config; - $self->{staticbox}->SetLabel('Part Settings'); - - # get default values - @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; - } elsif ($itemData->{type} eq 'object') { - # select nothing in 3D preview - - # attach object config to settings panel - $self->{optgroup_movers}->disable; - $self->{staticbox}->SetLabel('Object Settings'); - @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); - $config = $self->{model_object}->config; - } - # get default values - my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); - - # decide which settings will be shown by default - if ($itemData->{type} eq 'object') { - $config->set_ifndef('wipe_into_objects', 0); - $config->set_ifndef('wipe_into_infill', 0); - } - - # append default extruder - push @opt_keys, 'extruder'; - $default_config->set('extruder', 0); - $config->set_ifndef('extruder', 0); - $self->{settings_panel}->set_default_config($default_config); - $self->{settings_panel}->set_config($config); - $self->{settings_panel}->set_opt_keys(\@opt_keys); - - # disable minus icon to remove the settings - if ($itemData->{type} eq 'object') { - $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]); - } else { - $self->{settings_panel}->set_fixed_options([qw(extruder)]); - } - - $self->{settings_panel}->enable; - } - - Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas}; -} - -sub on_btn_load { - my ($self, $is_modifier) = @_; - - my @input_files = wxTheApp->open_model($self); - foreach my $input_file (@input_files) { - my $model = eval { Slic3r::Model->read_from_file($input_file) }; - if ($@) { - Slic3r::GUI::show_error($self, $@); - next; - } - - foreach my $object (@{$model->objects}) { - foreach my $volume (@{$object->volumes}) { - my $new_volume = $self->{model_object}->add_volume($volume); - $new_volume->set_modifier($is_modifier); - $new_volume->set_name(basename($input_file)); - - # apply the same translation we applied to the object - $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); - - # set a default extruder value, since user can't add it manually - $new_volume->config->set_ifndef('extruder', 0); - - $self->{parts_changed} = 1; - } - } - } - - $self->{model_object}->center_around_origin if $self->{parts_changed}; - $self->_parts_changed; -} - -sub on_btn_lambda { - my ($self, $is_modifier) = @_; - - my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); - if ($dlg->ShowModal() == wxID_CANCEL) { - return; - } - my $params = $dlg->ObjectParameter; - my $type = "".$params->{"type"}; - my $name = "lambda-".$params->{"type"}; - my $mesh; - - if ($type eq "box") { - $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); - } elsif ($type eq "cylinder") { - $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); - } elsif ($type eq "sphere") { - $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); - } elsif ($type eq "slab") { - $mesh = Slic3r::TriangleMesh::cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); - # box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z - $mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); - } else { - return; - } - $mesh->repair; - my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); - $new_volume->set_modifier($is_modifier); - $new_volume->set_name($name); - - # set a default extruder value, since user can't add it manually - $new_volume->config->set_ifndef('extruder', 0); - - $self->{parts_changed} = 1; - $self->_parts_changed; -} - -sub on_tree_key_down { - my ($self, $event) = @_; - my $keycode = $event->GetKeyCode; - # Wx >= 0.9911 - if (defined(&Wx::TreeEvent::GetKeyEvent)) { - if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) { - if ($keycode == WXK_UP) { - $event->Skip; - $self->on_btn_move_up; - } elsif ($keycode == WXK_DOWN) { - $event->Skip; - $self->on_btn_move_down; - } - } elsif ($keycode == WXK_DELETE) { - $self->on_btn_delete; - } - } -} - -sub on_btn_move_up { - my ($self) = @_; - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume_id = $itemData->{volume_id}; - if ($self->{model_object}->move_volume_up($volume_id)) { - Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id); - $self->{parts_changed} = 1; - $self->reload_tree($volume_id - 1); - } - } -} - -sub on_btn_move_down { - my ($self) = @_; - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume_id = $itemData->{volume_id}; - if ($self->{model_object}->move_volume_down($volume_id)) { - Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id); - $self->{parts_changed} = 1; - $self->reload_tree($volume_id + 1); - } - } -} - -sub on_btn_delete { - my ($self) = @_; - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - - # if user is deleting the last solid part, throw error - if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) { - Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object."); - return; - } - - $self->{model_object}->delete_volume($itemData->{volume_id}); - $self->{parts_changed} = 1; - } - - $self->{model_object}->center_around_origin if $self->{parts_changed}; - $self->_parts_changed; -} - -sub on_btn_split { - my ($self) = @_; - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - my $nozzle_dmrs = $self->GetParent->GetParent->GetParent->{config}->get('nozzle_diameter'); - $self->{parts_changed} = 1 if $volume->split(scalar(@$nozzle_dmrs)) > 1; - } - - $self->_parts_changed; -} - -sub _parts_changed { - my ($self) = @_; - - $self->reload_tree; - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } -} - -sub CanClose { - my $self = shift; - - return 1; # skip validation for now - - # validate options before allowing user to dismiss the dialog - # the validate method only works on full configs so we have - # to merge our settings with the default ones - my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone; - eval { - $config->apply($self->model_object->config); - $config->validate; - }; - return ! Slic3r::GUI::catch_error($self); -} - -sub Destroy { - my ($self) = @_; - $self->{canvas}->Destroy if ($self->{canvas}); -} - -sub PartsChanged { - my ($self) = @_; - return $self->{parts_changed}; -} - -sub PartSettingsChanged { - my ($self) = @_; - return $self->{part_settings_changed}; -} - -sub _update_canvas { - my ($self) = @_; - - if ($self->{canvas}) { - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]); - - # restore selection, if any - if (my $itemData = $self->get_selection) { - if ($itemData->{type} eq 'volume') { - Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id}); - } - } - - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); - } -} - -sub _update { - my ($self) = @_; - my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z}); - my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z}); - - my $itemData = $self->get_selection; - if ($itemData && $itemData->{type} eq 'volume') { - my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z); - my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - $volume->mesh->translate(@{$d}); - $self->{last_coords}{x} = $m_x; - $self->{last_coords}{y} = $m_y; - $self->{last_coords}{z} = $m_z; - } - - $self->{parts_changed} = 1; - my @objects = (); - push @objects, $self->{model_object}; - Slic3r::GUI::_3DScene::reset_volumes($self->{canvas}); - Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects; - Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas}); - Slic3r::GUI::_3DScene::render($self->{canvas}); -} - -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm deleted file mode 100644 index 3befba708..000000000 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ /dev/null @@ -1,224 +0,0 @@ -# This dialog opens up when double clicked on an object line in the list at the right side of the platter. -# One may load additional STLs and additional modifier STLs, -# one may change the properties of the print per each modifier mesh or a Z-span. - -package Slic3r::GUI::Plater::ObjectSettingsDialog; -use strict; -use warnings; -use utf8; - -use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp); -use Wx::Event qw(EVT_BUTTON); -use base 'Wx::Dialog'; - -# Called with -# %params{object} of a Perl type Slic3r::GUI::Plater::Object -# %params{model_object} of a C++ type Slic3r::ModelObject -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [700,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{$_} = $params{$_} for keys %params; - - $self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); - $self->{tabpanel}->AddPage($self->{parts} = Slic3r::GUI::Plater::ObjectPartsPanel->new($self->{tabpanel}, model_object => $params{model_object}), "Parts"); - $self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}), "Layers"); - - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - EVT_BUTTON($self, wxID_OK, sub { - # validate user input - return if !$self->{parts}->CanClose; - return if !$self->{layers}->CanClose; - - # notify tabs - $self->{layers}->Closing; - - # save window size - wxTheApp->save_window_pos($self, "object_settings"); - - $self->EndModal(wxID_OK); - $self->{parts}->Destroy; - $self->Destroy; - }); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{tabpanel}, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); - $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - $self->SetSizer($sizer); - $self->SetMinSize($self->GetSize); - - $self->Layout; - - wxTheApp->restore_window_pos($self, "object_settings"); - - return $self; -} - -sub PartsChanged { - my ($self) = @_; - return $self->{parts}->PartsChanged; -} - -sub PartSettingsChanged { - my ($self) = @_; - return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged; -} - -package Slic3r::GUI::Plater::ObjectDialog::BaseTab; -use base 'Wx::Panel'; - -sub model_object { - my ($self) = @_; - # $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog - return $self->GetParent->GetParent->{model_object}; -} - -package Slic3r::GUI::Plater::ObjectDialog::LayersTab; -use Wx qw(:dialog :id :misc :sizer :systemsettings); -use Wx::Grid; -use Wx::Event qw(EVT_GRID_CELL_CHANGED); -use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - - { - my $label = Wx::StaticText->new($self, -1, "You can use this section to override the default layer height for parts of this object.", - wxDefaultPosition, [-1, 40]); - $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - $sizer->Add($label, 0, wxEXPAND | wxALL, 10); - } - - my $grid = $self->{grid} = Wx::Grid->new($self, -1, wxDefaultPosition, wxDefaultSize); - $sizer->Add($grid, 1, wxEXPAND | wxALL, 10); - $grid->CreateGrid(0, 3); - $grid->DisableDragRowSize; - $grid->HideRowLabels; - $grid->SetColLabelValue(0, "Min Z (mm)"); - $grid->SetColLabelValue(1, "Max Z (mm)"); - $grid->SetColLabelValue(2, "Layer height (mm)"); - $grid->SetColSize($_, 135) for 0..2; - $grid->SetDefaultCellAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE); - - # load data - foreach my $range (@{ $self->model_object->layer_height_ranges }) { - $grid->AppendRows(1); - my $i = $grid->GetNumberRows-1; - $grid->SetCellValue($i, $_, $range->[$_]) for 0..2; - } - $grid->AppendRows(1); # append one empty row - - EVT_GRID_CELL_CHANGED($grid, sub { - my ($grid, $event) = @_; - - # remove any non-numeric character - my $value = $grid->GetCellValue($event->GetRow, $event->GetCol); - $value =~ s/,/./g; - $value =~ s/[^0-9.]//g; - $grid->SetCellValue($event->GetRow, $event->GetCol, ($event->GetCol == 2) ? $self->_clamp_layer_height($value) : $value); - - # if there's no empty row, let's append one - for my $i (0 .. $grid->GetNumberRows) { - if ($i == $grid->GetNumberRows) { - # if we're here then we found no empty row - $grid->AppendRows(1); - last; - } - if (!grep $grid->GetCellValue($i, $_), 0..2) { - # exit loop if this row is empty - last; - } - } - - $self->{layers_changed} = 1; - }); - - $self->SetSizer($sizer); - $sizer->SetSizeHints($self); - - return $self; -} - -sub _clamp_layer_height -{ - my ($self, $value) = @_; - # $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog - my $config = $self->GetParent->GetParent->{config}; - if ($value =~ /^[0-9,.E]+$/) { - # Looks like a number. Validate the layer height. - my $nozzle_dmrs = $config->get('nozzle_diameter'); - my $min_layer_heights = $config->get('min_layer_height'); - my $max_layer_heights = $config->get('max_layer_height'); - my $min_layer_height = 1000.; - my $max_layer_height = 0.; - my $max_nozzle_dmr = 0.; - for (my $i = 0; $i < int(@{$nozzle_dmrs}); $i += 1) { - $min_layer_height = $min_layer_heights->[$i] if ($min_layer_heights->[$i] < $min_layer_height); - $max_layer_height = $max_layer_heights->[$i] if ($max_layer_heights->[$i] > $max_layer_height); - $max_nozzle_dmr = $nozzle_dmrs ->[$i] if ($nozzle_dmrs ->[$i] > $max_nozzle_dmr ); - } - $min_layer_height = 0.005 if ($min_layer_height < 0.005); - $max_layer_height = $max_nozzle_dmr * 0.75 if ($max_layer_height == 0.); - $max_layer_height = $max_nozzle_dmr if ($max_layer_height > $max_nozzle_dmr); - return ($value < $min_layer_height) ? $min_layer_height : - ($value > $max_layer_height) ? $max_layer_height : $value; - } else { - # If an invalid numeric value has been entered, use the default layer height. - return $config->get('layer_height'); - } -} - -sub CanClose { - my $self = shift; - - # validate ranges before allowing user to dismiss the dialog - - foreach my $range ($self->_get_ranges) { - my ($min, $max, $height) = @$range; - if ($max <= $min) { - Slic3r::GUI::show_error($self, "Invalid Z range $min-$max."); - return 0; - } - if ($min < 0 || $max < 0) { - Slic3r::GUI::show_error($self, "Invalid Z range $min-$max."); - return 0; - } - if ($height < 0) { - Slic3r::GUI::show_error($self, "Invalid layer height $height."); - return 0; - } - # TODO: check for overlapping ranges - } - - return 1; -} - -sub Closing { - my $self = shift; - - # save ranges into the plater object - $self->model_object->set_layer_height_ranges([ $self->_get_ranges ]); -} - -sub _get_ranges { - my $self = shift; - - my @ranges = (); - for my $i (0 .. $self->{grid}->GetNumberRows-1) { - my ($min, $max, $height) = map $self->{grid}->GetCellValue($i, $_), 0..2; - next if $min eq '' || $max eq '' || $height eq ''; - push @ranges, [ $min, $max, $height ]; - } - return sort { $a->[0] <=> $b->[0] } @ranges; -} - -sub LayersChanged { - my ($self) = @_; - return $self->{layers_changed}; -} - -1; diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm deleted file mode 100644 index ea4ce7132..000000000 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ /dev/null @@ -1,182 +0,0 @@ -# Included in ObjectSettingsDialog -> ObjectPartsPanel. -# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh. - -package Slic3r::GUI::Plater::OverrideSettingsPanel; -use strict; -use warnings; -use utf8; - -use List::Util qw(first); -use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG - wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU); -use base 'Wx::ScrolledWindow'; - -use constant ICON_MATERIAL => 0; -use constant ICON_SOLIDMESH => 1; -use constant ICON_MODIFIERMESH => 2; - -my %icons = ( - 'Advanced' => 'wand.png', - 'Extruders' => 'funnel.png', - 'Extrusion Width' => 'funnel.png', - 'Infill' => 'infill.png', - 'Layers and Perimeters' => 'layers.png', - 'Skirt and brim' => 'box.png', - 'Speed' => 'time.png', - 'Speed > Acceleration' => 'time.png', - 'Support material' => 'building.png', -); - -sub new { - my ($class, $parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - # C++ class Slic3r::DynamicPrintConfig, initially empty. - $self->{default_config} = Slic3r::Config->new; - $self->{config} = Slic3r::Config->new; - # On change callback. - $self->{on_change} = $params{on_change}; - $self->{fixed_options} = {}; - - $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); - - $self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL); - $self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0); - - # option selector - { - # create the button - my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); - EVT_LEFT_DOWN($btn, sub { - my $menu = Wx::Menu->new; - # create category submenus - my %categories = (); # category => submenu - foreach my $opt_key (@{$self->{options}}) { - if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) { - $categories{$cat} //= Wx::Menu->new; - } - } - # append submenus to main menu - my @categories = ('Layers and Perimeters', 'Infill', 'Support material', 'Speed', 'Extruders', 'Extrusion Width', 'Advanced'); - #foreach my $cat (sort keys %categories) { - foreach my $cat (@categories) { - wxTheApp->append_submenu($menu, $cat, "", $categories{$cat}, undef, $icons{$cat}); - } - # append options to submenus - foreach my $opt_key (@{$self->{options}}) { - my $cat = $Slic3r::Config::Options->{$opt_key}{category} or next; - my $cb = sub { - $self->{config}->set($opt_key, $self->{default_config}->get($opt_key)); - $self->update_optgroup; - $self->{on_change}->($opt_key) if $self->{on_change}; - }; - wxTheApp->append_menu_item($categories{$cat}, $self->{option_labels}{$opt_key}, - $Slic3r::Config::Options->{$opt_key}{tooltip}, $cb); - } - $self->PopupMenu($menu, $btn->GetPosition); - $menu->Destroy; - }); - - my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $h_sizer->Add($btn, 0, wxALL, 0); - $self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxBOTTOM, 10); - } - - $self->SetSizer($self->{sizer}); - $self->SetScrollbars(0, 1, 0, 1); - - $self->set_opt_keys($params{opt_keys}) if $params{opt_keys}; - $self->update_optgroup; - - return $self; -} - -sub set_default_config { - my ($self, $config) = @_; - $self->{default_config} = $config; -} - -sub set_config { - my ($self, $config) = @_; - $self->{config} = $config; - $self->update_optgroup; -} - -sub set_opt_keys { - my ($self, $opt_keys) = @_; - # sort options by category+label - $self->{option_labels} = { map { $_ => $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label} } @$opt_keys }; - $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ]; -} - -sub set_fixed_options { - my ($self, $opt_keys) = @_; - $self->{fixed_options} = { map {$_ => 1} @$opt_keys }; - $self->update_optgroup; -} - -sub update_optgroup { - my $self = shift; - - $self->{options_sizer}->Clear(1); - return if !defined $self->{config}; - - my %categories = (); - foreach my $opt_key (@{$self->{config}->get_keys}) { - my $category = $Slic3r::Config::Options->{$opt_key}{category}; - $categories{$category} ||= []; - push @{$categories{$category}}, $opt_key; - } - foreach my $category (sort keys %categories) { - my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( - parent => $self, - title => $category, - config => $self->{config}, - full_labels => 1, - label_font => $Slic3r::GUI::small_font, - sidetext_font => $Slic3r::GUI::small_font, - label_width => 150, - on_change => sub { $self->{on_change}->() if $self->{on_change} }, - extra_column => sub { - my ($line) = @_; - - my $opt_key = $line->get_options->[0]->opt_id; # we assume that we have one option per line - - # disallow deleting fixed options - return undef if $self->{fixed_options}{$opt_key}; - - my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); - EVT_BUTTON($self, $btn, sub { - $self->{config}->erase($opt_key); - $self->{on_change}->() if $self->{on_change}; - wxTheApp->CallAfter(sub { $self->update_optgroup }); - }); - return $btn; - }, - ); - foreach my $opt_key (sort @{$categories{$category}}) { - $optgroup->append_single_option_line($opt_key); - } - $self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0); - } - $self->GetParent->Layout; # we need this for showing scrollbars -} - -# work around a wxMAC bug causing controls not being disabled when calling Disable() on a Window -sub enable { - my ($self) = @_; - - $self->{btn_add}->Enable; - $self->Enable; -} - -sub disable { - my ($self) = @_; - - $self->{btn_add}->Disable; - $self->Disable; -} - -1; diff --git a/lib/Slic3r/GUI/SystemInfo.pm b/lib/Slic3r/GUI/SystemInfo.pm deleted file mode 100644 index 717ff12e5..000000000 --- a/lib/Slic3r/GUI/SystemInfo.pm +++ /dev/null @@ -1,70 +0,0 @@ -package Slic3r::GUI::SystemInfo; -use strict; -use warnings; -use utf8; - -use Wx qw(:font :html :misc :dialog :sizer :systemsettings :frame :id wxTheClipboard); -use Wx::Event qw(EVT_HTML_LINK_CLICKED EVT_LEFT_DOWN EVT_BUTTON); -use Wx::Html; -use base 'Wx::Dialog'; - -sub new { - my ($class, %params) = @_; - my $self = $class->SUPER::new($params{parent}, -1, 'Slic3r Prusa Edition - System Information', wxDefaultPosition, [600, 340], - wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxRESIZE_BORDER); - $self->{text_info} = $params{text_info}; - - $self->SetBackgroundColour(Wx::wxWHITE); - my $vsizer = Wx::BoxSizer->new(wxVERTICAL); - $self->SetSizer($vsizer); - - # text - my $text = - '' . - '
' . - ($params{slic3r_info} // '') . - ($params{copyright_info} // '') . - ($params{system_info} // '') . - ($params{opengl_info} // '') . - '' . - ''; - my $html = $self->{html} = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); - my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - my $size = &Wx::wxMSW ? 8 : 10; - $html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size * 1.5, $size * 1.4, $size * 1.3, $size, $size, $size, $size]); - $html->SetBorders(10); - $html->SetPage($text); - $vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0); - EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked); - - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - my $btn_copy_to_clipboard = Wx::Button->new($self, -1, "Copy to Clipboard", wxDefaultPosition, wxDefaultSize); - $buttons->Insert(0, $btn_copy_to_clipboard, 0, wxLEFT, 5); - EVT_BUTTON($self, $btn_copy_to_clipboard, \©_to_clipboard); - $self->SetEscapeId(wxID_CLOSE); - EVT_BUTTON($self, wxID_CLOSE, sub { - $self->EndModal(wxID_CLOSE); - $self->Close; - }); -# $vsizer->Add($buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); - $vsizer->Add($buttons, 0, wxEXPAND | wxALL, 5); - - return $self; -} - -sub link_clicked { - my ($self, $event) = @_; - - Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref); - $event->Skip(0); -} - -sub copy_to_clipboard { - my ($self, $event) = @_; - my $data = $self->{text_info}; - wxTheClipboard->Open; - wxTheClipboard->SetData(Wx::TextDataObject->new($data)); - wxTheClipboard->Close; -} - -1; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 180d0d028..286a73e2d 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -122,6 +122,7 @@ sub line_intersection { : undef; } +# Used by test cases. sub collinear { my ($line1, $line2, $require_overlapping) = @_; my $intersection = _line_intersection(map @$_, @$line1, @$line2); @@ -226,6 +227,7 @@ sub bounding_box { return @bb[X1,Y1,X2,Y2]; } +# used by ExPolygon::size sub size_2D { my @bounding_box = bounding_box(@_); return ( @@ -234,6 +236,7 @@ sub size_2D { ); } +# Used by sub collinear, which is used by test cases. # bounding_box_intersect($d, @a, @b) # Return true if the given bounding boxes @a and @b intersect # in $d dimensions. Used by sub collinear. @@ -252,6 +255,7 @@ sub bounding_box_intersect { return 1; } +# Used by test cases. # this assumes a CCW rotation from $p2 to $p3 around $p1 sub angle3points { my ($p1, $p2, $p3) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 38aa318e6..85c3ed757 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -24,7 +24,7 @@ sub run_post_process_scripts { my ($self, $output_file) = @_; # run post-processing scripts if (@{$self->config->post_process}) { -# $self->status_cb->(95, "Running post-processing scripts"); +# $self->set_status(95, "Running post-processing scripts"); $self->config->setenv; for my $script (@{$self->config->post_process}) { # Ignore empty post processing script lines. @@ -57,13 +57,13 @@ sub export_png { for(my $oi = 0; $oi < $objnum; $oi++) { $sobjects[$oi]->slice; - $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing..."); + $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); } my $fh = $params{output_file}; - $self->status_cb->(90, "Exporting zipped archive..."); + $self->set_status(90, "Exporting zipped archive..."); $self->print_to_png($fh); - $self->status_cb->(100, "Done."); + $self->set_status(100, "Done."); } # Export SVG slices for the offline SLA printing. @@ -77,7 +77,7 @@ sub export_svg { for(my $oi = 0; $oi < $objnum; $oi++) { $sobjects[$oi]->slice; - $self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing..."); + $self->set_status(($oi + 1)*100/$objnum - 1, "Slicing..."); } my $fh = $params{output_fh}; diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm deleted file mode 100644 index 1aa7dd27d..000000000 --- a/lib/Slic3r/SVG.pm +++ /dev/null @@ -1,142 +0,0 @@ -package Slic3r::SVG; -use strict; -use warnings; - -use SVG; - -use constant X => 0; -use constant Y => 1; - -our $filltype = 'evenodd'; - -sub factor { - return &Slic3r::SCALING_FACTOR * 10; -} - -sub svg { - my $svg = SVG->new(width => 200 * 10, height => 200 * 10); - my $marker_end = $svg->marker( - id => "endArrow", - viewBox => "0 0 10 10", - refX => "1", - refY => "5", - markerUnits => "strokeWidth", - orient => "auto", - markerWidth => "10", - markerHeight => "8", - ); - $marker_end->polyline( - points => "0,0 10,5 0,10 1,5", - fill => "darkblue", - ); - - return $svg; -} - -sub output { - my ($filename, @things) = @_; - - my $svg = svg(); - my $arrows = 1; - - while (my $type = shift @things) { - my $value = shift @things; - - if ($type eq 'no_arrows') { - $arrows = 0; - } elsif ($type =~ /^(?:(.+?)_)?expolygons$/) { - my $colour = $1; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 0, - 'stroke' => $colour || 'black', - 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), - 'fill-type' => $filltype, - }, - ); - foreach my $expolygon (@$value) { - my $points = join ' ', map "M $_ z", map join(" ", reverse map $_->[0]*factor() . " " . $_->[1]*factor(), @$_), @$expolygon; - $g->path( - d => $points, - ); - } - } elsif ($type =~ /^(?:(.+?)_)?(polygon|polyline)s$/) { - my ($colour, $method) = ($1, $2); - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => ($method eq 'polyline') ? 1 : 0, - 'stroke' => $colour || 'black', - 'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')), - }, - ); - foreach my $polygon (@$value) { - my $path = $svg->get_path( - 'x' => [ map($_->[X] * factor(), @$polygon) ], - 'y' => [ map($_->[Y] * factor(), @$polygon) ], - -type => 'polygon', - ); - $g->$method( - %$path, - 'marker-end' => !$arrows ? "" : "url(#endArrow)", - ); - } - } elsif ($type =~ /^(?:(.+?)_)?points$/) { - my $colour = $1 // 'black'; - my $r = $colour eq 'black' ? 1 : 3; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 2, - 'stroke' => $colour, - 'fill' => $colour, - }, - ); - foreach my $point (@$value) { - $g->circle( - cx => $point->[X] * factor(), - cy => $point->[Y] * factor(), - r => $r, - ); - } - } elsif ($type =~ /^(?:(.+?)_)?lines$/) { - my $colour = $1; - $value = [ map $_->pp, @$value ]; - - my $g = $svg->group( - style => { - 'stroke-width' => 2, - }, - ); - foreach my $line (@$value) { - $g->line( - x1 => $line->[0][X] * factor(), - y1 => $line->[0][Y] * factor(), - x2 => $line->[1][X] * factor(), - y2 => $line->[1][Y] * factor(), - style => { - 'stroke' => $colour || 'black', - }, - 'marker-end' => !$arrows ? "" : "url(#endArrow)", - ); - } - } - } - - write_svg($svg, $filename); -} - -sub write_svg { - my ($svg, $filename) = @_; - - Slic3r::open(\my $fh, '>', $filename); - print $fh $svg->xmlify; - close $fh; - printf "SVG written to %s\n", $filename; -} - -1; diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl deleted file mode 100644 index 03153ab3f..000000000 --- a/utils/view-mesh.pl +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'cut=f' => \$opt{cut}, - 'enable-moving' => \$opt{enable_moving}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $_->center_around_origin for @{$model->objects}; # and align to Z = 0 - - my $app = Slic3r::ViewMesh->new; - $app->{canvas}->enable_picking(1); - $app->{canvas}->enable_moving($opt{enable_moving}); - $app->{canvas}->load_object($model, 0); - $app->{canvas}->set_auto_bed_shape; - $app->{canvas}->zoom_to_volumes; - $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-mesh.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --cut Z Display the cutting plane at the given Z - -EOF - exit ($exit_code || 0); -} - -package Slic3r::ViewMesh; -use Wx qw(:sizer); -use base qw(Wx::App); - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]); - my $panel = Wx::Panel->new($frame, -1); - - $self->{canvas} = Slic3r::GUI::3DScene->new($panel); - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($self->{canvas}, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - $sizer->SetSizeHints($panel); - - $frame->Show(1); -} - -__END__ diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl deleted file mode 100755 index e064885ca..000000000 --- a/utils/view-toolpaths.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/perl -# This script displays 3D preview of a mesh - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::GUI; -use Slic3r::GUI::3DScene; -$|++; - -my %opt = (); -{ - my %options = ( - 'help' => sub { usage() }, - 'load=s' => \$opt{load}, - '3D' => \$opt{d3}, - 'duplicate=i' => \$opt{duplicate}, - ); - GetOptions(%options) or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - - # load config - my $config = Slic3r::Config::new_from_defaults; - if ($opt{load}) { - $config->apply(Slic3r::Config::load($opt{load})); - } - - # init print - my $sprint = Slic3r::Print::Simple->new; - $sprint->duplicate($opt{duplicate} // 1); - $sprint->apply_config($config); - $sprint->set_model($model); - $sprint->process; - - # visualize toolpaths - $Slic3r::ViewToolpaths::print = $sprint->_print; - $Slic3r::ViewToolpaths::d3 = $opt{d3}; - my $app = Slic3r::ViewToolpaths->new; - $app->MainLoop; -} - - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: view-toolpaths.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --load CONFIG Loads the supplied config file - -EOF - exit ($exit_code || 0); -} - - -package Slic3r::ViewToolpaths; -use Wx qw(:sizer); -use base qw(Wx::App Class::Accessor); - -our $print; -our $d3; - -sub OnInit { - my $self = shift; - - my $frame = Wx::Frame->new(undef, -1, 'Toolpaths', [-1, -1], [500, 500]); - my $panel = Wx::Panel->new($frame, -1); - - my $canvas; - if ($d3) { - $canvas = Slic3r::GUI::3DScene->new($panel); - $canvas->set_bed_shape($print->config->bed_shape); - $canvas->load_print_toolpaths($print); - - foreach my $object (@{$print->objects}) { - #$canvas->load_print_object_slices($object); - $canvas->load_print_object_toolpaths($object); - #$canvas->load_object($object->model_object); - } - $canvas->zoom_to_volumes; - } else { - $canvas = Slic3r::GUI::Plater::2DToolpaths->new($panel, $print); - } - - my $sizer = Wx::BoxSizer->new(wxVERTICAL); - $sizer->Add($canvas, 1, wxEXPAND, 0); - $panel->SetSizer($sizer); - - $frame->Show(1); -} - -__END__ diff --git a/utils/wireframe.pl b/utils/wireframe.pl deleted file mode 100644 index 5399f31ac..000000000 --- a/utils/wireframe.pl +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/perl -# This script exports experimental G-code for wireframe printing -# (inspired by the brilliant WirePrint concept) - -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Getopt::Long qw(:config no_auto_abbrev); -use Slic3r; -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale X Y PI); - -my %opt = ( - step_height => 5, - nozzle_angle => 30, - nozzle_width => 10, - first_layer_height => 0.3, -); -{ - my %options = ( - 'help' => sub { usage() }, - 'output|o=s' => \$opt{output_file}, - 'step-height|h=f' => \$opt{step_height}, - 'nozzle-angle|a=f' => \$opt{nozzle_angle}, - 'nozzle-width|w=f' => \$opt{nozzle_width}, - 'first-layer-height=f' => \$opt{first_layer_height}, - ); - GetOptions(%options) or usage(1); - $opt{output_file} or usage(1); - $ARGV[0] or usage(1); -} - -{ - # load model - my $model = Slic3r::Model->read_from_file($ARGV[0]); - $model->center_instances_around_point(Slic3r::Pointf->new(100,100)); - my $mesh = $model->mesh; - $mesh->translate(0, 0, -$mesh->bounding_box->z_min); - - # get slices - my @z = (); - my $z_max = $mesh->bounding_box->z_max; - for (my $z = $opt{first_layer_height}; $z <= $z_max; $z += $opt{step_height}) { - push @z, $z; - } - my @slices = @{$mesh->slice(\@z)}; - - my $flow = Slic3r::Flow->new( - width => 0.35, - height => 0.35, - nozzle_diameter => 0.35, - bridge => 1, - ); - - my $config = Slic3r::Config::Print->new; - $config->set('gcode_comments', 1); - - open my $fh, '>', $opt{output_file}; - my $gcodegen = Slic3r::GCode->new( - enable_loop_clipping => 0, # better bonding - ); - $gcodegen->apply_print_config($config); - $gcodegen->set_extruders([0]); - print $fh $gcodegen->set_extruder(0); - print $fh $gcodegen->writer->preamble; - - my $e = $gcodegen->writer->extruder->e_per_mm3 * $flow->mm3_per_mm; - - foreach my $layer_id (0..$#z) { - my $z = $z[$layer_id]; - - foreach my $island (@{$slices[$layer_id]}) { - foreach my $polygon (@$island) { - if ($layer_id > 0) { - # find the lower polygon that we want to connect to this one - my $lower = $slices[$layer_id-1]->[0]->contour; # 't was easy, wasn't it? - my $lower_z = $z[$layer_id-1]; - - { - my @points = (); - - # keep all points with strong angles - { - my @pp = @$polygon; - foreach my $i (0..$#pp) { - push @points, $pp[$i-1] if abs($pp[$i-1]->ccw_angle($pp[$i-2], $pp[$i]) - PI) > PI/3; - } - } - - $polygon = Slic3r::Polygon->new(@points); - } - #$polygon = Slic3r::Polygon->new(@{$polygon->split_at_first_point->equally_spaced_points(scale $opt{nozzle_width})}); - - # find vertical lines - my @vertical = (); - foreach my $point (@{$polygon}) { - push @vertical, Slic3r::Line->new($point->projection_onto_polygon($lower), $point); - } - - next if !@vertical; - - my @points = (); - foreach my $line (@vertical) { - push @points, Slic3r::Pointf3->new( - unscale($line->a->x), - unscale($line->a->y), #)) - $lower_z, - ); - push @points, Slic3r::Pointf3->new( - unscale($line->b->x), - unscale($line->b->y), #)) - $z, - ); - } - - # reappend first point as destination of the last diagonal segment - push @points, Slic3r::Pointf3->new( - unscale($vertical[0]->a->x), - unscale($vertical[0]->a->y), #)) - $lower_z, - ); - - # move to the position of the first vertical line - print $fh $gcodegen->writer->travel_to_xyz(shift @points); - - # extrude segments - foreach my $point (@points) { - print $fh $gcodegen->writer->extrude_to_xyz($point, $e * $gcodegen->writer->get_position->distance_to($point)); - } - } - } - - print $fh $gcodegen->writer->travel_to_z($z); - foreach my $polygon (@$island) { - #my $polyline = $polygon->split_at_vertex(Slic3r::Point->new_scale(@{$gcodegen->writer->get_position}[0,1])); - my $polyline = $polygon->split_at_first_point; - print $fh $gcodegen->writer->travel_to_xy(Slic3r::Pointf->new_unscale(@{ $polyline->first_point }), "move to first contour point"); - - foreach my $line (@{$polyline->lines}) { - my $point = Slic3r::Pointf->new_unscale(@{ $line->b }); - print $fh $gcodegen->writer->extrude_to_xy($point, $e * unscale($line->length)); - } - } - } - } - - close $fh; -} - -sub usage { - my ($exit_code) = @_; - - print <<"EOF"; -Usage: wireframe.pl [ OPTIONS ] file.stl - - --help Output this usage screen and exit - --output, -o Write to the specified file - --step-height, -h Use the specified step height - --nozzle-angle, -a Max nozzle angle - --nozzle-width, -w External nozzle diameter - -EOF - exit ($exit_code || 0); -} - -__END__ diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a677d9e5a..651513c62 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -28,11 +28,13 @@ template class PrintState