mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-02 12:00:37 +08:00
Merge remote-tracking branch 'origin/master' into gui3
This commit is contained in:
commit
ccaafba4b8
@ -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
|
||||
|
142
README.md
142
README.md
@ -1,63 +1,50 @@
|
||||
_Q: Oh cool, a new RepRap slicer?_
|
||||
|
||||
A: Yes.
|
||||
|
||||
Slic3r [](https://travis-ci.org/alexrj/Slic3r) [](https://ci.appveyor.com/project/lordofhyphens/slic3r) [](http://osx-build.slic3r.org:8080/job/Slic3r)
|
||||
 Slic3r [](https://travis-ci.org/alexrj/Slic3r) [](https://ci.appveyor.com/project/lordofhyphens/slic3r) [](http://osx-build.slic3r.org:8080/job/Slic3r)
|
||||
======
|
||||
Prebuilt Windows (64-bit) and OSX (>10.7) builds:
|
||||
* http://dl.slic3r.org/dev/
|
||||
|
||||
<img width=256 src=https://cloud.githubusercontent.com/assets/31754/22719818/09998c92-ed6d-11e6-9fa0-09de638f3a36.png />
|
||||
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: <img width=150 src=https://cloud.githubusercontent.com/assets/31754/22719818/09998c92-ed6d-11e6-9fa0-09de638f3a36.png />
|
||||
|
||||
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](#features) 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.
|
||||
|
||||
### <a name="features"></a>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, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James.
|
||||
|
||||
### How can I invoke Slic3r using the command line?
|
||||
|
||||
Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
|
||||
|
||||
@ -383,11 +382,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).
|
||||
|
@ -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_textctrl->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) = @_;
|
||||
|
||||
|
@ -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) = @_;
|
||||
|
@ -238,43 +238,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);
|
||||
|
||||
@ -897,6 +864,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;
|
||||
}
|
||||
|
||||
{
|
||||
@ -1045,6 +1015,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
|
||||
@ -1688,6 +1659,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) = @_;
|
||||
|
||||
|
@ -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 map @{$_->instances}, @objects;
|
||||
|
||||
$self->delete_object($_) for reverse 0..($self->objects_count-2);
|
||||
}
|
||||
|
||||
# Extends C++ class Slic3r::ModelMaterial
|
||||
package Slic3r::Model::Material;
|
||||
|
||||
|
@ -91,6 +91,8 @@ cat << EOF >> $plistfile
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.7</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
@ -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" $@
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
19
t/fill.t
19
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';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
21
utils/estimate-gcode-time.pl
Executable file
21
utils/estimate-gcode-time.pl
Executable file
@ -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__
|
@ -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";
|
||||
|
||||
|
@ -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};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "GCodeReader.hpp"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
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<std::string> 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<std::string> 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
|
||||
|
@ -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<void(GCodeReader&, GCodeLine&)> callback_t;
|
||||
typedef std::function<void(GCodeReader&, const GCodeLine&)> 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;
|
||||
|
@ -8,16 +8,36 @@
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__linux) || defined(__OpenBSD__)
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#if __APPLE__
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#endif
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/serial.h>
|
||||
#include <fcntl.h>
|
||||
#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 <termios.h> 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,17 @@ 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 | 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));
|
||||
|
||||
#elif __OpenBSD__
|
||||
struct termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
@ -296,17 +305,20 @@ GCodeSender::on_read(const boost::system::error_code& error,
|
||||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
if (error.value() == 45) {
|
||||
#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;
|
||||
}
|
||||
|
78
xs/src/libslic3r/GCodeTimeEstimator.cpp
Normal file
78
xs/src/libslic3r/GCodeTimeEstimator.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
#include <cmath>
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
24
xs/src/libslic3r/GCodeTimeEstimator.hpp
Normal file
24
xs/src/libslic3r/GCodeTimeEstimator.hpp
Normal file
@ -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_ */
|
@ -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;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Geometry.hpp"
|
||||
#include "IO.hpp"
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
@ -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<coordf_t> 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)
|
||||
@ -597,6 +635,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()
|
||||
{
|
||||
|
@ -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.
|
||||
@ -149,6 +151,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);
|
||||
|
@ -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");
|
||||
|
@ -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';
|
||||
|
15
xs/xsp/GCodeTimeEstimator.xsp
Normal file
15
xs/xsp/GCodeTimeEstimator.xsp
Normal file
@ -0,0 +1,15 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#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);
|
||||
};
|
@ -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();
|
||||
};
|
||||
|
||||
@ -207,6 +209,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)
|
||||
|
@ -214,6 +214,10 @@ GCodeSender* O_OBJECT_SLIC3R
|
||||
Ref<GCodeSender> O_OBJECT_SLIC3R_T
|
||||
Clone<GCodeSender> O_OBJECT_SLIC3R_T
|
||||
|
||||
GCodeTimeEstimator* O_OBJECT_SLIC3R
|
||||
Ref<GCodeTimeEstimator> O_OBJECT_SLIC3R_T
|
||||
Clone<GCodeTimeEstimator> O_OBJECT_SLIC3R_T
|
||||
|
||||
GCodeWriter* O_OBJECT_SLIC3R
|
||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||
|
Loading…
x
Reference in New Issue
Block a user