diff --git a/.gitignore b/.gitignore index f24586461..c8c13aed4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ xs/assertlib* local-lib package/osx/Slic3r*.app *.dmg +*.swp +*.swo diff --git a/.travis.yml b/.travis.yml index a34433a9b..f399c1de8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,50 @@ language: perl -install: export LDLOADLIBS=-lstdc++ -script: perl ./Build.PL -perl: - - "5.14" - - "5.18" - - "5.20" +before_install: +- sh package/linux/travis-decrypt-key +install: +- export LDLOADLIBS=-lstdc++ +- export BOOST_DIR=$HOME/boost_1_63_0 +- export SLIC3R_STATIC=1 +- export CXX=g++-4.9 +- export CC=g++-4.9 +- source $HOME/perl5/perlbrew/etc/bashrc +script: +- bash package/linux/travis-setup.sh +- perlbrew switch slic3r-perl +- perl ./Build.PL +after_success: +- eval $(perl -Mlocal::lib=$TRAVIS_BUILD_DIR/local-lib) +- LD_LIBRARY_PATH=$WXDIR/lib package/linux/make_archive.sh linux-x64 +- package/deploy/sftp.sh linux ~/slic3r-upload.rsa *.bz2 branches: only: - - master - - xsgui -sudo: false + - master + - xsgui cache: apt: true directories: - - local-lib + - "$HOME/cache" addons: apt: sources: - - boost-latest - ubuntu-toolchain-r-test packages: - - libboost-thread1.55-dev - - libboost-system1.55-dev - - libboost-filesystem1.55-dev - - liblocal-lib-perl - g++-4.9 -env: CC=g++-4.9 + - gcc-4.9 + - libgtk2.0-0 + - libgtk2.0-dev + ssh_known_hosts: dl.slic3r.org notifications: irc: channels: - - "chat.freenode.net#slic3r" + - chat.freenode.net#slic3r on_success: change on_failure: always use_notice: true +sudo: required +dist: trusty +env: + matrix: + global: + - secure: eEVRZNMv7FM6jrOU9iAFkDhWxFQ1WtHBEaObImcvtFUxy6vWSt3ehFFeTRouj3uHQAnbvUzziDyvPPm8/95alv5g/du8ML6YzzqKBKfazM0xQ7SF6R2DQL8lfFIp+RSV7T02byEP1f1g7Zva7xH9szIlDcSfU0pXW4KWbkBFMd8= + - secure: gj338h+qHGccTD/VQFmEJkqdg2McIe2pO0iZ4Ae9BvY5vxkIML4BpoYZQXQTqiAOETnUjlcknY9lx0hI/PfkDD9MSJc5BC/3fMYRCu3SgAclEwklWf9vvtodUeT69mtnZuw1zze1nTbExuOw2mepbqFjxKKMl+9l5oCz4O54fXU= diff --git a/README.md b/README.md index 2a96554fc..7f3de1b8b 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ The main author of Slic3r is Alessandro Ranellucci (@alexrj, *Sound* in IRC, [@a Joseph Lenox (@lordofhyphens, *Loh* in IRC) is the current co-maintainer. -Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, Silk Icon Set designed by Mark James. +Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, Silk Icon Set designed by Mark James. ### How can I invoke Slic3r using the command line? diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..701f5ca6c --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,45 @@ +version: 1.3.0-{branch}-{build} +image: WMF 5 +init: +- ps: +environment: + LDLOADLIBS: -lstdc++ + SLIC3R_STATIC: 1 + SLIC3R_VERSION: 1.3.0 + BOOST_DIR: C:\dev\boost_1_63_0 + WXSHARED: SHARED=0 + FORCE_WX_BUILD: 0 + FORCE_BOOST_REINSTALL: 0 + ENC_SECRET: + secure: QfeTOSKXz1uFCEACqFKLNw== + UPLOAD_USER: + secure: fYPwnI3p6HNR+eMRJR3JfmyNolFn+Uc0MUn2bBXp9uU= + +install: + - IF DEFINED ENC_SECRET nuget install secure-file -ExcludeVersion + - IF DEFINED ENC_SECRET secure-file\tools\secure-file -decrypt package/deploy/slic3r-upload.ppk.enc -secret %ENC_SECRET% + - ps: "& package/win/appveyor_preinstall.ps1" +cache: +- C:\Users\appveyor\boost.1.63.0.7z +- C:\Users\appveyor\local-lib.7z +- C:\Strawberry\perl\site +- C:\Users\appveyor\freeglut.7z +- C:\users\appveyor\strawberry.msi +- C:\Users\appveyor\winscp.zip +- C:\Users\appveyor\extra_perl.7z +- C:\Users\appveyor\wxwidgets.7z +build_script: +- ps: "& package/win/appveyor_buildscript.ps1" +test_script: +- ps: "mkdir C:\\Andrés\nwget \"http://www.thingiverse.com/download:73351\" -o\"C:\\Andrés\\5mm.stl\"\necho \"bed_temperature=60\" > C:\\Andrés\\test.ini\n\ncd C:\\projects\\slic3r\nperl slic3r.pl --load \"C:\\Andrés\\test.ini\" \"C:\\Andrés\\5mm.stl\"\n\nif (!(Test-Path \"C:\\Andrés\\5mm.gcode\")) {\necho \"IS IT HERE\"\n}" +artifacts: +- path: .\slic3r*zip + name: slic3r-dev +deploy_script: +- ps: "& package/win/appveyor_deploy.ps1" +on_success: +- ps: +on_failure: +- ps: +on_finish: +- ps: diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index a52378502..f93bfc633 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -103,11 +103,14 @@ sub spawn_thread { my $parent_tid = threads->tid; lock @threads; + # Set up a default handler for preventing crashes in case signals are received before + # thread sets its handlers. + $SIG{'STOP'} = sub {}; + @_ = (); my $thread = threads->create(sub { @my_threads = (); - Slic3r::debugf "Starting thread %d (parent: %d)...\n", threads->tid, $parent_tid; local $SIG{'KILL'} = sub { Slic3r::debugf "Exiting thread %d...\n", threads->tid; $parallel_sema->up if $parallel_sema; @@ -119,6 +122,7 @@ sub spawn_thread { $pause_sema->down; $pause_sema->up; }; + Slic3r::debugf "Starting thread %d (parent: %d)...\n", threads->tid, $parent_tid; $cb->(); }); push @my_threads, $thread->tid; diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b68ef2a3e..167c733dd 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -269,6 +269,13 @@ sub validate { qw(perimeter infill solid_infill top_infill support_material first_layer); } + # support material + if ($self->support_material) { + die "Value of 0 is illegal. Use some % value instead (e.g. 150%) for auto.\n" + if $self->support_material_threshold =~ /^0+/; + } + + # general validation, quick and dirty foreach my $opt_key (@{$self->get_keys}) { my $opt = $Options->{$opt_key}; diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index ec3d92f56..8f244ecb7 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -30,13 +30,6 @@ sub offset_ex { return Slic3r::Geometry::Clipper::offset_ex(\@$self, @_); } -sub noncollapsing_offset_ex { - my $self = shift; - my ($distance, @params) = @_; - - return $self->offset_ex($distance + 1, @params); -} - sub bounding_box { my $self = shift; return $self->contour->bounding_box; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index a6b946f9e..bdc9de27e 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -3,6 +3,19 @@ use strict; use warnings; use utf8; +use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow + :filedialog :font); + +BEGIN { + # Wrap the Wx::_load_plugin() function which doesn't work with non-ASCII paths + no warnings 'redefine'; + my $orig = *Wx::_load_plugin{CODE}; + *Wx::_load_plugin = sub { + $_[0] = Slic3r::decode_path($_[0]); + $orig->(@_); + }; +} + use File::Basename qw(basename); use FindBin; use List::Util qw(first any); @@ -42,8 +55,6 @@ use Slic3r::GUI::SLAPrintOptions; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_LWP = eval "use LWP::UserAgent; 1"; -use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow - :filedialog :font); use Wx::Event qw(EVT_IDLE EVT_COMMAND); use base 'Wx::App'; diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm index 831377a1f..85d7a6fda 100644 --- a/lib/Slic3r/GUI/AboutDialog.pm +++ b/lib/Slic3r/GUI/AboutDialog.pm @@ -47,11 +47,11 @@ sub new { '' . '' . '' . - 'Copyright © 2011-2016 Alessandro Ranellucci.
' . + 'Copyright © 2011-2017 Alessandro Ranellucci.
' . 'Slic3r is licensed under the ' . 'GNU Affero General Public License, version 3.' . '


