From 488cc02f531d63e8d87a3a572853a391ae8a7647 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 12:49:25 +0100 Subject: [PATCH 01/21] Try to fix OctoPrint issues. #3789 --- lib/Slic3r/GUI/Plater.pm | 87 +++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b7393c0af..d7bb3195d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -256,43 +256,10 @@ sub new { EVT_LEFT_UP($self->{btn_send_gcode}, sub { my (undef, $e) = @_; - my $filename = basename($self->{print}->output_filepath($main::opt{output} // '')); - - if (!$e->AltDown) { - # When the alt key is pressed, bypass the dialog. - my $dlg = Slic3r::GUI::Plater::OctoPrintSpoolDialog->new($self, $filename); - return unless $dlg->ShowModal == wxID_OK; - $filename = $dlg->{filename}; - } - - if (!$Slic3r::GUI::Settings->{octoprint}{overwrite}) { - my $progress = Wx::ProgressDialog->new('Querying OctoPrint…', - "Checking whether file already exists…", 100, $self, 0); - $progress->Pulse; - - my $ua = LWP::UserAgent->new; - $ua->timeout(5); - my $res = $ua->get("http://" . $self->{config}->octoprint_host . "/api/files/local"); - $progress->Destroy; - if ($res->is_success) { - if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { - my $dialog = Wx::MessageDialog->new($self, - "It looks like a file with the same name already exists in the server. " - . "Shall I overwrite it?", - 'OctoPrint', wxICON_WARNING | wxYES | wxNO); - if ($dialog->ShowModal() == wxID_NO) { - return; - } - } - } else { - my $message = "Error while connecting to the OctoPrint server: " . $res->status_line; - Slic3r::GUI::show_error($self, $message); - return; - } - } - - $self->{send_gcode_file_print} = $Slic3r::GUI::Settings->{octoprint}{start}; - $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/" . $filename); + my $alt = $e->AltDown; + wxTheApp->CallAfter(sub { + $self->prepare_send($alt); + }); }); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); @@ -1452,6 +1419,52 @@ sub do_print { $self->GetFrame->select_tab(1); } +sub prepare_send { + my ($self, $skip_dialog) = @_; + + return if !$self->{btn_send_gcode}->IsEnabled; + my $filename = basename($self->{print}->output_filepath($main::opt{output} // '')); + + if (!$skip_dialog) { + # When the alt key is pressed, bypass the dialog. + my $dlg = Slic3r::GUI::Plater::OctoPrintSpoolDialog->new($self, $filename); + return unless $dlg->ShowModal == wxID_OK; + $filename = $dlg->{filename}; + } + + if (!$Slic3r::GUI::Settings->{octoprint}{overwrite}) { + my $progress = Wx::ProgressDialog->new('Querying OctoPrint…', + "Checking whether file already exists…", 100, $self, 0); + $progress->Pulse; + + my $ua = LWP::UserAgent->new; + $ua->timeout(5); + my $res = $ua->get( + "http://" . $self->{config}->octoprint_host . "/api/files/local", + 'X-Api-Key' => $self->{config}->octoprint_apikey, + ); + $progress->Destroy; + if ($res->is_success) { + if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { + my $dialog = Wx::MessageDialog->new($self, + "It looks like a file with the same name already exists in the server. " + . "Shall I overwrite it?", + 'OctoPrint', wxICON_WARNING | wxYES | wxNO); + if ($dialog->ShowModal() == wxID_NO) { + return; + } + } + } else { + my $message = "Error while connecting to the OctoPrint server: " . $res->status_line; + Slic3r::GUI::show_error($self, $message); + return; + } + } + + $self->{send_gcode_file_print} = $Slic3r::GUI::Settings->{octoprint}{start}; + $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/" . $filename); +} + sub send_gcode { my ($self) = @_; From 0de3a72eb40a408d64ecfb5fa7ca461718fc4ba8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 12:59:05 +0100 Subject: [PATCH 02/21] Bugfix: canceling the "Set Copies" dialog didn't work. #3787 --- lib/Slic3r/GUI/Plater.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d7bb3195d..30745ab80 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -840,6 +840,7 @@ sub set_number_of_copies { # prompt user my $copies = Wx::GetNumberFromUser("", "Enter the number of copies of the selected object:", "Copies", $model_object->instances_count, 0, 1000, $self); + return if $copies == -1; my $diff = $copies - $model_object->instances_count; if ($diff == 0) { # no variation From 84a5075fceccddd7a24fda88200a2f2e2f620b98 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 13:57:18 +0100 Subject: [PATCH 03/21] Bugfix: excessive copies were created when converting a multi-object AMF file into a multi-part object. #3788 --- lib/Slic3r/Model.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 99c69cd4a..0b6cefd75 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -74,7 +74,7 @@ sub convert_multipart_object { my $volume = $object->add_volume($v); $volume->set_name($v->object->name); } - $object->add_instance($_) for map @{$_->instances}, @objects; + $object->add_instance($_) for @{$objects[0]->instances}; $self->delete_object($_) for reverse 0..($self->objects_count-2); } From bd2117d3462965d7c508b646b3d03235024a36f1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 14:10:41 +0100 Subject: [PATCH 04/21] Align objects imported from AMF to ground. #3785 --- lib/Slic3r/GUI/Plater.pm | 3 +++ xs/src/libslic3r/Model.cpp | 14 ++++++++++++++ xs/src/libslic3r/Model.hpp | 1 + xs/xsp/Model.xsp | 1 + 4 files changed, 19 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 30745ab80..d43f1cf52 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -671,6 +671,9 @@ sub load_model_objects { # add a default instance and center object around origin $o->center_around_origin; # also aligns object to Z = 0 $o->add_instance(offset => $bed_centerf); + } else { + # if object has defined positions we still need to ensure it's aligned to Z = 0 + $o->align_to_ground; } { diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 9182cf56b..b1694190e 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -597,6 +597,20 @@ ModelObject::instance_bounding_box(size_t instance_idx) const return bb; } +void +ModelObject::align_to_ground() +{ + // calculate the displacements needed to + // center this object around the origin + BoundingBoxf3 bb; + for (const ModelVolume* v : this->volumes) + if (!v->modifier) + bb.merge(v->mesh.bounding_box()); + + this->translate(0, 0, -bb.min.z); + this->origin_translation.translate(0, 0, -bb.min.z); +} + void ModelObject::center_around_origin() { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 9c8e0d1e2..1b6c6d6c2 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -149,6 +149,7 @@ class ModelObject TriangleMesh raw_mesh() const; BoundingBoxf3 raw_bounding_box() const; BoundingBoxf3 instance_bounding_box(size_t instance_idx) const; + void align_to_ground(); void center_around_origin(); void translate(const Vectorf3 &vector); void translate(coordf_t x, coordf_t y, coordf_t z); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index c5c700606..53f6cc8f3 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -207,6 +207,7 @@ ModelMaterial::attributes() bool needed_repair() const; int materials_count() const; int facets_count(); + void align_to_ground(); void center_around_origin(); void translate(double x, double y, double z); void scale_xyz(Pointf3* versor) From 3ee628c29f1168f56896555f09a1f29dc9b53ac6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 14:23:28 +0100 Subject: [PATCH 05/21] Ported couple Model methods to C++ --- lib/Slic3r/Model.pm | 27 --------------------------- xs/src/libslic3r/Model.cpp | 38 ++++++++++++++++++++++++++++++++++++++ xs/src/libslic3r/Model.hpp | 2 ++ xs/xsp/Model.xsp | 2 ++ 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 0b6cefd75..cba64323a 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -52,33 +52,6 @@ sub set_material { return $material; } -sub looks_like_multipart_object { - my ($self) = @_; - - return 0 if $self->objects_count == 1; - return 0 if any { $_->volumes_count > 1 } @{$self->objects}; - return 0 if any { @{$_->config->get_keys} > 1 } @{$self->objects}; - - my %heights = map { $_ => 1 } map $_->mesh->bounding_box->z_min, map @{$_->volumes}, @{$self->objects}; - return scalar(keys %heights) > 1; -} - -sub convert_multipart_object { - my ($self) = @_; - - my @objects = @{$self->objects}; - my $object = $self->add_object( - input_file => $objects[0]->input_file, - ); - foreach my $v (map @{$_->volumes}, @objects) { - my $volume = $object->add_volume($v); - $volume->set_name($v->object->name); - } - $object->add_instance($_) for @{$objects[0]->instances}; - - $self->delete_object($_) for reverse 0..($self->objects_count-2); -} - # Extends C++ class Slic3r::ModelMaterial package Slic3r::Model::Material; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index b1694190e..5243799ca 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -2,6 +2,7 @@ #include "Geometry.hpp" #include "IO.hpp" #include +#include #include #include @@ -372,6 +373,43 @@ Model::print_info() const (*o)->print_info(); } +bool +Model::looks_like_multipart_object() const +{ + if (this->objects.size() == 1) return false; + for (const ModelObject* o : this->objects) { + if (o->volumes.size() > 1) return false; + if (o->config.keys().size() > 1) return false; + } + + std::set heights; + for (const ModelObject* o : this->objects) + for (const ModelVolume* v : o->volumes) + heights.insert(v->mesh.bounding_box().min.z); + return heights.size() > 1; +} + +void +Model::convert_multipart_object() +{ + if (this->objects.empty()) return; + + ModelObject* object = this->add_object(); + object->input_file = this->objects.front()->input_file; + + for (const ModelObject* o : this->objects) { + for (const ModelVolume* v : o->volumes) { + ModelVolume* v2 = object->add_volume(*v); + v2->name = o->name; + } + } + for (const ModelInstance* i : this->objects.front()->instances) + object->add_instance(*i); + + while (this->objects.size() > 1) + this->delete_object(0); +} + ModelMaterial::ModelMaterial(Model *model) : model(model) {} ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), model(model) diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 1b6c6d6c2..36571ff5d 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -74,6 +74,8 @@ class Model void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); void print_info() const; + bool looks_like_multipart_object() const; + void convert_multipart_object(); }; // Material, which may be shared across multiple ModelObjects of a single Model. diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 53f6cc8f3..cb415ad14 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -95,6 +95,8 @@ void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); void print_info(); + bool looks_like_multipart_object(); + void convert_multipart_object(); void repair(); }; From b755e2424f30af3b856593102ecc3d0b7900c0dc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 14:26:56 +0100 Subject: [PATCH 06/21] Removed warning --- xs/t/15_config.t | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 214baad9f..f6efc8a5d 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -286,6 +286,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is_deeply $config->get('retract_speed'), [0.4, 0.5], 'read_cli(): floats array'; } { + no warnings 'qw'; my $config = $parse->(qw(--extruder-offset 0,0 --extruder-offset 10x5)); is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[0,0], [10,5]], 'read_cli(): points array'; From e359cd0a4a2254526b9f3d08ff5773122731dff1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 14:46:45 +0100 Subject: [PATCH 07/21] Support Mac Retina displays --- package/osx/plist.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/osx/plist.sh b/package/osx/plist.sh index 898ae3be2..38e03edbc 100644 --- a/package/osx/plist.sh +++ b/package/osx/plist.sh @@ -91,6 +91,8 @@ cat << EOF >> $plistfile LSMinimumSystemVersion 10.7 + NSPrincipalClass + NSApplication EOF From 48910a79708e9001e73529787634a289ac304d4e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 15:08:00 +0100 Subject: [PATCH 08/21] Remove the stable branch from Travis and add IRC notifications --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2d84fd28d..5e6930fbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ perl: branches: only: - master - - stable sudo: false cache: apt: true @@ -26,3 +25,10 @@ addons: - liblocal-lib-perl - g++-4.9 env: CC=g++-4.9 +notifications: + irc: + channels: + - "chat.freenode.net#slic3r" + on_success: change + on_failure: always + use_notice: true From 1843e377d3279a31ff0dfd1c312e295680489ced Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Mar 2017 17:07:11 +0100 Subject: [PATCH 09/21] Bugfix: missing infill segments in some rare circumstances --- t/fill.t | 19 ++++++++++++++++++- xs/src/libslic3r/Fill/FillRectilinear.cpp | 11 ++++++++--- xs/src/libslic3r/LayerRegionFill.cpp | 6 ++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/t/fill.t b/t/fill.t index c9cb21391..710e60536 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 93; +plan tests => 95; BEGIN { use FindBin; @@ -86,6 +86,23 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } 'paths don\'t cross hole') or done_testing, exit; } } + + # rotated square + $filler->set_angle(PI/4); + $filler->set_dont_adjust(0); + $filler->set_min_spacing(0.654498); + $filler->set_endpoints_overlap(unscale(359974)); + $filler->set_density(1); + $filler->set_layer_id(66); + $filler->set_z(20.15); + { + my $e = Slic3r::ExPolygon->new( + Slic3r::Polygon->new([25771516,14142125],[14142138,25771515],[2512749,14142131],[14142125,2512749]), + ); + my $paths = $test->($e); + is(scalar @$paths, 1, 'one continuous path') or done_testing, exit; + ok abs($paths->[0]->length - scale(3*100 + 2*50)) - scaled_epsilon, 'path has expected length'; + } } { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index c74da2877..e02c3a298 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -37,9 +37,14 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, if (bounding_box.size().x < min_spacing) return; // Due to integer rounding, rotated polygons might not preserve verticality - // (i.e. when rotating by PI/2 two points having the same x coordinate - // they might get different y coordinates), thus the first line will be skipped. - bounding_box.offset(-1); + // (i.e. when rotating by PI/2 two points having the same y coordinate + // they might get different x coordinates), thus the first line will be skipped. + // Reducing by 1 is not enough, as we observed normal squares being off by about 30 + // units along x between points supposed to be vertically aligned (coming from an + // axis-aligned polygon edge). We need to be very tolerant here, especially when + // making solid infill where lack of lines is visible. + bounding_box.min.x += SCALED_EPSILON; + bounding_box.max.x -= SCALED_EPSILON; // define flow spacing according to requested density if (this->density > 0.9999f && !this->dont_adjust) { diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 954f1d135..d6b567ceb 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -246,6 +246,12 @@ LayerRegion::make_fill() // apply half spacing using this flow's own spacing and generate infill f->density = density/100; f->dont_adjust = false; + /* + std::cout << surface.expolygon.dump_perl() << std::endl + << " layer_id: " << f->layer_id << " z: " << f->z + << " angle: " << f->angle << " min-spacing: " << f->min_spacing + << " endpoints_overlap: " << f->endpoints_overlap << std::endl << std::endl; + */ Polylines polylines = f->fill_surface(surface); if (polylines.empty()) continue; From aa02dfe21434d22455440582bbaf3491a49be1fe Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 23 Mar 2017 15:48:53 -0500 Subject: [PATCH 10/21] working on rc file to define manifest and filetype (#3797) * working on rc file to define manifest and filetype * use number 24 instead of name manifest --- package/win/slic3r.rc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package/win/slic3r.rc b/package/win/slic3r.rc index 20cfa265e..290d51939 100644 --- a/package/win/slic3r.rc +++ b/package/win/slic3r.rc @@ -2,6 +2,8 @@ id ICON "../../var/Slic3r.ico" 1 VERSIONINFO FILEVERSION 1,3,0,0 PRODUCTVERSION 1,3,0,0 +FILEOS 0x4 +FILETYPE 0x1 BEGIN BLOCK "StringFileInfo" BEGIN @@ -10,7 +12,7 @@ BEGIN VALUE "CompanyName", "Slic3r.org" VALUE "FileDescription", "3D Printer Slicer application" VALUE "FileVersion", "1.3.0" - VALUE "InternalName", "slic3r" + VALUE "InternalName", "slic3r.exe" VALUE "LegalCopyright", "Alessandro Ranellucci" VALUE "OriginalFilename", "slic3r.exe" VALUE "ProductName", "Slic3r" @@ -22,4 +24,4 @@ BEGIN VALUE "Translation", 0x409, 1252 END END -1 Manifest slic3r.exe.manifest +1 24 "slic3r.exe.manifest" From 8f27e8f1c0a064ead20d53dc7f9051e8adaf3b2c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 24 Mar 2017 00:55:24 +0100 Subject: [PATCH 11/21] Some updates to the README --- README.md | 142 ++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 57010dd4c..23b74dc6f 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,50 @@ -_Q: Oh cool, a new RepRap slicer?_ - -A: Yes. - -Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.svg?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) [![Build Status](http://osx-build.slic3r.org:8080/buildStatus/icon?job=Slic3r)](http://osx-build.slic3r.org:8080/job/Slic3r) +![](var/Slic3r_128px.png) Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.svg?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) [![Build Status](http://osx-build.slic3r.org:8080/buildStatus/icon?job=Slic3r)](http://osx-build.slic3r.org:8080/job/Slic3r) ====== -Prebuilt Windows (64-bit) and OSX (>10.7) builds: -* http://dl.slic3r.org/dev/ - +We have automated builds for Windows (64-bit) and OSX (>= 10.7). [Get a fresh build now](http://dl.slic3r.org/dev/) and stay up-to-date with the development! -Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for -3D printers. It's compatible with any modern printer based on the RepRap toolchain, -including all those based on the Marlin, Sprinter and Repetier firmware. It also works -with Mach3, LinuxCNC and Machinekit controllers. +The MacOS X build server is kindly sponsored by: -See the [project homepage](http://slic3r.org/) at slic3r.org and the -[manual](http://manual.slic3r.org/) for more information. +### So, what's this Slic3r? + +Slic3r is mainly a **toolpath generator** for 3D printers: it reads 3D models (STL, OBJ, AMF) and it converts them into **G-code** instructions for 3D printers. But it does much more than that, see the [features list](#feature) below. + +Slic3r was born in **2011** within the RepRap community and thanks to its high configurability became the swiss-army knife for 3D printing. It served as a platform for experimenting several **new ideas that later became technology standards**, such as multiple extruders, brim, variable-height layers, per-object settings, modifiers, post-processing scripts, G-code macros and more. Despite being based on volunteer efforts, Slic3r is still pushing the boundaries of 3D printing. + +Slic3r is: + +* **Open:** it is totally **open source** and it's **independent from any commercial company** or printer manufacturer. We want to keep 3D printing open and free. +* **Compatible:** it supports all the known G-code dialects (Marlin, Repetier, Mach3, LinuxCNC, Machinekit, Smoothie, Makerware, Sailfish). +* **Advanced:** many configuration options allow for fine-tuning and full control. While novice users often need just few options, Slic3r is mostly used by advanced users. +* **Community-driven:** new features or issues are discussed in the [GitHub repository](https://github.com/alexrj/Slic3r/issues). Join our collaborative effort and help improve it! +* **Robust:** the codebase includes more than 1,000 unit and regression tests, collected in 6 years of development. +* **Modular:** the core of Slic3r is libslic3r, a C++ library that provides a granular API and reusable components. +* **Embeddable:** a complete and powerful command line interface allows to use Slic3r from the shell or to integrate it in server-side applications. +* **Powerful:** see the list below! + +See the [project homepage](http://slic3r.org/) at slic3r.org for more information. + +### Features + +(Most of these are also available in the command line interface.) + +* **G-code generation** for FFF/FDM printers; +* **conversion** between STL, OBJ, AMF and POV formats; +* **auto-repair** of non-manifold meshes (and ability to re-export them); +* **SVG export** of slices; +* built-in **USB/serial** host controller, supporting **multiple simultaneous printers** each one with a spool queue; +* **OctoPrint integration** (send to printer); +* built-in **projector and host for DLP printers**; +* tool for **cutting meshes** in multiple solid parts with visual preview (also in batch using a grid); +* tool for **extruding 2.5D TIN meshes**. ### What language is it written in? -The core geometric algorithms and data structures are written in C++, -and Perl is used for high-level flow abstraction, GUI and testing. -If you're wondering why Perl, see https://xkcd.com/224/ - -The C++ API is public and its use in other projects is encouraged. -The goal is to make Slic3r fully modular so that any part of its logic -can be used separately. - -### What are Slic3r's main features? - -Key features are: - -* **multi-platform** (Linux/Mac/Win) and packaged as standalone-app with no dependencies required -* complete **command-line interface** to use it with no GUI -* multi-material **(multiple extruders)** object printing -* multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.) -* ability to plate **multiple objects having distinct print settings** -* **multithread** processing -* **STL auto-repair** (tolerance for broken models) -* wide automated unit testing - -Other major features are: - -* combine infill every 'n' perimeters layer to speed up printing -* **3D preview** (including multi-material files) -* **multiple layer heights** in a single print -* **spiral vase** mode for bumpless vases -* fine-grained configuration of speed, acceleration, extrusion width -* several infill patterns including honeycomb, spirals, Hilbert curves -* support material, raft, brim, skirt -* **standby temperature** and automatic wiping for multi-extruder printing -* customizable **G-code macros** and output filename with variable placeholders -* support for **post-processing scripts** -* **cooling logic** controlling fan speed and dynamic print speed +The core parts of Slic3r are written in C++11, with multithreading. The graphical interface is still mostly written in Perl, but we're gradually porting it to C++ (want to help?). The goal is to port everything to C++. ### How to install? -You can download a precompiled package from [slic3r.org](http://slic3r.org/); -it will run without the need for any dependency. +You can download a precompiled package from [slic3r.org](http://slic3r.org/) (releases) or from [dl.slicr3r.org](http://dl.slic3r.org/dev/) (automated builds). If you want to compile the source yourself follow the instructions on one of these wiki pages: * [Linux](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-GNU-Linux) @@ -67,28 +54,40 @@ If you want to compile the source yourself follow the instructions on one of the ### Can I help? Sure! You can do the following to find things that are available to help with: -* [Pull Request Milestone](https://github.com/alexrj/Slic3r/milestone/31) - * Please comment in the related github issue that you are working on it so that other people know. -* Items in the [TODO](https://github.com/alexrj/Slic3r/wiki/TODO) wiki page. - * Please comment in the related github issue that you are working on it so that other people know. -* Drop me a line at aar@cpan.org. -* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](https://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels. -* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the github tracker if it isn't already present. -Before sending patches and pull requests contact me (preferably through opening a github issue or commenting on an existing, related, issue) to discuss your proposed -changes: this way we'll ensure nobody wastes their time and no conflicts arise -in development. +* Development + * [Low Effort tasks](https://github.com/alexrj/Slic3r/labels/Low%20Effort): pick one of them! + * [More available tasks](https://github.com/alexrj/Slic3r/milestone/31): let's discuss together before you start working on them + * Please comment in the related github issue that you are working on it so that other people know. + * Please comment in the related GitHub issue that you are working on it so that other people know. +* Contribute to the [Manual](http://manual.slic3r.org/)! (see its [GitHub repository](https://github.com/alexrj/Slic3r-Manual)) +* You can also find us in #slic3r on [FreeNode](https://webchat.freenode.net): talk to _Sound_, _LoH_ or the other members of the Slic3r community. +* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the GitHub tracker if it isn't already present. +* Drop Alessandro a line at aar@cpan.org. -### What's Slic3r license? +### Directory structure -Slic3r is licensed under the _GNU Affero General Public License, version 3_. -The author is Alessandro Ranellucci. +* `Build.PL`: this script installs dependencies into `local-lib/`, compiles the C++ part and runs tests +* `lib/`: the Perl code +* `package/`: the scripts used for packaging the executables +* `slic3r.pl`: the main executable script, launching the GUI and providing the CLI +* `src/`: the C++ source of the `slic3r` executable the and CMake definition file for compiling it (note that this C++ `slic3r` executable can do many things but can't generate G-code yet because the porting isn't finished yet - the main executable is `slic3r.pl`) +* `t/`: the test suite +* `utils/`: various useful scripts +* `xs/src/libslic3r/`: C++ sources for libslic3r +* `xs/src/slic3r/`: C++ sources for the Slic3r GUI application +* `xs/t/`: test suite for libslic3r +* `xs/xsp/`: bindings for calling libslic3r from Perl (XS) -The [Silk icon set](http://www.famfamfam.com/lab/icons/silk/) used in Slic3r is -licensed under the _Creative Commons Attribution 3.0 License_. -The author of the Silk icon set is Mark James. +### Acknowledgements -### How can I invoke slic3r.pl using the command line? +The main author of Slic3r is Alessandro Ranellucci (@alexrj, *Sound* in IRC, [@alranel](http://twitter.com/alranel) on Twitter), who started the project in 2011 and still leads development. + +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. + +### How can I invoke Slic3r using the command line? Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ... @@ -384,11 +383,4 @@ The author of the Silk icon set is Mark James. Temperature difference to be applied when an extruder is not active and --ooze-prevention is enabled (default: -5) - -If you want to change a preset file, just do - - slic3r.pl --load config.ini --layer-height 0.25 --save config.ini - -If you want to slice a file overriding an option contained in your preset file: - - slic3r.pl --load config.ini --layer-height 0.25 file.stl +For more information about command line usage see the relevant [manual page](http://manual.slic3r.org/advanced/command-line). From 5d2626fd3a35ba2093f0773d460dcb8dcf662ccb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 24 Mar 2017 00:57:07 +0100 Subject: [PATCH 12/21] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23b74dc6f..926126bde 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The MacOS X build server is kindly sponsored by: Date: Fri, 24 Mar 2017 17:54:02 +0100 Subject: [PATCH 13/21] New GCodeTimeEstimator class, with basic estimation code by @lordofhyphens. #3747 --- src/CMakeLists.txt | 1 + utils/estimate-gcode-time.pl | 21 +++++ xs/MANIFEST | 3 + xs/src/libslic3r/GCode/SpiralVase.cpp | 2 +- xs/src/libslic3r/GCodeReader.cpp | 117 +++++++++++++----------- xs/src/libslic3r/GCodeReader.hpp | 5 +- xs/src/libslic3r/GCodeTimeEstimator.cpp | 78 ++++++++++++++++ xs/src/libslic3r/GCodeTimeEstimator.hpp | 24 +++++ xs/src/perlglue.cpp | 1 + xs/xsp/GCodeTimeEstimator.xsp | 15 +++ xs/xsp/my.map | 4 + 11 files changed, 218 insertions(+), 53 deletions(-) create mode 100755 utils/estimate-gcode-time.pl create mode 100644 xs/src/libslic3r/GCodeTimeEstimator.cpp create mode 100644 xs/src/libslic3r/GCodeTimeEstimator.hpp create mode 100644 xs/xsp/GCodeTimeEstimator.xsp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 85e98471e..14f4a657a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/GCode/SpiralVase.cpp ${LIBDIR}/libslic3r/GCodeReader.cpp ${LIBDIR}/libslic3r/GCodeSender.cpp + ${LIBDIR}/libslic3r/GCodeTimeEstimator.cpp ${LIBDIR}/libslic3r/GCodeWriter.cpp ${LIBDIR}/libslic3r/Geometry.cpp ${LIBDIR}/libslic3r/IO.cpp diff --git a/utils/estimate-gcode-time.pl b/utils/estimate-gcode-time.pl new file mode 100755 index 000000000..43c558b68 --- /dev/null +++ b/utils/estimate-gcode-time.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; +} + +use Slic3r; + +die "Usage: estimate-gcode-time.pl FILE\n" + if @ARGV != 1; + +my $estimator = Slic3r::GCode::TimeEstimator->new; +$estimator->parse_file($ARGV[0]); +printf "Time: %d minutes and %d seconds\n", int($estimator->time / 60), $estimator->time % 60; + +__END__ diff --git a/xs/MANIFEST b/xs/MANIFEST index 61a97afed..5b2925f2c 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -72,6 +72,8 @@ src/libslic3r/GCodeReader.cpp src/libslic3r/GCodeReader.hpp src/libslic3r/GCodeSender.cpp src/libslic3r/GCodeSender.hpp +src/libslic3r/GCodeTimeEstimator.cpp +src/libslic3r/GCodeTimeEstimator.hpp src/libslic3r/GCodeWriter.cpp src/libslic3r/GCodeWriter.hpp src/libslic3r/Geometry.cpp @@ -180,6 +182,7 @@ xsp/Filler.xsp xsp/Flow.xsp xsp/GCode.xsp xsp/GCodeSender.xsp +xsp/GCodeTimeEstimator.xsp xsp/GCodeWriter.xsp xsp/Geometry.xsp xsp/GUI.xsp diff --git a/xs/src/libslic3r/GCode/SpiralVase.cpp b/xs/src/libslic3r/GCode/SpiralVase.cpp index b56c9e58b..9299da86a 100644 --- a/xs/src/libslic3r/GCode/SpiralVase.cpp +++ b/xs/src/libslic3r/GCode/SpiralVase.cpp @@ -39,7 +39,7 @@ SpiralVase::process_layer(const std::string &gcode) { GCodeReader r = this->_reader; // clone r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z] - (GCodeReader &, GCodeReader::GCodeLine &line) { + (GCodeReader &, const GCodeReader::GCodeLine &line) { if (line.cmd == "G1") { if (line.extruding()) { total_layer_length += line.dist_XY(); diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp index 1a0742864..6ad937bbd 100644 --- a/xs/src/libslic3r/GCodeReader.cpp +++ b/xs/src/libslic3r/GCodeReader.cpp @@ -1,6 +1,7 @@ #include "GCodeReader.hpp" #include #include +#include #include namespace Slic3r { @@ -17,59 +18,73 @@ GCodeReader::parse(const std::string &gcode, callback_t callback) { std::istringstream ss(gcode); std::string line; - while (std::getline(ss, line)) { - GCodeLine gline(this); - gline.raw = line; - if (this->verbose) - std::cout << line << std::endl; - - // strip comment - { - size_t pos = line.find(';'); - if (pos != std::string::npos) { - gline.comment = line.substr(pos+1); - line.erase(pos); - } - } - - // command and args - { - std::vector args; - boost::split(args, line, boost::is_any_of(" ")); - - // first one is cmd - gline.cmd = args.front(); - args.erase(args.begin()); - - for (std::string &arg : args) { - if (arg.size() < 2) continue; - gline.args.insert(std::make_pair(arg[0], arg.substr(1))); - } - } - - // convert extrusion axis - if (this->_extrusion_axis != 'E') { - const auto it = gline.args.find(this->_extrusion_axis); - if (it != gline.args.end()) { - std::swap(gline.args['E'], it->second); - gline.args.erase(it); - } - } - - if (gline.has('E') && this->_config.use_relative_e_distances) - this->E = 0; - - if (callback) callback(*this, gline); - - // update coordinates - if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") { - this->X = gline.new_X(); - this->Y = gline.new_Y(); - this->Z = gline.new_Z(); - this->E = gline.new_E(); - this->F = gline.new_F(); + while (std::getline(ss, line)) + this->parse_line(line, callback); +} + +void +GCodeReader::parse_line(std::string line, callback_t callback) +{ + GCodeLine gline(this); + gline.raw = line; + if (this->verbose) + std::cout << line << std::endl; + + // strip comment + { + size_t pos = line.find(';'); + if (pos != std::string::npos) { + gline.comment = line.substr(pos+1); + line.erase(pos); } } + + // command and args + { + std::vector args; + boost::split(args, line, boost::is_any_of(" ")); + + // first one is cmd + gline.cmd = args.front(); + args.erase(args.begin()); + + for (std::string &arg : args) { + if (arg.size() < 2) continue; + gline.args.insert(std::make_pair(arg[0], arg.substr(1))); + } + } + + // convert extrusion axis + if (this->_extrusion_axis != 'E') { + const auto it = gline.args.find(this->_extrusion_axis); + if (it != gline.args.end()) { + std::swap(gline.args['E'], it->second); + gline.args.erase(it); + } + } + + if (gline.has('E') && this->_config.use_relative_e_distances) + this->E = 0; + + if (callback) callback(*this, gline); + + // update coordinates + if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") { + this->X = gline.new_X(); + this->Y = gline.new_Y(); + this->Z = gline.new_Z(); + this->E = gline.new_E(); + this->F = gline.new_F(); + } +} + +void +GCodeReader::parse_file(const std::string &file, callback_t callback) +{ + std::ifstream f(file); + std::string line; + while (std::getline(f, line)) + this->parse_line(line, callback); } void diff --git a/xs/src/libslic3r/GCodeReader.hpp b/xs/src/libslic3r/GCodeReader.hpp index 267ac0e18..c627fc851 100644 --- a/xs/src/libslic3r/GCodeReader.hpp +++ b/xs/src/libslic3r/GCodeReader.hpp @@ -25,6 +25,7 @@ class GCodeReader { GCodeLine(GCodeReader* _reader) : reader(_reader) {}; bool has(char arg) const { return this->args.count(arg) > 0; }; + float get_float(char arg) const { return atof(this->args.at(arg).c_str()); }; float new_X() const { return this->has('X') ? atof(this->args.at('X').c_str()) : this->reader->X; }; float new_Y() const { return this->has('Y') ? atof(this->args.at('Y').c_str()) : this->reader->Y; }; float new_Z() const { return this->has('Z') ? atof(this->args.at('Z').c_str()) : this->reader->Z; }; @@ -44,7 +45,7 @@ class GCodeReader { bool travel() const { return this->cmd == "G1" && !this->has('E'); }; void set(char arg, std::string value); }; - typedef std::function callback_t; + typedef std::function callback_t; float X, Y, Z, E, F; bool verbose; @@ -53,6 +54,8 @@ class GCodeReader { GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {}; void apply_config(const PrintConfigBase &config); void parse(const std::string &gcode, callback_t callback); + void parse_line(std::string line, callback_t callback); + void parse_file(const std::string &file, callback_t callback); private: GCodeConfig _config; diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp new file mode 100644 index 000000000..c6fa353b4 --- /dev/null +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -0,0 +1,78 @@ +#include "GCodeTimeEstimator.hpp" +#include +#include + +namespace Slic3r { + +void +GCodeTimeEstimator::parse(const std::string &gcode) +{ + GCodeReader::parse(gcode, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); +} + +void +GCodeTimeEstimator::parse_file(const std::string &file) +{ + GCodeReader::parse_file(file, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); +} + +void +GCodeTimeEstimator::_parser(GCodeReader&, const GCodeReader::GCodeLine &line) +{ + // std::cout << "[" << this->time << "] " << line.raw << std::endl; + if (line.cmd == "G1") { + const float dist_XY = line.dist_XY(); + const float new_F = line.new_F(); + + if (dist_XY > 0) { + //this->time += dist_XY / new_F * 60; + this->time += _accelerated_move(dist_XY, new_F/60, this->acceleration); + } else { + //this->time += std::abs(line.dist_E()) / new_F * 60; + this->time += _accelerated_move(std::abs(line.dist_E()), new_F/60, this->acceleration); + } + //this->time += std::abs(line.dist_Z()) / new_F * 60; + this->time += _accelerated_move(std::abs(line.dist_Z()), new_F/60, this->acceleration); + } else if (line.cmd == "M204" && line.has('S')) { + this->acceleration = line.get_float('S'); + } else if (line.cmd == "G4") { // swell + if (line.has('S')) { + this->time += line.get_float('S'); + } else if (line.has('P')) { + this->time += line.get_float('P')/1000; + } + } +} + +// Wildly optimistic acceleration "bell" curve modeling. +// Returns an estimate of how long the move with a given accel +// takes in seconds. +// It is assumed that the movement is smooth and uniform. +float +GCodeTimeEstimator::_accelerated_move(double length, double v, double acceleration) +{ + // for half of the move, there are 2 zones, where the speed is increasing/decreasing and + // where the speed is constant. + // Since the slowdown is assumed to be uniform, calculate the average velocity for half of the + // expected displacement. + // final velocity v = a*t => a * (dx / 0.5v) => v^2 = 2*a*dx + // v_avg = 0.5v => 2*v_avg = v + // d_x = v_avg*t => t = d_x / v_avg + acceleration = (acceleration == 0.0 ? 4000.0 : acceleration); // Set a default accel to use for print time in case it's 0 somehow. + auto half_length = length / 2.0; + auto t_init = v / acceleration; // time to final velocity + auto dx_init = (0.5*v*t_init); // Initial displacement for the time to get to final velocity + auto t = 0.0; + if (half_length >= dx_init) { + half_length -= (0.5*v*t_init); + t += t_init; + t += (half_length / v); // rest of time is at constant speed. + } else { + // If too much displacement for the expected final velocity, we don't hit the max, so reduce + // the average velocity to fit the displacement we actually are looking for. + t += std::sqrt(std::abs(length) * 2.0 * acceleration) / acceleration; + } + return 2.0*t; // cut in half before, so double to get full time spent. +} + +} diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp new file mode 100644 index 000000000..dd301c929 --- /dev/null +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -0,0 +1,24 @@ +#ifndef slic3r_GCodeTimeEstimator_hpp_ +#define slic3r_GCodeTimeEstimator_hpp_ + +#include "libslic3r.h" +#include "GCodeReader.hpp" + +namespace Slic3r { + +class GCodeTimeEstimator : public GCodeReader { + public: + float time = 0; // in seconds + + void parse(const std::string &gcode); + void parse_file(const std::string &file); + + protected: + float acceleration = 9000; + void _parser(GCodeReader&, const GCodeReader::GCodeLine &line); + static float _accelerated_move(double length, double v, double acceleration); +}; + +} /* namespace Slic3r */ + +#endif /* slic3r_GCodeTimeEstimator_hpp_ */ diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 9ce012a36..a60de7252 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -19,6 +19,7 @@ REGISTER_CLASS(SpiralVase, "GCode::SpiralVase"); REGISTER_CLASS(Wipe, "GCode::Wipe"); REGISTER_CLASS(GCode, "GCode"); REGISTER_CLASS(GCodeSender, "GCode::Sender"); +REGISTER_CLASS(GCodeTimeEstimator, "GCode::TimeEstimator"); REGISTER_CLASS(GCodeWriter, "GCode::Writer"); REGISTER_CLASS(Layer, "Layer"); REGISTER_CLASS(SupportLayer, "Layer::Support"); diff --git a/xs/xsp/GCodeTimeEstimator.xsp b/xs/xsp/GCodeTimeEstimator.xsp new file mode 100644 index 000000000..9489ff917 --- /dev/null +++ b/xs/xsp/GCodeTimeEstimator.xsp @@ -0,0 +1,15 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/GCodeTimeEstimator.hpp" +%} + +%name{Slic3r::GCode::TimeEstimator} class GCodeTimeEstimator { + GCodeTimeEstimator(); + ~GCodeTimeEstimator(); + + float time %get{time}; + void parse(std::string gcode); + void parse_file(std::string file); +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index c47d79e00..b1f026eaa 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -214,6 +214,10 @@ GCodeSender* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +GCodeTimeEstimator* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + GCodeWriter* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T From 116cfefb086b07e0383f82b3dda52bb054ecde04 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 00:31:29 +0100 Subject: [PATCH 14/21] Fix serial connection on Linux --- xs/src/libslic3r/GCodeSender.cpp | 87 +++++++++++++++++++------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index c1568790e..951662765 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -8,16 +8,36 @@ #include #include -#if defined(__APPLE__) || defined(__linux) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__OpenBSD__) #include #endif -#if __APPLE__ +#ifdef __APPLE__ #include #include #endif -#ifdef __linux +#ifdef __linux__ #include -#include +#include +#include "/usr/include/asm-generic/ioctls.h" + +/* The following definitions are kindly borrowed from: + /usr/include/asm-generic/termbits.h + Unfortunately we cannot just include that one because + it would redefine the "struct termios" already defined + the already included by Boost.ASIO. */ +#define K_NCCS 19 +struct termios2 { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_line; + cc_t c_cc[K_NCCS]; + speed_t c_ispeed; + speed_t c_ospeed; +}; +#define BOTHER CBAUDEX + #endif //#define DEBUG_SERIAL @@ -65,7 +85,6 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate) this->open = true; this->reset(); } catch (boost::system::system_error &e) { - printf("Caught error\n"); this->set_error_status(true); return false; } @@ -107,27 +126,15 @@ GCodeSender::set_baud_rate(unsigned int baud_rate) ioctl(handle, IOSSIOSPEED, &newSpeed); ::tcsetattr(handle, TCSANOW, &ios); #elif __linux - termios ios; - ::tcgetattr(handle, &ios); - ::cfsetispeed(&ios, B38400); - ::cfsetospeed(&ios, B38400); - ::tcflush(handle, TCIFLUSH); - ::tcsetattr(handle, TCSANOW, &ios); - - struct serial_struct ss; - ioctl(handle, TIOCGSERIAL, &ss); - ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; - ss.custom_divisor = (ss.baud_base + (baud_rate / 2)) / baud_rate; - //cout << "bbase " << ss.baud_base << " div " << ss.custom_divisor; - long closestSpeed = ss.baud_base / ss.custom_divisor; - //cout << " Closest speed " << closestSpeed << endl; - ss.reserved_char[0] = 0; - if (closestSpeed < baud_rate * 98 / 100 || closestSpeed > baud_rate * 102 / 100) { - printf("Failed to set baud rate\n"); - } - - ioctl(handle, TIOCSSERIAL, &ss); - printf("< set_baud_rate: %u\n", baud_rate); + termios2 ios; + if (ioctl(handle, TCGETS2, &ios)) + printf("Error in TCGETS2: %s\n", strerror(errno)); + ios.c_ispeed = ios.c_ospeed = baud_rate; + ios.c_cflag &= ~CBAUD; + ios.c_cflag |= BOTHER; + if (ioctl(handle, TCSETS2, &ios)) + printf("Error in TCSETS2: %s\n", strerror(errno)); + #elif __OpenBSD__ struct termios ios; ::tcgetattr(handle, &ios); @@ -296,17 +303,27 @@ GCodeSender::on_read(const boost::system::error_code& error, { this->set_error_status(false); if (error) { - if (error.value() == 45) { + #ifdef __linux__ + if (error.value() == 2) { + this->do_read(); + return; + } + #endif + + #ifdef __APPLE__ + if (error.value() == 45 || ) { // OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html this->do_read(); - } else { - // printf("ERROR: [%d] %s\n", error.value(), error.message().c_str()); - // error can be true even because the serial port was closed. - // In this case it is not a real error, so ignore. - if (this->open) { - this->do_close(); - this->set_error_status(true); - } + return; + } + #endif + + // printf("ERROR: [%d] %s\n", error.value(), error.message().c_str()); + // error can be true even because the serial port was closed. + // In this case it is not a real error, so ignore. + if (this->open) { + this->do_close(); + this->set_error_status(true); } return; } From 5f9adf8922cedb0e6a4aecd9d75426cdd2b53881 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 13:23:33 +0100 Subject: [PATCH 15/21] Workaround for eof errors on Linux --- xs/src/libslic3r/GCodeSender.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index 951662765..646fdb4bd 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -131,7 +131,9 @@ GCodeSender::set_baud_rate(unsigned int baud_rate) printf("Error in TCGETS2: %s\n", strerror(errno)); ios.c_ispeed = ios.c_ospeed = baud_rate; ios.c_cflag &= ~CBAUD; - ios.c_cflag |= BOTHER; + ios.c_cflag |= BOTHER | CLOCAL | CREAD; + ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read + ios.c_cc[VTIME] = 1; if (ioctl(handle, TCSETS2, &ios)) printf("Error in TCSETS2: %s\n", strerror(errno)); @@ -303,13 +305,6 @@ GCodeSender::on_read(const boost::system::error_code& error, { this->set_error_status(false); if (error) { - #ifdef __linux__ - if (error.value() == 2) { - this->do_read(); - return; - } - #endif - #ifdef __APPLE__ if (error.value() == 45 || ) { // OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html From 67f0fe334406d159de5ff3c2094b22be676c8fdc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 13:57:43 +0100 Subject: [PATCH 16/21] Removed duplicated console log in manual control dialog; process Enter key in manual commands; append outgoing commands to log --- .../GUI/Controller/ManualControlDialog.pm | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index 37dec1634..9d2fb5cc6 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -1,5 +1,3 @@ -# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected? - package Slic3r::GUI::Controller::ManualControlDialog; use strict; use warnings; @@ -9,7 +7,7 @@ use Scalar::Util qw(looks_like_number); use Slic3r::Geometry qw(PI X Y unscale); use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap :textctrl wxBORDER_NONE wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); +use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_TEXT_ENTER); use base qw(Wx::Dialog Class::Accessor); __PACKAGE__->mk_accessors(qw(sender writer config2 x_homed y_homed)); @@ -163,6 +161,7 @@ sub new { return; } my $cmd = $self->writer->set_temperature($self->config2->{temperature}); + $self->GetParent->append_to_log(">> $cmd\n"); $self->sender->send($cmd, 1); }); $optgroup->append_line($line); @@ -182,6 +181,7 @@ sub new { return; } my $cmd = $self->writer->set_bed_temperature($self->config2->{bed_temperature}); + $self->GetParent->append_to_log(">> $cmd\n"); $self->sender->send($cmd, 1); }); $optgroup->append_line($line); @@ -189,19 +189,12 @@ sub new { } { - my $box = Wx::StaticBox->new($self, -1, "Console"); + my $box = Wx::StaticBox->new($self, -1, "Send manual command"); my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); - $right_sizer->Add($sbsizer, 1, wxEXPAND, 0); - - my $log = $self->{log_textctrl} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxBORDER_NONE); - $log->SetBackgroundColour($self->GetBackgroundColour); - $log->SetFont($Slic3r::GUI::small_font); - $log->SetEditable(0); - $sbsizer->Add($self->{log_textctrl}, 1, wxEXPAND, 0); + $right_sizer->Add($sbsizer, 1, wxEXPAND | wxALL, 10); my $cmd_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $cmd_textctrl = Wx::TextCtrl->new($self, -1, ''); + my $cmd_textctrl = Wx::TextCtrl->new($self, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); $cmd_sizer->Add($cmd_textctrl, 1, wxEXPAND, 0); my $btn = Wx::Button->new($self, -1, @@ -212,13 +205,17 @@ sub new { } $cmd_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 5); - EVT_BUTTON($self, $btn, sub { - return if $cmd_textctrl->GetValue eq ''; - $self->sender->send($cmd_textctrl->GetValue, 1); + my $do_send = sub { + my $cmd = $cmd_textctro->GetValue; + return if cmd eq ''; + $self->GetParent->append_to_log(">> $cmd\n"); + $self->sender->send($cmd, 1); $cmd_textctrl->SetValue(''); - }); + }; + EVT_BUTTON($self, $btn, $do_send); + EVT_TEXT_ENTER($self, $cmd_textctrl, $do_send); - $sbsizer->Add($cmd_sizer, 0, wxEXPAND | wxTOP, 2); + $sbsizer->Add($cmd_sizer, 0, wxEXPAND | wxTOP, 5); } my $main_sizer = Wx::BoxSizer->new(wxHORIZONTAL); @@ -239,12 +236,6 @@ sub new { return $self; } -sub update_log { - my ($self, $log) = @_; - - $self->{log_textctrl}->SetValue($log); -} - sub abs_xy_move { my ($self, $pos) = @_; From 42e9ec90bef32c5443b6fbb4d00bcb1cffd8677e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 13:59:29 +0100 Subject: [PATCH 17/21] Updated log method calls --- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm index 53850a69e..6a28e2ace 100644 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -43,9 +43,7 @@ sub new { $self->print_completed; } } - $self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log}; - $self->{manual_control_dialog}->update_log($self->{log_textctrl}->GetValue) - if $self->{manual_control_dialog}; + $self->append_to_log("$_\n") for @{$self->sender->purge_log}; { my $temp = $self->sender->getT; if ($temp eq '') { @@ -272,6 +270,12 @@ sub new { return $self; } +sub append_to_log { + my ($self, $text) = @_; + + $self->{log_textctrl}->AppendText($text); +} + sub is_connected { my ($self) = @_; return $self->sender && $self->sender->is_connected; @@ -409,9 +413,9 @@ sub print_job { $self->Layout; $self->set_status('Printing...'); - $self->{log_textctrl}->AppendText(sprintf "=====\n"); - $self->{log_textctrl}->AppendText(sprintf "Printing %s\n", $job->name); - $self->{log_textctrl}->AppendText(sprintf "Print started at %s\n", $self->_timestamp); + $self->append_to_log(sprintf "=====\n"); + $self->append_to_log(sprintf "Printing %s\n", $job->name); + $self->append_to_log(sprintf "Print started at %s\n", $self->_timestamp); } sub print_completed { @@ -426,7 +430,7 @@ sub print_completed { $self->Layout; $self->set_status('Print completed.'); - $self->{log_textctrl}->AppendText(sprintf "Print completed at %s\n", $self->_timestamp); + $self->append_to_log(sprintf "Print completed at %s\n", $self->_timestamp); $self->reload_jobs; } @@ -479,7 +483,7 @@ sub reload_jobs { $self->{gauge}->Disable; $self->{gauge}->Hide; $self->set_status('Print was aborted.'); - $self->{log_textctrl}->AppendText(sprintf "Print aborted at %s\n", $self->_timestamp); + $self->append_to_log(sprintf "Print aborted at %s\n", $self->_timestamp); }); $panel->on_resume_print(sub { my ($job) = @_; From d77469c6582d694624f4d1a949509a61ff0fb4ba Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 14:00:21 +0100 Subject: [PATCH 18/21] Update Slic3r::GCode::Sender API --- utils/send-gcode.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/send-gcode.pl b/utils/send-gcode.pl index 0b803baa6..d87e6826a 100644 --- a/utils/send-gcode.pl +++ b/utils/send-gcode.pl @@ -14,7 +14,8 @@ use Slic3r; die "Usage: send-gcode.pl SERIALPORT BAUDRATE GCODE_FILE\n" if @ARGV != 3; -my $serial = Slic3r::GCode::Sender->new($ARGV[0], $ARGV[1]); +my $serial = Slic3r::GCode::Sender->new; +$serial->connect($ARGV[0], $ARGV[1]); 1 until $serial->is_connected; print "Connected to printer\n"; From 9a1dc1a35e0aab8ece8e5ca90e1b0830102157b0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 14:03:00 +0100 Subject: [PATCH 19/21] Typos --- lib/Slic3r/GUI/Controller/ManualControlDialog.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index 9d2fb5cc6..424ca3d73 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -206,8 +206,8 @@ sub new { $cmd_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 5); my $do_send = sub { - my $cmd = $cmd_textctro->GetValue; - return if cmd eq ''; + my $cmd = $cmd_textctrl->GetValue; + return if $cmd eq ''; $self->GetParent->append_to_log(">> $cmd\n"); $self->sender->send($cmd, 1); $cmd_textctrl->SetValue(''); From 5fc5bf06db6f318593736838b842a19868d13b27 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 14:09:05 +0100 Subject: [PATCH 20/21] Smarter BOOST_DIR --- xs/Build.PL | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xs/Build.PL b/xs/Build.PL index 619cc879f..2fc5b33c1 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -70,6 +70,9 @@ if (defined $ENV{BOOST_INCLUDEDIR}) { my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include'); if (-d $subdir) { push @boost_include, $subdir; + } elsif (-d "../$subdir") { + # User might have provided a path relative to the Build.PL in the main directory + push @boost_include, "../$subdir"; } else { push @boost_include, $ENV{BOOST_DIR}; } @@ -99,6 +102,9 @@ if (defined $ENV{BOOST_LIBRARYPATH}) { my $subdir = $ENV{BOOST_DIR} . ($mswin ? '\stage\lib' : '/stage/lib'); if (-d $subdir) { push @boost_libs, $subdir; + } elsif (-d "../$subdir") { + # User might have provided a path relative to the Build.PL in the main directory + push @boost_libs, "../$subdir"; } else { push @boost_libs, $ENV{BOOST_DIR}; } From 684b8de2364fbc880551b2c26406c4ce353209b7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Mar 2017 15:24:06 +0100 Subject: [PATCH 21/21] Use exec for running the OSX binary --- package/osx/startup_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/osx/startup_script.sh b/package/osx/startup_script.sh index 4f665ac64..c7fd92d2b 100644 --- a/package/osx/startup_script.sh +++ b/package/osx/startup_script.sh @@ -1,4 +1,4 @@ #!/bin/bash DIR=$(dirname "$0") -"$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@ +exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@