' . - 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. ' . + 'Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. ' . 'Manual by Gary Hodgson. Inspired by the RepRap community.
' . 'Slic3r logo designed by Corey Daniels, Silk Icon Set designed by Mark James. ' . '
' . diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index c4ceb7780..1b07d37ef 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -99,9 +99,8 @@ sub _init_tabpanel { }); $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater"); - if (!$self->{no_controller}) { - $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller"); - } + $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller") + unless ($Slic3r::GUI::Settings->{_}{no_controller}); } sub _init_menubar { @@ -259,7 +258,7 @@ sub _init_menubar { }, undef, 'application_view_tile.png'); $self->_append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub { $self->select_tab(1); - }, undef, 'printer_empty.png') if !$self->{no_controller}; + }, undef, 'printer_empty.png') unless ($Slic3r::GUI::Settings->{_}{no_controller}); $self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub { $self->{plater}->pause_background_process; Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal; diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index e652c7550..65cea5f08 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -22,9 +22,7 @@ sub get_value { sub set_tooltip { my ($self, $tooltip) = @_; - - $self->SetToolTipString($tooltip) - if $tooltip && $self->can('SetToolTipString'); + die "Method not implemented"; } sub toggle { @@ -67,8 +65,14 @@ sub _default_size { sub _trigger_wxWindow { my ($self) = @_; - $self->wxWindow->SetToolTipString($self->option->tooltip) - if $self->option->tooltip && $self->wxWindow->can('SetToolTipString'); + $self->set_tooltip($self->option->tooltip); +} + +sub set_tooltip { + my ($self, $tooltip) = @_; + + $self->wxWindow->SetToolTipString($tooltip) + if $self->wxWindow->can('SetToolTipString'); } sub set_value { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ca17a8ee0..505108a6a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1079,6 +1079,23 @@ sub set_number_of_copies { } } +sub center_selected_object_on_bed { + my ($self) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + my $bb = $model_object->bounding_box; + my $size = $bb->size; + my $vector = Slic3r::Pointf->new( + $self->bed_centerf->x - $bb->x_min - $size->x/2, + $self->bed_centerf->y - $bb->y_min - $size->y/2, #// + ); + $_->offset->translate(@$vector) for @{$model_object->instances}; + $self->refresh_canvases; +} + sub rotate { my $self = shift; my ($angle, $axis) = @_; @@ -1388,12 +1405,12 @@ sub async_apply_config { $self->{preview3D}->reload_print if $self->{preview3D}; $self->{AdaptiveLayersDialog}->reload_preview if $self->{AdaptiveLayersDialog}; + if (!$Slic3r::GUI::Settings->{_}{background_processing}) { + $self->hide_preview if $invalidated; + return; + } + if ($invalidated) { - if (!$Slic3r::GUI::Settings->{_}{background_processing}) { - $self->hide_preview; - return; - } - # kill current thread if any $self->stop_background_process; # remove the sliced statistics box because something changed. @@ -2331,6 +2348,9 @@ sub object_menu { $self->set_number_of_copies; }, undef, 'textfield.png'); $menu->AppendSeparator(); + $frame->_append_menu_item($menu, "Move to bed center", 'Center object around bed center', sub { + $self->center_selected_object_on_bed; + }, undef, 'arrow_in.png'); $frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub { $self->rotate(-45); }, undef, 'arrow_rotate_clockwise.png'); @@ -2578,7 +2598,7 @@ sub new { EVT_BUTTON($self, wxID_OK, sub { wxTheApp->save_settings; $self->EndModal(wxID_OK); - $self->Close; # needed on Linux + $self->Destroy; }); $self->SetSizer($sizer); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 1898ac6a0..17090438d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -9,7 +9,9 @@ use utf8; use File::Basename qw(basename); use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED); +use List::Util qw(max); +use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_ITEM_RIGHT_CLICK); +use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg); use base 'Wx::Panel'; use constant ICON_OBJECT => 0; @@ -156,11 +158,47 @@ sub new { return if $self->{disable_tree_sel_changed_event}; $self->selection_changed; }); + EVT_TREE_ITEM_RIGHT_CLICK($self, $tree, sub { + my ($self, $event) = @_; + my $item = $event->GetItem; + my $frame = $self->GetFrame; + my $menu = Wx::Menu->new; + + my $scaleToSizeMenu = Wx::Menu->new; + my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis'); + wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png'); + $frame->_append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes', sub { + $self->changescale(undef, 1); + }); + $frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { + $self->changescale(X, 1); + }, undef, 'bullet_red.png'); + $frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub { + $self->changescale(Y, 1); + }, undef, 'bullet_green.png'); + $frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub { + $self->changescale(Z, 1); + }, undef, 'bullet_blue.png'); + my $rotateMenu = Wx::Menu->new; + my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle'); + wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png'); + $frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub { + $self->rotate(undef, X); + }, undef, 'bullet_red.png'); + $frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub { + $self->rotate(undef, Y); + }, undef, 'bullet_green.png'); + $frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub { + $self->rotate(undef, Z); + }, undef, 'bullet_blue.png'); + + $frame->PopupMenu($menu, $event->GetPoint); + }); 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); - + $self->reload_tree; return $self; @@ -450,4 +488,82 @@ sub _update { $self->{canvas}->Render; } +sub changescale { + my ($self, $axis, $tosize) = @_; + my $itemData = $self->get_selection; + if ($itemData && $itemData->{type} eq 'volume') { + my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; + my $object_size = $volume->bounding_box->size; + if (defined $axis) { + my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z'; + my $scale; + if (defined $tosize) { + my $cursize = $object_size->[$axis]; + # Wx::GetNumberFromUser() does not support decimal numbers + my $newsize = Wx::GetTextFromUser( + sprintf("Enter the new size for the selected mesh:"), + "Scale along $axis_name", + $cursize, $self); + return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0; + $scale = $newsize / $cursize * 100; + } else { + # Wx::GetNumberFromUser() does not support decimal numbers + $scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", + "Scale along $axis_name", 100, $self); + $scale =~ s/%$//; + return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0; + } + my $versor = [1,1,1]; + $versor->[$axis] = $scale/100; + $volume->mesh->scale_xyz(Slic3r::Pointf3->new(@$versor)); + } else { + my $scale; + if ($tosize) { + my $cursize = max(@$object_size); + # Wx::GetNumberFromUser() does not support decimal numbers + my $newsize = Wx::GetTextFromUser("Enter the new max size for the selected object:", + "Scale", $cursize, $self); + return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0; + $scale = $newsize / $cursize; + } else { + # max scale factor should be above 2540 to allow importing files exported in inches + # Wx::GetNumberFromUser() does not support decimal numbers + $scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", 'Scale', + 100, $self); + return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0; + } + return if !$scale || $scale < 0; + $volume->mesh->scale($scale); + } + $self->_parts_changed; + } +} + +sub rotate { + my $self = shift; + my ($angle, $axis) = @_; + # angle is in degrees + my $itemData = $self->get_selection; + if ($itemData && $itemData->{type} eq 'volume') { + my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; + if (!defined $angle) { + my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z'; + my $default = $axis == Z ? 0 : 0; + # Wx::GetNumberFromUser() does not support decimal numbers + $angle = Wx::GetTextFromUser("Enter the rotation angle:", "Rotate around $axis_name axis", + $default, $self); + return if !$angle || $angle !~ /^-?\d*(?:\.\d*)?$/ || $angle == -1; + } + if ($axis == X) { $volume->mesh->rotate_x(deg2rad($angle)); } + + if ($axis == Y) { $volume->mesh->rotate_y(deg2rad($angle)); } + if ($axis == Z) { $volume->mesh->rotate_z(deg2rad($angle)); } + + $self->_parts_changed; + } +} +sub GetFrame { + my ($self) = @_; + return &Wx::GetTopLevelParent($self); +} 1; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 1b6c558e1..99b569684 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -7,8 +7,8 @@ use strict; use warnings; use utf8; -use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_BUTTON); +use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp); +use Wx::Event qw(EVT_BUTTON EVT_MENU); use base 'Wx::Dialog'; sub new { @@ -58,6 +58,17 @@ sub PartSettingsChanged { my ($self) = @_; return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged; } +sub _append_menu_item { + my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_; + + $id //= &Wx::NewId(); + my $item = $menu->Append($id, $string, $description, $kind); + wxTheApp->set_menu_item_icon($item, $icon); + + EVT_MENU($self, $id, $cb); + return $item; +} + package Slic3r::GUI::Plater::ObjectDialog::BaseTab; use base 'Wx::Panel'; diff --git a/lib/Slic3r/GUI/PresetEditor.pm b/lib/Slic3r/GUI/PresetEditor.pm index f6c8b6c48..7bf7e3811 100644 --- a/lib/Slic3r/GUI/PresetEditor.pm +++ b/lib/Slic3r/GUI/PresetEditor.pm @@ -1177,11 +1177,13 @@ sub options { octoprint_host octoprint_apikey use_firmware_retraction pressure_advance vibration_limit use_volumetric_e - start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode + start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode + notes nozzle_diameter extruder_offset retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below printer_settings_id + printer_notes ); } @@ -1377,10 +1379,31 @@ sub build { $option->height(150); $optgroup->append_single_option_line($option); } + { + my $optgroup = $page->new_optgroup('Between objects G-code (for sequential printing)', + label_width => 0, + ); + my $option = $optgroup->get_option('between_objects_gcode'); + $option->full_width(1); + $option->height(150); + $optgroup->append_single_option_line($option); + } } - + $self->{extruder_pages} = []; $self->_build_extruder_pages; + { + my $page = $self->add_options_page('Notes', 'note.png'); + { + my $optgroup = $page->new_optgroup('Notes', + label_width => 0, + ); + my $option = $optgroup->get_option('notes'); + $option->full_width(1); + $option->height(250); + $optgroup->append_single_option_line($option); + } + } $self->_update_serial_ports unless $Slic3r::GUI::Settings->{_}{no_controller}; } diff --git a/lib/Slic3r/GUI/SLAPrintOptions.pm b/lib/Slic3r/GUI/SLAPrintOptions.pm index 042ddc288..289f4e5a1 100644 --- a/lib/Slic3r/GUI/SLAPrintOptions.pm +++ b/lib/Slic3r/GUI/SLAPrintOptions.pm @@ -12,6 +12,10 @@ sub new { $self->config(Slic3r::Config::SLAPrint->new); $self->config->apply_dynamic(wxTheApp->{mainframe}->{plater}->config); + # Set some defaults + $self->config->set('infill_extrusion_width', 0.5) if $self->config->infill_extrusion_width == 0; + $self->config->set('perimeter_extrusion_width', 1) if $self->config->perimeter_extrusion_width == 0; + my $sizer = Wx::BoxSizer->new(wxVERTICAL); my $new_optgroup = sub { my ($title) = @_; diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 3faad8317..710125c5e 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -80,22 +80,56 @@ sub export { my $layer_height = $first_object->config->layer_height; for my $region_id (0..$#{$self->print->regions}) { my $region = $self->print->regions->[$region_id]; - printf $fh "; external perimeters extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; perimeters extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; solid infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; top infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; support material extrusion width = %.2fmm\n", - $self->objects->[0]->support_material_flow->width - if $self->print->has_support_material; - printf $fh "; first layer extrusion width = %.2fmm\n", + + { + my $flow = $region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object); + my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('external_perimeter_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; external perimeters extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + { + my $flow = $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object); + my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('perimeter_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; perimeters extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + { + my $flow = $region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object); + my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('infill_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; infill extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + { + my $flow = $region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object); + my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('solid_infill_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; solid infill extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + { + my $flow = $region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object); + my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('top_solid_infill_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; top infill extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + + if ($self->print->has_support_material) { + my $object0 = $self->objects->[0]; + my $flow = $object0->support_material_flow; + my $vol_speed = $flow->mm3_per_mm * $object0->config->get_abs_value('support_material_speed'); + $vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0; + printf $fh "; support material extrusion width = %.2fmm (%.2fmm^3/s)\n", + $flow->width, $vol_speed; + } + + printf $fh "; first layer extrusion width = %.2fmm (%.2fmm^3/s)\n", $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1, -1, $self->objects->[0])->width if $region->config->first_layer_extrusion_width; + print $fh "\n"; } @@ -112,12 +146,14 @@ sub export { } # set extruder(s) temperature before and after start G-code - $self->_print_first_layer_temperature(0); + $self->_print_first_layer_temperature(0) + if $self->config->start_gcode !~ /M(?:109|104)/i; printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->start_gcode); foreach my $start_gcode (@{ $self->config->start_filament_gcode }) { # process filament gcode in order printf $fh "%s\n", $gcodegen->placeholder_parser->process($start_gcode); } - $self->_print_first_layer_temperature(1); + $self->_print_first_layer_temperature(1) + if $self->config->start_gcode !~ /M(?:109|104)/i; # set other general things print $fh $gcodegen->preamble; @@ -212,8 +248,12 @@ sub export { # is triggered, so machine has more time to reach such temperatures if ($layer->id == 0 && $finished_objects > 0) { printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature), - if $self->config->first_layer_bed_temperature && $self->config->has_heatbed; - $self->_print_first_layer_temperature(0); + if $self->config->first_layer_bed_temperature + && $self->config->has_heatbed + && $self->config->between_objects_gcode !~ /M(?:190|140)/i; + $self->_print_first_layer_temperature(0) + if $self->config->between_objects_gcode !~ /M(?:109|104)/i; + printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->between_objects_gcode); } $self->process_layer($layer, [$copy]); } @@ -302,7 +342,6 @@ sub export { sub _print_first_layer_temperature { my ($self, $wait) = @_; - return if $self->config->start_gcode =~ /M(?:109|104)/i; for my $t (@{$self->print->extruders}) { my $temp = $self->config->get_at('first_layer_temperature', $t); $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 38698e47a..43b389b21 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -155,12 +155,20 @@ sub detect_surfaces_type { sub prepare_infill { my ($self) = @_; + return if $self->step_done(STEP_PREPARE_INFILL); + + # This prepare_infill() is not really idempotent. + # TODO: It should clear and regenerate fill_surfaces at every run + # instead of modifying it in place. + $self->invalidate_step(STEP_PERIMETERS); + $self->make_perimeters; + + # Do this after invalidating STEP_PERIMETERS because that would re-invalidate STEP_PREPARE_INFILL + $self->set_step_started(STEP_PREPARE_INFILL); + # prerequisites - $self->make_perimeters; # do we need them? TODO: check $self->detect_surfaces_type; - return if $self->step_done(STEP_PREPARE_INFILL); - $self->set_step_started(STEP_PREPARE_INFILL); $self->print->status_cb->(30, "Preparing infill"); # decide what surfaces are to be filled diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 4314e4fa6..ecb044068 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 - intersection_pl offset2_ex diff_pl); + intersection_pl offset2_ex diff_pl diff_ex); use Slic3r::Surface ':types'; has 'print_config' => (is => 'rw', required => 1); @@ -96,7 +96,7 @@ sub contact_area { # if user specified a custom angle threshold, convert it to radians my $threshold_rad; - if (!$self->object_config->support_material_threshold =~ /%$/) { + if (!($self->object_config->support_material_threshold =~ /%$/)) { $threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); } @@ -722,7 +722,7 @@ sub generate_toolpaths { # interface and contact infill if (@$interface || @$contact_infill) { - $fillers{interface}->set_angle($interface_angle); + $fillers{interface}->set_angle(deg2rad($interface_angle)); $fillers{interface}->set_min_spacing($_interface_flow->spacing); # find centerline of the external loop @@ -772,7 +772,7 @@ sub generate_toolpaths { # support or flange if (@$base) { my $filler = $fillers{support}; - $filler->set_angle($angles[ ($layer_id) % @angles ]); + $filler->set_angle(deg2rad($angles[ ($layer_id) % @angles ])); # We don't use $base_flow->spacing because we need a constant spacing # value that guarantees that all layers are correctly aligned. @@ -782,20 +782,31 @@ sub generate_toolpaths { my $base_flow = $_flow; # find centerline of the external loop/extrusions - my $to_infill = offset2_ex($base, +scaled_epsilon, -(scaled_epsilon + $_flow->scaled_width/2)); + my $to_infill = offset2($base, +scaled_epsilon, -(scaled_epsilon + $_flow->scaled_width/2)); my @paths = (); # base flange if ($layer_id == 0) { $filler = $fillers{interface}; - $filler->set_angle($self->object_config->support_material_angle + 90); + $filler->set_angle(deg2rad($self->object_config->support_material_angle + 90)); $density = 0.5; $base_flow = $self->first_layer_flow; # use the proper spacing for first layer as we don't need to align # its pattern to the other layers $filler->set_min_spacing($base_flow->spacing); + + # subtract brim so that it goes around the object fully (and support gets its own brim) + if ($self->print_config->brim_width > 0) { + my $d = +scale $self->print_config->brim_width*2; + $to_infill = diff_ex( + $to_infill, + offset($object->get_layer(0)->slices->polygons, $d), + ); + } else { + $to_infill = union_ex($to_infill); + } } else { # draw a perimeter all around support infill # TODO: use brim ordering algorithm @@ -806,10 +817,10 @@ sub generate_toolpaths { mm3_per_mm => $mm3_per_mm, width => $_flow->width, height => $layer->height, - ), map @$_, @$to_infill; + ), @$to_infill; # TODO: use offset2_ex() - $to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing); + $to_infill = offset_ex($to_infill, -$_flow->scaled_spacing); } my $mm3_per_mm = $base_flow->mm3_per_mm; diff --git a/package/win/shell.cpp b/package/common/shell.cpp similarity index 83% rename from package/win/shell.cpp rename to package/common/shell.cpp index e4fe45d20..4ce3c9afa 100644 --- a/package/win/shell.cpp +++ b/package/common/shell.cpp @@ -1,26 +1,23 @@ #include // from the Perl distribution #include // from the Perl distribution +#ifdef WIN32 // Perl win32 specific includes, found in perl\\lib\\CORE\\win32.h // Defines the windows specific convenience RunPerl() function, // which is not available on other operating systems. #include -// the standard Windows. include -//#include +#include +#endif + #include #include -#include +#ifdef WIN32 int main(int argc, char **argv, char **env) { - // replaces following Windows batch file: @"%~dp0\perl5.24.0.exe" - // "%~dp0\slic3r.pl" --DataDir "C:\Users\Public\Documents\Prusa3D\Slic3r - // settings MK2"%* - // If the Slic3r is installed in a localized directory (containing non-iso // characters), spaces or semicolons, use short file names. - char exe_path[MAX_PATH] = {0}; char script_path[MAX_PATH]; char gui_flag[6] = {"--gui"}; @@ -95,4 +92,25 @@ int main(int argc, char **argv, char **env) #endif free(command_line); } +#else +int main(int argc, char **argv, char **env) +{ + PerlInterpreter *my_perl = perl_alloc(); + if (my_perl == NULL) { + fprintf(stderr, "Cannot start perl interpreter. Exiting.\n"); + return -1; + } + perl_construct(my_perl); + +#ifdef FORCE_GUI + char* command_line[] = { "slic3r", "slic3r.pl", "--gui" }; +#else + char* command_line[] = { "slic3r", "slic3r.pl" }; +#endif + perl_parse(my_perl, NULL, 3, command_line, (char **)NULL); + perl_run(my_perl); + perl_destruct(my_perl); + perl_free(my_perl); +} +#endif diff --git a/package/common/util.sh b/package/common/util.sh new file mode 100644 index 000000000..6292d15e4 --- /dev/null +++ b/package/common/util.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# must be run from the root +function set_version () +{ +SLIC3R_VERSION=$(grep "VERSION" xs/src/libslic3r/libslic3r.h | awk -F\" '{print $2}') +} +# Cache the SHA1 for this build commit. +function get_commit () { + if [ ! -z ${TRAVIS_COMMIT+x} ]; then + # Travis sets the sha1 in TRAVIS_COMMIT + COMMIT_SHA1=$(git rev-parse --short $TRAVIS_COMMIT) + else + # should be able to get it properly + COMMIT_SHA1=$(git rev-parse --short HEAD) + fi +} +function set_build_id () +{ +echo "Setting SLIC3R_BUILD_ID" +if [ $(git describe &>/dev/null) ]; then + SLIC3R_BUILD_ID=$(git describe) + TAGGED=true +else + SLIC3R_BUILD_ID=${SLIC3R_VERSION}-${COMMIT_SHA1} +fi + +} + +function set_branch () +{ + echo "Setting current_branch" + if [ -z ${TRAVIS_BRANCH} ] && [ -z ${GIT_BRANCH+x} ] && [ -z ${APPVEYOR_REPO_BRANCH+x} ]; then + current_branch=$(git symbolic-ref HEAD | sed 's!refs\/heads\/!!') + else + current_branch="unknown" + if [ ! -z ${GIT_BRANCH+x} ]; then + echo "Setting to GIT_BRANCH" + current_branch=$(echo $GIT_BRANCH | cut -d / -f 2) + fi + if [ ! -z ${APPVEYOR_REPO_BRANCH+x} ]; then + echo "Setting to APPVEYOR_REPO_BRANCH" + current_branch=$APPVEYOR_REPO_BRANCH + fi + if [ ! -z ${TRAVIS_BRANCH} ]; then + if [ "${TRAVIS_BRANCH}" != "false" ]; then + echo "Setting to TRAVIS_BRANCH" + current_branch=$TRAVIS_BRANCH + fi + fi + fi + + if [ -z ${current_branch+x} ]; then + current_branch="unknown" + fi +} + +function set_app_name () +{ + set_branch + if [ "$current_branch" == "master" ]; then + appname=Slic3r + else + appname=Slic3r-${current_branch} + fi +} + + +function set_pr_id () +{ + echo "Setting PR_ID if available." + if [ ! -z ${GITHUB_PR_NUMBER+x} ]; then + PR_ID=$GITHUB_PR_NUMBER + fi + if [ ! -z ${APPVEYOR_PULL_REQUEST_NUMBER+x} ]; then + PR_ID=$APPVEYOR_PULL_REQUEST_NUMBER + fi + if [ ! -z ${TRAVIS_PULL_REQUEST_BRANCH+x} ] && [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then + PR_ID=$TRAVIS_PULL_REQUEST + fi + if [ ! -z ${PR_ID+x} ]; then + echo "Setting PR_ID to $PR_ID." + else + echo "PR_ID remains unset." + fi +} + +function install_par () +{ + cpanm -q PAR::Packer + cpanm -q Wx::Perl::Packager +} diff --git a/package/deploy-bintray.sh b/package/deploy/bintray.sh similarity index 68% rename from package/deploy-bintray.sh rename to package/deploy/bintray.sh index c24fa62b8..35b73da49 100755 --- a/package/deploy-bintray.sh +++ b/package/deploy/bintray.sh @@ -5,39 +5,12 @@ # BINTRAY_API_USER - Bintray username. # Run this from the repository root (required to get slic3r version) +source $(dirname $0)/common/util.sh SLIC3R_VERSION=$(grep "VERSION" xs/src/libslic3r/libslic3r.h | awk -F\" '{print $2}') -if [ $(git describe &>/dev/null) ]; then - SLIC3R_BUILD_ID=$(git describe) - TAGGED=true -else - SLIC3R_BUILD_ID=${SLIC3R_VERSION}-$(git rev-parse --short HEAD) -fi -if [ -z ${GIT_BRANCH+x} ] && [ -z ${APPVEYOR_REPO_BRANCH+x} ]; then - current_branch=$(git symbolic-ref HEAD | sed 's!refs\/heads\/!!') -else - current_branch="unknown" - if [ ! -z ${GIT_BRANCH+x} ]; then - echo "Setting to GIT_BRANCH" - current_branch=$(echo $GIT_BRANCH | cut -d / -f 2) - fi - if [ ! -z ${APPVEYOR_REPO_BRANCH+x} ]; then - echo "Setting to APPVEYOR_REPO_BRANCH" - current_branch=$APPVEYOR_REPO_BRANCH - fi -fi - -if [ -z ${current_branch+x} ]; then - current_branch="unknown" -fi - -if [ ! -z ${GITHUB_PR_NUMBER+x} ]; then - PR_ID=$GITHUB_PR_NUMBER -fi -if [ ! -z ${APPVEYOR_PULL_REQUEST_NUMBER+x} ]; then - PR_ID=$APPVEYOR_PULL_REQUEST_NUMBER -fi - - +get_commit +set_build_id +set_branch +set_pr_id if [ "$current_branch" == "master" ] && [ -z ${PR_ID} ]; then # If building master, goes in slic3r_dev or slic3r, depending on whether or not this is a tagged build if [ -z ${TAGGED+x} ]; then diff --git a/package/deploy/sftp.sh b/package/deploy/sftp.sh new file mode 100755 index 000000000..3fc786fc3 --- /dev/null +++ b/package/deploy/sftp.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Prerequisites +# Environment Variables: +# UPLOAD_USER - user to upload to sftp server +# KEY is assumed to be path to a ssh key for UPLOAD_USER + +DIR=$1 +shift +KEY=$1 +shift +FILES=$* +source $(dirname $0)/../common/util.sh +set_pr_id +set_branch +if [ ! -z ${PR_ID+x} ] || [ $current_branch != "master" ]; then + DIR=${DIR}/branches +fi + +if [ -s $KEY ]; then + for i in $FILES; do + filepath=$(readlink -f "$i") + echo put $filepath | sftp -i$KEY "${UPLOAD_USER}@dl.slic3r.org:$DIR/" + done +else + echo "$KEY is not available, not deploying." +fi diff --git a/package/deploy/slic3r-upload.ppk.enc b/package/deploy/slic3r-upload.ppk.enc new file mode 100644 index 000000000..0bc2ead4f Binary files /dev/null and b/package/deploy/slic3r-upload.ppk.enc differ diff --git a/package/deploy/slic3r-upload.rsa.enc b/package/deploy/slic3r-upload.rsa.enc new file mode 100644 index 000000000..9f3b9935b Binary files /dev/null and b/package/deploy/slic3r-upload.rsa.enc differ diff --git a/package/deploy/winscp.ps1 b/package/deploy/winscp.ps1 new file mode 100755 index 000000000..422d950e8 --- /dev/null +++ b/package/deploy/winscp.ps1 @@ -0,0 +1,19 @@ +# Prerequisites +# Environment Variables: +# UPLOAD_USER - user to upload to sftp server +# KEY is assumed to be path to a ssh key for UPLOAD_USER + +Param( + [string]$DIR, + [string]$KEY, + [string]$FILE +) +Set-Variable -Name "UUSER" -Value "$env:UPLOAD_USER" +Set-Variable -Name "UPLOAD" -Value "$($FILE | Resolve-Path)" +if (Test-Path $KEY) { + if ($env:APPVEYOR_PULL_REQUEST_NUMBER -Or $env:APPVEYOR_REPO_BRANCH -ne "master" ) { + winscp.com /privatekey=$KEY /command "open sftp://$UUSER@dl.slic3r.org/$DIR/branches -hostkey=*" "put $UPLOAD ./$FILE" "exit" + } else { + winscp.com /privatekey=$KEY /command "open sftp://$UUSER@dl.slic3r.org/$DIR -hostkey=*" "put $UPLOAD ./$FILE" "exit" + } +} diff --git a/package/linux/appimage-bundler.sh b/package/linux/appimage-bundler.sh new file mode 100755 index 000000000..d12cb2129 --- /dev/null +++ b/package/linux/appimage-bundler.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +WD=$(dirname $0) +source $(dirname $0)/../common/util.sh +set_version +set_app_name +LOWERAPP=${appname,,} +wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh +. ./functions.sh + +srcfolder="$WD/${appname}" +if [ ! -e $srcfolder ]; then + echo "Run make_archive first." + exit 1 +fi + +cd $srcfolder + +# make shell exec and call it Slic3r + +mkdir -p /usr/{lib,bin} +mv -R Slic3r local-lib var perl-local slic3r.pl /usr/bin +mv -R bin/* /usr/lib +rm -rf bin + +get_apprun + +# Copy desktop and icon file to application root dir for Apprun to pick them up. +sed -e "s|SLIC3R_VERSION|$SLIC3R_VERSION|" -e"s|APPLICATION_NAME|$appname|" ../slic3r.desktop.in > ../slic3r.desktop +cp ../slic3r.desktop $LOWERAPP.desktop +cp ./var/Slic3r_192px_transparent.png ./slic3r.png + +# archive directory has everything we need. +delete_blacklisted + +get_desktopintegration $LOWERAPP + +GLIBC_NEEDED=$(glibc_needed) +VERSION=git$GIT_REV-glibc$GLIBC_NEEDED + +cd .. +mkdir -p out +generate_appimage + +transfer ../out/* diff --git a/package/linux/build_shell.mk b/package/linux/build_shell.mk new file mode 100644 index 000000000..5f7a56f75 --- /dev/null +++ b/package/linux/build_shell.mk @@ -0,0 +1,31 @@ +src=../common/shell.cpp + +# override with environment variable +CXX ?= g++ + +# Path to perl header files +INCLUDEDIR ?= ${HOME}/perl5/perlbrew/perls/slic3r-perl/lib/5.24.0/x86_64-linux-thread-multi/CORE + +# path to library files for perl +LIBDIR ?= ${HOME}/perl5/perlbrew/perls/slic3r-perl/lib/5.24.0/x86_64-linux-thread-multi/CORE + +LIBS += -lperl -lpthread -lcrypt + +CXXFLAGS += -std=c++11 -static-libgcc -static-libstdc++ -I${INCLUDEDIR} +LDFLAGS += -L${LIBDIR} + +.PHONY: all clean +all: Slic3r Slic3r-console + +Slic3r: slic3r.o + ${CXX} ${LDFLAGS} -o $@ $< ${LIBS} + +Slic3r-console: slic3r-console.o + ${CXX} ${LDFLAGS} -o $@ $< ${LIBS} +slic3r-console.o: ${src} + ${CXX} -c ${CXXFLAGS} -o $@ $< +slic3r.o: ${src} + ${CXX} -c -DFORCE_GUI ${CXXFLAGS} -o $@ $< + +clean: + rm *.o Slic3r* diff --git a/package/linux/libpaths.txt b/package/linux/libpaths.txt new file mode 100644 index 000000000..798e9103d --- /dev/null +++ b/package/linux/libpaths.txt @@ -0,0 +1,28 @@ +/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_baseu-3.0.so.0 +/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_adv-3.0.so.0 +/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_core-3.0.so.0 +/lib/x86_64-linux-gnu/liblzma.so.5 +/lib/x86_64-linux-gnu/libm.so.6 +/lib/x86_64-linux-gnu/libpcre.so.3 +/lib/x86_64-linux-gnu/libpng12.so.0 +/lib/x86_64-linux-gnu/libresolv.so.2 +/lib/x86_64-linux-gnu/libuuid.so.1 +/usr/lib/x86_64-linux-gnu/libSM.so.6 +/usr/lib/x86_64-linux-gnu/libatk-1.0.so.0 +/usr/lib/x86_64-linux-gnu/libdatrie.so.1 +/usr/lib/x86_64-linux-gnu/libfreetype.so.6 +/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libgraphite2.so.3 +/usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 +/usr/lib/x86_64-linux-gnu/libharfbuzz.so.0 +/usr/lib/x86_64-linux-gnu/libjbig.so.0 +/usr/lib/x86_64-linux-gnu/libjpeg.so.8 +/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 +/usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 +/usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 +/usr/lib/x86_64-linux-gnu/libpixman-1.so.0 +/usr/lib/x86_64-linux-gnu/libtiff.so.5 diff --git a/package/linux/make_archive.sh b/package/linux/make_archive.sh new file mode 100755 index 000000000..422ac19a0 --- /dev/null +++ b/package/linux/make_archive.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +# Assembles an installation archive from a built copy of Slic3r. +# Requires PAR::Packer to be installed for the version of +# perl copied. +# Adapted from script written by bubnikv for Prusa3D. +# Run from slic3r repo root directory. + +if [ "$#" -ne 1 ]; then + echo "Usage: $(basename $0) arch_name" + exit 1; +fi +libdirs=$(find ./local-lib -iname *.so -exec dirname {} \; | sort -u | paste -sd ";" -) +WD=./$(dirname $0) +source $(dirname $0)/../common/util.sh +# Determine if this is a tagged (release) commit. +# Change the build id accordingly. + +set_version +get_commit +set_build_id +set_branch +set_app_name +set_pr_id +install_par + +# If we're on a branch, add the branch name to the app name. +if [ "$current_branch" == "master" ]; then + if [ ! -z ${PR_ID+x} ]; then + dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-PR${PR_ID}.tar.bz2 + else + dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}.tar.bz2 + fi +else + dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2 +fi + +rm -rf $WD/_tmp +mkdir -p $WD/_tmp + +# Set the application folder infomation. +appfolder="$WD/${appname}" +archivefolder=$appfolder +resourcefolder=$appfolder + +echo "Appfolder: $appfolder, archivefolder: $archivefolder" + +# Our slic3r dir and location of perl +PERL_BIN=$(which perl) +PP_BIN=$(which pp) +SLIC3R_DIR="./" + +if [[ -d "${appfolder}" ]]; then + echo "Deleting old working folder: ${appfolder}" + rm -rf ${appfolder} +fi + +if [[ -e "${dmgfile}" ]]; then + echo "Deleting old archive: ${dmgfile}" + rm -rf ${dmgfile} +fi + +echo "Creating new app folder: $appfolder" +mkdir -p $appfolder + +echo "Copying resources..." +cp -rf $SLIC3R_DIR/var $resourcefolder/ + +echo "Copying Slic3r..." +cp $SLIC3R_DIR/slic3r.pl $archivefolder/slic3r.pl +cp -fRP $SLIC3R_DIR/local-lib $archivefolder/local-lib +cp -fRP $SLIC3R_DIR/lib/* $archivefolder/local-lib/lib/perl5/ + +mkdir $archivefolder/bin +echo "Installing libraries to $archivefolder/bin ..." +if [ -z ${WXDIR+x} ]; then + for bundle in $(find $archivefolder/local-lib/lib/perl5 -name '*.so' | grep "Wx") $(find $archivefolder/local-lib/lib/perl5 -name '*.so' -type f | grep "wxWidgets"); do + echo "$(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}')" + for dylib in $(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}'); do + install -v $dylib $archivefolder/bin + done + done +else + echo "Copying libraries from $WXDIR/lib to $archivefolder/bin" + for dylib in $(find $WXDIR/lib | grep "so"); do + cp -P -v $dylib $archivefolder/bin + done +fi + +echo "Copying startup script..." +cp -f $WD/startup_script.sh $archivefolder/$appname +chmod +x $archivefolder/$appname + +echo "Copying perl from $PERL_BIN" +cp -f $PERL_BIN $archivefolder/perl-local +${PP_BIN} wxextension .0 \ + -M attributes -M base -M bytes -M B -M POSIX \ + -M FindBin -M Unicode::Normalize -M Tie::Handle \ + -M Time::Local -M Math::Trig \ + -M lib -M overload \ + -M warnings -M local::lib \ + -M strict -M utf8 -M parent \ + -B -p -e "print 123" -o $WD/_tmp/test.par +unzip -qq -o $WD/_tmp/test.par -d $WD/_tmp/ +cp -rf $WD/_tmp/lib/* $archivefolder/local-lib/lib/perl5/ +cp -rf $WD/_tmp/shlib $archivefolder/ +rm -rf $WD/_tmp +for i in $(cat $WD/libpaths.txt); do + install -v $i $archivefolder/bin +done + +echo "Cleaning local-lib" +rm -rf $archivefolder/local-lib/bin +rm -rf $archivefolder/local-lib/man +rm -f $archivefolder/local-lib/lib/perl5/Algorithm/*.pl +rm -rf $archivefolder/local-lib/lib/perl5/unicore +rm -rf $archivefolder/local-lib/lib/perl5/App +rm -rf $archivefolder/local-lib/lib/perl5/Devel/CheckLib.pm +rm -rf $archivefolder/local-lib/lib/perl5/ExtUtils +rm -rf $archivefolder/local-lib/lib/perl5/Module/Build* +rm -rf $(pwd)$archivefolder/local-lib/lib/perl5/TAP +rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test* +find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \ + -or -name DocView -or -name STC -or -name IPC \ + -or -name AUI -or -name Calendar -or -name DataView \ + -or -name DateTime -or -name Media -or -name PerlTest \ + -or -name Ribbon \) -exec rm -rf "{}" \; +rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include +find $archivefolder/local-lib -depth -type d -empty -exec rmdir "{}" \; + +tar -C$(pwd)/$(dirname $appfolder) -cjf $(pwd)/$dmgfile "$appname" diff --git a/package/linux/package_linux.sh b/package/linux/package_linux.sh deleted file mode 100755 index 9133f23b2..000000000 --- a/package/linux/package_linux.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Written by Joseph Lenox -# Licensed under the same license as the rest of Slic3r. -# ------------------------ - -pp -a "../../utils;utils" -a "../../var;var" -a "../../lib;lib" -a "../../local-lib;local-lib" -a "../../slic3r.pl;slic3r.pl" -M AutoLoader -M B -M Carp -M Class::Accessor -M Class::XSAccessor -M Class::XSAccessor::Heavy -M Config -M Cwd -M Devel::GlobalDestruction -M Digest -M Digest::MD5 -M Digest::SHA -M Digest::base -M DynaLoader -M Errno -M Exporter -M Exporter::Heavy -M Fcntl -M File::Basename -M File::Glob -M File::Spec -M File::Spec::Unix -M File::Spec::Win32 -M FindBin -M HTTP::Config -M HTTP::Date -M HTTP::Headers -M HTTP::Headers::Util -M HTTP::Message -M HTTP::Request -M HTTP::Request::Common -M HTTP::Response -M HTTP::Status -M IO -M IO::Handle -M IO::Select -M LWP -M LWP::MediaTypes -M LWP::MemberMixin -M LWP::Protocol -M LWP::Protocol::http -M LWP::UserAgent -M List::Util -M Math::Trig -M Method::Generate::Accessor -M Method::Generate::BuildAll -M Method::Generate::Constructor -M Module::Runtime -M POSIX -M Pod::Escapes -M Pod::Text -M Pod::Usage -M SelectSaver -M Socket -M Socket6 -M Storable -M Sub::Defer -M Sub::Exporter -M Sub::Exporter::Progressive -M Sub::Name -M Symbol -M Term::Cap -M Text::ParseWords -M Thread -M Thread::Queue -M Thread::Semaphore -M Tie::Handle -M Tie::Hash -M Tie::StdHandle -M Time::Local -M URI -M URI::Escape -M URI::http -M Unicode::Normalize -M XSLoader -B -M lib -p ../../slic3r.pl -o ../../slic3r.par diff --git a/package/linux/slic3r.desktop.in b/package/linux/slic3r.desktop.in new file mode 100644 index 000000000..0e1203ebf --- /dev/null +++ b/package/linux/slic3r.desktop.in @@ -0,0 +1,13 @@ +[Desktop Entry] + +Type=Application + +Version=SLIC3R_VERSION + +Name=APPLICATION_NAME + +Comment=Prepare 3D Models for printing + +Icon=slic3r + +Exec=Slic3r diff --git a/package/linux/startup_script.sh b/package/linux/startup_script.sh new file mode 100644 index 000000000..53cea63d6 --- /dev/null +++ b/package/linux/startup_script.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +DIR=$(dirname "$0") +export LD_LIBRARY_PATH=./shlib/x86_64-linux-thread-multi/ +exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@ diff --git a/package/linux/travis-decrypt-key b/package/linux/travis-decrypt-key new file mode 100644 index 000000000..cfc6bc911 --- /dev/null +++ b/package/linux/travis-decrypt-key @@ -0,0 +1,7 @@ +#!/bin/bash +# Script to only decrypt if it is available + +if [ ! -z ${encrypted_daaf322d08bf_key+x} ]; then + openssl aes-256-cbc -K $encrypted_daaf322d08bf_key -iv $encrypted_daaf322d08bf_iv -in $TRAVIS_BUILD_DIR/package/deploy/slic3r-upload.rsa.enc -out ~/slic3r-upload.rsa -d + chmod 600 ~/slic3r-upload.rsa +fi diff --git a/package/linux/travis-setup.sh b/package/linux/travis-setup.sh new file mode 100755 index 000000000..3c9f55ecf --- /dev/null +++ b/package/linux/travis-setup.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Script to configure travis environment prior to build +WXVERSION=302 +CACHE=$HOME/cache +mkdir -p $CACHE + +if [ ! -e $CACHE/slic3r-perlbrew-5.24.tar.bz2 ]; then + echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2 => $CACHE/slic3r-perlbrew-5.24.tar.bz2" + curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2" -o $CACHE/slic3r-perlbrew-5.24.tar.bz2; +fi + +if [ ! -e $CACHE/boost-compiled.tar.bz2 ]; then + echo "Downloading http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2 => $CACHE/boost-compiled.tar.bz2" + curl -L "http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2" -o $CACHE/boost-compiled.tar.bz2 +fi + +if [ ! -e $CACHE/local-lib-wx${WXVERSION}.tar.bz2 ]; then + echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2 => $CACHE/local-lib-wx${WXVERSION}.tar.bz2" + curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2" -o $CACHE/local-lib-wx${WXVERSION}.tar.bz2 +fi + +if [ ! -e $CACHE/wx${WXVERSION}.tar.bz2 ]; then + echo "Downloading http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2 => $CACHE/wx${WXVERSION}.tar.bz2" + curl -L "http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2" -o $CACHE/wx${WXVERSION}.tar.bz2 +fi + +tar -C$TRAVIS_BUILD_DIR -xjf $CACHE/local-lib-wx${WXVERSION}.tar.bz2 +tar -C$HOME/perl5/perlbrew/perls -xjf $CACHE/slic3r-perlbrew-5.24.tar.bz2 +tar -C$HOME -xjf $CACHE/boost-compiled.tar.bz2 +tar -C$HOME -xjf $CACHE/wx${WXVERSION}.tar.bz2 diff --git a/package/osx/make_dmg.sh b/package/osx/make_dmg.sh index f82464daa..8167e6d06 100755 --- a/package/osx/make_dmg.sh +++ b/package/osx/make_dmg.sh @@ -133,7 +133,6 @@ find -d $macosfolder/local-lib -type d -path '*/Wx/*' \( -name WebView \ -or -name Ribbon \) -exec rm -rf "{}" \; find -d $macosfolder/local-lib -name libwx_osx_cocoau_ribbon-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_aui-3.0.0.2.0.dylib -delete -find -d $macosfolder/local-lib -name libwx_osx_cocoau_media-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_stc-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_webview-3.0.0.2.0.dylib -delete rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include diff --git a/package/osx/plist.sh b/package/osx/plist.sh index 38e03edbc..2d305f405 100644 --- a/package/osx/plist.sh +++ b/package/osx/plist.sh @@ -93,6 +93,8 @@ cat << EOF >> $plistfile 10.7 NSPrincipalClass NSApplication + NSHighResolutionCapable + EOF diff --git a/package/win/appveyor_buildscript.ps1 b/package/win/appveyor_buildscript.ps1 new file mode 100644 index 000000000..4f98dcbf4 --- /dev/null +++ b/package/win/appveyor_buildscript.ps1 @@ -0,0 +1,21 @@ +if (!(Test-Path "C:\users\appveyor\local-lib.7z")) { + wget "http://www.siusgs.com/slic3r/buildserver/win/slic3r-perl-dependencies-5.24.0-win-seh-gcc6.3.0-x64.7z" -o "C:\users\appveyor\local-lib.7z" | Write-Output +} + +if (Test-Path "C:\users\appveyor\local-lib.7z") { + cmd /c "7z x C:\Users\appveyor\local-lib.7z -oC:\projects\slic3r" -y | Write-Output + rm -r 'C:\projects\slic3r\local-lib\Slic3r*' +} + +$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;" + $env:Path +cd C:\projects\slic3r +rm -r 'C:\Program Files (x86)\Microsoft Vis*\bin' -Force +Add-AppveyorCompilationMessage -Message "Building Slic3r XS" +perl Build.pl +if (-NOT ($LASTEXITCODE -eq 0)) { + Add-AppveyorCompilationMessage -Message "XS Failed to Build" -Category Error +} +Add-AppveyorCompilationMessage -Message "Making ZIP package" +cd package/win +./compile_wrapper.ps1 524| Write-Output +./package_win32.ps1 524| Write-Output diff --git a/package/win/appveyor_deploy.ps1 b/package/win/appveyor_deploy.ps1 new file mode 100644 index 000000000..28616f02c --- /dev/null +++ b/package/win/appveyor_deploy.ps1 @@ -0,0 +1,8 @@ +Add-AppveyorCompilationMessage -Message "Making ZIP package" +cd package/win +./compile_wrapper.ps1 524 | Write-Output +./package_win32.ps1 524| Write-Output +cd ../../ + +Add-AppveyorCompilationMessage -Message "Uploading to server." +& ./package/deploy/winscp.ps1 -DIR win -KEY $env:APPVEYOR_BUILD_FOLDER/package/deploy/slic3r-upload.ppk -FILE *.zip *>> ./sftplog.txt diff --git a/package/win/appveyor_preinstall.ps1 b/package/win/appveyor_preinstall.ps1 new file mode 100644 index 000000000..4ba912e14 --- /dev/null +++ b/package/win/appveyor_preinstall.ps1 @@ -0,0 +1,71 @@ +mkdir C:\projects\slic3r\FreeGLUT +if (!(Test-Path "C:\users\appveyor\freeglut.7z")) +{ + wget "http://www.siusgs.com/slic3r/buildserver/win/freeglut-mingw-3.0.0.win64.7z" -o C:\users\appveyor\freeglut.7z +} +cmd /c "7z x C:\Users\appveyor\freeglut.7z -oC:\projects\slic3r\FreeGLUT" + +if (!(Test-Path "C:\users\appveyor\strawberry.msi")) { + wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=slic3r-perl-5.24.1.4-64bit.msi" -o "C:\users\appveyor\strawberry.msi" | Write-Output +} +if (!(Test-Path "C:\users\appveyor\extra_perl.7z")) { + wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=Strawberry-6.3.0-seg-archive.7z" -o "C:\users\appveyor\extra_perl.7z" | Write-Output +} + +msiexec.exe /i "C:\users\appveyor\strawberry.msi" /quiet +cmd /c "7z x -aoa C:\Users\appveyor\extra_perl.7z -oC:\" + +if (!(Test-Path "C:\users\appveyor\winscp.zip")) { + wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=WinSCP-5.9.4-Portable.zip" -o "C:\users\appveyor\winscp.zip" | Write-Output +} +cmd /c "7z x C:\Users\appveyor\winscp.zip -oC:\Strawberry\c\bin" + +rm -r C:\min* -Force +rm -r C:\msys64\mingw* -Force +rm -r C:\cygwin* -Force +rm -r C:\Perl -Force + +$PERLDIR = 'C:\Strawberry' +$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;C:\Strawberry\perl\vendor\bin;" + $env:Path + +if(Test-Path -Path 'C:\Strawberry' ) { + copy C:\Strawberry\c\bin\gcc.exe C:\Strawberry\c\bin\cc.exe + cmd /c mklink /D C:\Perl C:\Strawberry\perl + mkdir C:\dev + if (!(Test-Path "C:\users\appveyor\boost.1.63.0.7z") -Or $env:FORCE_BOOST_REINSTALL -eq 1) { + wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=boost_1_63_0-x64-gcc-6.3.0-seh.7z" -O "C:\users\appveyor\boost.1.63.0.7z" | Write-Output + } + Add-AppveyorCompilationMessage -Message "Extracting cached archive." + cmd /c "7z x C:\Users\appveyor\boost.1.63.0.7z -oC:\dev" + + mkdir C:\dev\CitrusPerl + cmd /C mklink /D C:\dev\CitrusPerl\mingw32 C:\Strawberry\c + cd C:\projects\slic3r + cpanm ExtUtils::Typemaps::Basic + cpanm ExtUtils::Typemaps::Default + cpanm local::lib + Add-AppveyorCompilationMessage -Message "Finished install script." + rm -r 'C:\Program Files\Git\usr\bin' -Force +} else { + Add-AppveyorCompilationMessage -Message "No strawberry perl!" +} + + +Add-AppveyorCompilationMessage -Message "Installing wxWidgets (xsgui dependency))" +if ($env:FORCE_WX_BUILD -eq 1) { + rm "C:\Users\appveyor\wxwidgets.7z" -Force +} +if (!(Test-Path "C:\Users\appveyor\wxwidgets.7z")) { + Add-AppveyorCompilationMessage -Message "Compiling wxWidgets" + git clone https://github.com/wxWidgets/wxWidgets -b "v3.1.0" -q C:\dev\wxWidgets + cd C:\dev\wxwidgets + cp .\include\wx\msw\setup0.h include/wx/msw/setup.h + cd build\msw + mingw32-make -f makefile.gcc CXXFLAGS="-std=gnu++11" BUILD=release VENDOR=Slic3r + cd C:\dev + 7z a C:\Users\appveyor\wxwidgets.7z wxwidgets + cd C:\projects\slic3r +} else { + Add-AppveyorCompilationMessage -Message "Extracting prebuilt wxWidgets." + 7z x "C:\Users\appveyor\wxwidgets.7z" -oC:\dev +} diff --git a/package/win/autorun.bat b/package/win/autorun.bat deleted file mode 100644 index be2c7b3f0..000000000 --- a/package/win/autorun.bat +++ /dev/null @@ -1 +0,0 @@ -@perl5.24.0.exe slic3r.pl %* diff --git a/package/win/compile_wrapper.ps1 b/package/win/compile_wrapper.ps1 index 5704788ff..c5d603ef3 100644 --- a/package/win/compile_wrapper.ps1 +++ b/package/win/compile_wrapper.ps1 @@ -4,14 +4,27 @@ if ($args[0]) $perlver = $args[0] } else { - $perlver = 518 + $perlver = 524 } $perllib = "-lperl$perlver" +$shell_loc = "..\common\shell.cpp" +# Build the resource file (used to load icon, etc) windres slic3r.rc -O coff -o slic3r.res -g++ -c -I'C:\strawberry\perl\lib\CORE\' shell.cpp -o slic3r.o -g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI shell.cpp -o slic3r-gui.o -g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host -g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host + +# Compile an object file that does not have gui forced. +g++ -c -I'C:\strawberry\perl\lib\CORE\' $shell_loc -o slic3r.o + +# Compile an object file with --gui automatically passed as an argument +g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI $shell_loc -o slic3r-gui.o + +# Build the EXE for the unforced version as slic3r-console +g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host + +# Build the EXE for the forced GUI +g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -mwindows -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host + +# Build an extra copy of the GUI version that creates a console window +g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r-debug-console.exe | Write-Host diff --git a/package/win/package_win32.ps1 b/package/win/package_win32.ps1 index 302eded3d..07396ea37 100644 --- a/package/win/package_win32.ps1 +++ b/package/win/package_win32.ps1 @@ -1,7 +1,7 @@ -# Written by Joseph Lenox +# Written by Joseph Lenox # Licensed under the same license as the rest of Slic3r. # ------------------------ -# You need to have Strawberry Perl 5.24.0.1 installed for this to work, +# You need to have Strawberry Perl 5.24.0.1 (or slic3r-perl) installed for this to work, param ( [switch]$exe = $false ) @@ -44,7 +44,8 @@ cpanm "PAR::Packer" pp ` -a "slic3r.exe;slic3r.exe" ` --a "slic3r.exe;slic3r-console.exe" ` +-a "slic3r-console.exe;slic3r-console.exe" ` +-a "slic3r-debug-console.exe;slic3r-debug-console.exe" ` -a "../../lib;lib" ` -a "../../local-lib;local-lib" ` -a "../../slic3r.pl;slic3r.pl" ` diff --git a/slic3r.pl b/slic3r.pl index 75bf3b961..9526ab6c2 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -51,6 +51,10 @@ my %cli_options = (); 'duplicate-grid=s' => \$opt{duplicate_grid}, 'print-center=s' => \$opt{print_center}, 'dont-arrange' => \$opt{dont_arrange}, + + # legacy options, ignored + 'no-plater' => \$opt{no_plater}, + 'gui-mode=s' => \$opt{gui_mode}, ); foreach my $opt_key (keys %{$Slic3r::Config::Options}) { my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next; @@ -60,6 +64,9 @@ my %cli_options = (); @ARGV = grep !/^-psn_\d/, @ARGV if $^O eq 'darwin'; GetOptions(%options) or usage(1); + + warn "--no-plater option is deprecated; ignoring\n" if $opt{no_plater}; + warn "--gui-mode option is deprecated (Slic3r now has only Expert Mode); ignoring\n" if $opt{gui_mode}; } # load configuration files diff --git a/t/adaptive_width.t b/t/adaptive_width.t new file mode 100644 index 000000000..7a0baa752 --- /dev/null +++ b/t/adaptive_width.t @@ -0,0 +1,92 @@ +use Test::More; +use strict; +use warnings; + +plan tests => 32; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; +} + +use List::Util qw(first); +use Slic3r; +use Slic3r::Geometry qw(X Y scale epsilon); +use Slic3r::Surface ':types'; + +sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } + +{ + my $test = sub { + my ($expolygon, $fw, $expected, $descr) = @_; + + my $flow = Slic3r::Flow->new( + nozzle_diameter => 0.5, + height => 0.4, + width => $fw, + ); + my $slices = Slic3r::Surface::Collection->new; + $slices->append(Slic3r::Surface->new( + surface_type => S_TYPE_INTERNAL, + expolygon => $expolygon, + )); + my $config = Slic3r::Config::Full->new; + my $loops = Slic3r::ExtrusionPath::Collection->new; + my $gap_fill = Slic3r::ExtrusionPath::Collection->new; + my $fill_surfaces = Slic3r::Surface::Collection->new; + my $pg = Slic3r::Layer::PerimeterGenerator->new( + $slices, $flow->height, $flow, + $config, $config, $config, + $loops, $gap_fill, $fill_surfaces, + ); + $pg->process; + + $loops = $loops->flatten; + my @single = grep $_->isa('Slic3r::ExtrusionPath'), @$loops; + my @loops = grep $_->isa('Slic3r::ExtrusionLoop'), @$loops; + + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "output.svg", + expolygons => [$expolygon], + polylines => [ map $_->isa('Slic3r::ExtrusionPath') ? $_->polyline : $_->polygon->split_at_first_point, @$loops ], + red_polylines => [ map $_->polyline, @$gap_fill ], + ); + } + + is scalar(@single), $expected->{single} // 0, "expected number of single lines ($descr)"; + is scalar(@loops), $expected->{loops} // 0, "expected number of loops ($descr)"; + is scalar(@$gap_fill), $expected->{gaps} // 0, "expected number of gap fills ($descr)"; + + if ($expected->{single}) { + ok abs($loops->[0]->width - $expected->{width}) < epsilon, "single line covers the full width ($descr)"; + } + if ($expected->{loops}) { + my $loop_width = $loops[0][0]->width; + my $gap_fill_width = @$gap_fill ? $gap_fill->[0]->width : 0; + ok $loop_width * $expected->{loops} * 2 + $gap_fill_width > $expected->{width} - epsilon, + "loop total width + gap fill covers the full width ($descr)"; + } + }; + + my $fw = 0.7; + my $test_rect = sub { + my ($width, $expected) = @_; + + my $e = Slic3r::ExPolygon->new([ scale_points [0,0], [100,0], [100,$width], [0,$width] ]); + $expected->{width} = $width; + $test->($e, $fw, $expected, $width); + }; + $test_rect->($fw * 1, { single => 1, gaps => 0 }); + $test_rect->($fw * 1.3, { single => 1, gaps => 0 }); + $test_rect->($fw * 1.5, { single => 1, gaps => 0 }); + $test_rect->($fw * 2, { loops => 1, gaps => 0 }); + $test_rect->($fw * 2.5, { loops => 1, gaps => 1 }); + $test_rect->($fw * 3, { loops => 1, gaps => 1 }); + $test_rect->($fw * 3.5, { loops => 2, gaps => 0 }); + $test_rect->($fw * 4, { loops => 2, gaps => 1 }); +} + +__END__ diff --git a/t/bridges.t b/t/bridges.t index 51dda5c64..95db67daf 100644 --- a/t/bridges.t +++ b/t/bridges.t @@ -1,4 +1,4 @@ -use Test::More tests => 16; +use Test::More tests => 18; use strict; use warnings; @@ -82,8 +82,25 @@ use Slic3r::Test; ok check_angle($lower, $bridge, 45, undef, $bridge->area/2), 'correct bridge angle for square overhang with L-shaped anchors'; } +{ + # GH #2477: This test case failed when we computed coverage by summing length of centerlines + # instead of summing their covered area. + my $bridge = Slic3r::ExPolygon->new( + Slic3r::Polygon->new([30299990,14299990],[1500010,14299990],[1500010,1500010],[30299990,1500010]), + ); + my $lower = [ + Slic3r::ExPolygon->new( + Slic3r::Polygon->new([31800000,15800000],[0,15800000],[0,0],[31800000,0]), + Slic3r::Polygon->new([1499999,1500000],[1499999,14300000],[30300000,14300000],[30300000,1500000]), + ), + ]; + + ok check_angle($lower, $bridge, 90, undef, $bridge->area, 500000), + 'correct bridge angle for rectangle'; +} + sub check_angle { - my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_; + my ($lower, $bridge, $expected, $tolerance, $expected_coverage, $extrusion_width) = @_; if (ref($lower) eq 'ARRAY') { $lower = Slic3r::ExPolygon::Collection->new(@$lower); @@ -91,8 +108,9 @@ sub check_angle { $expected_coverage //= -1; $expected_coverage = $bridge->area if $expected_coverage == -1; + $extrusion_width //= scale 0.5; - my $bd = Slic3r::BridgeDetector->new($bridge, $lower, scale 0.5); + my $bd = Slic3r::BridgeDetector->new($bridge, $lower, $extrusion_width); $tolerance //= rad2deg($bd->resolution) + epsilon; $bd->detect_angle; diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 653bb26ae..9b58344ef 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 15; +use Test::More tests => 16; use strict; use warnings; @@ -132,4 +132,13 @@ use Slic3r::Test; 'layer_num grows continously'; # i.e. no duplicates or regressions } +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('complete_objects', 1); + $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); + my $gcode = Slic3r::Test::gcode($print); + is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; +} + __END__ diff --git a/t/dynamic.t b/t/dynamic.t deleted file mode 100644 index 5d4d3ceb4..000000000 --- a/t/dynamic.t +++ /dev/null @@ -1,93 +0,0 @@ -use Test::More; -use strict; -use warnings; - -plan skip_all => 'variable-width paths are currently disabled'; -plan tests => 20; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(first); -use Slic3r; -use Slic3r::Geometry qw(X Y scale epsilon); -use Slic3r::Surface ':types'; - -sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } - -{ - my $square = Slic3r::ExPolygon->new([ - scale_points [0,0], [10,0], [10,10], [0,10], - ]); - - my @offsets = @{$square->noncollapsing_offset_ex(- scale 5)}; - is scalar @offsets, 1, 'non-collapsing offset'; -} - -{ - local $Slic3r::Config = Slic3r::Config->new( - perimeters => 3, - ); - my $w = 0.7; - my $perimeter_flow = Slic3r::Flow->new( - nozzle_diameter => 0.5, - layer_height => 0.4, - width => $w, - ); - - my $print = Slic3r::Print->new; - my $region = Slic3r::Print::Region->new( - print => $print, - flows => { perimeter => $perimeter_flow }, - ); - push @{$print->regions}, $region; - my $object = Slic3r::Print::Object->new( - print => $print, - size => [1,1], - ); - my $make_layer = sub { - my ($width) = @_; - my $layer = Slic3r::Layer->new( - object => $object, - id => 1, - slices => [ - Slic3r::Surface->new( - surface_type => S_TYPE_INTERNAL, - expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,$width], [0,$width] ]), - ), - ], - thin_walls => [], - ); - my $layerm = $layer->region(0); - $layer->make_perimeters; - return $layerm; - }; - - my %widths = ( - 1 * $w => { perimeters => 1, gaps => 0 }, - 1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.2 * $w)->spacing }, - 1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.5 * $w)->spacing }, - 2 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing }, - 2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 1.5 * $w)->spacing }, - 3 * $w => { perimeters => 2, gaps => 0 }, - 4 * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing }, - ); - - foreach my $width (sort keys %widths) { - my $layerm = $make_layer->($width); - is scalar @{$layerm->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters'; - is scalar @{$layerm->thin_fills} ? 1 : 0, $widths{$width}{gaps}, - ($widths{$width}{gaps} ? 'gaps were filled' : 'no gaps detected'); # TODO: we should check the exact number of gaps, but we need a better medial axis algorithm - - my @gaps = map $_, @{$layerm->thin_fills}; - if (@gaps) { - ok +(!first { abs($_->flow_spacing - $widths{$width}{gap_flow_spacing}) > epsilon } @gaps), - 'flow spacing was dynamically adjusted'; - } - } -} - -__END__ diff --git a/t/fill.t b/t/fill.t index 710e60536..765cfd479 100644 --- a/t/fill.t +++ b/t/fill.t @@ -22,7 +22,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { my $print = Slic3r::Print->new; my $surface_width = 250; - my $distance = Slic3r::Filler::adjust_solid_spacing($surface_width, 47); + my $distance = Slic3r::Flow::solid_spacing($surface_width, 47); is $distance, 50, 'adjusted solid distance'; is $surface_width % $distance, 0, 'adjusted solid distance'; } diff --git a/t/loops.t b/t/loops.t index e662469ca..d2bf77176 100644 --- a/t/loops.t +++ b/t/loops.t @@ -2,7 +2,6 @@ use Test::More; use strict; use warnings; -plan skip_all => 'temporarily disabled'; plan tests => 4; BEGIN { @@ -12,46 +11,41 @@ BEGIN { } use Slic3r; +use Slic3r::Geometry qw(Z epsilon scale); use Slic3r::Test; { - # We only need to slice at one height, so we'll build a non-manifold mesh - # that still produces complete loops at that height. Triangular walls are - # enough for this purpose. - # Basically we want to check what happens when three concentric loops happen + # We want to check what happens when three concentric loops happen # to be at the same height, the two external ones being ccw and the other being - # a hole, thus cw. - my (@vertices, @facets) = (); - Slic3r::Test::add_facet($_, \@vertices, \@facets) for - # external surface below the slicing Z - [ [0,0,0], [20,0,10], [0,0,10] ], - [ [20,0,0], [20,20,10], [20,0,10] ], - [ [20,20,0], [0,20,10], [20,20,10] ], - [ [0,20,0], [0,0,10], [0,20,10] ], - - # external insetted surface above the slicing Z - [ [2,2,10], [18,2,10], [2,2,20] ], - [ [18,2,10], [18,18,10], [18,2,20] ], - [ [18,18,10], [2,18,10], [18,18,20] ], - [ [2,18,10], [2,2,10], [2,18,20] ], - - # insetted hole below the slicing Z - [ [15,5,0], [5,5,10], [15,5,10] ], - [ [15,15,0], [15,5,10], [15,15,10] ], - [ [5,15,0], [15,15,10], [5,15,10] ], - [ [5,5,0], [5,15,10], [5,5,10] ]; + # a hole, thus cw. So we create three cubes, centered around origin, the internal + # one having reversed normals. + my $mesh1 = Slic3r::Test::mesh('20mm_cube'); - my $mesh = Slic3r::TriangleMesh->new; - $mesh->ReadFromPerl(\@vertices, \@facets); - $mesh->analyze; - my @lines = map $mesh->intersect_facet($_, 10), 0..$#facets; - my $loops = Slic3r::TriangleMesh::make_loops(\@lines); - is scalar(@$loops), 3, 'correct number of loops detected'; - is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected'; + # center around origin + my $bb = $mesh1->bounding_box; + $mesh1->translate( + -($bb->x_min + $bb->size->x/2), + -($bb->y_min + $bb->size->y/2), #// + -($bb->z_min + $bb->size->z/2), + ); - my @surfaces = Slic3r::Layer::Region::_merge_loops($loops, 0); - is scalar(@surfaces), 1, 'one surface detected'; - is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole'; + my $mesh2 = $mesh1->clone; + $mesh2->scale(1.2); + + my $mesh3 = $mesh2->clone; + $mesh3->scale(1.2); + + $mesh1->reverse_normals; + ok $mesh1->volume < 0, 'reverse_normals'; + + my $all_meshes = Slic3r::TriangleMesh->new; + $all_meshes->merge($_) for $mesh1, $mesh2, $mesh3; + + my $loops = $all_meshes->slice_at(Z, 0); + is scalar(@$loops), 1, 'one expolygon returned'; + is scalar(@{$loops->[0]->holes}), 1, 'expolygon has one hole'; + ok abs(-$loops->[0]->holes->[0]->area - scale($bb->size->x)*scale($bb->size->y)) < epsilon, #)) + 'hole has expected size'; } __END__ diff --git a/utils/post-processing/flowrate.pl b/utils/post-processing/flowrate.pl index ac09d5e9a..a928d734f 100755 --- a/utils/post-processing/flowrate.pl +++ b/utils/post-processing/flowrate.pl @@ -16,7 +16,7 @@ while (<>) { if (/^G1.*? F([0-9.]+)/) { $F = $1; } - if (/^G1 X([0-9.]+) Y([0-9.]+).*? E([0-9.]+)/) { + if (/^G1 X([0-9.-]+) Y([0-9.-]+).*? E([0-9.-]+)/) { my ($x, $y, $e) = ($1, $2, $3); my $e_length = $e - $E; if ($e_length > 0 && defined $X && defined $Y) { diff --git a/var/arrow_in.png b/var/arrow_in.png new file mode 100755 index 000000000..745c65134 Binary files /dev/null and b/var/arrow_in.png differ diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index f9061e405..33c239d6f 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -5,24 +5,6 @@ namespace Slic3r { -class BridgeDirectionComparator { - public: - std::map dir_coverage; // angle => score - - BridgeDirectionComparator(double _extrusion_width) - : extrusion_width(_extrusion_width) - {}; - - // the best direction is the one causing most lines to be bridged (thus most coverage) - bool operator() (double a, double b) { - // Initial sort by coverage only - comparator must obey strict weak ordering - return (this->dir_coverage[a] > this->dir_coverage[b]); - }; - - private: - double extrusion_width; -}; - BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width) : expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width), @@ -44,143 +26,129 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges this->_anchors = intersection_ex(grown, this->lower_slices, true); - /* - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("bridge.svg", - expolygons => [ $self->expolygon ], - red_expolygons => $self->lower_slices, - polylines => $self->_edges, - ); + #if 0 + { + SVG svg("bridge.svg"); + svg.draw(this->expolygon); + svg.draw(this->lower_slices, "red"); + svg.draw(this->_anchors, "yellow"); + svg.draw(this->_edges, "black", scale_(0.2)); + svg.Close(); } - */ + #endif } bool BridgeDetector::detect_angle() { + // Do nothing if the bridging region is completely in the air + // and there are no anchors available at the layer below. if (this->_edges.empty() || this->_anchors.empty()) return false; /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to clip our test lines and be sure that their endpoints are inside the anchors and not on their contours leading to false negatives. */ - Polygons clip_area = offset(this->expolygon, +this->extrusion_width/2); + const Polygons clip_area = offset(this->expolygon, +this->extrusion_width/2); /* we'll now try several directions using a rudimentary visibility check: bridge in several directions and then sum the length of lines having both endpoints within anchors */ - // we test angles according to configured resolution - std::vector angles; - for (int i = 0; i <= PI/this->resolution; ++i) - angles.push_back(i * this->resolution); - - // we also test angles of each bridge contour + // generate the list of candidate angles + std::vector candidates; { - Polygons pp = this->expolygon; - for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - Lines lines = p->lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) - angles.push_back(line->direction()); + // we test angles according to configured resolution + std::vector angles; + for (int i = 0; i <= PI/this->resolution; ++i) + angles.push_back(i * this->resolution); + + // we also test angles of each bridge contour + for (const Polygon &p : (Polygons)this->expolygon) + for (const Line &line : p.lines()) + angles.push_back(line.direction()); + + /* we also test angles of each open supporting edge + (this finds the optimal angle for C-shaped supports) */ + for (const Polyline &edge : this->_edges) { + if (edge.first_point().coincides_with(edge.last_point())) continue; + angles.push_back(Line(edge.first_point(), edge.last_point()).direction()); } - } - /* we also test angles of each open supporting edge - (this finds the optimal angle for C-shaped supports) */ - for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { - if (edge->first_point().coincides_with(edge->last_point())) continue; - angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); - } - - // remove duplicates - double min_resolution = PI/180.0; // 1 degree - std::sort(angles.begin(), angles.end()); - for (size_t i = 1; i < angles.size(); ++i) { - if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { - angles.erase(angles.begin() + i); - --i; + // remove duplicates + constexpr double min_resolution = PI/180.0; // 1 degree + std::sort(angles.begin(), angles.end()); + for (size_t i = 1; i < angles.size(); ++i) { + if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { + angles.erase(angles.begin() + i); + --i; + } } + /* compare first value with last one and remove the greatest one (PI) + in case they are parallel (PI, 0) */ + if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) + angles.pop_back(); + + for (auto angle : angles) + candidates.push_back(BridgeDirection(angle)); } - /* compare first value with last one and remove the greatest one (PI) - in case they are parallel (PI, 0) */ - if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) - angles.pop_back(); - BridgeDirectionComparator bdcomp(this->extrusion_width); - std::map dir_avg_length; - double line_increment = this->extrusion_width; + const coord_t line_increment = this->extrusion_width; bool have_coverage = false; - for (std::vector::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) { + for (BridgeDirection &candidate : candidates) { Polygons my_clip_area = clip_area; ExPolygons my_anchors = this->_anchors; // rotate everything - the center point doesn't matter - for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it) - it->rotate(-*angle, Point(0,0)); - for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) - it->rotate(-*angle, Point(0,0)); + for (Polygon &p : my_clip_area) + p.rotate(-candidate.angle, Point(0,0)); + for (ExPolygon &e : my_anchors) + e.rotate(-candidate.angle, Point(0,0)); // generate lines in this direction BoundingBox bb; - for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) - bb.merge((Points)*it); + for (const ExPolygon &e : my_anchors) + bb.merge(e.bounding_box()); Lines lines; for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment) lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y))); - Lines clipped_lines = intersection_ln(lines, my_clip_area); + const Lines clipped_lines = intersection_ln(lines, my_clip_area); - // remove any line not having both endpoints within anchors - for (size_t i = 0; i < clipped_lines.size(); ++i) { - Line &line = clipped_lines[i]; + for (const Line &line : clipped_lines) { + // skip any line not having both endpoints within anchors if (!Slic3r::Geometry::contains(my_anchors, line.a) - || !Slic3r::Geometry::contains(my_anchors, line.b)) { - clipped_lines.erase(clipped_lines.begin() + i); - --i; - } + || !Slic3r::Geometry::contains(my_anchors, line.b)) + continue; + + candidate.max_length = std::max(candidate.max_length, line.length()); + // Calculate coverage as actual covered area, because length of centerlines + // is not accurate enough when such lines are slightly skewed and not parallel + // to the sides; calculating area will compute them as triangles. + // TODO: use a faster algorithm for computing covered area by using a sweep line + // instead of intersecting many lines. + candidate.coverage += Slic3r::Geometry::area(intersection( + my_clip_area, + offset((Polyline)line, +this->extrusion_width/2) + )); } - - std::vector lengths; - double total_length = 0; - for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) { - double len = line->length(); - lengths.push_back(len); - total_length += len; - } - if (total_length) have_coverage = true; - - // sum length of bridged lines - bdcomp.dir_coverage[*angle] = total_length; - - /* The following produces more correct results in some cases and more broken in others. - TODO: investigate, as it looks more reliable than line clipping. */ - // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0; - - // max length of bridged lines - dir_avg_length[*angle] = !lengths.empty() - ? *std::max_element(lengths.begin(), lengths.end()) - : 0; + if (candidate.coverage > 0) have_coverage = true; } // if no direction produced coverage, then there's no bridge direction if (!have_coverage) return false; // sort directions by coverage - most coverage first - std::sort(angles.begin(), angles.end(), bdcomp); - this->angle = angles.front(); + std::sort(candidates.begin(), candidates.end()); // if any other direction is within extrusion width of coverage, prefer it if shorter // TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred? - double most_coverage_angle = this->angle; - for (std::vector::const_iterator angle = angles.begin() + 1; - angle != angles.end() && bdcomp.dir_coverage[most_coverage_angle] - bdcomp.dir_coverage[*angle] < this->extrusion_width; - ++angle - ) { - if (dir_avg_length[*angle] < dir_avg_length[this->angle]) { - this->angle = *angle; - } - } + size_t i_best = 0; + for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->extrusion_width; ++ i) + if (candidates[i].max_length < candidates[i_best].max_length) + i_best = i; + + this->angle = candidates[i_best].angle; if (this->angle >= PI) this->angle -= PI; @@ -208,21 +176,21 @@ BridgeDetector::coverage(double angle) const // Compute trapezoids according to a vertical orientation Polygons trapezoids; - for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) - it->get_trapezoids2(&trapezoids, PI/2.0); + for (const ExPolygon &e : grown) + e.get_trapezoids2(&trapezoids, PI/2.0); // get anchors, convert them to Polygons and rotate them too Polygons anchors; - for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) { - Polygons pp = *anchor; - for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p) - p->rotate(PI/2.0 - angle, Point(0,0)); - anchors.insert(anchors.end(), pp.begin(), pp.end()); + for (const ExPolygon &anchor : this->_anchors) { + Polygons pp = anchor; + for (Polygon &p : pp) + p.rotate(PI/2.0 - angle, Point(0,0)); + append_to(anchors, pp); } Polygons covered; - for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { - Lines supported = intersection_ln(trapezoid->lines(), anchors); + for (const Polygon &trapezoid : trapezoids) { + Lines supported = intersection_ln(trapezoid.lines(), anchors); // not nice, we need a more robust non-numeric check for (size_t i = 0; i < supported.size(); ++i) { @@ -232,17 +200,17 @@ BridgeDetector::coverage(double angle) const } } - if (supported.size() >= 2) covered.push_back(*trapezoid); + if (supported.size() >= 2) covered.push_back(trapezoid); } // merge trapezoids and rotate them back - Polygons _coverage = union_(covered); - for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p) - p->rotate(-(PI/2.0 - angle), Point(0,0)); + covered = union_(covered); + for (Polygon &p : covered) + p.rotate(-(PI/2.0 - angle), Point(0,0)); // intersect trapezoids with actual bridge area to remove extra margins // and append it to result - return intersection(_coverage, this->expolygon); + return intersection(covered, this->expolygon); /* if (0) { @@ -272,10 +240,8 @@ BridgeDetector::unsupported_edges(double angle) const // get bridge edges (both contour and holes) Polylines bridge_edges; - { - Polygons pp = this->expolygon; - bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end()); // this uses split_at_first_point() - } + for (const Polygon &p : (Polygons)this->expolygon) + bridge_edges.push_back(p.split_at_first_point()); // get unsupported edges Polylines _unsupported = diff_pl( @@ -290,13 +256,10 @@ BridgeDetector::unsupported_edges(double angle) const direction might still benefit from anchors if long enough) double angle_tolerance = PI / 180.0 * 5.0; */ Polylines unsupported; - for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) { - Lines lines = polyline->lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - if (!Slic3r::Geometry::directions_parallel(line->direction(), angle)) - unsupported.push_back(*line); - } - } + for (const Polyline &polyline : _unsupported) + for (const Line &line : polyline.lines()) + if (!Slic3r::Geometry::directions_parallel(line.direction(), angle)) + unsupported.push_back(line); return unsupported; /* diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index bbbd26df9..84f9f3848 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -15,7 +15,7 @@ public: // Lower slices, all regions. ExPolygonCollection lower_slices; // Scaled extrusion width of the infill. - double extrusion_width; + coord_t extrusion_width; // Angle resolution for the brute force search of the best bridging angle. double resolution; // The final optimal angle. @@ -31,6 +31,19 @@ private: Polylines _edges; // Closed polygons representing the supporting areas. ExPolygons _anchors; + + class BridgeDirection { + public: + BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {} + // the best direction is the one causing most lines to be bridged (thus most coverage) + bool operator<(const BridgeDirection &other) const { + // Initial sort by coverage only - comparator must obey strict weak ordering + return this->coverage > other.coverage; + }; + double angle; + double coverage; + double max_length; + }; }; } diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 404a4385e..565a7ae2a 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -2,6 +2,7 @@ #define slic3r_ExPolygon_hpp_ #include "libslic3r.h" +#include "BoundingBox.hpp" #include "Polygon.hpp" #include "Polyline.hpp" #include @@ -30,6 +31,7 @@ class ExPolygon bool contains(const Point &point) const; bool contains_b(const Point &point) const; bool has_boundary_point(const Point &point) const; + BoundingBox bounding_box() const { return this->contour.bounding_box(); }; void remove_vertical_collinear_points(coord_t tolerance); void simplify_p(double tolerance, Polygons* polygons) const; Polygons simplify_p(double tolerance) const; diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 73b64e286..b5bff5ab3 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -142,7 +142,6 @@ class ExtrusionLoop : public ExtrusionEntity void split_at(const Point &point, bool prefer_non_overhang = false); void clip_end(double distance, ExtrusionPaths* paths) const; // Test, whether the point is extruded by a bridging flow. - // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; bool is_perimeter() const { return this->paths.front().role == erPerimeter diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index c88a32514..39c9107b9 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -1,3 +1,6 @@ +#define DEBUG +#undef NDEBUG +#include #include #include @@ -69,36 +72,6 @@ Fill::fill_surface(const Surface &surface) return polylines_out; } -// Calculate a new spacing to fill width with possibly integer number of lines, -// the first and last line being centered at the interval ends. -// This function possibly increases the spacing, never decreases, -// and for a narrow width the increase in spacing may become severe, -// therefore the adjustment is limited to 20% increase. -coord_t -Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) -{ - assert(width >= 0); - assert(distance > 0); - const int number_of_intervals = floor(width / distance); - if (number_of_intervals == 0) return distance; - - coord_t distance_new = (width / number_of_intervals); - - const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); - assert(factor > 1. - 1e-5); - - // How much could the extrusion width be increased? By 20%. - // Because of this limit, this method is not idempotent: each run - // will increment distance by 20%. - const coordf_t factor_max = 1.2; - if (factor > factor_max) - distance_new = floor((double)distance * factor_max + 0.5); - - assert((distance_new * number_of_intervals) <= width); - - return distance_new; -} - // Returns orientation of the infill and the reference point of the infill pattern. // For a normal print, the reference point is the center of a bounding box of the STL. Fill::direction_t diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index abb9e1c65..a72d5528c 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -64,7 +64,6 @@ public: public: static Fill* new_from_type(const InfillPattern type); static Fill* new_from_type(const std::string &type); - static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); virtual Fill* clone() const = 0; virtual ~Fill() {}; diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index 24d8f318a..992dfaa3b 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -1,5 +1,6 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" +#include "../Flow.hpp" #include "../Surface.hpp" #include "FillConcentric.hpp" @@ -20,7 +21,7 @@ FillConcentric::_fill_surface_single( if (this->density > 0.9999f && !this->dont_adjust) { BoundingBox bounding_box = expolygon.contour.bounding_box(); - distance = this->adjust_solid_spacing(bounding_box.size().x, distance); + distance = Flow::solid_spacing(bounding_box.size().x, distance); this->_spacing = unscale(distance); } diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index e02c3a298..f2e5ba7a3 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -6,6 +6,7 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" +#include "../Flow.hpp" #include "../PolylineCollection.hpp" #include "../Surface.hpp" #include @@ -48,7 +49,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // define flow spacing according to requested density if (this->density > 0.9999f && !this->dont_adjust) { - line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); + line_spacing = Flow::solid_spacing(bounding_box.size().x, line_spacing); this->_spacing = unscale(line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers @@ -339,7 +340,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // Get the first lower point. vertical_t::iterator it = v.begin(); // minimum x,y IntersectionPoint p = it->second; - assert(p.type == IntersectionPoint::ipTypeLower); + if (p.type != IntersectionPoint::ipTypeLower) { + // Degenerate polygon, this shouldn't happen. + // We used to have an assert here, but let's be tolerant. + grid.erase(p.x); + continue; + } // Start our polyline. Polyline polyline; @@ -350,17 +356,32 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // Complete the vertical line by finding the corresponding upper or lower point. if (p.type == IntersectionPoint::ipTypeUpper) { // find first point along c.x with y < c.y - assert(it != grid[p.x].begin()); + if (it == grid[p.x].begin()) { + // Degenerate polygon, this shouldn't happen. + // We used to have an assert here, but let's be tolerant. + grid.erase(p.x); + break; + } --it; } else { // find first point along c.x with y > c.y ++it; - assert(it != grid[p.x].end()); + if (it == grid[p.x].end()) { + // Degenerate polygon, this shouldn't happen. + // We used to have an assert here, but let's be tolerant. + grid.erase(p.x); + break; + } } // Append the point to our polyline. IntersectionPoint b = it->second; - assert(b.type != p.type); + if (b.type == p.type) { + // Degenerate polygon, this shouldn't happen. + // We used to have an assert here, but let's be tolerant. + grid.erase(p.x); + break; + } polyline.append(b); polyline.points.back().y += this->endpoints_overlap * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); @@ -408,8 +429,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // If the connection brought us to another x coordinate, we expect the point // type to be the same. - assert((p.type == b.type && p.x > b.x) - || (p.type != b.type && p.x == b.x)); + if (!(p.type == b.type && p.x > b.x) && !(p.type != b.type && p.x == b.x)) { + // Degenerate polygon, this shouldn't happen. + // We used to have an assert here, but let's be tolerant. + grid.erase(p.x); + break; + } } // Yay, we have a polyline! diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index 6131534e7..42caf9f1a 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -50,6 +50,11 @@ Flow::spacing() const { return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing); } +void +Flow::set_spacing(float spacing) { + this->width = Flow::_width_from_spacing(spacing, this->nozzle_diameter, this->height, this->bridge); +} + /* This method returns the centerline spacing between an extrusion using this flow and another one using another flow. this->spacing(other) shall return the same value as other.spacing(*this) */ @@ -115,4 +120,37 @@ Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bo return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); } +// Calculate a new spacing to fill width with possibly integer number of lines, +// the first and last line being centered at the interval ends. +// This function possibly increases the spacing, never decreases, +// and for a narrow width the increase in spacing may become severe, +// therefore the adjustment is limited to 20% increase. +template +T +Flow::solid_spacing(const T total_width, const T spacing) +{ + assert(total_width >= 0); + assert(spacing > 0); + const int number_of_intervals = floor(total_width / spacing); + if (number_of_intervals == 0) return spacing; + + T spacing_new = (total_width / number_of_intervals); + + const double factor = (double)spacing_new / (double)spacing; + assert(factor > 1. - 1e-5); + + // How much could the extrusion width be increased? By 20%. + // Because of this limit, this method is not idempotent: each run + // will increment spacing by 20%. + const double factor_max = 1.2; + if (factor > factor_max) + spacing_new = floor((double)spacing * factor_max + 0.5); + + assert((spacing_new * number_of_intervals) <= total_width); + + return spacing_new; +} +template coord_t Flow::solid_spacing(const coord_t total_width, const coord_t spacing); +template coordf_t Flow::solid_spacing(const coordf_t total_width, const coordf_t spacing); + } diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index fdfcac695..7149df6b4 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -30,6 +30,13 @@ class Flow : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; float spacing() const; float spacing(const Flow &other) const; + void set_spacing(float spacing); + void set_solid_spacing(const coord_t total_width) { + this->set_spacing(Flow::solid_spacing(total_width, this->scaled_spacing())); + }; + void set_solid_spacing(const coordf_t total_width) { + this->set_spacing(Flow::solid_spacing(total_width, (coordf_t)this->spacing())); + }; double mm3_per_mm() const; coord_t scaled_width() const { return scale_(this->width); @@ -43,12 +50,12 @@ class Flow static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + template static T solid_spacing(const T total_width, const T spacing); private: static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio); static float _auto_width(FlowRole role, float nozzle_diameter, float height); static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); - static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); }; } diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index b8584000f..6012fd53b 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -296,13 +296,27 @@ template bool contains(const std::vector &vector, const Point &point) { - for (typename std::vector::const_iterator it = vector.begin(); it != vector.end(); ++it) { - if (it->contains(point)) return true; - } + for (const T &it : vector) + if (it.contains(point)) + return true; + return false; } +template bool contains(const Polygons &vector, const Point &point); template bool contains(const ExPolygons &vector, const Point &point); +template +double +area(const std::vector &vector) +{ + double area = 0; + for (const T &it : vector) + area += it.area(); + + return area; +} +template double area(const Polygons &vector); + double rad2deg(double angle) { diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 55c760f7e..7a4c304f4 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -20,6 +20,7 @@ void chained_path(const Points &points, std::vector &retval); template void chained_path_items(Points &points, T &items, T &retval); bool directions_parallel(double angle1, double angle2, double max_diff = 0); template bool contains(const std::vector &vector, const Point &point); +template double area(const std::vector &vector); double rad2deg(double angle); double rad2deg_dir(double angle); double deg2rad(double angle); diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 1baaa0bef..5b4a6cc5c 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -1,6 +1,7 @@ #include "Layer.hpp" #include "BridgeDetector.hpp" #include "ClipperUtils.hpp" +#include "Geometry.hpp" #include "PerimeterGenerator.hpp" #include "Print.hpp" #include "Surface.hpp" @@ -65,34 +66,63 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* g.process(); } -// This function reads lower_layer->slices and writes this->bridged and this->fill_surfaces, -// so it's thread-safe. +// This function reads layer->slices and lower_layer->slices +// and writes this->bridged and this->fill_surfaces, so it's thread-safe. void LayerRegion::process_external_surfaces() { - const Surfaces &surfaces = this->fill_surfaces.surfaces; - const double margin = scale_(EXTERNAL_INFILL_MARGIN); + Surfaces &surfaces = this->fill_surfaces.surfaces; + + for (size_t j = 0; j < surfaces.size(); ++j) { + Surface &surface = surfaces[j]; + + if (this->layer()->lower_layer != NULL && surface.is_bridge()) { + // If this bridge has one or more holes that are internal surfaces + // (thus not visible from the outside), like a slab sustained by + // pillars, include them in the bridge in order to have better and + // more continuous bridging. + Polygons &holes = surface.expolygon.holes; + for (int i = 0; i < holes.size(); ++i) { + // reverse the hole and consider it a polygon + Polygon h = holes[i]; + h.reverse(); + + // Is this hole fully contained in the layer slices? + if (diff(h, this->layer()->slices).empty()) { + // remove any other surface contained in this hole + for (int k = 0; k < surfaces.size(); ++k) { + if (k == j) continue; + if (h.contains(surfaces[k].expolygon.contour.first_point())) { + surfaces.erase(surfaces.begin() + k); + --k; + } + } + + holes.erase(holes.begin() + i); + --i; + } + } + } + } SurfaceCollection bottom; - for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { - if (!surface->is_bottom()) continue; - - const ExPolygons grown = offset_ex(surface->expolygon, +margin); + for (const Surface &surface : surfaces) { + if (!surface.is_bottom()) continue; /* detect bridge direction before merging grown surfaces otherwise adjacent bridges would get merged into a single one while they need different directions also, supply the original expolygon instead of the grown one, because in case of very thin (but still working) anchors, the grown expolygon would go beyond them */ double angle = -1; - if (this->layer()->lower_layer != NULL && surface->is_bridge()) { + if (this->layer()->lower_layer != NULL && surface.is_bridge()) { BridgeDetector bd( - surface->expolygon, + surface.expolygon, this->layer()->lower_layer->slices, this->flow(frInfill, true).scaled_width() ); #ifdef SLIC3R_DEBUG - printf("Processing bridge at layer %zu:\n", this->layer()->id()); + printf("Processing bridge at layer %zu (z = %f):\n", this->layer()->id(), this->layer()->print_z); #endif if (bd.detect_angle()) { @@ -105,28 +135,22 @@ LayerRegion::process_external_surfaces() } } - for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { - Surface s = *surface; - s.expolygon = *it; - s.bridge_angle = angle; - bottom.surfaces.push_back(s); - } + const ExPolygons grown = offset_ex(surface.expolygon, +SCALED_EXTERNAL_INFILL_MARGIN); + Surface templ = surface; + templ.bridge_angle = angle; + bottom.append(grown, templ); } SurfaceCollection top; - for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { - if (surface->surface_type != stTop) continue; + for (const Surface &surface : surfaces) { + if (surface.surface_type != stTop) continue; // give priority to bottom surfaces ExPolygons grown = diff_ex( - offset(surface->expolygon, +margin), + offset(surface.expolygon, +SCALED_EXTERNAL_INFILL_MARGIN), (Polygons)bottom ); - for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { - Surface s = *surface; - s.expolygon = *it; - top.surfaces.push_back(s); - } + top.append(grown, surface); } /* if we're slicing with no infill, we can't extend external surfaces @@ -135,10 +159,9 @@ LayerRegion::process_external_surfaces() if (this->region()->config.fill_density.value > 0) { fill_boundaries = SurfaceCollection(surfaces); } else { - for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) { - if (it->surface_type != stInternal) - fill_boundaries.surfaces.push_back(*it); - } + for (const Surface &s : surfaces) + if (s.surface_type != stInternal) + fill_boundaries.surfaces.push_back(s); } // intersect the grown surfaces with the actual fill boundaries @@ -152,10 +175,10 @@ LayerRegion::process_external_surfaces() std::vector groups; tb.group(&groups); - for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + for (const SurfacesConstPtr &g : groups) { Polygons subject; - for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s) - append_to(subject, (Polygons)**s); + for (const Surface* s : g) + append_to(subject, (Polygons)*s); ExPolygons expp = intersection_ex( subject, @@ -163,45 +186,36 @@ LayerRegion::process_external_surfaces() true // to ensure adjacent expolygons are unified ); - for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { - Surface s = *g->front(); - s.expolygon = *ex; - new_surfaces.surfaces.push_back(s); - } + new_surfaces.append(expp, *g.front()); } } /* subtract the new top surfaces from the other non-top surfaces and re-add them */ { SurfaceCollection other; - for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - if (s->surface_type != stTop && !s->is_bottom()) - other.surfaces.push_back(*s); - } + for (const Surface &s : surfaces) + if (s.surface_type != stTop && !s.is_bottom()) + other.surfaces.push_back(s); // group surfaces std::vector groups; other.group(&groups); - for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + for (const SurfacesConstPtr &g : groups) { Polygons subject; - for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s) - append_to(subject, (Polygons)**s); + for (const Surface* s : g) + append_to(subject, (Polygons)*s); ExPolygons expp = diff_ex( subject, (Polygons)new_surfaces ); - for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { - Surface s = *g->front(); - s.expolygon = *ex; - new_surfaces.surfaces.push_back(s); - } + new_surfaces.append(expp, *g.front()); } } - this->fill_surfaces = new_surfaces; + this->fill_surfaces = std::move(new_surfaces); } void @@ -234,7 +248,8 @@ LayerRegion::prepare_fill_surfaces() const float &fill_density = this->region()->config.fill_density; if (fill_density > 0 && fill_density < 100) { // scaling an area requires two calls! - const double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); + // (we don't use scale_() because it would overflow the coord_t range + const double min_area = this->region()->config.solid_infill_below_area.value / SCALING_FACTOR / SCALING_FACTOR; for (Surface &surface : this->fill_surfaces.surfaces) { if (surface.surface_type == stInternal && surface.area() <= min_area) surface.surface_type = stInternalSolid; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index d3966e2be..78a847f58 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -753,18 +753,20 @@ ModelObject::mirror(const Axis &axis) } void -ModelObject::transform_by_instance(const ModelInstance &instance, bool dont_translate) +ModelObject::transform_by_instance(ModelInstance instance, bool dont_translate) { + // We get instance by copy because we would alter it in the loop below, + // causing inconsistent values in subsequent instances. this->rotate(instance.rotation, Z); this->scale(instance.scaling_factor); if (!dont_translate) this->translate(instance.offset.x, instance.offset.y, 0); - for (ModelInstancePtrs::iterator i = this->instances.begin(); i != this->instances.end(); ++i) { - (*i)->rotation -= instance.rotation; - (*i)->scaling_factor /= instance.scaling_factor; + for (ModelInstance* i : this->instances) { + i->rotation -= instance.rotation; + i->scaling_factor /= instance.scaling_factor; if (!dont_translate) - (*i)->offset.translate(-instance.offset.x, -instance.offset.y); + i->offset.translate(-instance.offset.x, -instance.offset.y); } this->origin_translation = Pointf3(0,0,0); this->invalidate_bounding_box(); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 79b00de69..1b9eaa4b9 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -163,7 +163,7 @@ class ModelObject void scale_to_fit(const Sizef3 &size); void rotate(float angle, const Axis &axis); void mirror(const Axis &axis); - void transform_by_instance(const ModelInstance &instance, bool dont_translate = false); + void transform_by_instance(ModelInstance instance, bool dont_translate = false); size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 2100f638d..e41f30a83 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -163,6 +163,10 @@ Print::invalidate_state_by_config(const PrintConfigBase &config) || opt_key == "min_skirt_length" || opt_key == "ooze_prevention") { steps.insert(psSkirt); + } else if (opt_key == "brim_width") { + steps.insert(psBrim); + steps.insert(psSkirt); + osteps.insert(posSupportMaterial); } else if (opt_key == "brim_width" || opt_key == "interior_brim_width" || opt_key == "brim_connections_width") { @@ -176,6 +180,7 @@ Print::invalidate_state_by_config(const PrintConfigBase &config) } else if (opt_key == "avoid_crossing_perimeters" || opt_key == "bed_shape" || opt_key == "bed_temperature" + || opt_key == "between_objects_gcode" || opt_key == "bridge_acceleration" || opt_key == "bridge_fan_speed" || opt_key == "complete_objects" @@ -760,13 +765,18 @@ Print::brim_flow() const extruders and take the one with, say, the smallest index. The same logic should be applied to the code that selects the extruder during G-code generation as well. */ - return Flow::new_from_config_width( + Flow flow = Flow::new_from_config_width( frPerimeter, width, this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1), this->skirt_first_layer_height(), 0 ); + + // Adjust extrusion width in order to fill the total brim width with an integer number of lines. + flow.set_solid_spacing(this->config.brim_width.value); + + return flow; } Flow @@ -814,23 +824,21 @@ Print::_make_brim() const coord_t grow_distance = flow.scaled_width()/2; Polygons islands; - FOREACH_OBJECT(this, object) { - const Layer &layer0 = *(*object)->get_layer(0); + for (PrintObject* object : this->objects) { + const Layer* layer0 = object->get_layer(0); - Polygons object_islands = layer0.slices.contours(); + Polygons object_islands = layer0->slices.contours(); - if (!(*object)->support_layers.empty()) { - const SupportLayer &support_layer0 = *(*object)->get_support_layer(0); + if (!object->support_layers.empty()) { + const SupportLayer* support_layer0 = object->get_support_layer(0); - for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_fills.entities.begin(); - it != support_layer0.support_fills.entities.end(); ++it) - append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + for (const ExtrusionEntity* e : support_layer0->support_fills.entities) + append_to(object_islands, offset(e->as_polyline(), grow_distance)); - for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_interface_fills.entities.begin(); - it != support_layer0.support_interface_fills.entities.end(); ++it) - append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + for (const ExtrusionEntity* e : support_layer0->support_interface_fills.entities) + append_to(object_islands, offset(e->as_polyline(), grow_distance)); } - for (const Point © : (*object)->_shifted_copies) { + for (const Point © : object->_shifted_copies) { for (Polygon p : object_islands) { p.translate(copy); islands.push_back(p); @@ -847,8 +855,8 @@ Print::_make_brim() // perimeters because here we're offsetting outwards) append_to(loops, offset2( islands, - flow.scaled_width() + flow.scaled_spacing() * (i - 1.0 + 0.5), - flow.scaled_spacing() * -1.0, + flow.scaled_width() + flow.scaled_spacing() * (i - 1.5 + 0.5), + flow.scaled_spacing() * -0.5, 100000, ClipperLib::jtSquare )); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a5102ae2e..3e73e4829 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -81,6 +81,15 @@ PrintConfigDef::PrintConfigDef() def->height = 50; def->default_value = new ConfigOptionString(""); + def = this->add("between_objects_gcode", coString); + def->label = "Between objects G-code"; + def->tooltip = "This code is inserted between objects when using sequential printing. By default extruder and bed temperature are reset using non-wait command; however if M104, M109, M140 or M190 are detected in this custom code, Slic3r will not add temperature commands. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."; + def->cli = "between-objects-gcode=s"; + def->multiline = true; + def->full_width = true; + def->height = 120; + def->default_value = new ConfigOptionString(""); + def = this->add("bottom_infill_pattern", external_fill_pattern); def->label = "Bottom"; def->full_label = "Bottom infill pattern"; @@ -786,7 +795,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("max_volumetric_speed", coFloat); def->label = "Max volumetric speed"; def->category = "Speed"; - def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports."; + def->tooltip = "If set to a non-zero value, extrusion will be limited to this volumetric speed. You may want to set it to your extruder maximum. As a hint, you can read calculated volumetric speeds in the comments of any G-code file you export from Slic3r."; def->sidetext = "mm³/s"; def->cli = "max-volumetric-speed=f"; def->min = 0; @@ -961,6 +970,19 @@ PrintConfigDef::PrintConfigDef() def->height = 60; def->default_value = new ConfigOptionStrings(); + def = this->add("printer_notes", coStrings); + def->label = "Printer notes"; + def->tooltip = "You can put your notes regarding the printer here."; + def->cli = "printer-notes=s@"; + def->multiline = true; + def->full_width = true; + def->height = 130; + { + ConfigOptionStrings* opt = new ConfigOptionStrings(); + opt->values.push_back(""); + def->default_value = opt; + } + def = this->add("print_settings_id", coString); def->default_value = new ConfigOptionString(""); @@ -1623,7 +1645,7 @@ PrintConfigDef::PrintConfigDef() def->default_value = new ConfigOptionFloat(0); } -PrintConfigDef print_config_def; +const PrintConfigDef print_config_def; void DynamicPrintConfig::normalize() { @@ -1856,6 +1878,6 @@ CLIConfigDef::CLIConfigDef() def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0)); } -CLIConfigDef cli_config_def; +const CLIConfigDef cli_config_def; } diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index c6803c3cd..754ac95d6 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -103,7 +103,7 @@ class PrintConfigDef : public ConfigDef // The one and only global definition of SLic3r configuration options. // This definition is constant. -extern PrintConfigDef print_config_def; +extern const PrintConfigDef print_config_def; // Slic3r configuration storage with print_config_def assigned. class PrintConfigBase : public virtual ConfigBase @@ -298,6 +298,7 @@ class GCodeConfig : public virtual StaticPrintConfig { public: ConfigOptionString before_layer_gcode; + ConfigOptionString between_objects_gcode; ConfigOptionString end_gcode; ConfigOptionStrings end_filament_gcode; ConfigOptionString extrusion_axis; @@ -335,6 +336,7 @@ class GCodeConfig : public virtual StaticPrintConfig virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(before_layer_gcode); + OPT_PTR(between_objects_gcode); OPT_PTR(end_gcode); OPT_PTR(end_filament_gcode); OPT_PTR(extrusion_axis); @@ -426,6 +428,7 @@ class PrintConfig : public GCodeConfig ConfigOptionString output_filename_format; ConfigOptionFloat perimeter_acceleration; ConfigOptionStrings post_process; + ConfigOptionStrings printer_notes; ConfigOptionFloat resolution; ConfigOptionFloats retract_before_travel; ConfigOptionBools retract_layer_change; @@ -490,6 +493,7 @@ class PrintConfig : public GCodeConfig OPT_PTR(output_filename_format); OPT_PTR(perimeter_acceleration); OPT_PTR(post_process); + OPT_PTR(printer_notes); OPT_PTR(resolution); OPT_PTR(retract_before_travel); OPT_PTR(retract_layer_change); @@ -580,18 +584,6 @@ class SLAPrintConfig ConfigOptionFloat support_material_spacing; ConfigOptionInt threads; - SLAPrintConfig() : StaticPrintConfig() { - this->set_defaults(); - - // override some defaults - this->fill_density.value = 100; - this->fill_pattern.value = ipGrid; - this->infill_extrusion_width.value = 0.5; - this->infill_extrusion_width.percent = false; - this->perimeter_extrusion_width.value = 1; - this->perimeter_extrusion_width.percent = false; - }; - virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(fill_angle); OPT_PTR(fill_density); @@ -617,7 +609,7 @@ class CLIConfigDef : public ConfigDef CLIConfigDef(); }; -extern CLIConfigDef cli_config_def; +extern const CLIConfigDef cli_config_def; class CLIConfig : public virtual ConfigBase, public StaticConfig diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp index cb4e7fa2e..c5a1c948e 100644 --- a/xs/src/libslic3r/PrintRegion.cpp +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -85,11 +85,19 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config) steps.insert(posPerimeters); } else if (opt_key == "first_layer_extrusion_width") { steps.insert(posSupportMaterial); + } else if (opt_key == "solid_infill_below_area") { + const float &cur_value = config.opt(opt_key)->value; + const float &new_value = this->config.solid_infill_below_area.value; + if (new_value >= cur_value) { + steps.insert(posPrepareInfill); + } else { + // prepare_infill is not idempotent when solid_infill_below_area is reduced + steps.insert(posPerimeters); + } } else if (opt_key == "infill_every_layers" || opt_key == "solid_infill_every_layers" || opt_key == "bottom_solid_layers" || opt_key == "top_solid_layers" - || opt_key == "solid_infill_below_area" || opt_key == "infill_extruder" || opt_key == "solid_infill_extruder" || opt_key == "infill_extrusion_width") { @@ -108,10 +116,10 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config) } else if (opt_key == "fill_density") { const float &cur_value = config.opt("fill_density")->value; const float &new_value = this->config.fill_density.value; - if ((cur_value == 0) != (new_value == 0)) + if ((cur_value == 0) != (new_value == 0) || (cur_value == 100) != (new_value == 100)) steps.insert(posPerimeters); - steps.insert(posPrepareInfill); + steps.insert(posInfill); } else if (opt_key == "external_perimeter_extrusion_width" || opt_key == "perimeter_extruder") { steps.insert(posPerimeters); diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index 5fb0e7d5f..c0a2f1ff4 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -35,6 +35,11 @@ SVG::SVG(const char* filename, const BoundingBox &bbox) h, w); } +SVG::~SVG() +{ + this->Close(); +} + void SVG::draw(const Line &line, std::string stroke, coord_t stroke_width) { @@ -68,10 +73,10 @@ void SVG::draw(const ThickLine &line, const std::string &fill, const std::string } void -SVG::draw(const Lines &lines, std::string stroke) +SVG::draw(const Lines &lines, std::string stroke, coord_t stroke_width) { for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) - this->draw(*it, stroke); + this->draw(*it, stroke, stroke_width); } void @@ -87,18 +92,30 @@ SVG::draw(const ExPolygon &expolygon, std::string fill) this->fill = fill; std::string d; - Polygons pp = expolygon; - for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - d += this->get_path_d(*p, true) + " "; - } + for (const Polygon &p : (Polygons)expolygon) + d += this->get_path_d(p, true) + " "; + this->path(d, true); } +void +SVG::draw(const ExPolygonCollection &coll, std::string fill) +{ + this->draw(coll.expolygons, fill); +} + +void +SVG::draw(const SurfaceCollection &coll, std::string fill) +{ + for (const Surface &s : coll.surfaces) + this->draw(s.expolygon, fill); +} + void SVG::draw(const ExPolygons &expolygons, std::string fill) { - for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it) - this->draw(*it, fill); + for (const ExPolygon &e : expolygons) + this->draw(e, fill); } void @@ -126,7 +143,7 @@ void SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width) { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw(*it, fill, stroke_width); + this->draw(*it, stroke, stroke_width); } void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width) @@ -202,9 +219,12 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const void SVG::Close() { - fprintf(this->f, "\n"); - fclose(this->f); - printf("SVG written to %s\n", this->filename.c_str()); + if (this->f != NULL) { + fprintf(this->f, "\n"); + fclose(this->f); + this->f = NULL; + printf("SVG written to %s\n", this->filename.c_str()); + } } } diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index 94cfc27dd..b66af152a 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -3,7 +3,9 @@ #include "libslic3r.h" #include "ExPolygon.hpp" +#include "ExPolygonCollection.hpp" #include "Line.hpp" +#include "SurfaceCollection.hpp" #include "TriangleMesh.hpp" namespace Slic3r { @@ -17,12 +19,15 @@ class SVG SVG(const char* filename); SVG(const char* filename, const BoundingBox &bbox); + ~SVG(); void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0); void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0); - void draw(const Lines &lines, std::string stroke = "black"); + void draw(const Lines &lines, std::string stroke = "black", coord_t stroke_width = 0); void draw(const IntersectionLines &lines, std::string stroke = "black"); void draw(const ExPolygon &expolygon, std::string fill = "grey"); void draw(const ExPolygons &expolygons, std::string fill = "grey"); + void draw(const ExPolygonCollection &coll, std::string fill = "grey"); + void draw(const SurfaceCollection &coll, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey"); void draw(const Polygons &polygons, std::string fill = "grey"); void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0); diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index 590ea4aae..bb75e0d0d 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -41,6 +41,7 @@ class SurfaceCollection bool empty() const { return this->surfaces.empty(); }; size_t size() const { return this->surfaces.size(); }; void clear() { this->surfaces.clear(); }; + void erase(size_t i) { this->surfaces.erase(this->surfaces.begin() + i); }; }; } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 2a52c865f..cba0a7977 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -511,6 +511,13 @@ TriangleMesh::require_shared_vertices() if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl)); } +void +TriangleMesh::reverse_normals() +{ + stl_reverse_all_facets(&this->stl); + if (this->stl.stats.volume != -1) this->stl.stats.volume *= -1.0; +} + void TriangleMesh::extrude_tin(float offset) { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 51a4e9df4..271324282 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -59,6 +59,7 @@ class TriangleMesh size_t facets_count() const; void extrude_tin(float offset); void require_shared_vertices(); + void reverse_normals(); static TriangleMesh make_cube(double x, double y, double z); static TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 94b8141de..780b3b442 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -64,6 +64,7 @@ constexpr auto LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15; constexpr coord_t SMALL_PERIMETER_LENGTH = scale_(6.5) * 2 * PI; constexpr coordf_t INSET_OVERLAP_TOLERANCE = 0.4; constexpr coordf_t EXTERNAL_INFILL_MARGIN = 3; +constexpr coord_t SCALED_EXTERNAL_INFILL_MARGIN = scale_(EXTERNAL_INFILL_MARGIN); enum Axis { X=0, Y, Z }; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 6744a1e10..75dc35405 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -110,92 +110,90 @@ PROTOTYPES: DISABLE SV* print_config_def() CODE: - t_optiondef_map &def = Slic3r::print_config_def.options; - HV* options_hv = newHV(); - for (t_optiondef_map::iterator oit = def.begin(); oit != def.end(); ++oit) { + for (const auto &oit : Slic3r::print_config_def.options) { HV* hv = newHV(); - t_config_option_key opt_key = oit->first; - ConfigOptionDef* optdef = &oit->second; + const t_config_option_key &opt_key = oit.first; + const ConfigOptionDef &optdef = oit.second; const char* opt_type; - if (optdef->type == coFloat || optdef->type == coFloats || optdef->type == coFloatOrPercent) { + if (optdef.type == coFloat || optdef.type == coFloats || optdef.type == coFloatOrPercent) { opt_type = "f"; - } else if (optdef->type == coPercent) { + } else if (optdef.type == coPercent) { opt_type = "percent"; - } else if (optdef->type == coInt || optdef->type == coInts) { + } else if (optdef.type == coInt || optdef.type == coInts) { opt_type = "i"; - } else if (optdef->type == coString) { + } else if (optdef.type == coString) { opt_type = "s"; - } else if (optdef->type == coStrings) { + } else if (optdef.type == coStrings) { opt_type = "s@"; - } else if (optdef->type == coPoint || optdef->type == coPoint3 || optdef->type == coPoints) { + } else if (optdef.type == coPoint || optdef.type == coPoint3 || optdef.type == coPoints) { opt_type = "point"; - } else if (optdef->type == coBool || optdef->type == coBools) { + } else if (optdef.type == coBool || optdef.type == coBools) { opt_type = "bool"; - } else if (optdef->type == coEnum) { + } else if (optdef.type == coEnum) { opt_type = "select"; } else { throw "Unknown option type"; } (void)hv_stores( hv, "type", newSVpv(opt_type, 0) ); - (void)hv_stores( hv, "gui_type", newSVpvn(optdef->gui_type.c_str(), optdef->gui_type.length()) ); - (void)hv_stores( hv, "gui_flags", newSVpvn(optdef->gui_flags.c_str(), optdef->gui_flags.length()) ); - (void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) ); - if (!optdef->full_label.empty()) - (void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) ); - (void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length(), true) ); - (void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) ); - (void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) ); - (void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) ); - (void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) ); - (void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) ); - (void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) ); - (void)hv_stores( hv, "readonly", newSViv(optdef->readonly ? 1 : 0) ); - (void)hv_stores( hv, "height", newSViv(optdef->height) ); - (void)hv_stores( hv, "width", newSViv(optdef->width) ); - (void)hv_stores( hv, "min", newSViv(optdef->min) ); - (void)hv_stores( hv, "max", newSViv(optdef->max) ); + (void)hv_stores( hv, "gui_type", newSVpvn(optdef.gui_type.c_str(), optdef.gui_type.length()) ); + (void)hv_stores( hv, "gui_flags", newSVpvn(optdef.gui_flags.c_str(), optdef.gui_flags.length()) ); + (void)hv_stores( hv, "label", newSVpvn_utf8(optdef.label.c_str(), optdef.label.length(), true) ); + if (!optdef.full_label.empty()) + (void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef.full_label.c_str(), optdef.full_label.length(), true) ); + (void)hv_stores( hv, "category", newSVpvn_utf8(optdef.category.c_str(), optdef.category.length(), true) ); + (void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef.tooltip.c_str(), optdef.tooltip.length(), true) ); + (void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef.sidetext.c_str(), optdef.sidetext.length(), true) ); + (void)hv_stores( hv, "cli", newSVpvn(optdef.cli.c_str(), optdef.cli.length()) ); + (void)hv_stores( hv, "ratio_over", newSVpvn(optdef.ratio_over.c_str(), optdef.ratio_over.length()) ); + (void)hv_stores( hv, "multiline", newSViv(optdef.multiline ? 1 : 0) ); + (void)hv_stores( hv, "full_width", newSViv(optdef.full_width ? 1 : 0) ); + (void)hv_stores( hv, "readonly", newSViv(optdef.readonly ? 1 : 0) ); + (void)hv_stores( hv, "height", newSViv(optdef.height) ); + (void)hv_stores( hv, "width", newSViv(optdef.width) ); + (void)hv_stores( hv, "min", newSViv(optdef.min) ); + (void)hv_stores( hv, "max", newSViv(optdef.max) ); // aliases - if (!optdef->aliases.empty()) { + if (!optdef.aliases.empty()) { AV* av = newAV(); - av_fill(av, optdef->aliases.size()-1); - for (std::vector::iterator it = optdef->aliases.begin(); it != optdef->aliases.end(); ++it) - av_store(av, it - optdef->aliases.begin(), newSVpvn(it->c_str(), it->length())); + av_fill(av, optdef.aliases.size()-1); + for (std::vector::const_iterator it = optdef.aliases.begin(); it != optdef.aliases.end(); ++it) + av_store(av, it - optdef.aliases.begin(), newSVpvn(it->c_str(), it->length())); (void)hv_stores( hv, "aliases", newRV_noinc((SV*)av) ); } // shortcut - if (!optdef->shortcut.empty()) { + if (!optdef.shortcut.empty()) { AV* av = newAV(); - av_fill(av, optdef->shortcut.size()-1); - for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) - av_store(av, it - optdef->shortcut.begin(), newSVpvn(it->c_str(), it->length())); + av_fill(av, optdef.shortcut.size()-1); + for (std::vector::const_iterator it = optdef.shortcut.begin(); it != optdef.shortcut.end(); ++it) + av_store(av, it - optdef.shortcut.begin(), newSVpvn(it->c_str(), it->length())); (void)hv_stores( hv, "shortcut", newRV_noinc((SV*)av) ); } // enum_values - if (!optdef->enum_values.empty()) { + if (!optdef.enum_values.empty()) { AV* av = newAV(); - av_fill(av, optdef->enum_values.size()-1); - for (std::vector::iterator it = optdef->enum_values.begin(); it != optdef->enum_values.end(); ++it) - av_store(av, it - optdef->enum_values.begin(), newSVpvn(it->c_str(), it->length())); + av_fill(av, optdef.enum_values.size()-1); + for (std::vector::const_iterator it = optdef.enum_values.begin(); it != optdef.enum_values.end(); ++it) + av_store(av, it - optdef.enum_values.begin(), newSVpvn(it->c_str(), it->length())); (void)hv_stores( hv, "values", newRV_noinc((SV*)av) ); } // enum_labels - if (!optdef->enum_labels.empty()) { + if (!optdef.enum_labels.empty()) { AV* av = newAV(); - av_fill(av, optdef->enum_labels.size()-1); - for (std::vector::iterator it = optdef->enum_labels.begin(); it != optdef->enum_labels.end(); ++it) - av_store(av, it - optdef->enum_labels.begin(), newSVpvn_utf8(it->c_str(), it->length(), true)); + av_fill(av, optdef.enum_labels.size()-1); + for (std::vector::const_iterator it = optdef.enum_labels.begin(); it != optdef.enum_labels.end(); ++it) + av_store(av, it - optdef.enum_labels.begin(), newSVpvn_utf8(it->c_str(), it->length(), true)); (void)hv_stores( hv, "labels", newRV_noinc((SV*)av) ); } - if (optdef->default_value != NULL) - (void)hv_stores( hv, "default", ConfigOption_to_SV(*optdef->default_value, *optdef) ); + if (optdef.default_value != NULL) + (void)hv_stores( hv, "default", ConfigOption_to_SV(*optdef.default_value, optdef) ); (void)hv_store( options_hv, opt_key.c_str(), opt_key.length(), newRV_noinc((SV*)hv), 0 ); } diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 11b728a91..290265b19 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -37,7 +37,7 @@ SV* ExtrusionLoop::arrayref() CODE: AV* av = newAV(); - av_fill(av, THIS->paths.size()-1); + if (!THIS->paths.empty()) av_extend(av, THIS->paths.size()-1); for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) { av_store(av, it - THIS->paths.begin(), perl_to_SV_ref(*it)); } diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 0c5c08b6c..3208ac29a 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -78,8 +78,3 @@ new_from_type(CLASS, type) %} }; - -%package{Slic3r::Filler}; - -coord_t adjust_solid_spacing(coord_t width, coord_t distance) - %code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %}; diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index d09f0b351..05cc828b9 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -81,3 +81,6 @@ _constant() %} +coord_t solid_spacing(coord_t width, coord_t distance) + %code{% RETVAL = Flow::solid_spacing(width, distance); %}; + diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 01ba73bab..21c1e9113 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -253,6 +253,15 @@ ModelMaterial::attributes() void set_material_id(t_model_material_id material_id) %code%{ THIS->material_id(material_id); %}; Ref material(); + + Clone bounding_box() + %code%{ + try { + RETVAL = THIS->mesh.bounding_box(); + } catch (std::exception& e) { + croak("%s", e.what()); + } + %}; Ref config() %code%{ RETVAL = &THIS->config; %}; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 373cba12e..47f7c7558 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -41,6 +41,7 @@ %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); + void reverse_normals(); %{