mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-09-24 06:53:16 +08:00
Merge branch 'master' into adaptive-slicing-spline
and added Spline variable to ModelObject
This commit is contained in:
commit
f43e004f44
@ -8,7 +8,7 @@ perl:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
- stable
|
- xsgui
|
||||||
sudo: false
|
sudo: false
|
||||||
cache:
|
cache:
|
||||||
apt: true
|
apt: true
|
||||||
@ -26,3 +26,10 @@ addons:
|
|||||||
- liblocal-lib-perl
|
- liblocal-lib-perl
|
||||||
- g++-4.9
|
- g++-4.9
|
||||||
env: CC=g++-4.9
|
env: CC=g++-4.9
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "chat.freenode.net#slic3r"
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
|
use_notice: true
|
||||||
|
144
README.md
144
README.md
@ -1,63 +1,50 @@
|
|||||||
_Q: Oh cool, a new RepRap slicer?_
|
 Slic3r [](https://travis-ci.org/alexrj/Slic3r) [](https://ci.appveyor.com/project/lordofhyphens/slic3r) [](http://osx-build.slic3r.org:8080/job/Slic3r)
|
||||||
|
|
||||||
A: Yes.
|
|
||||||
|
|
||||||
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
|
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 />
|
||||||
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.
|
|
||||||
|
|
||||||
See the [project homepage](http://slic3r.org/) at slic3r.org and the
|
### So, what's this Slic3r?
|
||||||
[manual](http://manual.slic3r.org/) for more information.
|
|
||||||
|
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?
|
### What language is it written in?
|
||||||
|
|
||||||
The core geometric algorithms and data structures are written in C++,
|
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++.
|
||||||
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
|
|
||||||
|
|
||||||
### How to install?
|
### How to install?
|
||||||
|
|
||||||
You can download a precompiled package from [slic3r.org](http://slic3r.org/);
|
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).
|
||||||
it will run without the need for any dependency.
|
|
||||||
|
|
||||||
If you want to compile the source yourself follow the instructions on one of these wiki pages:
|
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)
|
* [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?
|
### Can I help?
|
||||||
|
|
||||||
Sure! You can do the following to find things that are available to help with:
|
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
|
* Development
|
||||||
changes: this way we'll ensure nobody wastes their time and no conflicts arise
|
* [Low Effort tasks](https://github.com/alexrj/Slic3r/labels/Low%20Effort): pick one of them!
|
||||||
in development.
|
* [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_.
|
* `Build.PL`: this script installs dependencies into `local-lib/`, compiles the C++ part and runs tests
|
||||||
The author is Alessandro Ranellucci.
|
* `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
|
### Acknowledgements
|
||||||
licensed under the _Creative Commons Attribution 3.0 License_.
|
|
||||||
The author of the Silk icon set is Mark James.
|
|
||||||
|
|
||||||
### 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 ] ...
|
Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
|
||||||
|
|
||||||
@ -116,8 +115,6 @@ The author of the Silk icon set is Mark James.
|
|||||||
GUI options:
|
GUI options:
|
||||||
--gui Forces the GUI launch instead of command line slicing (if you
|
--gui Forces the GUI launch instead of command line slicing (if you
|
||||||
supply a model file, it will be loaded into the plater)
|
supply a model file, it will be loaded into the plater)
|
||||||
--no-plater Disable the plater tab
|
|
||||||
--gui-mode Overrides the configured mode (simple/expert)
|
|
||||||
--autosave <file> Automatically export current configuration to the specified file
|
--autosave <file> Automatically export current configuration to the specified file
|
||||||
|
|
||||||
Output options:
|
Output options:
|
||||||
@ -384,11 +381,4 @@ The author of the Silk icon set is Mark James.
|
|||||||
Temperature difference to be applied when an extruder is not active and
|
Temperature difference to be applied when an extruder is not active and
|
||||||
--ooze-prevention is enabled (default: -5)
|
--ooze-prevention is enabled (default: -5)
|
||||||
|
|
||||||
|
For more information about command line usage see the relevant [manual page](http://manual.slic3r.org/advanced/command-line).
|
||||||
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
|
|
||||||
|
@ -42,7 +42,11 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
|
|||||||
|
|
||||||
use FindBin;
|
use FindBin;
|
||||||
# Path to the images.
|
# Path to the images.
|
||||||
our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] };
|
my $varpath = decode_path($FindBin::Bin) . "/var";
|
||||||
|
if ($^O eq 'darwin' && !-d $varpath) {
|
||||||
|
$varpath = decode_path($FindBin::Bin) . "/../Resources/var";
|
||||||
|
}
|
||||||
|
our $var = sub { "$varpath/$_[0]" };
|
||||||
|
|
||||||
use Moo 1.003001;
|
use Moo 1.003001;
|
||||||
|
|
||||||
|
@ -30,12 +30,11 @@ sub new_from_defaults {
|
|||||||
my (@opt_keys) = @_;
|
my (@opt_keys) = @_;
|
||||||
|
|
||||||
my $self = $class->new;
|
my $self = $class->new;
|
||||||
my $defaults = Slic3r::Config::Full->new;
|
|
||||||
if (@opt_keys) {
|
if (@opt_keys) {
|
||||||
$self->set($_, $defaults->get($_))
|
$self->set($_, $Options->{$_}{default})
|
||||||
for grep $defaults->has($_), @opt_keys;
|
for grep exists $Options->{$_}{default}, @opt_keys;
|
||||||
} else {
|
} else {
|
||||||
$self->apply_static($defaults);
|
$self->apply_static(Slic3r::Config::Full->new);
|
||||||
}
|
}
|
||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
@ -190,8 +189,11 @@ sub validate {
|
|||||||
die "Invalid value for --gcode-flavor\n"
|
die "Invalid value for --gcode-flavor\n"
|
||||||
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
||||||
|
|
||||||
die "--use-firmware-retraction is only supported by Marlin and Machinekit firmware\n"
|
die "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware\n"
|
||||||
if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie' && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
|
if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie'
|
||||||
|
&& $self->gcode_flavor ne 'reprap'
|
||||||
|
&& $self->gcode_flavor ne 'machinekit'
|
||||||
|
&& $self->gcode_flavor ne 'repetier';
|
||||||
|
|
||||||
die "--use-firmware-retraction is not compatible with --wipe\n"
|
die "--use-firmware-retraction is not compatible with --wipe\n"
|
||||||
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
|
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
|
||||||
|
@ -5,7 +5,9 @@ use utf8;
|
|||||||
|
|
||||||
use File::Basename qw(basename);
|
use File::Basename qw(basename);
|
||||||
use FindBin;
|
use FindBin;
|
||||||
use List::Util qw(first);
|
use List::Util qw(first any);
|
||||||
|
use Slic3r::Geometry qw(X Y);
|
||||||
|
|
||||||
use Slic3r::GUI::2DBed;
|
use Slic3r::GUI::2DBed;
|
||||||
use Slic3r::GUI::AboutDialog;
|
use Slic3r::GUI::AboutDialog;
|
||||||
use Slic3r::GUI::BedShapeDialog;
|
use Slic3r::GUI::BedShapeDialog;
|
||||||
@ -33,9 +35,10 @@ use Slic3r::GUI::ProgressStatusBar;
|
|||||||
use Slic3r::GUI::Projector;
|
use Slic3r::GUI::Projector;
|
||||||
use Slic3r::GUI::OptionsGroup;
|
use Slic3r::GUI::OptionsGroup;
|
||||||
use Slic3r::GUI::OptionsGroup::Field;
|
use Slic3r::GUI::OptionsGroup::Field;
|
||||||
use Slic3r::GUI::SimpleTab;
|
use Slic3r::GUI::Preset;
|
||||||
|
use Slic3r::GUI::PresetEditor;
|
||||||
|
use Slic3r::GUI::PresetEditorDialog;
|
||||||
use Slic3r::GUI::SLAPrintOptions;
|
use Slic3r::GUI::SLAPrintOptions;
|
||||||
use Slic3r::GUI::Tab;
|
|
||||||
|
|
||||||
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
||||||
our $have_LWP = eval "use LWP::UserAgent; 1";
|
our $have_LWP = eval "use LWP::UserAgent; 1";
|
||||||
@ -61,15 +64,12 @@ use constant AMF_MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(amf)};
|
|||||||
our $datadir;
|
our $datadir;
|
||||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||||
our $no_controller;
|
our $no_controller;
|
||||||
our $no_plater;
|
|
||||||
our $mode;
|
|
||||||
our $autosave;
|
our $autosave;
|
||||||
our $threads;
|
our $threads;
|
||||||
our @cb;
|
our @cb;
|
||||||
|
|
||||||
our $Settings = {
|
our $Settings = {
|
||||||
_ => {
|
_ => {
|
||||||
mode => 'simple',
|
|
||||||
version_check => 1,
|
version_check => 1,
|
||||||
autocenter => 1,
|
autocenter => 1,
|
||||||
invert_zoom => 0,
|
invert_zoom => 0,
|
||||||
@ -102,6 +102,7 @@ sub OnInit {
|
|||||||
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
||||||
|
|
||||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||||
|
$self->{presets} = { print => [], filament => [], printer => [] };
|
||||||
|
|
||||||
# locate or create data directory
|
# locate or create data directory
|
||||||
# Unix: ~/.Slic3r
|
# Unix: ~/.Slic3r
|
||||||
@ -133,19 +134,23 @@ sub OnInit {
|
|||||||
for grep !exists $ini->{_}{$_}, keys %{$Settings->{_}};
|
for grep !exists $ini->{_}{$_}, keys %{$Settings->{_}};
|
||||||
$Settings = $ini;
|
$Settings = $ini;
|
||||||
}
|
}
|
||||||
|
delete $Settings->{_}{mode}; # handle legacy
|
||||||
}
|
}
|
||||||
$Settings->{_}{version} = $Slic3r::VERSION;
|
$Settings->{_}{version} = $Slic3r::VERSION;
|
||||||
$Settings->{_}{threads} = $threads if $threads;
|
$Settings->{_}{threads} = $threads if $threads;
|
||||||
$self->save_settings;
|
$self->save_settings;
|
||||||
|
|
||||||
|
if (-f "$enc_datadir/simple.ini") {
|
||||||
|
# The Simple Mode settings were already automatically duplicated to presets
|
||||||
|
# named "Simple Mode" in each group, so we already support retrocompatibility.
|
||||||
|
unlink "$enc_datadir/simple.ini";
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->load_presets;
|
||||||
|
|
||||||
# application frame
|
# application frame
|
||||||
Wx::Image::AddHandler(Wx::PNGHandler->new);
|
Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new;
|
||||||
mode => $mode // $Settings->{_}{mode},
|
|
||||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
|
||||||
no_controller => $no_controller // $Settings->{_}{no_controller},
|
|
||||||
no_plater => $no_plater,
|
|
||||||
);
|
|
||||||
$self->SetTopWindow($frame);
|
$self->SetTopWindow($frame);
|
||||||
|
|
||||||
# load init bundle
|
# load init bundle
|
||||||
@ -281,21 +286,64 @@ sub save_settings {
|
|||||||
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
|
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presets {
|
sub presets { return $_[0]->{presets}; }
|
||||||
my ($self, $section) = @_;
|
|
||||||
|
|
||||||
my %presets = ();
|
sub load_presets {
|
||||||
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
|
my ($self) = @_;
|
||||||
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
|
|
||||||
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
for my $group (qw(printer filament print)) {
|
||||||
$file = Slic3r::decode_path($file);
|
my $presets = $self->{presets}{$group};
|
||||||
my $name = basename($file);
|
|
||||||
$name =~ s/\.ini$//;
|
# keep external or dirty presets
|
||||||
$presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
|
@$presets = grep { ($_->external && $_->file_exists) || $_->dirty } @$presets;
|
||||||
|
|
||||||
|
my $dir = "$Slic3r::GUI::datadir/$group";
|
||||||
|
opendir my $dh, Slic3r::encode_path($dir)
|
||||||
|
or die "Failed to read directory $dir (errno: $!)\n";
|
||||||
|
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
||||||
|
$file = Slic3r::decode_path($file);
|
||||||
|
my $name = basename($file);
|
||||||
|
$name =~ s/\.ini$//i;
|
||||||
|
|
||||||
|
# skip if we already have it
|
||||||
|
next if any { $_->name eq $name } @$presets;
|
||||||
|
|
||||||
|
push @$presets, Slic3r::GUI::Preset->new(
|
||||||
|
group => $group,
|
||||||
|
name => $name,
|
||||||
|
file => "$dir/$file",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
closedir $dh;
|
||||||
|
|
||||||
|
@$presets = sort { $a->name cmp $b->name } @$presets;
|
||||||
|
|
||||||
|
unshift @$presets, Slic3r::GUI::Preset->new(
|
||||||
|
group => $group,
|
||||||
|
default => 1,
|
||||||
|
name => '- default -',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
closedir $dh;
|
}
|
||||||
|
|
||||||
return %presets;
|
sub add_external_preset {
|
||||||
|
my ($self, $file) = @_;
|
||||||
|
|
||||||
|
my $name = basename($file); # keep .ini suffix
|
||||||
|
for my $group (qw(printer filament print)) {
|
||||||
|
my $presets = $self->{presets}{$group};
|
||||||
|
|
||||||
|
# remove any existing preset with the same name
|
||||||
|
@$presets = grep { $_->name ne $name } @$presets;
|
||||||
|
|
||||||
|
push @$presets, Slic3r::GUI::Preset->new(
|
||||||
|
group => $group,
|
||||||
|
name => $name,
|
||||||
|
file => $file,
|
||||||
|
external => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub have_version_check {
|
sub have_version_check {
|
||||||
@ -379,4 +427,38 @@ sub scan_serial_ports {
|
|||||||
return grep !/Bluetooth|FireFly/, @ports;
|
return grep !/Bluetooth|FireFly/, @ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_menu_item_icon {
|
||||||
|
my ($self, $menuItem, $icon) = @_;
|
||||||
|
|
||||||
|
# SetBitmap was not available on OS X before Wx 0.9927
|
||||||
|
if ($icon && $menuItem->can('SetBitmap')) {
|
||||||
|
$menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub save_window_pos {
|
||||||
|
my ($self, $window, $name) = @_;
|
||||||
|
|
||||||
|
$Settings->{_}{"${name}_pos"} = join ',', $window->GetScreenPositionXY;
|
||||||
|
$Settings->{_}{"${name}_size"} = join ',', $window->GetSizeWH;
|
||||||
|
$Settings->{_}{"${name}_maximized"} = $window->IsMaximized;
|
||||||
|
$self->save_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub restore_window_pos {
|
||||||
|
my ($self, $window, $name) = @_;
|
||||||
|
|
||||||
|
if (defined $Settings->{_}{"${name}_pos"}) {
|
||||||
|
my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ];
|
||||||
|
$window->SetSize($size);
|
||||||
|
|
||||||
|
my $display = Wx::Display->new->GetClientArea();
|
||||||
|
my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ];
|
||||||
|
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
|
||||||
|
$window->Move($pos);
|
||||||
|
}
|
||||||
|
$window->Maximize(1) if $Settings->{_}{"${name}_maximized"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -54,13 +54,13 @@ use constant HAS_VBO => eval { glGenBuffersARB_p(0); 1 };
|
|||||||
|
|
||||||
|
|
||||||
# phi / theta angles to orient the camera.
|
# phi / theta angles to orient the camera.
|
||||||
use constant VIEW_ISO => [45.0,45.0];
|
|
||||||
use constant VIEW_LEFT => [90.0,90.0];
|
|
||||||
use constant VIEW_RIGHT => [-90.0,90.0];
|
|
||||||
use constant VIEW_TOP => [0.0,0.0];
|
use constant VIEW_TOP => [0.0,0.0];
|
||||||
use constant VIEW_BOTTOM => [0.0,180.0];
|
use constant VIEW_BOTTOM => [0.0,180.0];
|
||||||
|
use constant VIEW_LEFT => [90.0,90.0];
|
||||||
|
use constant VIEW_RIGHT => [-90.0,90.0];
|
||||||
use constant VIEW_FRONT => [0.0,90.0];
|
use constant VIEW_FRONT => [0.0,90.0];
|
||||||
use constant VIEW_REAR => [180.0,90.0];
|
use constant VIEW_BACK => [180.0,90.0];
|
||||||
|
use constant VIEW_DIAGONAL => [45.0,45.0];
|
||||||
|
|
||||||
use constant GIMBAL_LOCK_THETA_MAX => 170;
|
use constant GIMBAL_LOCK_THETA_MAX => 170;
|
||||||
|
|
||||||
@ -338,20 +338,20 @@ sub select_view {
|
|||||||
if (ref($direction)) {
|
if (ref($direction)) {
|
||||||
$dirvec = $direction;
|
$dirvec = $direction;
|
||||||
} else {
|
} else {
|
||||||
if ($direction eq 'iso') {
|
if ($direction eq 'top') {
|
||||||
$dirvec = VIEW_ISO;
|
$dirvec = VIEW_TOP;
|
||||||
|
} elsif ($direction eq 'bottom') {
|
||||||
|
$dirvec = VIEW_BOTTOM;
|
||||||
} elsif ($direction eq 'left') {
|
} elsif ($direction eq 'left') {
|
||||||
$dirvec = VIEW_LEFT;
|
$dirvec = VIEW_LEFT;
|
||||||
} elsif ($direction eq 'right') {
|
} elsif ($direction eq 'right') {
|
||||||
$dirvec = VIEW_RIGHT;
|
$dirvec = VIEW_RIGHT;
|
||||||
} elsif ($direction eq 'top') {
|
|
||||||
$dirvec = VIEW_TOP;
|
|
||||||
} elsif ($direction eq 'bottom') {
|
|
||||||
$dirvec = VIEW_BOTTOM;
|
|
||||||
} elsif ($direction eq 'front') {
|
} elsif ($direction eq 'front') {
|
||||||
$dirvec = VIEW_FRONT;
|
$dirvec = VIEW_FRONT;
|
||||||
} elsif ($direction eq 'rear') {
|
} elsif ($direction eq 'back') {
|
||||||
$dirvec = VIEW_REAR;
|
$dirvec = VIEW_BACK;
|
||||||
|
} elsif ($direction eq 'diagonal') {
|
||||||
|
$dirvec = VIEW_DIAGONAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->_sphi($dirvec->[0]);
|
$self->_sphi($dirvec->[0]);
|
||||||
|
@ -7,6 +7,7 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
|
use List::Util qw(any);
|
||||||
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
|
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
|
||||||
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
|
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
|
||||||
use base qw(Wx::ScrolledWindow Class::Accessor);
|
use base qw(Wx::ScrolledWindow Class::Accessor);
|
||||||
@ -39,14 +40,14 @@ sub new {
|
|||||||
|
|
||||||
EVT_LEFT_DOWN($btn, sub {
|
EVT_LEFT_DOWN($btn, sub {
|
||||||
my $menu = Wx::Menu->new;
|
my $menu = Wx::Menu->new;
|
||||||
my %presets = wxTheApp->presets('printer');
|
my %presets = map { $_->name => $_ } wxTheApp->presets('printer');
|
||||||
|
|
||||||
# remove printers that already exist
|
# remove printers that already exist
|
||||||
my @panels = $self->print_panels;
|
my @panels = $self->print_panels;
|
||||||
delete $presets{$_} for map $_->printer_name, @panels;
|
delete $presets{$_} for map $_->printer_name, @panels;
|
||||||
|
|
||||||
foreach my $preset_name (sort keys %presets) {
|
foreach my $preset_name (sort keys %presets) {
|
||||||
my $config = Slic3r::Config->load($presets{$preset_name});
|
my $config = $presets{$preset_name}->dirty_config;
|
||||||
next if !$config->serial_port;
|
next if !$config->serial_port;
|
||||||
|
|
||||||
my $id = &Wx::NewId();
|
my $id = &Wx::NewId();
|
||||||
@ -100,8 +101,8 @@ sub OnActivate {
|
|||||||
# get all available presets
|
# get all available presets
|
||||||
my %presets = ();
|
my %presets = ();
|
||||||
{
|
{
|
||||||
my %all = wxTheApp->presets('printer');
|
my %all = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
|
||||||
my %configs = map { my $name = $_; $name => Slic3r::Config->load($all{$name}) } keys %all;
|
my %configs = map { my $name = $_; $name => $all{$name}->load_config } keys %all;
|
||||||
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
|
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +178,12 @@ sub print_panels {
|
|||||||
map $_->GetWindow, $self->{sizer}->GetChildren;
|
map $_->GetWindow, $self->{sizer}->GetChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub printing {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return any { $_->printing } $self->print_panels;
|
||||||
|
}
|
||||||
|
|
||||||
sub update_presets {
|
sub update_presets {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($group, $presets, $selected, $is_dirty) = @_;
|
my ($group, $presets, $selected, $is_dirty) = @_;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected?
|
|
||||||
|
|
||||||
package Slic3r::GUI::Controller::ManualControlDialog;
|
package Slic3r::GUI::Controller::ManualControlDialog;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
@ -9,7 +7,7 @@ use Scalar::Util qw(looks_like_number);
|
|||||||
use Slic3r::Geometry qw(PI X Y unscale);
|
use Slic3r::Geometry qw(PI X Y unscale);
|
||||||
use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap :textctrl
|
use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap :textctrl
|
||||||
wxBORDER_NONE wxTAB_TRAVERSAL);
|
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);
|
use base qw(Wx::Dialog Class::Accessor);
|
||||||
|
|
||||||
__PACKAGE__->mk_accessors(qw(sender writer config2 x_homed y_homed));
|
__PACKAGE__->mk_accessors(qw(sender writer config2 x_homed y_homed));
|
||||||
@ -163,6 +161,7 @@ sub new {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
my $cmd = $self->writer->set_temperature($self->config2->{temperature});
|
my $cmd = $self->writer->set_temperature($self->config2->{temperature});
|
||||||
|
$self->GetParent->append_to_log(">> $cmd\n");
|
||||||
$self->sender->send($cmd, 1);
|
$self->sender->send($cmd, 1);
|
||||||
});
|
});
|
||||||
$optgroup->append_line($line);
|
$optgroup->append_line($line);
|
||||||
@ -182,6 +181,7 @@ sub new {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
my $cmd = $self->writer->set_bed_temperature($self->config2->{bed_temperature});
|
my $cmd = $self->writer->set_bed_temperature($self->config2->{bed_temperature});
|
||||||
|
$self->GetParent->append_to_log(">> $cmd\n");
|
||||||
$self->sender->send($cmd, 1);
|
$self->sender->send($cmd, 1);
|
||||||
});
|
});
|
||||||
$optgroup->append_line($line);
|
$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);
|
my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
||||||
$right_sizer->Add($sbsizer, 1, wxEXPAND, 0);
|
$right_sizer->Add($sbsizer, 1, wxEXPAND | wxALL, 10);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
my $cmd_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
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);
|
$cmd_sizer->Add($cmd_textctrl, 1, wxEXPAND, 0);
|
||||||
|
|
||||||
my $btn = Wx::Button->new($self, -1,
|
my $btn = Wx::Button->new($self, -1,
|
||||||
@ -212,13 +205,17 @@ sub new {
|
|||||||
}
|
}
|
||||||
$cmd_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 5);
|
$cmd_sizer->Add($btn, 0, wxEXPAND | wxLEFT, 5);
|
||||||
|
|
||||||
EVT_BUTTON($self, $btn, sub {
|
my $do_send = sub {
|
||||||
return if $cmd_textctrl->GetValue eq '';
|
my $cmd = $cmd_textctrl->GetValue;
|
||||||
$self->sender->send($cmd_textctrl->GetValue, 1);
|
return if $cmd eq '';
|
||||||
|
$self->GetParent->append_to_log(">> $cmd\n");
|
||||||
|
$self->sender->send($cmd, 1);
|
||||||
$cmd_textctrl->SetValue('');
|
$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);
|
my $main_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
@ -239,12 +236,6 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update_log {
|
|
||||||
my ($self, $log) = @_;
|
|
||||||
|
|
||||||
$self->{log_textctrl}->SetValue($log);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub abs_xy_move {
|
sub abs_xy_move {
|
||||||
my ($self, $pos) = @_;
|
my ($self, $pos) = @_;
|
||||||
|
|
||||||
|
@ -43,9 +43,7 @@ sub new {
|
|||||||
$self->print_completed;
|
$self->print_completed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log};
|
$self->append_to_log("$_\n") for @{$self->sender->purge_log};
|
||||||
$self->{manual_control_dialog}->update_log($self->{log_textctrl}->GetValue)
|
|
||||||
if $self->{manual_control_dialog};
|
|
||||||
{
|
{
|
||||||
my $temp = $self->sender->getT;
|
my $temp = $self->sender->getT;
|
||||||
if ($temp eq '') {
|
if ($temp eq '') {
|
||||||
@ -128,7 +126,7 @@ sub new {
|
|||||||
my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
{
|
{
|
||||||
$self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize,
|
$self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize,
|
||||||
["115200", "250000"]);
|
["57600", "115200", "250000"]);
|
||||||
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
|
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
|
||||||
$serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0);
|
$serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||||
}
|
}
|
||||||
@ -272,6 +270,12 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub append_to_log {
|
||||||
|
my ($self, $text) = @_;
|
||||||
|
|
||||||
|
$self->{log_textctrl}->AppendText($text);
|
||||||
|
}
|
||||||
|
|
||||||
sub is_connected {
|
sub is_connected {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return $self->sender && $self->sender->is_connected;
|
return $self->sender && $self->sender->is_connected;
|
||||||
@ -409,9 +413,9 @@ sub print_job {
|
|||||||
$self->Layout;
|
$self->Layout;
|
||||||
|
|
||||||
$self->set_status('Printing...');
|
$self->set_status('Printing...');
|
||||||
$self->{log_textctrl}->AppendText(sprintf "=====\n");
|
$self->append_to_log(sprintf "=====\n");
|
||||||
$self->{log_textctrl}->AppendText(sprintf "Printing %s\n", $job->name);
|
$self->append_to_log(sprintf "Printing %s\n", $job->name);
|
||||||
$self->{log_textctrl}->AppendText(sprintf "Print started at %s\n", $self->_timestamp);
|
$self->append_to_log(sprintf "Print started at %s\n", $self->_timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_completed {
|
sub print_completed {
|
||||||
@ -426,7 +430,7 @@ sub print_completed {
|
|||||||
$self->Layout;
|
$self->Layout;
|
||||||
|
|
||||||
$self->set_status('Print completed.');
|
$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;
|
$self->reload_jobs;
|
||||||
}
|
}
|
||||||
@ -479,7 +483,7 @@ sub reload_jobs {
|
|||||||
$self->{gauge}->Disable;
|
$self->{gauge}->Disable;
|
||||||
$self->{gauge}->Hide;
|
$self->{gauge}->Hide;
|
||||||
$self->set_status('Print was aborted.');
|
$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 {
|
$panel->on_resume_print(sub {
|
||||||
my ($job) = @_;
|
my ($job) = @_;
|
||||||
|
@ -18,7 +18,7 @@ our $qs_last_output_file;
|
|||||||
our $last_config;
|
our $last_config;
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ($class, %params) = @_;
|
my ($class) = @_;
|
||||||
|
|
||||||
my $self = $class->SUPER::new(undef, -1, 'Slic3r', wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
|
my $self = $class->SUPER::new(undef, -1, 'Slic3r', wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
|
||||||
if ($^O eq 'MSWin32') {
|
if ($^O eq 'MSWin32') {
|
||||||
@ -27,12 +27,6 @@ sub new {
|
|||||||
$self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
|
$self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
|
||||||
}
|
}
|
||||||
|
|
||||||
# store input params
|
|
||||||
$self->{mode} = $params{mode};
|
|
||||||
$self->{mode} = 'expert' if $self->{mode} !~ /^(?:simple|expert)$/;
|
|
||||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
|
||||||
$self->{no_controller} = $params{no_controller};
|
|
||||||
$self->{no_plater} = $params{no_plater};
|
|
||||||
$self->{loaded} = 0;
|
$self->{loaded} = 0;
|
||||||
|
|
||||||
# initialize tabpanel and menubar
|
# initialize tabpanel and menubar
|
||||||
@ -59,19 +53,8 @@ sub new {
|
|||||||
$self->SetSizer($sizer);
|
$self->SetSizer($sizer);
|
||||||
$self->Fit;
|
$self->Fit;
|
||||||
$self->SetMinSize([760, 490]);
|
$self->SetMinSize([760, 490]);
|
||||||
if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) {
|
$self->SetSize($self->GetMinSize);
|
||||||
my $size = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ];
|
wxTheApp->restore_window_pos($self, "main_frame");
|
||||||
$self->SetSize($size);
|
|
||||||
|
|
||||||
my $display = Wx::Display->new->GetClientArea();
|
|
||||||
my $pos = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ];
|
|
||||||
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
|
|
||||||
$self->Move($pos);
|
|
||||||
}
|
|
||||||
$self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized};
|
|
||||||
} else {
|
|
||||||
$self->SetSize($self->GetMinSize);
|
|
||||||
}
|
|
||||||
$self->Show;
|
$self->Show;
|
||||||
$self->Layout;
|
$self->Layout;
|
||||||
}
|
}
|
||||||
@ -80,16 +63,24 @@ sub new {
|
|||||||
EVT_CLOSE($self, sub {
|
EVT_CLOSE($self, sub {
|
||||||
my (undef, $event) = @_;
|
my (undef, $event) = @_;
|
||||||
|
|
||||||
if ($event->CanVeto && !$self->check_unsaved_changes) {
|
if ($event->CanVeto) {
|
||||||
$event->Veto;
|
if (!$self->{plater}->prompt_unsaved_changes) {
|
||||||
return;
|
$event->Veto;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self->{controller} && $self->{controller}->printing) {
|
||||||
|
my $confirm = Wx::MessageDialog->new($self, "You are currently printing. Do you want to stop printing and continue anyway?",
|
||||||
|
'Unfinished Print', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||||
|
if ($confirm->ShowModal == wxID_NO) {
|
||||||
|
$event->Veto;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# save window size
|
# save window size
|
||||||
$Slic3r::GUI::Settings->{_}{main_frame_pos} = join ',', $self->GetScreenPositionXY;
|
wxTheApp->save_window_pos($self, "main_frame");
|
||||||
$Slic3r::GUI::Settings->{_}{main_frame_size} = join ',', $self->GetSizeWH;
|
|
||||||
$Slic3r::GUI::Settings->{_}{main_frame_maximized} = $self->IsMaximized;
|
|
||||||
wxTheApp->save_settings;
|
|
||||||
|
|
||||||
# propagate event
|
# propagate event
|
||||||
$event->Skip;
|
$event->Skip;
|
||||||
@ -107,71 +98,10 @@ sub _init_tabpanel {
|
|||||||
$panel->OnActivate if $panel->can('OnActivate');
|
$panel->OnActivate if $panel->can('OnActivate');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!$self->{no_plater}) {
|
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
|
||||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
|
|
||||||
}
|
|
||||||
if (!$self->{no_controller}) {
|
if (!$self->{no_controller}) {
|
||||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
||||||
}
|
}
|
||||||
$self->{options_tabs} = {};
|
|
||||||
|
|
||||||
my $simple_config;
|
|
||||||
if ($self->{mode} eq 'simple') {
|
|
||||||
$simple_config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini")
|
|
||||||
if -e Slic3r::encode_path("$Slic3r::GUI::datadir/simple.ini");
|
|
||||||
}
|
|
||||||
|
|
||||||
my $class_prefix = $self->{mode} eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::";
|
|
||||||
for my $tab_name (qw(print filament printer)) {
|
|
||||||
my $tab;
|
|
||||||
$tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new(
|
|
||||||
$panel,
|
|
||||||
no_controller => $self->{no_controller});
|
|
||||||
$tab->on_value_change(sub {
|
|
||||||
my ($opt_key, $value) = @_;
|
|
||||||
|
|
||||||
my $config = $tab->config;
|
|
||||||
if ($self->{plater}) {
|
|
||||||
$self->{plater}->on_config_change($config); # propagate config change events to the plater
|
|
||||||
$self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count';
|
|
||||||
}
|
|
||||||
if ($self->{loaded}) { # don't save while loading for the first time
|
|
||||||
if ($self->{mode} eq 'simple') {
|
|
||||||
# save config
|
|
||||||
$self->config->save("$Slic3r::GUI::datadir/simple.ini");
|
|
||||||
|
|
||||||
# save a copy into each preset section
|
|
||||||
# so that user gets the config when switching to expert mode
|
|
||||||
$config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $tab->name, 'Simple Mode');
|
|
||||||
$Slic3r::GUI::Settings->{presets}{$tab->name} = 'Simple Mode.ini';
|
|
||||||
wxTheApp->save_settings;
|
|
||||||
}
|
|
||||||
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$tab->on_presets_changed(sub {
|
|
||||||
if ($self->{plater}) {
|
|
||||||
$self->{plater}->update_presets($tab_name, @_);
|
|
||||||
$self->{plater}->on_config_change($tab->config);
|
|
||||||
if ($self->{controller}) {
|
|
||||||
$self->{controller}->update_presets($tab_name, @_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$tab->load_presets;
|
|
||||||
$panel->AddPage($tab, $tab->title);
|
|
||||||
$tab->load_config($simple_config) if $simple_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($self->{plater}) {
|
|
||||||
$self->{plater}->on_select_preset(sub {
|
|
||||||
my ($group, $i) = @_;
|
|
||||||
$self->{options_tabs}{$group}->select_preset($i);
|
|
||||||
});
|
|
||||||
|
|
||||||
# load initial config
|
|
||||||
$self->{plater}->on_config_change($self->config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _init_menubar {
|
sub _init_menubar {
|
||||||
@ -239,10 +169,22 @@ sub _init_menubar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Plater menu
|
# Plater menu
|
||||||
unless ($self->{no_plater}) {
|
{
|
||||||
my $plater = $self->{plater};
|
my $plater = $self->{plater};
|
||||||
|
|
||||||
$self->{plater_menu} = Wx::Menu->new;
|
$self->{plater_menu} = Wx::Menu->new;
|
||||||
|
{
|
||||||
|
my $selectMenu = $self->{plater_select_menu} = Wx::Menu->new;
|
||||||
|
my $selectMenuItem = $self->{plater_menu}->AppendSubMenu($selectMenu, "Select", 'Select an object in the plater');
|
||||||
|
wxTheApp->set_menu_item_icon($selectMenuItem, 'brick.png');
|
||||||
|
}
|
||||||
|
$self->_append_menu_item($self->{plater_menu}, "Select Next Object\tCtrl+Right", 'Select Next Object in the plater', sub {
|
||||||
|
$plater->select_next;
|
||||||
|
}, undef, 'arrow_right.png');
|
||||||
|
$self->_append_menu_item($self->{plater_menu}, "Select Prev Object\tCtrl+Left", 'Select Previous Object in the plater', sub {
|
||||||
|
$plater->select_prev;
|
||||||
|
}, undef, 'arrow_left.png');
|
||||||
|
$self->{plater_menu}->AppendSeparator();
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
|
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
|
||||||
$plater->export_gcode;
|
$plater->export_gcode;
|
||||||
}, undef, 'cog_go.png');
|
}, undef, 'cog_go.png');
|
||||||
@ -252,57 +194,35 @@ sub _init_menubar {
|
|||||||
$self->_append_menu_item($self->{plater_menu}, "Export plate with modifiers as AMF...", 'Export current plate as AMF, including all modifier meshes', sub {
|
$self->_append_menu_item($self->{plater_menu}, "Export plate with modifiers as AMF...", 'Export current plate as AMF, including all modifier meshes', sub {
|
||||||
$plater->export_amf;
|
$plater->export_amf;
|
||||||
}, undef, 'brick_go.png');
|
}, undef, 'brick_go.png');
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub {
|
|
||||||
$plater->pause_background_process;
|
|
||||||
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
|
|
||||||
$plater->resume_background_process;
|
|
||||||
}, undef, 'film.png');
|
|
||||||
|
|
||||||
$self->{object_menu} = $self->{plater}->object_menu;
|
$self->{object_menu} = $self->{plater}->object_menu;
|
||||||
$self->on_plater_selection_changed(0);
|
$self->on_plater_selection_changed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Window menu
|
# Settings menu
|
||||||
my $windowMenu = Wx::Menu->new;
|
my $settingsMenu = Wx::Menu->new;
|
||||||
{
|
{
|
||||||
my $tab_offset = 0;
|
$self->_append_menu_item($settingsMenu, "P&rint Settings…\tCtrl+1", 'Show the print settings editor', sub {
|
||||||
if (!$self->{no_plater}) {
|
$self->{plater}->show_preset_editor('print');
|
||||||
$self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub {
|
|
||||||
$self->select_tab(0);
|
|
||||||
}, undef, 'application_view_tile.png');
|
|
||||||
$tab_offset += 1;
|
|
||||||
}
|
|
||||||
if (!$self->{no_controller}) {
|
|
||||||
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
|
|
||||||
$self->select_tab(1);
|
|
||||||
}, undef, 'printer_empty.png');
|
|
||||||
$tab_offset += 1;
|
|
||||||
}
|
|
||||||
if ($tab_offset > 0) {
|
|
||||||
$windowMenu->AppendSeparator();
|
|
||||||
}
|
|
||||||
|
|
||||||
$self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub {
|
|
||||||
$self->select_tab($tab_offset+0);
|
|
||||||
}, undef, 'cog.png');
|
}, undef, 'cog.png');
|
||||||
$self->_append_menu_item($windowMenu, "Select &Filament Settings Tab\tCtrl+3", 'Show the filament settings', sub {
|
$self->_append_menu_item($settingsMenu, "&Filament Settings…\tCtrl+2", 'Show the filament settings editor', sub {
|
||||||
$self->select_tab($tab_offset+1);
|
$self->{plater}->show_preset_editor('filament');
|
||||||
}, undef, 'spool.png');
|
}, undef, 'spool.png');
|
||||||
$self->_append_menu_item($windowMenu, "Select Print&er Settings Tab\tCtrl+4", 'Show the printer settings', sub {
|
$self->_append_menu_item($settingsMenu, "Print&er Settings…\tCtrl+3", 'Show the printer settings editor', sub {
|
||||||
$self->select_tab($tab_offset+2);
|
$self->{plater}->show_preset_editor('printer');
|
||||||
}, undef, 'printer_empty.png');
|
}, undef, 'printer_empty.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
# View menu
|
# View menu
|
||||||
if (!$self->{no_plater}) {
|
{
|
||||||
$self->{viewMenu} = Wx::Menu->new;
|
$self->{viewMenu} = Wx::Menu->new;
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Top\tCtrl+4" , 'Top View' , sub { $self->select_view('top' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Bottom\tCtrl+5" , 'Bottom View' , sub { $self->select_view('bottom' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Left\tCtrl+6" , 'Left View' , sub { $self->select_view('left' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Right\tCtrl+7" , 'Right View' , sub { $self->select_view('right' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Rear" , 'Rear View' , sub { $self->select_view('rear' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Front\tCtrl+8" , 'Front View' , sub { $self->select_view('front' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Left" , 'Left View' , sub { $self->select_view('left' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Back\tCtrl+9" , 'Back View' , sub { $self->select_view('back' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, "Right" , 'Right View' , sub { $self->select_view('right' ); });
|
$self->_append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); });
|
||||||
$self->{viewMenu}->AppendSeparator();
|
$self->{viewMenu}->AppendSeparator();
|
||||||
$self->{color_toolpaths_by_role} = $self->_append_menu_item($self->{viewMenu},
|
$self->{color_toolpaths_by_role} = $self->_append_menu_item($self->{viewMenu},
|
||||||
"Color Toolpaths by Role",
|
"Color Toolpaths by Role",
|
||||||
@ -331,6 +251,22 @@ sub _init_menubar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Window menu
|
||||||
|
my $windowMenu = Wx::Menu->new;
|
||||||
|
{
|
||||||
|
$self->_append_menu_item($windowMenu, "&Plater\tCtrl+T", 'Show the plater', sub {
|
||||||
|
$self->select_tab(0);
|
||||||
|
}, undef, 'application_view_tile.png');
|
||||||
|
$self->_append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub {
|
||||||
|
$self->select_tab(1);
|
||||||
|
}, undef, 'printer_empty.png') if !$self->{no_controller};
|
||||||
|
$self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
|
||||||
|
$self->{plater}->pause_background_process;
|
||||||
|
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
|
||||||
|
$self->{plater}->resume_background_process;
|
||||||
|
}, undef, 'film.png');
|
||||||
|
}
|
||||||
|
|
||||||
# Help menu
|
# Help menu
|
||||||
my $helpMenu = Wx::Menu->new;
|
my $helpMenu = Wx::Menu->new;
|
||||||
{
|
{
|
||||||
@ -362,8 +298,9 @@ sub _init_menubar {
|
|||||||
$menubar->Append($fileMenu, "&File");
|
$menubar->Append($fileMenu, "&File");
|
||||||
$menubar->Append($self->{plater_menu}, "&Plater") if $self->{plater_menu};
|
$menubar->Append($self->{plater_menu}, "&Plater") if $self->{plater_menu};
|
||||||
$menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu};
|
$menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu};
|
||||||
$menubar->Append($windowMenu, "&Window");
|
$menubar->Append($settingsMenu, "&Settings");
|
||||||
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
|
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
|
||||||
|
$menubar->Append($windowMenu, "&Window");
|
||||||
$menubar->Append($helpMenu, "&Help");
|
$menubar->Append($helpMenu, "&Help");
|
||||||
$self->SetMenuBar($menubar);
|
$self->SetMenuBar($menubar);
|
||||||
}
|
}
|
||||||
@ -389,7 +326,7 @@ sub quick_slice {
|
|||||||
my $progress_dialog;
|
my $progress_dialog;
|
||||||
eval {
|
eval {
|
||||||
# validate configuration
|
# validate configuration
|
||||||
my $config = $self->config;
|
my $config = $self->{plater}->config;
|
||||||
$config->validate;
|
$config->validate;
|
||||||
|
|
||||||
# select input file
|
# select input file
|
||||||
@ -531,7 +468,7 @@ sub repair_stl {
|
|||||||
sub export_config {
|
sub export_config {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
my $config = $self->config;
|
my $config = $self->{plater}->config;
|
||||||
eval {
|
eval {
|
||||||
# validate configuration
|
# validate configuration
|
||||||
$config->validate;
|
$config->validate;
|
||||||
@ -557,7 +494,6 @@ sub load_config_file {
|
|||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
|
|
||||||
if (!$file) {
|
if (!$file) {
|
||||||
return unless $self->check_unsaved_changes;
|
|
||||||
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
||||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
@ -568,9 +504,10 @@ sub load_config_file {
|
|||||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||||
wxTheApp->save_settings;
|
wxTheApp->save_settings;
|
||||||
$last_config = $file;
|
$last_config = $file;
|
||||||
for my $tab (values %{$self->{options_tabs}}) {
|
|
||||||
$tab->load_config_file($file);
|
my $name = wxTheApp->add_external_preset($file);
|
||||||
}
|
$self->{plater}->load_presets;
|
||||||
|
$self->{plater}->select_preset_by_name($name, $_) for qw(print filament printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub export_configbundle {
|
sub export_configbundle {
|
||||||
@ -578,7 +515,7 @@ sub export_configbundle {
|
|||||||
|
|
||||||
eval {
|
eval {
|
||||||
# validate current configuration in case it's dirty
|
# validate current configuration in case it's dirty
|
||||||
$self->config->validate;
|
$self->{plater}->config->validate;
|
||||||
};
|
};
|
||||||
Slic3r::GUI::catch_error($self) and return;
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
|
|
||||||
@ -593,18 +530,14 @@ sub export_configbundle {
|
|||||||
|
|
||||||
# leave default category empty to prevent the bundle from being parsed as a normal config file
|
# leave default category empty to prevent the bundle from being parsed as a normal config file
|
||||||
my $ini = { _ => {} };
|
my $ini = { _ => {} };
|
||||||
$ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter mode);
|
$ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter);
|
||||||
$ini->{presets} = $Slic3r::GUI::Settings->{presets};
|
$ini->{presets} = $Slic3r::GUI::Settings->{presets};
|
||||||
if (-e "$Slic3r::GUI::datadir/simple.ini") {
|
|
||||||
my $config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini");
|
|
||||||
$ini->{simple} = $config->as_ini->{_};
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach my $section (qw(print filament printer)) {
|
foreach my $section (qw(print filament printer)) {
|
||||||
my %presets = wxTheApp->presets($section);
|
my @presets = @{wxTheApp->presets->{$section}};
|
||||||
foreach my $preset_name (keys %presets) {
|
foreach my $preset (@presets) {
|
||||||
my $config = Slic3r::Config->load($presets{$preset_name});
|
next if $preset->default || $preset->external;
|
||||||
$ini->{"$section:$preset_name"} = $config->as_ini->{_};
|
$ini->{"$section:" . $preset->name} = $preset->load_config->as_ini->{_};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,15 +572,6 @@ sub load_configbundle {
|
|||||||
$Slic3r::GUI::Settings->{presets} = $ini->{presets};
|
$Slic3r::GUI::Settings->{presets} = $ini->{presets};
|
||||||
wxTheApp->save_settings;
|
wxTheApp->save_settings;
|
||||||
}
|
}
|
||||||
if ($ini->{simple}) {
|
|
||||||
my $config = Slic3r::Config->load_ini_hash($ini->{simple});
|
|
||||||
$config->save("$Slic3r::GUI::datadir/simple.ini");
|
|
||||||
if ($self->{mode} eq 'simple') {
|
|
||||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
|
||||||
$tab->load_config($config) for values %{$self->{options_tabs}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
my $imported = 0;
|
my $imported = 0;
|
||||||
INI_BLOCK: foreach my $ini_category (sort keys %$ini) {
|
INI_BLOCK: foreach my $ini_category (sort keys %$ini) {
|
||||||
next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
|
next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
|
||||||
@ -656,11 +580,11 @@ sub load_configbundle {
|
|||||||
next if $skip_no_id && !$config->get($section . "_settings_id");
|
next if $skip_no_id && !$config->get($section . "_settings_id");
|
||||||
|
|
||||||
{
|
{
|
||||||
my %current_presets = Slic3r::GUI->presets($section);
|
my @current_presets = @{wxTheApp->presets->{$section}};
|
||||||
my %current_ids = map { $_ => 1 }
|
my %current_ids = map { $_ => 1 }
|
||||||
grep $_,
|
grep $_,
|
||||||
map Slic3r::Config->load($_)->get($section . "_settings_id"),
|
map $_->dirty_config->get($section . "_settings_id"),
|
||||||
values %current_presets;
|
@current_presets;
|
||||||
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
|
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,150 +592,34 @@ sub load_configbundle {
|
|||||||
Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name;
|
Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name;
|
||||||
$imported++;
|
$imported++;
|
||||||
}
|
}
|
||||||
if ($self->{mode} eq 'expert') {
|
$self->{plater}->load_presets;
|
||||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
|
||||||
$tab->load_presets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if !$imported;
|
return if !$imported;
|
||||||
|
|
||||||
my $message = sprintf "%d presets successfully imported.", $imported;
|
my $message = sprintf "%d presets successfully imported.", $imported;
|
||||||
if ($self->{mode} eq 'simple' && $Slic3r::GUI::Settings->{_}{mode} eq 'expert') {
|
Slic3r::GUI::show_info($self, $message);
|
||||||
Slic3r::GUI::show_info($self, "$message You need to restart Slic3r to make the changes effective.");
|
|
||||||
} else {
|
|
||||||
Slic3r::GUI::show_info($self, $message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_config {
|
sub load_config {
|
||||||
my $self = shift;
|
my ($self, $config) = @_;
|
||||||
my ($config) = @_;
|
|
||||||
|
|
||||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
$self->{plater}->load_config($config);
|
||||||
$tab->load_config($config);
|
|
||||||
}
|
|
||||||
if ($self->{plater}) {
|
|
||||||
$self->{plater}->on_config_change($config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub config_wizard {
|
sub config_wizard {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
return unless $self->check_unsaved_changes;
|
|
||||||
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
|
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
|
||||||
if ($self->{mode} eq 'expert') {
|
foreach my $group (qw(print filament printer)) {
|
||||||
for my $tab (values %{$self->{options_tabs}}) {
|
my $name = 'My Settings';
|
||||||
$tab->select_default_preset;
|
$config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $group, $name);
|
||||||
}
|
$Slic3r::GUI::Settings->{presets}{$group} = "$name.ini";
|
||||||
} else {
|
$self->{plater}->load_presets;
|
||||||
# TODO: select default settings in simple mode
|
$self->{plater}->select_preset_by_name($name, $group);
|
||||||
}
|
|
||||||
$self->load_config($config);
|
|
||||||
if ($self->{mode} eq 'expert') {
|
|
||||||
for my $tab (values %{$self->{options_tabs}}) {
|
|
||||||
$tab->save_preset('My Settings');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=head2 config
|
|
||||||
|
|
||||||
This method collects all config values from the tabs and merges them into a single config object.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub config {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
return Slic3r::Config->new_from_defaults
|
|
||||||
if !exists $self->{options_tabs}{print}
|
|
||||||
|| !exists $self->{options_tabs}{filament}
|
|
||||||
|| !exists $self->{options_tabs}{printer};
|
|
||||||
|
|
||||||
# retrieve filament presets and build a single config object for them
|
|
||||||
my $filament_config;
|
|
||||||
if (!$self->{plater} || $self->{plater}->filament_presets == 1 || $self->{mode} eq 'simple') {
|
|
||||||
$filament_config = $self->{options_tabs}{filament}->config;
|
|
||||||
} else {
|
|
||||||
my $i = -1;
|
|
||||||
foreach my $preset_idx ($self->{plater}->filament_presets) {
|
|
||||||
$i++;
|
|
||||||
my $config;
|
|
||||||
if ($preset_idx == $self->{options_tabs}{filament}->current_preset) {
|
|
||||||
# the selected preset for this extruder is the one in the tab
|
|
||||||
# use the tab's config instead of the preset in case it is dirty
|
|
||||||
# perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
|
|
||||||
$config = $self->{options_tabs}{filament}->config;
|
|
||||||
} else {
|
|
||||||
my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
|
|
||||||
$config = $self->{options_tabs}{filament}->get_preset_config($preset);
|
|
||||||
}
|
|
||||||
if (!$filament_config) {
|
|
||||||
$filament_config = $config->clone;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
foreach my $opt_key (@{$config->get_keys}) {
|
|
||||||
my $value = $filament_config->get($opt_key);
|
|
||||||
next unless ref $value eq 'ARRAY';
|
|
||||||
$value->[$i] = $config->get($opt_key)->[0];
|
|
||||||
$filament_config->set($opt_key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my $config = Slic3r::Config->merge(
|
|
||||||
Slic3r::Config->new_from_defaults,
|
|
||||||
$self->{options_tabs}{print}->config,
|
|
||||||
$self->{options_tabs}{printer}->config,
|
|
||||||
$filament_config,
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($self->{mode} eq 'simple') {
|
|
||||||
# set some sensible defaults
|
|
||||||
$config->set('first_layer_height', $config->nozzle_diameter->[0]);
|
|
||||||
$config->set('avoid_crossing_perimeters', 1);
|
|
||||||
$config->set('infill_every_layers', 10);
|
|
||||||
} else {
|
|
||||||
my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
|
|
||||||
$config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
|
|
||||||
for qw(perimeter infill solid_infill support_material support_material_interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub filament_preset_names {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
if ($self->{mode} eq 'simple') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return map $self->{options_tabs}{filament}->get_preset($_)->name,
|
|
||||||
$self->{plater}->filament_presets;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub check_unsaved_changes {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
my @dirty = ();
|
|
||||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
|
||||||
push @dirty, $tab->title if $tab->is_dirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@dirty) {
|
|
||||||
my $titles = join ', ', @dirty;
|
|
||||||
my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?",
|
|
||||||
'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
|
||||||
return ($confirm->ShowModal == wxID_YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub select_tab {
|
sub select_tab {
|
||||||
my ($self, $tab) = @_;
|
my ($self, $tab) = @_;
|
||||||
$self->{tabpanel}->SetSelection($tab);
|
$self->{tabpanel}->SetSelection($tab);
|
||||||
@ -820,9 +628,8 @@ sub select_tab {
|
|||||||
# Set a camera direction, zoom to all objects.
|
# Set a camera direction, zoom to all objects.
|
||||||
sub select_view {
|
sub select_view {
|
||||||
my ($self, $direction) = @_;
|
my ($self, $direction) = @_;
|
||||||
if (! $self->{no_plater}) {
|
|
||||||
$self->{plater}->select_view($direction);
|
$self->{plater}->select_view($direction);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _append_menu_item {
|
sub _append_menu_item {
|
||||||
@ -830,19 +637,10 @@ sub _append_menu_item {
|
|||||||
|
|
||||||
$id //= &Wx::NewId();
|
$id //= &Wx::NewId();
|
||||||
my $item = $menu->Append($id, $string, $description, $kind);
|
my $item = $menu->Append($id, $string, $description, $kind);
|
||||||
$self->_set_menu_item_icon($item, $icon);
|
wxTheApp->set_menu_item_icon($item, $icon);
|
||||||
|
|
||||||
EVT_MENU($self, $id, $cb);
|
EVT_MENU($self, $id, $cb);
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _set_menu_item_icon {
|
|
||||||
my ($self, $menuItem, $icon) = @_;
|
|
||||||
|
|
||||||
# SetBitmap was not available on OS X before Wx 0.9927
|
|
||||||
if ($icon && $menuItem->can('SetBitmap')) {
|
|
||||||
$menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# A dialog group object. Used by the Tab, SimpleTab, Preferences dialog, ManualControlDialog etc.
|
# A dialog group object. Used by the PresetEditor, Preferences dialog, ManualControlDialog etc.
|
||||||
|
|
||||||
package Slic3r::GUI::OptionsGroup;
|
package Slic3r::GUI::OptionsGroup;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,8 +15,8 @@ sub new {
|
|||||||
my ($parent, %params) = @_;
|
my ($parent, %params) = @_;
|
||||||
my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||||
$self->{model_object} = $params{model_object};
|
$self->{model_object} = $params{model_object};
|
||||||
my $model_object = $self->{model_object} = $params{model_object};
|
my $model_object = $self->{model_object} = $params{model_object};
|
||||||
my $obj_idx = $self->{obj_idx} = $params{obj_idx};
|
my $obj_idx = $self->{obj_idx} = $params{obj_idx};
|
||||||
my $plater = $self->{plater} = $parent;
|
my $plater = $self->{plater} = $parent;
|
||||||
my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx});
|
my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx});
|
||||||
|
|
||||||
@ -32,9 +32,9 @@ sub new {
|
|||||||
$self->{preview3D}->canvas->zoom_to_volumes;
|
$self->{preview3D}->canvas->zoom_to_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $object);
|
$self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $model_object);
|
||||||
|
|
||||||
my $quality_slider = $self->{quality_slider} = Wx::Slider->new(
|
my $quality_slider = $self->{quality_slider} = Wx::Slider->new(
|
||||||
$self, -1,
|
$self, -1,
|
||||||
0, # default
|
0, # default
|
||||||
0, # min
|
0, # min
|
||||||
@ -56,20 +56,20 @@ sub new {
|
|||||||
$value_label->SetFont($Slic3r::GUI::small_font);
|
$value_label->SetFont($Slic3r::GUI::small_font);
|
||||||
$speed_label->SetFont($Slic3r::GUI::small_font);
|
$speed_label->SetFont($Slic3r::GUI::small_font);
|
||||||
|
|
||||||
my $quality_label_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $quality_label_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$quality_label_sizer->Add($quality_label, 1, wxEXPAND | wxALL, 0);
|
$quality_label_sizer->Add($quality_label, 1, wxEXPAND | wxALL, 0);
|
||||||
$quality_label_sizer->Add($value_label, 1, wxEXPAND | wxALL, 0);
|
$quality_label_sizer->Add($value_label, 1, wxEXPAND | wxALL, 0);
|
||||||
$quality_label_sizer->Add($speed_label, 1, wxEXPAND | wxALL, 0);
|
$quality_label_sizer->Add($speed_label, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0);
|
$right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0);
|
||||||
$right_sizer->Add($quality_slider, 0, wxEXPAND | wxALL, 0);
|
$right_sizer->Add($quality_slider, 0, wxEXPAND | wxALL, 0);
|
||||||
$right_sizer->Add($quality_label_sizer, 0, wxEXPAND | wxALL, 0);
|
$right_sizer->Add($quality_label_sizer, 0, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
|
||||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D};
|
$self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D};
|
||||||
$self->{sizer}->Add($right_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
$self->{sizer}->Add($right_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||||
|
|
||||||
$self->SetSizerAndFit($self->{sizer});
|
$self->SetSizerAndFit($self->{sizer});
|
||||||
$self->SetSize([800, 600]);
|
$self->SetSize([800, 600]);
|
||||||
@ -79,10 +79,10 @@ sub new {
|
|||||||
# determine min and max layer height from perimeter extruder capabilities.
|
# determine min and max layer height from perimeter extruder capabilities.
|
||||||
my %extruders;
|
my %extruders;
|
||||||
for my $region_id (0 .. ($object->region_count - 1)) {
|
for my $region_id (0 .. ($object->region_count - 1)) {
|
||||||
foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) {
|
foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) {
|
||||||
my $extruder_id = $self->{plater}->{print}->get_region($region_id)->config->get($_)-1;
|
my $extruder_id = $self->{plater}->{print}->get_region($region_id)->config->get($_)-1;
|
||||||
$extruders{$extruder_id} = $extruder_id;
|
$extruders{$extruder_id} = $extruder_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders));
|
my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders));
|
||||||
my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders));
|
my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders));
|
||||||
@ -92,9 +92,10 @@ sub new {
|
|||||||
|
|
||||||
$self->{splineControl}->on_layer_update(sub {
|
$self->{splineControl}->on_layer_update(sub {
|
||||||
# trigger re-slicing
|
# trigger re-slicing
|
||||||
$self->{plater}->stop_background_process;
|
$self->_trigger_slicing;
|
||||||
$self->{object}->invalidate_step(STEP_SLICE);
|
#$self->{plater}->stop_background_process;
|
||||||
$self->{plater}->start_background_process;
|
#$self->{object}->invalidate_step(STEP_SLICE);
|
||||||
|
#$self->{plater}->start_background_process;
|
||||||
});
|
});
|
||||||
|
|
||||||
$self->{splineControl}->on_z_indicator(sub {
|
$self->{splineControl}->on_z_indicator(sub {
|
||||||
@ -117,28 +118,44 @@ sub new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EVT_SLIDER($self, $quality_slider, sub {
|
EVT_SLIDER($self, $quality_slider, sub {
|
||||||
$self->{plater}->pause_background_process;
|
|
||||||
my $quality_value = $quality_slider->GetValue/100;
|
my $quality_value = $quality_slider->GetValue/100;
|
||||||
$value_label->SetLabel(sprintf '%.2f', $quality_value);
|
$value_label->SetLabel(sprintf '%.2f', $quality_value);
|
||||||
$self->{model_object}->config->set('adaptive_slicing_quality', $quality_value);
|
$self->{model_object}->config->set('adaptive_slicing_quality', $quality_value);
|
||||||
|
|
||||||
# trigger re-slicing
|
# trigger re-slicing
|
||||||
$self->{plater}->stop_background_process;
|
$self->_trigger_slicing;
|
||||||
$self->{object}->invalidate_step(STEP_SLICE);
|
|
||||||
$self->{plater}->schedule_background_process;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _trigger_slicing {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->{plater}->stop_background_process;
|
||||||
|
$self->{object}->invalidate_step(STEP_SLICE);
|
||||||
|
if (!$Slic3r::GUI::Settings->{_}{background_processing}) {
|
||||||
|
$self->{plater}->statusbar->SetCancelCallback(sub {
|
||||||
|
$self->{plater}->stop_background_process;
|
||||||
|
$self->{plater}->statusbar->SetStatusText("Slicing cancelled");
|
||||||
|
$self->{plater}->preview_notebook->SetSelection(0);
|
||||||
|
});
|
||||||
|
$self->{plater}->{print}->reload_object($self->{obj_idx});
|
||||||
|
$self->{plater}->on_model_change;
|
||||||
|
$self->{plater}->start_background_process;
|
||||||
|
}else{
|
||||||
|
$self->{plater}->schedule_background_process;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub reload_preview {
|
sub reload_preview {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
$self->{splineControl}->update;
|
$self->{splineControl}->update;
|
||||||
$self->{preview3D}->reload_print;
|
$self->{preview3D}->reload_print;
|
||||||
if($self->{object}->layer_count-1 > 0) {
|
if($self->{object}->layer_count-1 > 0) {
|
||||||
my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1);
|
# causes segfault...
|
||||||
$self->{preview3D}->set_z($top_layer->print_z);
|
#my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1);
|
||||||
}
|
#$self->{preview3D}->set_z($top_layer->print_z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Included in ObjectSettingsDialog -> ObjectPartsPanel.
|
# Maintains, displays, adds and removes overrides of slicing parameters.
|
||||||
# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh.
|
|
||||||
|
|
||||||
package Slic3r::GUI::Plater::OverrideSettingsPanel;
|
package Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||||
use strict;
|
use strict;
|
||||||
@ -16,13 +15,26 @@ use constant ICON_MATERIAL => 0;
|
|||||||
use constant ICON_SOLIDMESH => 1;
|
use constant ICON_SOLIDMESH => 1;
|
||||||
use constant ICON_MODIFIERMESH => 2;
|
use constant ICON_MODIFIERMESH => 2;
|
||||||
|
|
||||||
|
my %icons = (
|
||||||
|
'Advanced' => 'wand.png',
|
||||||
|
'Extruders' => 'funnel.png',
|
||||||
|
'Extrusion Width' => 'funnel.png',
|
||||||
|
'Infill' => 'infill.png',
|
||||||
|
'Layers and Perimeters' => 'layers.png',
|
||||||
|
'Skirt and brim' => 'box.png',
|
||||||
|
'Speed' => 'time.png',
|
||||||
|
'Speed > Acceleration' => 'time.png',
|
||||||
|
'Support material' => 'building.png',
|
||||||
|
);
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($parent, %params) = @_;
|
my ($parent, %params) = @_;
|
||||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $params{size} // wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
$self->{default_config} = Slic3r::Config->new;
|
$self->{default_config} = Slic3r::Config->new;
|
||||||
$self->{config} = Slic3r::Config->new;
|
$self->{config} = Slic3r::Config->new;
|
||||||
$self->{on_change} = $params{on_change};
|
$self->{on_change} = $params{on_change};
|
||||||
|
$self->{editable} = 1;
|
||||||
$self->{fixed_options} = {};
|
$self->{fixed_options} = {};
|
||||||
|
|
||||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
@ -35,15 +47,31 @@ sub new {
|
|||||||
# create the button
|
# create the button
|
||||||
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
|
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
|
||||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||||
|
$btn->SetToolTipString("Override one more option")
|
||||||
|
if $btn->can('SetToolTipString');
|
||||||
EVT_LEFT_DOWN($btn, sub {
|
EVT_LEFT_DOWN($btn, sub {
|
||||||
my $menu = Wx::Menu->new;
|
my $menu = Wx::Menu->new;
|
||||||
|
my $last_cat = '';
|
||||||
foreach my $opt_key (@{$self->{options}}) {
|
foreach my $opt_key (@{$self->{options}}) {
|
||||||
my $id = &Wx::NewId();
|
my $id = &Wx::NewId();
|
||||||
$menu->Append($id, $self->{option_labels}{$opt_key});
|
|
||||||
|
# add icon, if we have one for this category
|
||||||
|
my $icon;
|
||||||
|
if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) {
|
||||||
|
if ($last_cat && $cat ne $last_cat) {
|
||||||
|
$menu->AppendSeparator;
|
||||||
|
}
|
||||||
|
$last_cat = $cat;
|
||||||
|
$icon = $icons{$cat};
|
||||||
|
}
|
||||||
|
|
||||||
|
my $menuItem = $menu->Append($id, $self->{option_labels}{$opt_key});
|
||||||
|
wxTheApp->set_menu_item_icon($menuItem, $icon) if $icon;
|
||||||
|
|
||||||
EVT_MENU($menu, $id, sub {
|
EVT_MENU($menu, $id, sub {
|
||||||
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
|
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
|
||||||
$self->update_optgroup;
|
$self->update_optgroup;
|
||||||
$self->{on_change}->() if $self->{on_change};
|
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$self->PopupMenu($menu, $btn->GetPosition);
|
$self->PopupMenu($menu, $btn->GetPosition);
|
||||||
@ -64,39 +92,59 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Sets the config used to get the default values for user-added options.
|
||||||
sub set_default_config {
|
sub set_default_config {
|
||||||
my ($self, $config) = @_;
|
my ($self, $config) = @_;
|
||||||
$self->{default_config} = $config;
|
$self->{default_config} = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Sets the target config, whose options will be displayed in the OptionsGroup.
|
||||||
sub set_config {
|
sub set_config {
|
||||||
my ($self, $config) = @_;
|
my ($self, $config) = @_;
|
||||||
$self->{config} = $config;
|
$self->{config} = $config;
|
||||||
$self->update_optgroup;
|
$self->update_optgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Sets the options listed in the Add button.
|
||||||
sub set_opt_keys {
|
sub set_opt_keys {
|
||||||
my ($self, $opt_keys) = @_;
|
my ($self, $opt_keys) = @_;
|
||||||
|
|
||||||
# sort options by category+label
|
# sort options by category+label
|
||||||
$self->{option_labels} = {
|
$self->{option_labels} = {};
|
||||||
map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) } @$opt_keys
|
foreach my $opt_key (@$opt_keys) {
|
||||||
|
my $def = $Slic3r::Config::Options->{$opt_key} or next;
|
||||||
|
if (!$def->{category}) {
|
||||||
|
#printf "Skipping %s\n", $opt_key;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$self->{option_labels}{$opt_key} = sprintf '%s > %s',
|
||||||
|
$def->{category},
|
||||||
|
$def->{full_label} // $def->{label};
|
||||||
};
|
};
|
||||||
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
|
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } keys %{$self->{option_labels}} ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Sets the options that user can't remove.
|
||||||
sub set_fixed_options {
|
sub set_fixed_options {
|
||||||
my ($self, $opt_keys) = @_;
|
my ($self, $opt_keys) = @_;
|
||||||
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
|
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
|
||||||
$self->update_optgroup;
|
$self->update_optgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub fixed_options {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return keys %{$self->{fixed_options}};
|
||||||
|
}
|
||||||
|
|
||||||
sub update_optgroup {
|
sub update_optgroup {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->{options_sizer}->Clear(1);
|
$self->{options_sizer}->Clear(1);
|
||||||
return if !defined $self->{config};
|
return if !defined $self->{config};
|
||||||
|
|
||||||
|
$self->{btn_add}->Show($self->{editable});
|
||||||
|
|
||||||
my %categories = ();
|
my %categories = ();
|
||||||
foreach my $opt_key (@{$self->{config}->get_keys}) {
|
foreach my $opt_key (@{$self->{config}->get_keys}) {
|
||||||
my $category = $Slic3r::Config::Options->{$opt_key}{category};
|
my $category = $Slic3r::Config::Options->{$opt_key}{category};
|
||||||
@ -104,7 +152,8 @@ sub update_optgroup {
|
|||||||
push @{$categories{$category}}, $opt_key;
|
push @{$categories{$category}}, $opt_key;
|
||||||
}
|
}
|
||||||
foreach my $category (sort keys %categories) {
|
foreach my $category (sort keys %categories) {
|
||||||
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
|
my $optgroup;
|
||||||
|
$optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
|
||||||
parent => $self,
|
parent => $self,
|
||||||
title => $category,
|
title => $category,
|
||||||
config => $self->{config},
|
config => $self->{config},
|
||||||
@ -112,27 +161,33 @@ sub update_optgroup {
|
|||||||
label_font => $Slic3r::GUI::small_font,
|
label_font => $Slic3r::GUI::small_font,
|
||||||
sidetext_font => $Slic3r::GUI::small_font,
|
sidetext_font => $Slic3r::GUI::small_font,
|
||||||
label_width => 120,
|
label_width => 120,
|
||||||
on_change => sub { $self->{on_change}->() if $self->{on_change} },
|
on_change => sub {
|
||||||
|
my ($opt_key) = @_;
|
||||||
|
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||||
|
},
|
||||||
extra_column => sub {
|
extra_column => sub {
|
||||||
my ($line) = @_;
|
my ($line) = @_;
|
||||||
|
|
||||||
my $opt_key = $line->get_options->[0]->opt_id; # we assume that we have one option per line
|
my $opt_id = $line->get_options->[0]->opt_id; # we assume that we have one option per line
|
||||||
|
my ($opt_key, $opt_index) = @{ $optgroup->_opt_map->{$opt_id} };
|
||||||
|
|
||||||
# disallow deleting fixed options
|
# disallow deleting fixed options
|
||||||
return undef if $self->{fixed_options}{$opt_key};
|
return undef if $self->{fixed_options}{$opt_key} || !$self->{editable};
|
||||||
|
|
||||||
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
|
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
|
||||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||||
EVT_BUTTON($self, $btn, sub {
|
EVT_BUTTON($self, $btn, sub {
|
||||||
$self->{config}->erase($opt_key);
|
$self->{config}->erase($opt_key);
|
||||||
$self->{on_change}->() if $self->{on_change};
|
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||||
wxTheApp->CallAfter(sub { $self->update_optgroup });
|
wxTheApp->CallAfter(sub { $self->update_optgroup });
|
||||||
});
|
});
|
||||||
return $btn;
|
return $btn;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
foreach my $opt_key (sort @{$categories{$category}}) {
|
foreach my $opt_key (sort @{$categories{$category}}) {
|
||||||
$optgroup->append_single_option_line($opt_key);
|
# For array options we override the first value.
|
||||||
|
my $opt_index = (ref($self->{config}->get($opt_key)) eq 'ARRAY') ? 0 : -1;
|
||||||
|
$optgroup->append_single_option_line($opt_key, $opt_index);
|
||||||
}
|
}
|
||||||
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0);
|
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0);
|
||||||
}
|
}
|
||||||
@ -154,4 +209,11 @@ sub disable {
|
|||||||
$self->Disable;
|
$self->Disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Shows or hides the Add button.
|
||||||
|
sub set_editable {
|
||||||
|
my ($self, $editable) = @_;
|
||||||
|
|
||||||
|
$self->{editable} = $editable;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -20,16 +20,6 @@ sub new {
|
|||||||
},
|
},
|
||||||
label_width => 200,
|
label_width => 200,
|
||||||
);
|
);
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'mode',
|
|
||||||
type => 'select',
|
|
||||||
label => 'Mode',
|
|
||||||
tooltip => 'Choose between a simpler, basic mode and an expert mode with more options and more complicated interface.',
|
|
||||||
labels => ['Simple','Expert'],
|
|
||||||
values => ['simple','expert'],
|
|
||||||
default => $Slic3r::GUI::Settings->{_}{mode},
|
|
||||||
width => 100,
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||||
opt_id => 'version_check',
|
opt_id => 'version_check',
|
||||||
type => 'bool',
|
type => 'bool',
|
||||||
|
210
lib/Slic3r/GUI/Preset.pm
Normal file
210
lib/Slic3r/GUI/Preset.pm
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package Slic3r::GUI::Preset;
|
||||||
|
use Moo;
|
||||||
|
|
||||||
|
use Unicode::Normalize;
|
||||||
|
use Wx qw(:dialog :icon :id wxTheApp);
|
||||||
|
|
||||||
|
has 'group' => (is => 'ro', required => 1);
|
||||||
|
has 'default' => (is => 'ro', default => sub { 0 });
|
||||||
|
has 'external' => (is => 'ro', default => sub { 0 });
|
||||||
|
has 'name' => (is => 'rw', required => 1);
|
||||||
|
has 'file' => (is => 'rw');
|
||||||
|
has '_config' => (is => 'rw', default => sub { Slic3r::Config->new });
|
||||||
|
has '_dirty_config' => (is => 'ro', default => sub { Slic3r::Config->new });
|
||||||
|
|
||||||
|
sub BUILD {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->name(Unicode::Normalize::NFC($self->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _loaded {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return !$self->_config->empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dirty_options {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my @dirty = ();
|
||||||
|
|
||||||
|
# Options present in both configs with different values:
|
||||||
|
push @dirty, @{$self->_config->diff($self->_dirty_config)};
|
||||||
|
|
||||||
|
# Overrides added to the dirty config:
|
||||||
|
my @extra = $self->_group_class->overriding_options;
|
||||||
|
push @dirty, grep { !$self->_config->has($_) && $self->_dirty_config->has($_) } @extra;
|
||||||
|
# Overrides removed from the dirty config:
|
||||||
|
push @dirty, grep { $self->_config->has($_) && !$self->_dirty_config->has($_) } @extra;
|
||||||
|
|
||||||
|
return @dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dirty {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return !!$self->dirty_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dropdown_name {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $name = $self->name;
|
||||||
|
$name .= " (modified)" if $self->dirty;
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub file_exists {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
die "Can't call file_exists() on a non-file preset" if !$self->file;
|
||||||
|
return -e Slic3r::encode_path($self->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rename {
|
||||||
|
my ($self, $name) = @_;
|
||||||
|
|
||||||
|
$self->name($name);
|
||||||
|
$self->file(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->group, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prompt_unsaved_changes {
|
||||||
|
my ($self, $parent) = @_;
|
||||||
|
|
||||||
|
if ($self->dirty) {
|
||||||
|
my $name = $self->default ? 'Default preset' : "Preset \"" . $self->name . "\"";
|
||||||
|
|
||||||
|
my $opts = '';
|
||||||
|
foreach my $opt_key ($self->dirty_options) {
|
||||||
|
my $opt = $Slic3r::Config::Options->{$opt_key};
|
||||||
|
my $name = $opt->{full_label} // $opt->{label};
|
||||||
|
if ($opt->{category}) {
|
||||||
|
$name = $opt->{category} . " > $name";
|
||||||
|
}
|
||||||
|
$opts .= "- $name\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $msg = sprintf "%s has unsaved changes:\n%s\nDo you want to save them?", $name, $opts;
|
||||||
|
my $confirm = Wx::MessageDialog->new($parent, $msg,
|
||||||
|
'Unsaved Changes', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION);
|
||||||
|
$confirm->SetYesNoCancelLabels('Save', 'Discard', 'Cancel');
|
||||||
|
my $res = $confirm->ShowModal;
|
||||||
|
|
||||||
|
if ($res == wxID_CANCEL) {
|
||||||
|
return 0;
|
||||||
|
} elsif ($res == wxID_YES) {
|
||||||
|
return $self->save($self->default ? undef : $self->name);
|
||||||
|
} elsif ($res == wxID_NO) {
|
||||||
|
$self->dismiss_changes;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub save {
|
||||||
|
my ($self, $name, $parent) = @_;
|
||||||
|
|
||||||
|
if (!$name) {
|
||||||
|
my $default_name = $self->default ? 'Untitled' : $self->name;
|
||||||
|
$default_name =~ s/\.ini$//i;
|
||||||
|
|
||||||
|
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
|
||||||
|
default => $default_name,
|
||||||
|
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
|
||||||
|
);
|
||||||
|
return 0 unless $dlg->ShowModal == wxID_OK;
|
||||||
|
$name = $dlg->get_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->rename($name);
|
||||||
|
|
||||||
|
if (!$self->file) {
|
||||||
|
die "Calling save() without setting filename";
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->_config->clear;
|
||||||
|
$self->_config->apply($self->_dirty_config);
|
||||||
|
$self->_config->save($self->file);
|
||||||
|
wxTheApp->load_presets;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dismiss_changes {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->_dirty_config->clear;
|
||||||
|
$self->_dirty_config->apply($self->_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delete {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
die "Default config can't be deleted" if $self->default;
|
||||||
|
die "External configs can't be deleted" if $self->external;
|
||||||
|
|
||||||
|
# Otherwise wxTheApp->load_presets() will keep it
|
||||||
|
$self->dismiss_changes;
|
||||||
|
|
||||||
|
if ($self->file) {
|
||||||
|
unlink Slic3r::encode_path($self->file) if $self->file_exists;
|
||||||
|
$self->file(undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# This returns the loaded config with the dirty options applied.
|
||||||
|
sub dirty_config {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->load_config if !$self->_loaded;
|
||||||
|
|
||||||
|
return $self->_dirty_config->clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load_config {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my @keys = $self->_group_class->options;
|
||||||
|
my @extra_keys = $self->_group_class->overriding_options;
|
||||||
|
|
||||||
|
if ($self->default) {
|
||||||
|
$self->_config(Slic3r::Config->new_from_defaults(@keys));
|
||||||
|
} elsif ($self->file) {
|
||||||
|
if (!$self->file_exists) {
|
||||||
|
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
my $external_config = Slic3r::Config->load($self->file);
|
||||||
|
if (!@keys) {
|
||||||
|
$self->_config($external_config);
|
||||||
|
} else {
|
||||||
|
# apply preset values on top of defaults
|
||||||
|
my $config = Slic3r::Config->new_from_defaults(@keys);
|
||||||
|
$config->set($_, $external_config->get($_))
|
||||||
|
for grep $external_config->has($_), @keys;
|
||||||
|
|
||||||
|
# For extra_keys we don't populate defaults.
|
||||||
|
if (@extra_keys && !$self->external) {
|
||||||
|
$config->set($_, $external_config->get($_))
|
||||||
|
for grep $external_config->has($_), @extra_keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->_config($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->_dirty_config->apply($self->_config);
|
||||||
|
|
||||||
|
return $self->_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _group_class {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return "Slic3r::GUI::PresetEditor::".ucfirst $self->group;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
File diff suppressed because it is too large
Load Diff
73
lib/Slic3r/GUI/PresetEditorDialog.pm
Normal file
73
lib/Slic3r/GUI/PresetEditorDialog.pm
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package Slic3r::GUI::PresetEditorDialog;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Wx qw(:dialog :id :misc :sizer :button :icon wxTheApp WXK_ESCAPE);
|
||||||
|
use Wx::Event qw(EVT_CLOSE EVT_CHAR_HOOK);
|
||||||
|
use base qw(Wx::Dialog Class::Accessor);
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
__PACKAGE__->mk_accessors(qw(preset_editor));
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, $parent) = @_;
|
||||||
|
my $self = $class->SUPER::new($parent, -1, "Settings", wxDefaultPosition, [900,500],
|
||||||
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_EX_METAL);
|
||||||
|
|
||||||
|
$self->preset_editor($self->preset_editor_class->new($self));
|
||||||
|
$self->SetTitle($self->preset_editor->title);
|
||||||
|
|
||||||
|
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
$sizer->Add($self->preset_editor, 1, wxEXPAND);
|
||||||
|
|
||||||
|
$self->SetSizer($sizer);
|
||||||
|
#$sizer->SetSizeHints($self);
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
my $buttons = $self->CreateStdDialogButtonSizer(wxCLOSE);
|
||||||
|
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxTheApp->restore_window_pos($self, "preset_editor");
|
||||||
|
|
||||||
|
EVT_CLOSE($self, sub {
|
||||||
|
my (undef, $event) = @_;
|
||||||
|
|
||||||
|
# save window size
|
||||||
|
wxTheApp->save_window_pos($self, "preset_editor");
|
||||||
|
|
||||||
|
# propagate event
|
||||||
|
$event->Skip;
|
||||||
|
});
|
||||||
|
|
||||||
|
EVT_CHAR_HOOK($self, sub {
|
||||||
|
my (undef, $event) = @_;
|
||||||
|
|
||||||
|
if ($event->GetKeyCode == WXK_ESCAPE) {
|
||||||
|
$self->Close;
|
||||||
|
} else {
|
||||||
|
$event->Skip;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
package Slic3r::GUI::PresetEditorDialog::Printer;
|
||||||
|
use base qw(Slic3r::GUI::PresetEditorDialog);
|
||||||
|
|
||||||
|
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Printer" }
|
||||||
|
|
||||||
|
|
||||||
|
package Slic3r::GUI::PresetEditorDialog::Filament;
|
||||||
|
use base qw(Slic3r::GUI::PresetEditorDialog);
|
||||||
|
|
||||||
|
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Filament" }
|
||||||
|
|
||||||
|
|
||||||
|
package Slic3r::GUI::PresetEditorDialog::Print;
|
||||||
|
use base qw(Slic3r::GUI::PresetEditorDialog);
|
||||||
|
|
||||||
|
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Print" }
|
||||||
|
|
||||||
|
1;
|
@ -44,7 +44,7 @@ sub new {
|
|||||||
$self->config(Slic3r::Config->new_from_defaults(
|
$self->config(Slic3r::Config->new_from_defaults(
|
||||||
qw(serial_port serial_speed bed_shape start_gcode end_gcode z_offset)
|
qw(serial_port serial_speed bed_shape start_gcode end_gcode z_offset)
|
||||||
));
|
));
|
||||||
$self->config->apply(wxTheApp->{mainframe}->config);
|
$self->config->apply(wxTheApp->{mainframe}->{plater}->config);
|
||||||
|
|
||||||
my @optgroups = ();
|
my @optgroups = ();
|
||||||
{
|
{
|
||||||
@ -520,9 +520,6 @@ sub _close {
|
|||||||
}
|
}
|
||||||
wxTheApp->{mainframe}->Show;
|
wxTheApp->{mainframe}->Show;
|
||||||
|
|
||||||
my $printer_tab = wxTheApp->{mainframe}{options_tabs}{printer};
|
|
||||||
$printer_tab->load_config($self->config);
|
|
||||||
|
|
||||||
$self->EndModal(wxID_OK);
|
$self->EndModal(wxID_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +552,7 @@ sub BUILD {
|
|||||||
# init print
|
# init print
|
||||||
{
|
{
|
||||||
my $print = Slic3r::SLAPrint->new(wxTheApp->{mainframe}->{plater}->{model});
|
my $print = Slic3r::SLAPrint->new(wxTheApp->{mainframe}->{plater}->{model});
|
||||||
$print->apply_config(wxTheApp->{mainframe}->config);
|
$print->apply_config(wxTheApp->{mainframe}->{plater}->config);
|
||||||
$self->_print($print);
|
$self->_print($print);
|
||||||
$self->screen->print($print);
|
$self->screen->print($print);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ sub new {
|
|||||||
my $self = $class->SUPER::new($parent, -1, "SLA/DLP Print", wxDefaultPosition, wxDefaultSize);
|
my $self = $class->SUPER::new($parent, -1, "SLA/DLP Print", wxDefaultPosition, wxDefaultSize);
|
||||||
|
|
||||||
$self->config(Slic3r::Config::SLAPrint->new);
|
$self->config(Slic3r::Config::SLAPrint->new);
|
||||||
$self->config->apply_dynamic(wxTheApp->{mainframe}->config);
|
$self->config->apply_dynamic(wxTheApp->{mainframe}->{plater}->config);
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
my $new_optgroup = sub {
|
my $new_optgroup = sub {
|
||||||
@ -103,8 +103,6 @@ sub _accept {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTheApp->{mainframe}->load_config($self->config->dynamic);
|
|
||||||
|
|
||||||
$self->EndModal(wxID_OK);
|
$self->EndModal(wxID_OK);
|
||||||
$self->Close; # needed on Linux
|
$self->Close; # needed on Linux
|
||||||
|
|
||||||
@ -113,6 +111,9 @@ sub _accept {
|
|||||||
# this double invocation is needed for properly hiding the MainFrame
|
# this double invocation is needed for properly hiding the MainFrame
|
||||||
$projector->Show;
|
$projector->Show;
|
||||||
$projector->ShowModal;
|
$projector->ShowModal;
|
||||||
|
|
||||||
|
# TODO: diff the new config with the selected presets and prompt the user for
|
||||||
|
# applying the changes to them.
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -1,351 +0,0 @@
|
|||||||
# The "Simple" Print Settings tab.
|
|
||||||
# The "Simple" mode is enabled by File->Preferences dialog.
|
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use utf8;
|
|
||||||
|
|
||||||
use File::Basename qw(basename);
|
|
||||||
use List::Util qw(first);
|
|
||||||
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :window :systemsettings);
|
|
||||||
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN);
|
|
||||||
use base 'Wx::ScrolledWindow';
|
|
||||||
|
|
||||||
sub new {
|
|
||||||
my $class = shift;
|
|
||||||
my ($parent, %params) = @_;
|
|
||||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
|
||||||
|
|
||||||
$self->SetScrollbars(1, 1, 1, 1);
|
|
||||||
|
|
||||||
$self->{config} = Slic3r::Config->new;
|
|
||||||
$self->{optgroups} = [];
|
|
||||||
|
|
||||||
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
|
|
||||||
$self->SetSizer($self->{vsizer});
|
|
||||||
$self->build;
|
|
||||||
$self->_update;
|
|
||||||
|
|
||||||
{
|
|
||||||
my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize);
|
|
||||||
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
|
||||||
$self->{vsizer}->Add($label, 0, wxEXPAND | wxALL, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub init_config_options {
|
|
||||||
my ($self, @opt_keys) = @_;
|
|
||||||
$self->{config}->apply(Slic3r::Config->new_from_defaults(@opt_keys));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub new_optgroup {
|
|
||||||
my ($self, $title, %params) = @_;
|
|
||||||
|
|
||||||
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
|
|
||||||
parent => $self,
|
|
||||||
title => $title,
|
|
||||||
config => $self->{config},
|
|
||||||
label_width => $params{label_width} // 200,
|
|
||||||
on_change => sub { $self->_on_value_change(@_) },
|
|
||||||
);
|
|
||||||
|
|
||||||
push @{$self->{optgroups}}, $optgroup;
|
|
||||||
$self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
|
||||||
|
|
||||||
return $optgroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load_config_file {
|
|
||||||
my $self = shift;
|
|
||||||
my ($file) = @_;
|
|
||||||
|
|
||||||
my $config = Slic3r::Config->load($file);
|
|
||||||
$self->load_config($config);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load_config {
|
|
||||||
my $self = shift;
|
|
||||||
my ($config) = @_;
|
|
||||||
|
|
||||||
foreach my $opt_key (@{$self->{config}->get_keys}) {
|
|
||||||
next unless $config->has($opt_key);
|
|
||||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
|
||||||
}
|
|
||||||
$_->reload_config for @{$self->{optgroups}};
|
|
||||||
$self->_update;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load_presets {}
|
|
||||||
|
|
||||||
sub is_dirty { 0 }
|
|
||||||
sub config { $_[0]->{config}->clone }
|
|
||||||
sub _update {}
|
|
||||||
|
|
||||||
sub on_value_change {
|
|
||||||
my ($self, $cb) = @_;
|
|
||||||
$self->{on_value_change} = $cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub on_presets_changed {}
|
|
||||||
|
|
||||||
# propagate event to the parent
|
|
||||||
sub _on_value_change {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$self->{on_value_change}->(@_) if $self->{on_value_change};
|
|
||||||
$self->_update;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_field {
|
|
||||||
my ($self, $opt_key, $opt_index) = @_;
|
|
||||||
|
|
||||||
foreach my $optgroup (@{ $self->{optgroups} }) {
|
|
||||||
my $field = $optgroup->get_fieldc($opt_key, $opt_index);
|
|
||||||
return $field if defined $field;
|
|
||||||
}
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Print;
|
|
||||||
use base 'Slic3r::GUI::SimpleTab';
|
|
||||||
|
|
||||||
use Wx qw(:sizer);
|
|
||||||
|
|
||||||
sub name { 'print' }
|
|
||||||
sub title { 'Print Settings' }
|
|
||||||
|
|
||||||
sub build {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$self->init_config_options(qw(
|
|
||||||
layer_height perimeters top_solid_layers bottom_solid_layers
|
|
||||||
fill_density fill_pattern top_infill_pattern bottom_infill_pattern
|
|
||||||
support_material support_material_spacing raft_layers
|
|
||||||
support_material_contact_distance dont_support_bridges
|
|
||||||
perimeter_speed infill_speed travel_speed
|
|
||||||
brim_width
|
|
||||||
xy_size_compensation
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('General');
|
|
||||||
$optgroup->append_single_option_line('layer_height');
|
|
||||||
$optgroup->append_single_option_line('perimeters');
|
|
||||||
|
|
||||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
|
||||||
label => 'Solid layers',
|
|
||||||
);
|
|
||||||
$line->append_option($optgroup->get_option('top_solid_layers'));
|
|
||||||
$line->append_option($optgroup->get_option('bottom_solid_layers'));
|
|
||||||
$optgroup->append_line($line);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Infill');
|
|
||||||
$optgroup->append_single_option_line('fill_density');
|
|
||||||
$optgroup->append_single_option_line('fill_pattern');
|
|
||||||
$optgroup->append_single_option_line('top_infill_pattern');
|
|
||||||
$optgroup->append_single_option_line('bottom_infill_pattern');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Support material');
|
|
||||||
$optgroup->append_single_option_line('support_material');
|
|
||||||
$optgroup->append_single_option_line('support_material_spacing');
|
|
||||||
$optgroup->append_single_option_line('support_material_contact_distance');
|
|
||||||
$optgroup->append_single_option_line('dont_support_bridges');
|
|
||||||
$optgroup->append_single_option_line('raft_layers');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Speed');
|
|
||||||
$optgroup->append_single_option_line('perimeter_speed');
|
|
||||||
$optgroup->append_single_option_line('infill_speed');
|
|
||||||
$optgroup->append_single_option_line('travel_speed');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Brim');
|
|
||||||
$optgroup->append_single_option_line('brim_width');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Other');
|
|
||||||
$optgroup->append_single_option_line('xy_size_compensation');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _update {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
my $config = $self->{config};
|
|
||||||
|
|
||||||
my $have_perimeters = $config->perimeters > 0;
|
|
||||||
$self->get_field($_)->toggle($have_perimeters)
|
|
||||||
for qw(perimeter_speed);
|
|
||||||
|
|
||||||
my $have_infill = $config->fill_density > 0;
|
|
||||||
my $have_solid_infill = $config->top_solid_layers > 0 || $config->bottom_solid_layers > 0;
|
|
||||||
$self->get_field($_)->toggle($have_infill)
|
|
||||||
for qw(fill_pattern);
|
|
||||||
$self->get_field($_)->toggle($have_solid_infill)
|
|
||||||
for qw(top_infill_pattern bottom_infill_pattern);
|
|
||||||
$self->get_field($_)->toggle($have_infill || $have_solid_infill)
|
|
||||||
for qw(infill_speed);
|
|
||||||
|
|
||||||
my $have_support_material = $config->support_material || $config->raft_layers > 0;
|
|
||||||
$self->get_field($_)->toggle($have_support_material)
|
|
||||||
for qw(support_material_spacing dont_support_bridges
|
|
||||||
support_material_contact_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Filament;
|
|
||||||
use base 'Slic3r::GUI::SimpleTab';
|
|
||||||
|
|
||||||
sub name { 'filament' }
|
|
||||||
sub title { 'Filament Settings' }
|
|
||||||
|
|
||||||
sub build {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$self->init_config_options(qw(
|
|
||||||
filament_diameter extrusion_multiplier
|
|
||||||
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Filament');
|
|
||||||
$optgroup->append_single_option_line('filament_diameter', 0);
|
|
||||||
$optgroup->append_single_option_line('extrusion_multiplier', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Temperature (°C)');
|
|
||||||
|
|
||||||
{
|
|
||||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
|
||||||
label => 'Extruder',
|
|
||||||
);
|
|
||||||
$line->append_option($optgroup->get_option('first_layer_temperature', 0));
|
|
||||||
$line->append_option($optgroup->get_option('temperature', 0));
|
|
||||||
$optgroup->append_line($line);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
|
||||||
label => 'Bed',
|
|
||||||
);
|
|
||||||
$line->append_option($optgroup->get_option('first_layer_bed_temperature'));
|
|
||||||
$line->append_option($optgroup->get_option('bed_temperature'));
|
|
||||||
$optgroup->append_line($line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Printer;
|
|
||||||
use base 'Slic3r::GUI::SimpleTab';
|
|
||||||
use Wx qw(:sizer :button :bitmap :misc :id);
|
|
||||||
use Wx::Event qw(EVT_BUTTON);
|
|
||||||
|
|
||||||
sub name { 'printer' }
|
|
||||||
sub title { 'Printer Settings' }
|
|
||||||
|
|
||||||
sub build {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$self->init_config_options(qw(
|
|
||||||
bed_shape
|
|
||||||
z_offset
|
|
||||||
gcode_flavor
|
|
||||||
nozzle_diameter
|
|
||||||
retract_length retract_lift wipe
|
|
||||||
start_gcode
|
|
||||||
end_gcode
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
my $bed_shape_widget = sub {
|
|
||||||
my ($parent) = @_;
|
|
||||||
|
|
||||||
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
|
||||||
$btn->SetFont($Slic3r::GUI::small_font);
|
|
||||||
if ($Slic3r::GUI::have_button_icons) {
|
|
||||||
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG));
|
|
||||||
}
|
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
|
||||||
$sizer->Add($btn);
|
|
||||||
|
|
||||||
EVT_BUTTON($self, $btn, sub {
|
|
||||||
my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape);
|
|
||||||
if ($dlg->ShowModal == wxID_OK) {
|
|
||||||
my $value = $dlg->GetValue;
|
|
||||||
$self->{config}->set('bed_shape', $value);
|
|
||||||
$self->_on_value_change('bed_shape', $value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return $sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
my $optgroup = $self->new_optgroup('Size and coordinates');
|
|
||||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
|
||||||
label => 'Bed shape',
|
|
||||||
widget => $bed_shape_widget,
|
|
||||||
);
|
|
||||||
$optgroup->append_line($line);
|
|
||||||
$optgroup->append_single_option_line('z_offset');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Firmware');
|
|
||||||
$optgroup->append_single_option_line('gcode_flavor');
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Extruder');
|
|
||||||
$optgroup->append_single_option_line('nozzle_diameter', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Retraction');
|
|
||||||
$optgroup->append_single_option_line('retract_length', 0);
|
|
||||||
$optgroup->append_single_option_line('retract_lift', 0);
|
|
||||||
$optgroup->append_single_option_line('wipe', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('Start G-code',
|
|
||||||
label_width => 0,
|
|
||||||
);
|
|
||||||
my $option = $optgroup->get_option('start_gcode');
|
|
||||||
$option->full_width(1);
|
|
||||||
$option->height(150);
|
|
||||||
$optgroup->append_single_option_line($option);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $optgroup = $self->new_optgroup('End G-code',
|
|
||||||
label_width => 0,
|
|
||||||
);
|
|
||||||
my $option = $optgroup->get_option('end_gcode');
|
|
||||||
$option->full_width(1);
|
|
||||||
$option->height(150);
|
|
||||||
$optgroup->append_single_option_line($option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _update {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
my $config = $self->{config};
|
|
||||||
|
|
||||||
my $have_retraction = $config->retract_length->[0] > 0;
|
|
||||||
$self->get_field($_, 0)->toggle($have_retraction)
|
|
||||||
for qw(retract_lift wipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
@ -125,28 +125,44 @@ sub slice {
|
|||||||
sub make_perimeters {
|
sub make_perimeters {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return if $self->step_done(STEP_PERIMETERS);
|
||||||
|
|
||||||
|
# Temporary workaround for detect_surfaces_type() not being idempotent (see #3764).
|
||||||
|
# We can remove this when idempotence is restored. This make_perimeters() method
|
||||||
|
# will just call merge_slices() to undo the typed slices and invalidate posDetectSurfaces.
|
||||||
|
if ($self->typed_slices) {
|
||||||
|
$self->invalidate_step(STEP_SLICE);
|
||||||
|
}
|
||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->slice;
|
$self->slice;
|
||||||
|
|
||||||
$self->_make_perimeters;
|
$self->_make_perimeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This will assign a type (top/bottom/internal) to $layerm->slices
|
||||||
|
# and transform $layerm->fill_surfaces from expolygon
|
||||||
|
# to typed top/bottom/internal surfaces;
|
||||||
|
sub detect_surfaces_type {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
# prerequisites
|
||||||
|
$self->slice;
|
||||||
|
|
||||||
|
$self->_detect_surfaces_type;
|
||||||
|
}
|
||||||
|
|
||||||
sub prepare_infill {
|
sub prepare_infill {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->make_perimeters;
|
$self->make_perimeters; # do we need them? TODO: check
|
||||||
|
$self->detect_surfaces_type;
|
||||||
|
|
||||||
return if $self->step_done(STEP_PREPARE_INFILL);
|
return if $self->step_done(STEP_PREPARE_INFILL);
|
||||||
$self->set_step_started(STEP_PREPARE_INFILL);
|
$self->set_step_started(STEP_PREPARE_INFILL);
|
||||||
$self->print->status_cb->(30, "Preparing infill");
|
$self->print->status_cb->(30, "Preparing infill");
|
||||||
|
|
||||||
# this will assign a type (top/bottom/internal) to $layerm->slices
|
|
||||||
# and transform $layerm->fill_surfaces from expolygon
|
|
||||||
# to typed top/bottom/internal surfaces;
|
|
||||||
$self->detect_surfaces_type;
|
|
||||||
$self->set_typed_slices(1);
|
|
||||||
|
|
||||||
# decide what surfaces are to be filled
|
# decide what surfaces are to be filled
|
||||||
$_->prepare_fill_surfaces for map @{$_->regions}, @{$self->layers};
|
$_->prepare_fill_surfaces for map @{$_->regions}, @{$self->layers};
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# Instantiated by Slic3r::Print::Object->_support_material()
|
||||||
|
# only generate() and contact_distance() are called from the outside of this module.
|
||||||
package Slic3r::Print::SupportMaterial;
|
package Slic3r::Print::SupportMaterial;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ use constant PILLAR_SIZE => 2.5;
|
|||||||
use constant PILLAR_SPACING => 10;
|
use constant PILLAR_SPACING => 10;
|
||||||
|
|
||||||
sub generate {
|
sub generate {
|
||||||
|
# $object is Slic3r::Print::Object
|
||||||
my ($self, $object) = @_;
|
my ($self, $object) = @_;
|
||||||
|
|
||||||
# Determine the top surfaces of the support, defined as:
|
# Determine the top surfaces of the support, defined as:
|
||||||
@ -88,6 +91,7 @@ sub generate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub contact_area {
|
sub contact_area {
|
||||||
|
# $object is Slic3r::Print::Object
|
||||||
my ($self, $object) = @_;
|
my ($self, $object) = @_;
|
||||||
|
|
||||||
# if user specified a custom angle threshold, convert it to radians
|
# if user specified a custom angle threshold, convert it to radians
|
||||||
@ -97,6 +101,12 @@ sub contact_area {
|
|||||||
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
|
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
|
||||||
|
# and subtract $buildplate_only_top_surfaces from the contact surfaces, so
|
||||||
|
# there is no contact surface supported by a top surface.
|
||||||
|
my $buildplate_only = $self->object_config->support_material && $self->object_config->support_material_buildplate_only;
|
||||||
|
my $buildplate_only_top_surfaces = [];
|
||||||
|
|
||||||
# determine contact areas
|
# determine contact areas
|
||||||
my %contact = (); # contact_z => [ polygons ]
|
my %contact = (); # contact_z => [ polygons ]
|
||||||
my %overhang = (); # contact_z => [ polygons ] - this stores the actual overhang supported by each contact layer
|
my %overhang = (); # contact_z => [ polygons ] - this stores the actual overhang supported by each contact layer
|
||||||
@ -114,6 +124,21 @@ sub contact_area {
|
|||||||
}
|
}
|
||||||
my $layer = $object->get_layer($layer_id);
|
my $layer = $object->get_layer($layer_id);
|
||||||
|
|
||||||
|
if ($buildplate_only) {
|
||||||
|
# Collect the top surfaces up to this layer and merge them.
|
||||||
|
my $projection_new = [];
|
||||||
|
push @$projection_new, ( map $_->p, map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions} );
|
||||||
|
if (@$projection_new) {
|
||||||
|
# Merge the new top surfaces with the preceding top surfaces.
|
||||||
|
# Apply the safety offset to the newly added polygons, so they will connect
|
||||||
|
# with the polygons collected before,
|
||||||
|
# but don't apply the safety offset during the union operation as it would
|
||||||
|
# inflate the polygons over and over.
|
||||||
|
push @$buildplate_only_top_surfaces, @{ offset($projection_new, scale(0.01)) };
|
||||||
|
$buildplate_only_top_surfaces = union($buildplate_only_top_surfaces, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# detect overhangs and contact areas needed to support them
|
# detect overhangs and contact areas needed to support them
|
||||||
my (@overhang, @contact) = ();
|
my (@overhang, @contact) = ();
|
||||||
if ($layer_id == 0) {
|
if ($layer_id == 0) {
|
||||||
@ -239,6 +264,12 @@ sub contact_area {
|
|||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} # if ($self->object_config->dont_support_bridges)
|
||||||
|
|
||||||
|
if ($buildplate_only) {
|
||||||
|
# Don't support overhangs above the top surfaces.
|
||||||
|
# This step is done before the contact surface is calcuated by growing the overhang region.
|
||||||
|
$diff = diff($diff, $buildplate_only_top_surfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
next if !@$diff;
|
next if !@$diff;
|
||||||
@ -249,11 +280,16 @@ sub contact_area {
|
|||||||
# We increment the area in steps because we don't want our support to overflow
|
# We increment the area in steps because we don't want our support to overflow
|
||||||
# on the other side of the object (if it's very thin).
|
# on the other side of the object (if it's very thin).
|
||||||
{
|
{
|
||||||
my @slices_margin = @{offset([ map @$_, @{$lower_layer->slices} ], +$fw/2)};
|
my $slices_margin = offset([ map @$_, @{$lower_layer->slices} ], +$fw/2);
|
||||||
|
if ($buildplate_only) {
|
||||||
|
# Trim the inflated contact surfaces by the top surfaces as well.
|
||||||
|
push @$slices_margin, map $_->clone, @{$buildplate_only_top_surfaces};
|
||||||
|
$slices_margin = union($slices_margin);
|
||||||
|
}
|
||||||
for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) {
|
for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) {
|
||||||
$diff = diff(
|
$diff = diff(
|
||||||
offset($diff, $_),
|
offset($diff, $_),
|
||||||
\@slices_margin,
|
$slices_margin,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,9 +316,10 @@ sub contact_area {
|
|||||||
|
|
||||||
if (0) {
|
if (0) {
|
||||||
require "Slic3r/SVG.pm";
|
require "Slic3r/SVG.pm";
|
||||||
Slic3r::SVG::output("contact_" . $contact_z . ".svg",
|
Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg",
|
||||||
expolygons => union_ex(\@contact),
|
green_expolygons => union_ex($buildplate_only_top_surfaces),
|
||||||
red_expolygons => union_ex(\@overhang),
|
blue_expolygons => union_ex(\@contact),
|
||||||
|
red_expolygons => union_ex(\@overhang),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,6 +334,8 @@ sub object_top {
|
|||||||
# find object top surfaces
|
# find object top surfaces
|
||||||
# we'll use them to clip our support and detect where does it stick
|
# we'll use them to clip our support and detect where does it stick
|
||||||
my %top = (); # print_z => [ expolygons ]
|
my %top = (); # print_z => [ expolygons ]
|
||||||
|
return \%top if ($self->object_config->support_material_buildplate_only);
|
||||||
|
|
||||||
my $projection = [];
|
my $projection = [];
|
||||||
foreach my $layer (reverse @{$object->layers}) {
|
foreach my $layer (reverse @{$object->layers}) {
|
||||||
if (my @top = map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions}) {
|
if (my @top = map @{$_->slices->filter_by_type(S_TYPE_TOP)}, @{$layer->regions}) {
|
||||||
@ -430,6 +469,9 @@ sub generate_interface_layers {
|
|||||||
sub generate_bottom_interface_layers {
|
sub generate_bottom_interface_layers {
|
||||||
my ($self, $support_z, $base, $top, $interface) = @_;
|
my ($self, $support_z, $base, $top, $interface) = @_;
|
||||||
|
|
||||||
|
# If no interface layers are allowed, don't generate bottom interface layers.
|
||||||
|
return if $self->object_config->support_material_interface_layers == 0;
|
||||||
|
|
||||||
my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
|
my $area_threshold = $self->interface_flow->scaled_spacing ** 2;
|
||||||
|
|
||||||
# loop through object's top surfaces
|
# loop through object's top surfaces
|
||||||
|
@ -62,31 +62,28 @@ PP_BIN=$(which pp)
|
|||||||
SLIC3R_DIR=$(perl -MCwd=realpath -e "print realpath '${WD}/../../'")
|
SLIC3R_DIR=$(perl -MCwd=realpath -e "print realpath '${WD}/../../'")
|
||||||
|
|
||||||
if [[ -d "${appfolder}" ]]; then
|
if [[ -d "${appfolder}" ]]; then
|
||||||
echo "Deleting old working folder."
|
echo "Deleting old working folder: ${appfolder}"
|
||||||
rm -rf ${appfolder}
|
rm -rf ${appfolder}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -e "${dmgfile}" ]]; then
|
if [[ -e "${dmgfile}" ]]; then
|
||||||
echo "Deleting old dmg ${dmgfile}."
|
echo "Deleting old dmg: ${dmgfile}"
|
||||||
rm -rf ${dmgfile}
|
rm -rf ${dmgfile}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Creating new app folder at $appfolder."
|
echo "Creating new app folder: $appfolder"
|
||||||
mkdir -p $appfolder
|
mkdir -p $appfolder
|
||||||
mkdir -p $macosfolder
|
mkdir -p $macosfolder
|
||||||
mkdir -p $resourcefolder
|
mkdir -p $resourcefolder
|
||||||
|
|
||||||
echo "Copying resources..."
|
echo "Copying resources..."
|
||||||
cp -r $SLIC3R_DIR/var $macosfolder/
|
cp -rf $SLIC3R_DIR/var $resourcefolder/
|
||||||
mv $macosfolder/var/Slic3r.icns $resourcefolder
|
mv $resourcefolder/var/Slic3r.icns $resourcefolder
|
||||||
|
|
||||||
echo "Copying Slic3r..."
|
echo "Copying Slic3r..."
|
||||||
cp $SLIC3R_DIR/slic3r.pl $macosfolder/slic3r.pl
|
cp $SLIC3R_DIR/slic3r.pl $macosfolder/slic3r.pl
|
||||||
cp -RP $SLIC3R_DIR/local-lib $macosfolder/local-lib
|
cp -fRP $SLIC3R_DIR/local-lib $macosfolder/local-lib
|
||||||
cp -RP $SLIC3R_DIR/lib/* $macosfolder/local-lib/lib/perl5/
|
cp -fRP $SLIC3R_DIR/lib/* $macosfolder/local-lib/lib/perl5/
|
||||||
find $macosfolder/local-lib -name man -type d -delete
|
|
||||||
find $macosfolder/local-lib -name .packlist -delete
|
|
||||||
rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include
|
|
||||||
|
|
||||||
echo "Relocating dylib paths..."
|
echo "Relocating dylib paths..."
|
||||||
for bundle in $(find $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/auto/Wx -name '*.bundle') $(find $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets -name '*.dylib' -type f); do
|
for bundle in $(find $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/auto/Wx -name '*.bundle') $(find $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets -name '*.dylib' -type f); do
|
||||||
@ -97,11 +94,11 @@ for bundle in $(find $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "Copying startup script..."
|
echo "Copying startup script..."
|
||||||
cp $WD/startup_script.sh $macosfolder/$appname
|
cp -f $WD/startup_script.sh $macosfolder/$appname
|
||||||
chmod +x $macosfolder/$appname
|
chmod +x $macosfolder/$appname
|
||||||
|
|
||||||
echo "Copying perl from $PERL_BIN"
|
echo "Copying perl from $PERL_BIN"
|
||||||
cp $PERL_BIN $macosfolder/perl-local
|
cp -f $PERL_BIN $macosfolder/perl-local
|
||||||
${PP_BIN} -M attributes -M base -M bytes -M B -M POSIX \
|
${PP_BIN} -M attributes -M base -M bytes -M B -M POSIX \
|
||||||
-M FindBin -M Unicode::Normalize -M Tie::Handle \
|
-M FindBin -M Unicode::Normalize -M Tie::Handle \
|
||||||
-M Time::Local -M Math::Trig \
|
-M Time::Local -M Math::Trig \
|
||||||
@ -109,13 +106,51 @@ ${PP_BIN} -M attributes -M base -M bytes -M B -M POSIX \
|
|||||||
-M warnings -M local::lib \
|
-M warnings -M local::lib \
|
||||||
-M strict -M utf8 -M parent \
|
-M strict -M utf8 -M parent \
|
||||||
-B -p -e "print 123" -o $WD/_tmp/test.par
|
-B -p -e "print 123" -o $WD/_tmp/test.par
|
||||||
unzip $WD/_tmp/test.par -d $WD/_tmp/
|
unzip -o $WD/_tmp/test.par -d $WD/_tmp/
|
||||||
cp -r $WD/_tmp/lib/* $macosfolder/local-lib/lib/perl5/
|
cp -rf $WD/_tmp/lib/* $macosfolder/local-lib/lib/perl5/
|
||||||
rm -rf $WD/_tmp
|
rm -rf $WD/_tmp
|
||||||
|
|
||||||
|
echo "Cleaning bundle"
|
||||||
|
rm -rf $macosfolder/local-lib/bin
|
||||||
|
rm -rf $macosfolder/local-lib/man
|
||||||
|
rm -f $macosfolder/local-lib/lib/perl5/Algorithm/*.pl
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/unicore
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/App
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/Devel/CheckLib.pm
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/ExtUtils
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/Module/Build*
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/TAP
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/Test*
|
||||||
|
find -d $macosfolder/local-lib -name '*.pod' -delete
|
||||||
|
find -d $macosfolder/local-lib -name .packlist -delete
|
||||||
|
find -d $macosfolder/local-lib -name .meta -exec rm -rf "{}" \;
|
||||||
|
find -d $macosfolder/local-lib -name '*.h' -delete
|
||||||
|
find -d $macosfolder/local-lib -name wxPerl.app -exec rm -rf "{}" \;
|
||||||
|
find -d $macosfolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
|
||||||
|
-or -name DocView -or -name STC -or -name IPC \
|
||||||
|
-or -name AUI -or -name Calendar -or -name DataView \
|
||||||
|
-or -name DateTime -or -name Media -or -name PerlTest \
|
||||||
|
-or -name Ribbon \) -exec rm -rf "{}" \;
|
||||||
|
find -d $macosfolder/local-lib -name libwx_osx_cocoau_ribbon-3.0.0.2.0.dylib -delete
|
||||||
|
find -d $macosfolder/local-lib -name libwx_osx_cocoau_aui-3.0.0.2.0.dylib -delete
|
||||||
|
find -d $macosfolder/local-lib -name libwx_osx_cocoau_media-3.0.0.2.0.dylib -delete
|
||||||
|
find -d $macosfolder/local-lib -name libwx_osx_cocoau_stc-3.0.0.2.0.dylib -delete
|
||||||
|
find -d $macosfolder/local-lib -name libwx_osx_cocoau_webview-3.0.0.2.0.dylib -delete
|
||||||
|
rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include
|
||||||
|
find -d $macosfolder/local-lib -type d -empty -delete
|
||||||
|
|
||||||
make_plist
|
make_plist
|
||||||
|
|
||||||
echo $PkgInfoContents >$appfolder/Contents/PkgInfo
|
echo $PkgInfoContents >$appfolder/Contents/PkgInfo
|
||||||
|
|
||||||
|
if [[ -e "${KEYCHAIN_FILE}" ]]; then
|
||||||
|
echo "Signing app..."
|
||||||
|
chmod -R +w $macosfolder/*
|
||||||
|
security list-keychains -s "${KEYCHAIN_FILE}"
|
||||||
|
security default-keychain -s "${KEYCHAIN_FILE}"
|
||||||
|
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_FILE}"
|
||||||
|
codesign --sign "${KEYCHAIN_IDENTITY}" --deep "$appfolder"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Creating dmg file...."
|
echo "Creating dmg file...."
|
||||||
hdiutil create -fs HFS+ -srcfolder "$appfolder" -volname "$appname" "$dmgfile"
|
hdiutil create -fs HFS+ -srcfolder "$appfolder" -volname "$appname" "$dmgfile"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DIR=$(dirname "$0")
|
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" $@
|
||||||
|
@ -11,5 +11,7 @@ $perllib = "-lperl$perlver"
|
|||||||
|
|
||||||
windres slic3r.rc -O coff -o slic3r.res
|
windres slic3r.rc -O coff -o slic3r.res
|
||||||
g++ -c -I'C:\strawberry\perl\lib\CORE\' shell.cpp -o slic3r.o
|
g++ -c -I'C:\strawberry\perl\lib\CORE\' shell.cpp -o slic3r.o
|
||||||
g++ -v -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r.exe | Write-Host
|
g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI shell.cpp -o slic3r-gui.o
|
||||||
|
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host
|
||||||
|
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ cpanm "PAR::Packer"
|
|||||||
|
|
||||||
pp `
|
pp `
|
||||||
-a "slic3r.exe;slic3r.exe" `
|
-a "slic3r.exe;slic3r.exe" `
|
||||||
|
-a "slic3r.exe;slic3r-console.exe" `
|
||||||
-a "../../lib;lib" `
|
-a "../../lib;lib" `
|
||||||
-a "../../local-lib;local-lib" `
|
-a "../../local-lib;local-lib" `
|
||||||
-a "../../slic3r.pl;slic3r.pl" `
|
-a "../../slic3r.pl;slic3r.pl" `
|
||||||
@ -52,7 +53,8 @@ pp `
|
|||||||
-a "../../FreeGLUT/freeglut.dll;freeglut.dll" `
|
-a "../../FreeGLUT/freeglut.dll;freeglut.dll" `
|
||||||
-a "${STRAWBERRY_PATH}\perl\bin\perl${perlversion}.dll;perl${perlversion}.dll" `
|
-a "${STRAWBERRY_PATH}\perl\bin\perl${perlversion}.dll;perl${perlversion}.dll" `
|
||||||
-a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" `
|
-a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" `
|
||||||
-a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_sjlj-1.dll;libgcc_s_sjlj-1.dll" `
|
-a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_seh-1.dll;libgcc_s_seh-1.dll" `
|
||||||
|
-a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" `
|
||||||
-a "${STRAWBERRY_PATH}\c\bin\pthreadGC2-w64.dll;pthreadGC2-w64.dll" `
|
-a "${STRAWBERRY_PATH}\c\bin\pthreadGC2-w64.dll;pthreadGC2-w64.dll" `
|
||||||
-a "${STRAWBERRY_PATH}\c\bin\libglut-0__.dll;libglut-0__.dll" `
|
-a "${STRAWBERRY_PATH}\c\bin\libglut-0__.dll;libglut-0__.dll" `
|
||||||
-M AutoLoader `
|
-M AutoLoader `
|
||||||
@ -110,10 +112,8 @@ pp `
|
|||||||
-M Socket `
|
-M Socket `
|
||||||
-M Socket6 `
|
-M Socket6 `
|
||||||
-M Storable `
|
-M Storable `
|
||||||
-M Sub::Defer `
|
|
||||||
-M Sub::Exporter `
|
-M Sub::Exporter `
|
||||||
-M Sub::Exporter::Progressive `
|
-M Sub::Exporter::Progressive `
|
||||||
-M Sub::Name `
|
|
||||||
-M Symbol `
|
-M Symbol `
|
||||||
-M Term::Cap `
|
-M Term::Cap `
|
||||||
-M Text::ParseWords `
|
-M Text::ParseWords `
|
||||||
|
@ -23,7 +23,12 @@ int main(int argc, char **argv, char **env)
|
|||||||
|
|
||||||
char exe_path[MAX_PATH] = {0};
|
char exe_path[MAX_PATH] = {0};
|
||||||
char script_path[MAX_PATH];
|
char script_path[MAX_PATH];
|
||||||
|
char gui_flag[6] = {"--gui"};
|
||||||
|
#ifdef FORCE_GUI
|
||||||
|
char** command_line = (char**)malloc(sizeof(char*) * ((++ argc) + 2));
|
||||||
|
#else
|
||||||
char** command_line = (char**)malloc(sizeof(char*) * ((++ argc) + 1));
|
char** command_line = (char**)malloc(sizeof(char*) * ((++ argc) + 1));
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
// Unicode path. This will not be used directly, but to test, whether
|
// Unicode path. This will not be used directly, but to test, whether
|
||||||
// there are any non-ISO characters, in which case the path is converted to a
|
// there are any non-ISO characters, in which case the path is converted to a
|
||||||
@ -68,7 +73,12 @@ int main(int argc, char **argv, char **env)
|
|||||||
command_line[0] = exe_path;
|
command_line[0] = exe_path;
|
||||||
command_line[1] = script_path;
|
command_line[1] = script_path;
|
||||||
memcpy(command_line + 2, argv + 1, sizeof(char*) * (argc - 2));
|
memcpy(command_line + 2, argv + 1, sizeof(char*) * (argc - 2));
|
||||||
|
#ifdef FORCE_GUI
|
||||||
|
command_line[argc] = gui_flag;
|
||||||
|
command_line[argc+1] = NULL;
|
||||||
|
#else
|
||||||
command_line[argc] = NULL;
|
command_line[argc] = NULL;
|
||||||
|
#endif
|
||||||
// Unset the PERL5LIB and PERLLIB environment variables.
|
// Unset the PERL5LIB and PERLLIB environment variables.
|
||||||
SetEnvironmentVariable("PERL5LIB", NULL);
|
SetEnvironmentVariable("PERL5LIB", NULL);
|
||||||
SetEnvironmentVariable("PERLLIB", NULL);
|
SetEnvironmentVariable("PERLLIB", NULL);
|
||||||
@ -78,7 +88,11 @@ int main(int argc, char **argv, char **env)
|
|||||||
printf(" %d: %s\r\n", i, command_line[i]);
|
printf(" %d: %s\r\n", i, command_line[i]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#ifdef FORCE_GUI
|
||||||
|
RunPerl(argc+1, command_line, NULL);
|
||||||
|
#else
|
||||||
RunPerl(argc, command_line, NULL);
|
RunPerl(argc, command_line, NULL);
|
||||||
|
#endif
|
||||||
free(command_line);
|
free(command_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ id ICON "../../var/Slic3r.ico"
|
|||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,3,0,0
|
FILEVERSION 1,3,0,0
|
||||||
PRODUCTVERSION 1,3,0,0
|
PRODUCTVERSION 1,3,0,0
|
||||||
|
FILEOS 0x4
|
||||||
|
FILETYPE 0x1
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -10,7 +12,7 @@ BEGIN
|
|||||||
VALUE "CompanyName", "Slic3r.org"
|
VALUE "CompanyName", "Slic3r.org"
|
||||||
VALUE "FileDescription", "3D Printer Slicer application"
|
VALUE "FileDescription", "3D Printer Slicer application"
|
||||||
VALUE "FileVersion", "1.3.0"
|
VALUE "FileVersion", "1.3.0"
|
||||||
VALUE "InternalName", "slic3r"
|
VALUE "InternalName", "slic3r.exe"
|
||||||
VALUE "LegalCopyright", "Alessandro Ranellucci"
|
VALUE "LegalCopyright", "Alessandro Ranellucci"
|
||||||
VALUE "OriginalFilename", "slic3r.exe"
|
VALUE "OriginalFilename", "slic3r.exe"
|
||||||
VALUE "ProductName", "Slic3r"
|
VALUE "ProductName", "Slic3r"
|
||||||
@ -22,4 +24,4 @@ BEGIN
|
|||||||
VALUE "Translation", 0x409, 1252
|
VALUE "Translation", 0x409, 1252
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
1 Manifest slic3r.exe.manifest
|
1 24 "slic3r.exe.manifest"
|
||||||
|
38
slic3r.pl
38
slic3r.pl
@ -6,7 +6,7 @@ use warnings;
|
|||||||
BEGIN {
|
BEGIN {
|
||||||
use FindBin;
|
use FindBin;
|
||||||
use lib "$FindBin::Bin/lib";
|
use lib "$FindBin::Bin/lib";
|
||||||
use local::lib "$FindBin::Bin/local-lib";
|
use local::lib '--no-create', "$FindBin::Bin/local-lib";
|
||||||
}
|
}
|
||||||
|
|
||||||
use File::Basename qw(basename);
|
use File::Basename qw(basename);
|
||||||
@ -36,8 +36,6 @@ my %cli_options = ();
|
|||||||
'autosave=s' => \$opt{autosave},
|
'autosave=s' => \$opt{autosave},
|
||||||
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
|
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
|
||||||
'no-controller' => \$opt{no_controller},
|
'no-controller' => \$opt{no_controller},
|
||||||
'no-plater' => \$opt{no_plater},
|
|
||||||
'gui-mode=s' => \$opt{gui_mode},
|
|
||||||
'datadir=s' => \$opt{datadir},
|
'datadir=s' => \$opt{datadir},
|
||||||
'export-svg' => \$opt{export_svg},
|
'export-svg' => \$opt{export_svg},
|
||||||
'merge|m' => \$opt{merge},
|
'merge|m' => \$opt{merge},
|
||||||
@ -78,28 +76,26 @@ if ($opt{load}) {
|
|||||||
$opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n";
|
$opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# expand shortcuts before applying, otherwise destination values would be already filled with defaults
|
||||||
|
$_->normalize for @external_configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
# process command line options
|
# process command line options
|
||||||
my $cli_config = Slic3r::Config->new;
|
my $cli_config = Slic3r::Config->new_from_cli(%cli_options);
|
||||||
foreach my $c (@external_configs, Slic3r::Config->new_from_cli(%cli_options)) {
|
$cli_config->normalize; # expand shortcuts
|
||||||
$c->normalize; # expand shortcuts before applying, otherwise destination values would be already filled with defaults
|
|
||||||
$cli_config->apply($c);
|
|
||||||
}
|
|
||||||
|
|
||||||
# save configuration
|
# save configuration
|
||||||
if ($opt{save}) {
|
if ($opt{save}) {
|
||||||
if (@{$cli_config->get_keys} > 0) {
|
my $config = $cli_config->clone;
|
||||||
$cli_config->save($opt{save});
|
$config->apply($_) for @external_configs;
|
||||||
|
if (@{$config->get_keys} > 0) {
|
||||||
|
$config->save($opt{save});
|
||||||
} else {
|
} else {
|
||||||
Slic3r::Config->new_from_defaults->save($opt{save});
|
Slic3r::Config->new_from_defaults->save($opt{save});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# apply command line config on top of default config
|
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
|
||||||
$config->apply($cli_config);
|
|
||||||
|
|
||||||
# launch GUI
|
# launch GUI
|
||||||
my $gui;
|
my $gui;
|
||||||
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||||
@ -107,9 +103,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
|||||||
no warnings 'once';
|
no warnings 'once';
|
||||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
||||||
$Slic3r::GUI::no_controller = $opt{no_controller};
|
$Slic3r::GUI::no_controller = $opt{no_controller};
|
||||||
$Slic3r::GUI::no_plater = $opt{no_plater};
|
$Slic3r::GUI::autosave = Slic3r::decode_path($opt{autosave} // '');
|
||||||
$Slic3r::GUI::mode = $opt{gui_mode};
|
|
||||||
$Slic3r::GUI::autosave = $opt{autosave};
|
|
||||||
$Slic3r::GUI::threads = $opt{threads};
|
$Slic3r::GUI::threads = $opt{threads};
|
||||||
}
|
}
|
||||||
$gui = Slic3r::GUI->new;
|
$gui = Slic3r::GUI->new;
|
||||||
@ -119,7 +113,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
|||||||
$gui->{mainframe}->load_config($cli_config);
|
$gui->{mainframe}->load_config($cli_config);
|
||||||
foreach my $input_file (@ARGV) {
|
foreach my $input_file (@ARGV) {
|
||||||
$input_file = Slic3r::decode_path($input_file);
|
$input_file = Slic3r::decode_path($input_file);
|
||||||
$gui->{mainframe}{plater}->load_file($input_file) unless $opt{no_plater};
|
$gui->{mainframe}{plater}->load_file($input_file);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$gui->MainLoop;
|
$gui->MainLoop;
|
||||||
@ -128,6 +122,10 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
|||||||
die $@ if $@ && $opt{gui};
|
die $@ if $@ && $opt{gui};
|
||||||
|
|
||||||
if (@ARGV) { # slicing from command line
|
if (@ARGV) { # slicing from command line
|
||||||
|
# apply command line config on top of default config
|
||||||
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
|
$config->apply($_) for @external_configs;
|
||||||
|
$config->apply($cli_config);
|
||||||
$config->validate;
|
$config->validate;
|
||||||
|
|
||||||
if ($opt{repair}) {
|
if ($opt{repair}) {
|
||||||
@ -329,8 +327,6 @@ $j
|
|||||||
GUI options:
|
GUI options:
|
||||||
--gui Forces the GUI launch instead of command line slicing (if you
|
--gui Forces the GUI launch instead of command line slicing (if you
|
||||||
supply a model file, it will be loaded into the plater)
|
supply a model file, it will be loaded into the plater)
|
||||||
--no-plater Disable the plater tab
|
|
||||||
--gui-mode Overrides the configured mode (simple/expert)
|
|
||||||
--autosave <file> Automatically export current configuration to the specified file
|
--autosave <file> Automatically export current configuration to the specified file
|
||||||
|
|
||||||
Output options:
|
Output options:
|
||||||
@ -490,6 +486,8 @@ $j
|
|||||||
--support-material-enforce-layers
|
--support-material-enforce-layers
|
||||||
Enforce support material on the specified number of layers from bottom,
|
Enforce support material on the specified number of layers from bottom,
|
||||||
regardless of --support-material and threshold (0+, default: $config->{support_material_enforce_layers})
|
regardless of --support-material and threshold (0+, default: $config->{support_material_enforce_layers})
|
||||||
|
--support-material-buildplate-only
|
||||||
|
Only create support if it lies on a build plate. Don't create support on a print. (default: no)
|
||||||
--dont-support-bridges
|
--dont-support-bridges
|
||||||
Experimental option for preventing support material from being generated under bridged areas (default: yes)
|
Experimental option for preventing support material from being generated under bridged areas (default: yes)
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ add_library(libslic3r STATIC
|
|||||||
${LIBDIR}/libslic3r/GCode/SpiralVase.cpp
|
${LIBDIR}/libslic3r/GCode/SpiralVase.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeReader.cpp
|
${LIBDIR}/libslic3r/GCodeReader.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeSender.cpp
|
${LIBDIR}/libslic3r/GCodeSender.cpp
|
||||||
|
${LIBDIR}/libslic3r/GCodeTimeEstimator.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
${LIBDIR}/libslic3r/GCodeWriter.cpp
|
||||||
${LIBDIR}/libslic3r/Geometry.cpp
|
${LIBDIR}/libslic3r/Geometry.cpp
|
||||||
${LIBDIR}/libslic3r/IO.cpp
|
${LIBDIR}/libslic3r/IO.cpp
|
||||||
|
19
t/fill.t
19
t/fill.t
@ -2,7 +2,7 @@ use Test::More;
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
plan tests => 93;
|
plan tests => 95;
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
use FindBin;
|
use FindBin;
|
||||||
@ -86,6 +86,23 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||||||
'paths don\'t cross hole') or done_testing, exit;
|
'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"
|
die "Usage: send-gcode.pl SERIALPORT BAUDRATE GCODE_FILE\n"
|
||||||
if @ARGV != 3;
|
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;
|
1 until $serial->is_connected;
|
||||||
print "Connected to printer\n";
|
print "Connected to printer\n";
|
||||||
|
|
||||||
|
BIN
var/wand.png
Executable file
BIN
var/wand.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 570 B |
@ -70,6 +70,9 @@ if (defined $ENV{BOOST_INCLUDEDIR}) {
|
|||||||
my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include');
|
my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include');
|
||||||
if (-d $subdir) {
|
if (-d $subdir) {
|
||||||
push @boost_include, $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 {
|
} else {
|
||||||
push @boost_include, $ENV{BOOST_DIR};
|
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');
|
my $subdir = $ENV{BOOST_DIR} . ($mswin ? '\stage\lib' : '/stage/lib');
|
||||||
if (-d $subdir) {
|
if (-d $subdir) {
|
||||||
push @boost_libs, $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 {
|
} else {
|
||||||
push @boost_libs, $ENV{BOOST_DIR};
|
push @boost_libs, $ENV{BOOST_DIR};
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,8 @@ src/libslic3r/GCodeReader.cpp
|
|||||||
src/libslic3r/GCodeReader.hpp
|
src/libslic3r/GCodeReader.hpp
|
||||||
src/libslic3r/GCodeSender.cpp
|
src/libslic3r/GCodeSender.cpp
|
||||||
src/libslic3r/GCodeSender.hpp
|
src/libslic3r/GCodeSender.hpp
|
||||||
|
src/libslic3r/GCodeTimeEstimator.cpp
|
||||||
|
src/libslic3r/GCodeTimeEstimator.hpp
|
||||||
src/libslic3r/GCodeWriter.cpp
|
src/libslic3r/GCodeWriter.cpp
|
||||||
src/libslic3r/GCodeWriter.hpp
|
src/libslic3r/GCodeWriter.hpp
|
||||||
src/libslic3r/Geometry.cpp
|
src/libslic3r/Geometry.cpp
|
||||||
@ -182,6 +184,7 @@ xsp/Filler.xsp
|
|||||||
xsp/Flow.xsp
|
xsp/Flow.xsp
|
||||||
xsp/GCode.xsp
|
xsp/GCode.xsp
|
||||||
xsp/GCodeSender.xsp
|
xsp/GCodeSender.xsp
|
||||||
|
xsp/GCodeTimeEstimator.xsp
|
||||||
xsp/GCodeWriter.xsp
|
xsp/GCodeWriter.xsp
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
xsp/GUI.xsp
|
xsp/GUI.xsp
|
||||||
|
@ -131,6 +131,12 @@ stl_count_facets(stl_file *stl, const char *file) {
|
|||||||
if((!fread(&header_num_facets, sizeof(int), 1, stl->fp)) || (num_facets != header_num_facets)) {
|
if((!fread(&header_num_facets, sizeof(int), 1, stl->fp)) || (num_facets != header_num_facets)) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Warning: File size doesn't match number of facets in the header\n");
|
"Warning: File size doesn't match number of facets in the header\n");
|
||||||
|
|
||||||
|
if(num_facets > header_num_facets) {
|
||||||
|
// this file is garbage.
|
||||||
|
stl->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Otherwise, if the .STL file is ASCII, then do the following */
|
/* Otherwise, if the .STL file is ASCII, then do the following */
|
||||||
|
@ -242,48 +242,48 @@ ConfigDef::merge(const ConfigDef &other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ConfigBase::has(const t_config_option_key &opt_key) {
|
ConfigBase::has(const t_config_option_key &opt_key) const {
|
||||||
return (this->option(opt_key, false) != NULL);
|
return this->option(opt_key) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
|
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
|
||||||
// get list of option keys to apply
|
// apply all options
|
||||||
t_config_option_keys opt_keys = other.keys();
|
this->apply_only(other, other.keys(), ignore_nonexistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent) {
|
||||||
// loop through options and apply them
|
// loop through options and apply them
|
||||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
for (const t_config_option_key &opt_key : opt_keys) {
|
||||||
ConfigOption* my_opt = this->option(*it, true);
|
ConfigOption* my_opt = this->option(opt_key, true);
|
||||||
if (my_opt == NULL) {
|
if (my_opt == NULL) {
|
||||||
if (ignore_nonexistent == false) throw "Attempt to apply non-existent option";
|
if (ignore_nonexistent == false) throw "Attempt to apply non-existent option";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not the most efficient way, but easier than casting pointers to subclasses
|
// not the most efficient way, but easier than casting pointers to subclasses
|
||||||
bool res = my_opt->deserialize( other.option(*it)->serialize() );
|
bool res = my_opt->deserialize( other.option(opt_key)->serialize() );
|
||||||
if (!res) {
|
if (!res) {
|
||||||
std::string error = "Unexpected failure when deserializing serialized value for " + *it;
|
std::string error = "Unexpected failure when deserializing serialized value for " + opt_key;
|
||||||
CONFESS(error.c_str());
|
CONFESS(error.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ConfigBase::equals(ConfigBase &other) {
|
ConfigBase::equals(const ConfigBase &other) const {
|
||||||
return this->diff(other).empty();
|
return this->diff(other).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will *ignore* options not present in both configs
|
// this will *ignore* options not present in both configs
|
||||||
t_config_option_keys
|
t_config_option_keys
|
||||||
ConfigBase::diff(ConfigBase &other) {
|
ConfigBase::diff(const ConfigBase &other) const {
|
||||||
t_config_option_keys diff;
|
t_config_option_keys diff;
|
||||||
|
|
||||||
t_config_option_keys my_keys = this->keys();
|
for (const t_config_option_key &opt_key : this->keys())
|
||||||
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key) {
|
if (other.has(opt_key) && other.serialize(opt_key) != this->serialize(opt_key))
|
||||||
if (other.has(*opt_key) && other.serialize(*opt_key) != this->serialize(*opt_key)) {
|
diff.push_back(opt_key);
|
||||||
diff.push_back(*opt_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
@ -501,16 +501,6 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
|
|||||||
return this->options[opt_key];
|
return this->options[opt_key];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
|
||||||
T*
|
|
||||||
DynamicConfig::opt(const t_config_option_key &opt_key, bool create) {
|
|
||||||
return dynamic_cast<T*>(this->option(opt_key, create));
|
|
||||||
}
|
|
||||||
template ConfigOptionInt* DynamicConfig::opt<ConfigOptionInt>(const t_config_option_key &opt_key, bool create);
|
|
||||||
template ConfigOptionBool* DynamicConfig::opt<ConfigOptionBool>(const t_config_option_key &opt_key, bool create);
|
|
||||||
template ConfigOptionBools* DynamicConfig::opt<ConfigOptionBools>(const t_config_option_key &opt_key, bool create);
|
|
||||||
template ConfigOptionPercent* DynamicConfig::opt<ConfigOptionPercent>(const t_config_option_key &opt_key, bool create);
|
|
||||||
|
|
||||||
t_config_option_keys
|
t_config_option_keys
|
||||||
DynamicConfig::keys() const {
|
DynamicConfig::keys() const {
|
||||||
t_config_option_keys keys;
|
t_config_option_keys keys;
|
||||||
@ -524,6 +514,16 @@ DynamicConfig::erase(const t_config_option_key &opt_key) {
|
|||||||
this->options.erase(opt_key);
|
this->options.erase(opt_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DynamicConfig::clear() {
|
||||||
|
this->options.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DynamicConfig::empty() const {
|
||||||
|
return this->options.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
|
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
|
||||||
{
|
{
|
||||||
|
@ -644,14 +644,21 @@ class ConfigBase
|
|||||||
ConfigBase() : def(NULL) {};
|
ConfigBase() : def(NULL) {};
|
||||||
ConfigBase(const ConfigDef* def) : def(def) {};
|
ConfigBase(const ConfigDef* def) : def(def) {};
|
||||||
virtual ~ConfigBase() {};
|
virtual ~ConfigBase() {};
|
||||||
bool has(const t_config_option_key &opt_key);
|
bool has(const t_config_option_key &opt_key) const;
|
||||||
const ConfigOption* option(const t_config_option_key &opt_key) const;
|
const ConfigOption* option(const t_config_option_key &opt_key) const;
|
||||||
ConfigOption* option(const t_config_option_key &opt_key, bool create = false);
|
ConfigOption* option(const t_config_option_key &opt_key, bool create = false);
|
||||||
|
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false) {
|
||||||
|
return dynamic_cast<T*>(this->option(opt_key, create));
|
||||||
|
};
|
||||||
|
template<class T> const T* opt(const t_config_option_key &opt_key) const {
|
||||||
|
return dynamic_cast<const T*>(this->option(opt_key));
|
||||||
|
};
|
||||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
|
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
|
||||||
virtual t_config_option_keys keys() const = 0;
|
virtual t_config_option_keys keys() const = 0;
|
||||||
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
|
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
|
||||||
bool equals(ConfigBase &other);
|
void apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent = false);
|
||||||
t_config_option_keys diff(ConfigBase &other);
|
bool equals(const ConfigBase &other) const;
|
||||||
|
t_config_option_keys diff(const ConfigBase &other) const;
|
||||||
std::string serialize(const t_config_option_key &opt_key) const;
|
std::string serialize(const t_config_option_key &opt_key) const;
|
||||||
virtual bool set_deserialize(t_config_option_key opt_key, std::string str, bool append = false);
|
virtual bool set_deserialize(t_config_option_key opt_key, std::string str, bool append = false);
|
||||||
double get_abs_value(const t_config_option_key &opt_key) const;
|
double get_abs_value(const t_config_option_key &opt_key) const;
|
||||||
@ -672,10 +679,11 @@ class DynamicConfig : public virtual ConfigBase
|
|||||||
DynamicConfig& operator= (DynamicConfig other);
|
DynamicConfig& operator= (DynamicConfig other);
|
||||||
void swap(DynamicConfig &other);
|
void swap(DynamicConfig &other);
|
||||||
virtual ~DynamicConfig();
|
virtual ~DynamicConfig();
|
||||||
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false);
|
|
||||||
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
|
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
|
||||||
t_config_option_keys keys() const;
|
t_config_option_keys keys() const;
|
||||||
void erase(const t_config_option_key &opt_key);
|
void erase(const t_config_option_key &opt_key);
|
||||||
|
void clear();
|
||||||
|
bool empty() const;
|
||||||
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
|
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
|
||||||
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
||||||
|
|
||||||
|
@ -37,9 +37,14 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon,
|
|||||||
if (bounding_box.size().x < min_spacing) return;
|
if (bounding_box.size().x < min_spacing) return;
|
||||||
|
|
||||||
// Due to integer rounding, rotated polygons might not preserve verticality
|
// Due to integer rounding, rotated polygons might not preserve verticality
|
||||||
// (i.e. when rotating by PI/2 two points having the same x coordinate
|
// (i.e. when rotating by PI/2 two points having the same y coordinate
|
||||||
// they might get different y coordinates), thus the first line will be skipped.
|
// they might get different x coordinates), thus the first line will be skipped.
|
||||||
bounding_box.offset(-1);
|
// 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
|
// define flow spacing according to requested density
|
||||||
if (this->density > 0.9999f && !this->dont_adjust) {
|
if (this->density > 0.9999f && !this->dont_adjust) {
|
||||||
|
@ -39,7 +39,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||||||
{
|
{
|
||||||
GCodeReader r = this->_reader; // clone
|
GCodeReader r = this->_reader; // clone
|
||||||
r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
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.cmd == "G1") {
|
||||||
if (line.extruding()) {
|
if (line.extruding()) {
|
||||||
total_layer_length += line.dist_XY();
|
total_layer_length += line.dist_XY();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "GCodeReader.hpp"
|
#include "GCodeReader.hpp"
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -17,59 +18,73 @@ GCodeReader::parse(const std::string &gcode, callback_t callback)
|
|||||||
{
|
{
|
||||||
std::istringstream ss(gcode);
|
std::istringstream ss(gcode);
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ss, line)) {
|
while (std::getline(ss, line))
|
||||||
GCodeLine gline(this);
|
this->parse_line(line, callback);
|
||||||
gline.raw = line;
|
}
|
||||||
if (this->verbose)
|
|
||||||
std::cout << line << std::endl;
|
|
||||||
|
|
||||||
// strip comment
|
void
|
||||||
{
|
GCodeReader::parse_line(std::string line, callback_t callback)
|
||||||
size_t pos = line.find(';');
|
{
|
||||||
if (pos != std::string::npos) {
|
GCodeLine gline(this);
|
||||||
gline.comment = line.substr(pos+1);
|
gline.raw = line;
|
||||||
line.erase(pos);
|
if (this->verbose)
|
||||||
}
|
std::cout << line << std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
// command and args
|
// strip comment
|
||||||
{
|
{
|
||||||
std::vector<std::string> args;
|
size_t pos = line.find(';');
|
||||||
boost::split(args, line, boost::is_any_of(" "));
|
if (pos != std::string::npos) {
|
||||||
|
gline.comment = line.substr(pos+1);
|
||||||
// first one is cmd
|
line.erase(pos);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
void
|
||||||
|
@ -25,6 +25,7 @@ class GCodeReader {
|
|||||||
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
||||||
|
|
||||||
bool has(char arg) const { return this->args.count(arg) > 0; };
|
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_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_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; };
|
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'); };
|
bool travel() const { return this->cmd == "G1" && !this->has('E'); };
|
||||||
void set(char arg, std::string value);
|
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;
|
float X, Y, Z, E, F;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
@ -53,6 +54,8 @@ class GCodeReader {
|
|||||||
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {};
|
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), _extrusion_axis('E') {};
|
||||||
void apply_config(const PrintConfigBase &config);
|
void apply_config(const PrintConfigBase &config);
|
||||||
void parse(const std::string &gcode, callback_t callback);
|
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:
|
private:
|
||||||
GCodeConfig _config;
|
GCodeConfig _config;
|
||||||
|
@ -8,16 +8,36 @@
|
|||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__linux) || defined(__OpenBSD__)
|
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#endif
|
#endif
|
||||||
#if __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <IOKit/serial/ioss.h>
|
#include <IOKit/serial/ioss.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef __linux
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#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
|
#endif
|
||||||
|
|
||||||
//#define DEBUG_SERIAL
|
//#define DEBUG_SERIAL
|
||||||
@ -65,7 +85,6 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
|||||||
this->open = true;
|
this->open = true;
|
||||||
this->reset();
|
this->reset();
|
||||||
} catch (boost::system::system_error &e) {
|
} catch (boost::system::system_error &e) {
|
||||||
printf("Caught error\n");
|
|
||||||
this->set_error_status(true);
|
this->set_error_status(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -87,6 +106,9 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
|||||||
boost::thread t(boost::bind(&asio::io_service::run, &this->io));
|
boost::thread t(boost::bind(&asio::io_service::run, &this->io));
|
||||||
this->background_thread.swap(t);
|
this->background_thread.swap(t);
|
||||||
|
|
||||||
|
// always send a M105 to check for connection because firmware might be silent on connect
|
||||||
|
this->send("M105", true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,27 +129,17 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
|
|||||||
ioctl(handle, IOSSIOSPEED, &newSpeed);
|
ioctl(handle, IOSSIOSPEED, &newSpeed);
|
||||||
::tcsetattr(handle, TCSANOW, &ios);
|
::tcsetattr(handle, TCSANOW, &ios);
|
||||||
#elif __linux
|
#elif __linux
|
||||||
termios ios;
|
termios2 ios;
|
||||||
::tcgetattr(handle, &ios);
|
if (ioctl(handle, TCGETS2, &ios))
|
||||||
::cfsetispeed(&ios, B38400);
|
printf("Error in TCGETS2: %s\n", strerror(errno));
|
||||||
::cfsetospeed(&ios, B38400);
|
ios.c_ispeed = ios.c_ospeed = baud_rate;
|
||||||
::tcflush(handle, TCIFLUSH);
|
ios.c_cflag &= ~CBAUD;
|
||||||
::tcsetattr(handle, TCSANOW, &ios);
|
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));
|
||||||
|
|
||||||
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);
|
|
||||||
#elif __OpenBSD__
|
#elif __OpenBSD__
|
||||||
struct termios ios;
|
struct termios ios;
|
||||||
::tcgetattr(handle, &ios);
|
::tcgetattr(handle, &ios);
|
||||||
@ -296,17 +308,20 @@ GCodeSender::on_read(const boost::system::error_code& error,
|
|||||||
{
|
{
|
||||||
this->set_error_status(false);
|
this->set_error_status(false);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
#ifdef __APPLE__
|
||||||
if (error.value() == 45) {
|
if (error.value() == 45) {
|
||||||
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
|
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
|
||||||
this->do_read();
|
this->do_read();
|
||||||
} else {
|
return;
|
||||||
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
}
|
||||||
// error can be true even because the serial port was closed.
|
#endif
|
||||||
// In this case it is not a real error, so ignore.
|
|
||||||
if (this->open) {
|
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
||||||
this->do_close();
|
// error can be true even because the serial port was closed.
|
||||||
this->set_error_status(true);
|
// In this case it is not a real error, so ignore.
|
||||||
}
|
if (this->open) {
|
||||||
|
this->do_close();
|
||||||
|
this->set_error_status(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -343,6 +358,7 @@ GCodeSender::on_read(const boost::system::error_code& error,
|
|||||||
// extract the first number from line
|
// extract the first number from line
|
||||||
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
||||||
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
||||||
|
toresend++; // N is 0-based
|
||||||
if (toresend >= this->sent - this->last_sent.size()) {
|
if (toresend >= this->sent - this->last_sent.size()) {
|
||||||
{
|
{
|
||||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||||
@ -461,8 +477,8 @@ GCodeSender::do_send()
|
|||||||
if (line.empty()) return;
|
if (line.empty()) return;
|
||||||
|
|
||||||
// compute full line
|
// compute full line
|
||||||
this->sent++;
|
|
||||||
std::string full_line = "N" + boost::lexical_cast<std::string>(this->sent) + " " + line;
|
std::string full_line = "N" + boost::lexical_cast<std::string>(this->sent) + " " + line;
|
||||||
|
this->sent++;
|
||||||
|
|
||||||
// calculate checksum
|
// calculate checksum
|
||||||
int cs = 0;
|
int cs = 0;
|
||||||
|
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_ */
|
@ -197,11 +197,11 @@ Layer::make_perimeters()
|
|||||||
|
|
||||||
// merge the surfaces assigned to each group
|
// merge the surfaces assigned to each group
|
||||||
SurfaceCollection new_slices;
|
SurfaceCollection new_slices;
|
||||||
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) {
|
for (const auto &it : slices) {
|
||||||
ExPolygons expp = union_ex(it->second, true);
|
ExPolygons expp = union_ex(it.second, true);
|
||||||
for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
for (ExPolygon &ex : expp) {
|
||||||
Surface s = it->second.front(); // clone type and extra_perimeters
|
Surface s = it.second.front(); // clone type and extra_perimeters
|
||||||
s.expolygon = *ex;
|
s.expolygon = ex;
|
||||||
new_slices.surfaces.push_back(s);
|
new_slices.surfaces.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,6 +274,8 @@ Layer::detect_surfaces_type()
|
|||||||
Layer* const &lower_layer = this->lower_layer;
|
Layer* const &lower_layer = this->lower_layer;
|
||||||
|
|
||||||
// collapse very narrow parts (using the safety offset in the diff is not enough)
|
// collapse very narrow parts (using the safety offset in the diff is not enough)
|
||||||
|
// TODO: this offset2 makes this method not idempotent (see #3764), so we should
|
||||||
|
// move it to where we generate fill_surfaces instead and leave slices unaltered
|
||||||
const float offs = layerm.flow(frExternalPerimeter).scaled_width() / 10.f;
|
const float offs = layerm.flow(frExternalPerimeter).scaled_width() / 10.f;
|
||||||
|
|
||||||
const Polygons layerm_slices_surfaces = layerm.slices;
|
const Polygons layerm_slices_surfaces = layerm.slices;
|
||||||
|
@ -11,36 +11,34 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
LayerHeightSpline::LayerHeightSpline(coordf_t object_height)
|
LayerHeightSpline::LayerHeightSpline()
|
||||||
: _object_height(object_height), _layer_height_spline(NULL)
|
: _object_height(0)
|
||||||
{
|
{
|
||||||
this->_is_valid = false;
|
this->_is_valid = false;
|
||||||
this->_update_required = true;
|
this->_update_required = true;
|
||||||
this->_layers_updated = false;
|
this->_layers_updated = false;
|
||||||
this->_layer_heights_updated = false;
|
this->_layer_heights_updated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other)
|
LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other)
|
||||||
: _object_height(other._object_height), _layer_height_spline(NULL)
|
|
||||||
{
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerHeightSpline& LayerHeightSpline::operator=(const LayerHeightSpline &other)
|
||||||
|
{
|
||||||
|
this->_object_height = other._object_height;
|
||||||
this->_original_layers = other._original_layers;
|
this->_original_layers = other._original_layers;
|
||||||
this->_internal_layers = other._internal_layers;
|
this->_internal_layers = other._internal_layers;
|
||||||
this->_internal_layer_heights = other._internal_layer_heights;
|
this->_internal_layer_heights = other._internal_layer_heights;
|
||||||
this->_is_valid = other._is_valid;
|
this->_is_valid = other._is_valid;
|
||||||
this->_update_required = other._update_required;
|
this->_update_required = other._update_required;
|
||||||
this->_layers_updated = other._layers_updated;
|
this->_layers_updated = other._layers_updated;
|
||||||
this->_layer_heights_updated = other._layer_heights_updated;
|
this->_layer_heights_updated = other._layer_heights_updated;
|
||||||
if(this->_is_valid) {
|
if(this->_is_valid) {
|
||||||
this->_updateBSpline();
|
this->_updateBSpline();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LayerHeightSpline::~LayerHeightSpline()
|
|
||||||
{
|
|
||||||
if (this->_layer_height_spline) {
|
|
||||||
delete this->_layer_height_spline;
|
|
||||||
}
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -48,7 +46,7 @@ LayerHeightSpline::~LayerHeightSpline()
|
|||||||
*/
|
*/
|
||||||
bool LayerHeightSpline::hasData()
|
bool LayerHeightSpline::hasData()
|
||||||
{
|
{
|
||||||
return this->_is_valid;
|
return this->_is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -59,21 +57,21 @@ bool LayerHeightSpline::hasData()
|
|||||||
*/
|
*/
|
||||||
bool LayerHeightSpline::updateRequired()
|
bool LayerHeightSpline::updateRequired()
|
||||||
{
|
{
|
||||||
bool result = true; // update spline by default
|
bool result = true; // update spline by default
|
||||||
if(!this->_update_required && this->_is_valid) {
|
if(!this->_update_required && this->_is_valid) {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
this->_update_required = true; // reset to default after request
|
this->_update_required = true; // reset to default after request
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't require an update for exactly one iteration.
|
* Don't require an update for exactly one iteration.
|
||||||
*/
|
*/
|
||||||
void LayerHeightSpline::suppressUpdate() {
|
void LayerHeightSpline::suppressUpdate() {
|
||||||
if (this->_is_valid) {
|
if (this->_is_valid) {
|
||||||
this->_update_required = false;
|
this->_update_required = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -82,27 +80,27 @@ void LayerHeightSpline::suppressUpdate() {
|
|||||||
*/
|
*/
|
||||||
bool LayerHeightSpline::setLayers(std::vector<coordf_t> layers)
|
bool LayerHeightSpline::setLayers(std::vector<coordf_t> layers)
|
||||||
{
|
{
|
||||||
this->_original_layers = layers;
|
this->_original_layers = layers;
|
||||||
|
|
||||||
// generate updated layer height list from layers
|
// generate updated layer height list from layers
|
||||||
this->_internal_layer_heights.clear();
|
this->_internal_layer_heights.clear();
|
||||||
coordf_t last_z = 0;
|
coordf_t last_z = 0;
|
||||||
for (std::vector<coordf_t>::const_iterator l = this->_original_layers.begin(); l != this->_original_layers.end(); ++l) {
|
for (std::vector<coordf_t>::const_iterator l = this->_original_layers.begin(); l != this->_original_layers.end(); ++l) {
|
||||||
this->_internal_layer_heights.push_back(*l-last_z);
|
this->_internal_layer_heights.push_back(*l-last_z);
|
||||||
last_z = *l;
|
last_z = *l;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add 0-values at both ends to achieve correct boundary conditions
|
// add 0-values at both ends to achieve correct boundary conditions
|
||||||
this->_internal_layers = this->_original_layers;
|
this->_internal_layers = this->_original_layers;
|
||||||
this->_internal_layers.insert(this->_internal_layers.begin(), 0); // add z = 0 to the front
|
this->_internal_layers.insert(this->_internal_layers.begin(), 0); // add z = 0 to the front
|
||||||
//this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end
|
//this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end
|
||||||
this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]);
|
this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]);
|
||||||
//this->_internal_layer_heights.push_back(0);
|
//this->_internal_layer_heights.push_back(0);
|
||||||
|
|
||||||
this->_layers_updated = true;
|
this->_layers_updated = true;
|
||||||
this->_layer_heights_updated = false;
|
this->_layer_heights_updated = false;
|
||||||
|
|
||||||
return this->_updateBSpline();
|
return this->_updateBSpline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -114,23 +112,23 @@ bool LayerHeightSpline::setLayers(std::vector<coordf_t> layers)
|
|||||||
*/
|
*/
|
||||||
bool LayerHeightSpline::updateLayerHeights(std::vector<coordf_t> heights)
|
bool LayerHeightSpline::updateLayerHeights(std::vector<coordf_t> heights)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
// do we receive the correct number of values?
|
// do we receive the correct number of values?
|
||||||
if(heights.size() == this->_internal_layers.size()-1) {
|
if(heights.size() == this->_internal_layers.size()-1) {
|
||||||
this->_internal_layer_heights = heights;
|
this->_internal_layer_heights = heights;
|
||||||
// add leading and trailing 0-value
|
// add leading and trailing 0-value
|
||||||
this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]);
|
this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]);
|
||||||
//this->_internal_layer_heights.push_back(0);
|
//this->_internal_layer_heights.push_back(0);
|
||||||
result = this->_updateBSpline();
|
result = this->_updateBSpline();
|
||||||
}else{
|
}else{
|
||||||
std::cerr << "Unable to update layer heights. You provided " << heights.size() << " layers, but " << this->_internal_layers.size()-1 << " expected" << std::endl;
|
std::cerr << "Unable to update layer heights. You provided " << heights.size() << " layers, but " << this->_internal_layers.size()-1 << " expected" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->_layers_updated = false;
|
this->_layers_updated = false;
|
||||||
this->_layer_heights_updated = true;
|
this->_layer_heights_updated = true;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -138,14 +136,13 @@ bool LayerHeightSpline::updateLayerHeights(std::vector<coordf_t> heights)
|
|||||||
*/
|
*/
|
||||||
void LayerHeightSpline::clear()
|
void LayerHeightSpline::clear()
|
||||||
{
|
{
|
||||||
this->_original_layers.clear();
|
this->_original_layers.clear();
|
||||||
this->_internal_layers.clear();
|
this->_internal_layers.clear();
|
||||||
this->_internal_layer_heights.clear();
|
this->_internal_layer_heights.clear();
|
||||||
delete this->_layer_height_spline;
|
this->_layer_height_spline.reset();
|
||||||
this->_layer_height_spline = NULL;
|
this->_is_valid = false;
|
||||||
this->_is_valid = false;
|
this->_layers_updated = false;
|
||||||
this->_layers_updated = false;
|
this->_layer_heights_updated = false;
|
||||||
this->_layer_heights_updated = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -154,28 +151,28 @@ void LayerHeightSpline::clear()
|
|||||||
*/
|
*/
|
||||||
std::vector<coordf_t> LayerHeightSpline::getInterpolatedLayers() const
|
std::vector<coordf_t> LayerHeightSpline::getInterpolatedLayers() const
|
||||||
{
|
{
|
||||||
std::vector<coordf_t> layers;
|
std::vector<coordf_t> layers;
|
||||||
if(this->_is_valid) {
|
if(this->_is_valid) {
|
||||||
// preserve first layer for bed contact
|
// preserve first layer for bed contact
|
||||||
layers.push_back(this->_original_layers[0]);
|
layers.push_back(this->_original_layers[0]);
|
||||||
coordf_t z = this->_original_layers[0];
|
coordf_t z = this->_original_layers[0];
|
||||||
coordf_t h;
|
coordf_t h;
|
||||||
coordf_t h_diff = 0;
|
coordf_t h_diff = 0;
|
||||||
coordf_t eps = 0.0001;
|
coordf_t eps = 0.0001;
|
||||||
while(z <= this->_object_height) {
|
while(z <= this->_object_height) {
|
||||||
h = 0;
|
h = 0;
|
||||||
// find intersection between layer height and spline
|
// find intersection between layer height and spline
|
||||||
do {
|
do {
|
||||||
h += h_diff/2;
|
h += h_diff/2;
|
||||||
h = this->_layer_height_spline->evaluate(z+h);
|
h = this->_layer_height_spline->evaluate(z+h);
|
||||||
h_diff = this->_layer_height_spline->evaluate(z+h) - h;
|
h_diff = this->_layer_height_spline->evaluate(z+h) - h;
|
||||||
} while(std::abs(h_diff) > eps);
|
} while(std::abs(h_diff) > eps);
|
||||||
z += h;
|
z += h;
|
||||||
layers.push_back(z);
|
layers.push_back(z);
|
||||||
}
|
}
|
||||||
// how to make sure, the last layer is not higher than object while maintaining between min/max layer height?
|
// how to make sure, the last layer is not higher than object while maintaining between min/max layer height?
|
||||||
}
|
}
|
||||||
return layers;
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -183,11 +180,11 @@ std::vector<coordf_t> LayerHeightSpline::getInterpolatedLayers() const
|
|||||||
*/
|
*/
|
||||||
const coordf_t LayerHeightSpline::getLayerHeightAt(coordf_t height)
|
const coordf_t LayerHeightSpline::getLayerHeightAt(coordf_t height)
|
||||||
{
|
{
|
||||||
coordf_t result = 0;
|
coordf_t result = 0;
|
||||||
if (this->_is_valid) {
|
if (this->_is_valid) {
|
||||||
result = this->_layer_height_spline->evaluate(height);
|
result = this->_layer_height_spline->evaluate(height);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -195,27 +192,27 @@ const coordf_t LayerHeightSpline::getLayerHeightAt(coordf_t height)
|
|||||||
*/
|
*/
|
||||||
bool LayerHeightSpline::_updateBSpline()
|
bool LayerHeightSpline::_updateBSpline()
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
//TODO: exception if not enough points?
|
//TODO: exception if not enough points?
|
||||||
|
|
||||||
delete this->_layer_height_spline;
|
this->_layer_height_spline.reset(new BSpline<double>(&this->_internal_layers[0],
|
||||||
this->_layer_height_spline = new BSpline<double>(&this->_internal_layers[0],
|
this->_internal_layers.size(),
|
||||||
this->_internal_layers.size(),
|
|
||||||
&this->_internal_layer_heights[0],
|
&this->_internal_layer_heights[0],
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
0);
|
0)
|
||||||
|
);
|
||||||
|
|
||||||
if (this->_layer_height_spline->ok()) {
|
if (this->_layer_height_spline->ok()) {
|
||||||
result = true;
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
result = false;
|
result = false;
|
||||||
std::cerr << "Spline setup failed." << std::endl;
|
std::cerr << "Spline setup failed." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->_is_valid = result;
|
this->_is_valid = result;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,16 +4,16 @@
|
|||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "BSpline/BSpline.h" // Warning: original BSplineBase.h/cpp merged into BSpline.h/cpp to avoid dependency issues caused by Build::WithXSpp which tries to compile all .cpp files in /src
|
#include "BSpline/BSpline.h" // Warning: original BSplineBase.h/cpp merged into BSpline.h/cpp to avoid dependency issues caused by Build::WithXSpp which tries to compile all .cpp files in /src
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
class LayerHeightSpline
|
class LayerHeightSpline
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LayerHeightSpline(coordf_t object_height);
|
LayerHeightSpline();
|
||||||
LayerHeightSpline(const LayerHeightSpline &other);
|
LayerHeightSpline(const LayerHeightSpline &other);
|
||||||
~LayerHeightSpline();
|
LayerHeightSpline& operator=(const LayerHeightSpline &other);
|
||||||
|
void setObjectHeight(coordf_t object_height) { this->_object_height = object_height; };
|
||||||
bool hasData(); // indicate that we have valid data
|
bool hasData(); // indicate that we have valid data
|
||||||
bool updateRequired(); // indicate whether we want to generate a new spline from the layers
|
bool updateRequired(); // indicate whether we want to generate a new spline from the layers
|
||||||
void suppressUpdate();
|
void suppressUpdate();
|
||||||
@ -37,7 +37,7 @@ class LayerHeightSpline
|
|||||||
std::vector<coordf_t> _original_layers;
|
std::vector<coordf_t> _original_layers;
|
||||||
std::vector<coordf_t> _internal_layers;
|
std::vector<coordf_t> _internal_layers;
|
||||||
std::vector<coordf_t> _internal_layer_heights;
|
std::vector<coordf_t> _internal_layer_heights;
|
||||||
BSpline<double> *_layer_height_spline;
|
std::unique_ptr<BSpline<double>> _layer_height_spline;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -246,6 +246,12 @@ LayerRegion::make_fill()
|
|||||||
// apply half spacing using this flow's own spacing and generate infill
|
// apply half spacing using this flow's own spacing and generate infill
|
||||||
f->density = density/100;
|
f->density = density/100;
|
||||||
f->dont_adjust = false;
|
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);
|
Polylines polylines = f->fill_surface(surface);
|
||||||
if (polylines.empty())
|
if (polylines.empty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -433,6 +433,7 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum
|
|||||||
volumes(),
|
volumes(),
|
||||||
config(other.config),
|
config(other.config),
|
||||||
layer_height_ranges(other.layer_height_ranges),
|
layer_height_ranges(other.layer_height_ranges),
|
||||||
|
layer_height_spline(other.layer_height_spline),
|
||||||
origin_translation(other.origin_translation),
|
origin_translation(other.origin_translation),
|
||||||
_bounding_box(other._bounding_box),
|
_bounding_box(other._bounding_box),
|
||||||
_bounding_box_valid(other._bounding_box_valid),
|
_bounding_box_valid(other._bounding_box_valid),
|
||||||
@ -463,6 +464,7 @@ ModelObject::swap(ModelObject &other)
|
|||||||
std::swap(this->volumes, other.volumes);
|
std::swap(this->volumes, other.volumes);
|
||||||
std::swap(this->config, other.config);
|
std::swap(this->config, other.config);
|
||||||
std::swap(this->layer_height_ranges, other.layer_height_ranges);
|
std::swap(this->layer_height_ranges, other.layer_height_ranges);
|
||||||
|
std::swap(this->layer_height_spline, other.layer_height_spline);
|
||||||
std::swap(this->origin_translation, other.origin_translation);
|
std::swap(this->origin_translation, other.origin_translation);
|
||||||
std::swap(this->_bounding_box, other._bounding_box);
|
std::swap(this->_bounding_box, other._bounding_box);
|
||||||
std::swap(this->_bounding_box_valid, other._bounding_box_valid);
|
std::swap(this->_bounding_box_valid, other._bounding_box_valid);
|
||||||
@ -514,6 +516,7 @@ ModelObject::add_instance()
|
|||||||
ModelInstance* i = new ModelInstance(this);
|
ModelInstance* i = new ModelInstance(this);
|
||||||
this->instances.push_back(i);
|
this->instances.push_back(i);
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
|
this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,6 +526,7 @@ ModelObject::add_instance(const ModelInstance &other)
|
|||||||
ModelInstance* i = new ModelInstance(this, other);
|
ModelInstance* i = new ModelInstance(this, other);
|
||||||
this->instances.push_back(i);
|
this->instances.push_back(i);
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
|
this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,6 +536,9 @@ ModelObject::delete_instance(size_t idx)
|
|||||||
ModelInstancePtrs::iterator i = this->instances.begin() + idx;
|
ModelInstancePtrs::iterator i = this->instances.begin() + idx;
|
||||||
delete *i;
|
delete *i;
|
||||||
this->instances.erase(i);
|
this->instances.erase(i);
|
||||||
|
if(!this->instances.empty()) {
|
||||||
|
this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z);
|
||||||
|
}
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +553,7 @@ ModelObject::clear_instances()
|
|||||||
{
|
{
|
||||||
while (!this->instances.empty())
|
while (!this->instances.empty())
|
||||||
this->delete_last_instance();
|
this->delete_last_instance();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this returns the bounding box of the *transformed* instances
|
// this returns the bounding box of the *transformed* instances
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "Layer.hpp"
|
#include "Layer.hpp"
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
|
#include "LayerHeightSpline.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -119,6 +120,8 @@ class ModelObject
|
|||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
// Variation of a layer thickness for spans of Z coordinates.
|
// Variation of a layer thickness for spans of Z coordinates.
|
||||||
t_layer_height_ranges layer_height_ranges;
|
t_layer_height_ranges layer_height_ranges;
|
||||||
|
// Spline based variations of layer thickness for interactive user manipulation
|
||||||
|
LayerHeightSpline layer_height_spline;
|
||||||
|
|
||||||
/* This vector accumulates the total translation applied to the object by the
|
/* This vector accumulates the total translation applied to the object by the
|
||||||
center_around_origin() method. Callers might want to apply the same translation
|
center_around_origin() method. Callers might want to apply the same translation
|
||||||
|
@ -147,92 +147,95 @@ Print::delete_region(size_t idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Print::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
Print::invalidate_state_by_config(const PrintConfigBase &config)
|
||||||
{
|
{
|
||||||
|
const t_config_option_keys diff = this->config.diff(config);
|
||||||
|
|
||||||
std::set<PrintStep> steps;
|
std::set<PrintStep> steps;
|
||||||
std::set<PrintObjectStep> osteps;
|
std::set<PrintObjectStep> osteps;
|
||||||
|
bool all = false;
|
||||||
|
|
||||||
// this method only accepts PrintConfig option keys
|
// this method only accepts PrintConfig option keys
|
||||||
for (std::vector<t_config_option_key>::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) {
|
for (const t_config_option_key &opt_key : diff) {
|
||||||
if (*opt_key == "skirts"
|
if (opt_key == "skirts"
|
||||||
|| *opt_key == "skirt_height"
|
|| opt_key == "skirt_height"
|
||||||
|| *opt_key == "skirt_distance"
|
|| opt_key == "skirt_distance"
|
||||||
|| *opt_key == "min_skirt_length"
|
|| opt_key == "min_skirt_length"
|
||||||
|| *opt_key == "ooze_prevention") {
|
|| opt_key == "ooze_prevention") {
|
||||||
steps.insert(psSkirt);
|
steps.insert(psSkirt);
|
||||||
} else if (*opt_key == "brim_width"
|
} else if (opt_key == "brim_width"
|
||||||
|| *opt_key == "interior_brim_width"
|
|| opt_key == "interior_brim_width"
|
||||||
|| *opt_key == "brim_connections_width") {
|
|| opt_key == "brim_connections_width") {
|
||||||
steps.insert(psBrim);
|
steps.insert(psBrim);
|
||||||
steps.insert(psSkirt);
|
steps.insert(psSkirt);
|
||||||
} else if (*opt_key == "nozzle_diameter"
|
} else if (opt_key == "nozzle_diameter"
|
||||||
|| *opt_key == "resolution"
|
|| opt_key == "resolution"
|
||||||
|| *opt_key == "z_steps_per_mm") {
|
|| opt_key == "z_steps_per_mm") {
|
||||||
osteps.insert(posSlice);
|
osteps.insert(posSlice);
|
||||||
} else if (*opt_key == "avoid_crossing_perimeters"
|
} else if (opt_key == "avoid_crossing_perimeters"
|
||||||
|| *opt_key == "bed_shape"
|
|| opt_key == "bed_shape"
|
||||||
|| *opt_key == "bed_temperature"
|
|| opt_key == "bed_temperature"
|
||||||
|| *opt_key == "bridge_acceleration"
|
|| opt_key == "bridge_acceleration"
|
||||||
|| *opt_key == "bridge_fan_speed"
|
|| opt_key == "bridge_fan_speed"
|
||||||
|| *opt_key == "complete_objects"
|
|| opt_key == "complete_objects"
|
||||||
|| *opt_key == "cooling"
|
|| opt_key == "cooling"
|
||||||
|| *opt_key == "default_acceleration"
|
|| opt_key == "default_acceleration"
|
||||||
|| *opt_key == "disable_fan_first_layers"
|
|| opt_key == "disable_fan_first_layers"
|
||||||
|| *opt_key == "duplicate_distance"
|
|| opt_key == "duplicate_distance"
|
||||||
|| *opt_key == "end_gcode"
|
|| opt_key == "end_gcode"
|
||||||
|| *opt_key == "extruder_clearance_height"
|
|| opt_key == "extruder_clearance_height"
|
||||||
|| *opt_key == "extruder_clearance_radius"
|
|| opt_key == "extruder_clearance_radius"
|
||||||
|| *opt_key == "extruder_offset"
|
|| opt_key == "extruder_offset"
|
||||||
|| *opt_key == "extrusion_axis"
|
|| opt_key == "extrusion_axis"
|
||||||
|| *opt_key == "extrusion_multiplier"
|
|| opt_key == "extrusion_multiplier"
|
||||||
|| *opt_key == "fan_always_on"
|
|| opt_key == "fan_always_on"
|
||||||
|| *opt_key == "fan_below_layer_time"
|
|| opt_key == "fan_below_layer_time"
|
||||||
|| *opt_key == "filament_colour"
|
|| opt_key == "filament_colour"
|
||||||
|| *opt_key == "filament_diameter"
|
|| opt_key == "filament_diameter"
|
||||||
|| *opt_key == "first_layer_acceleration"
|
|| opt_key == "first_layer_acceleration"
|
||||||
|| *opt_key == "first_layer_bed_temperature"
|
|| opt_key == "first_layer_bed_temperature"
|
||||||
|| *opt_key == "first_layer_speed"
|
|| opt_key == "first_layer_speed"
|
||||||
|| *opt_key == "first_layer_temperature"
|
|| opt_key == "first_layer_temperature"
|
||||||
|| *opt_key == "gcode_arcs"
|
|| opt_key == "gcode_arcs"
|
||||||
|| *opt_key == "gcode_comments"
|
|| opt_key == "gcode_comments"
|
||||||
|| *opt_key == "gcode_flavor"
|
|| opt_key == "gcode_flavor"
|
||||||
|| *opt_key == "infill_acceleration"
|
|| opt_key == "infill_acceleration"
|
||||||
|| *opt_key == "infill_first"
|
|| opt_key == "infill_first"
|
||||||
|| *opt_key == "layer_gcode"
|
|| opt_key == "layer_gcode"
|
||||||
|| *opt_key == "min_fan_speed"
|
|| opt_key == "min_fan_speed"
|
||||||
|| *opt_key == "max_fan_speed"
|
|| opt_key == "max_fan_speed"
|
||||||
|| *opt_key == "min_print_speed"
|
|| opt_key == "min_print_speed"
|
||||||
|| *opt_key == "notes"
|
|| opt_key == "notes"
|
||||||
|| *opt_key == "only_retract_when_crossing_perimeters"
|
|| opt_key == "only_retract_when_crossing_perimeters"
|
||||||
|| *opt_key == "output_filename_format"
|
|| opt_key == "output_filename_format"
|
||||||
|| *opt_key == "perimeter_acceleration"
|
|| opt_key == "perimeter_acceleration"
|
||||||
|| *opt_key == "post_process"
|
|| opt_key == "post_process"
|
||||||
|| *opt_key == "pressure_advance"
|
|| opt_key == "pressure_advance"
|
||||||
|| *opt_key == "retract_before_travel"
|
|| opt_key == "retract_before_travel"
|
||||||
|| *opt_key == "retract_layer_change"
|
|| opt_key == "retract_layer_change"
|
||||||
|| *opt_key == "retract_length"
|
|| opt_key == "retract_length"
|
||||||
|| *opt_key == "retract_length_toolchange"
|
|| opt_key == "retract_length_toolchange"
|
||||||
|| *opt_key == "retract_lift"
|
|| opt_key == "retract_lift"
|
||||||
|| *opt_key == "retract_lift_above"
|
|| opt_key == "retract_lift_above"
|
||||||
|| *opt_key == "retract_lift_below"
|
|| opt_key == "retract_lift_below"
|
||||||
|| *opt_key == "retract_restart_extra"
|
|| opt_key == "retract_restart_extra"
|
||||||
|| *opt_key == "retract_restart_extra_toolchange"
|
|| opt_key == "retract_restart_extra_toolchange"
|
||||||
|| *opt_key == "retract_speed"
|
|| opt_key == "retract_speed"
|
||||||
|| *opt_key == "slowdown_below_layer_time"
|
|| opt_key == "slowdown_below_layer_time"
|
||||||
|| *opt_key == "spiral_vase"
|
|| opt_key == "spiral_vase"
|
||||||
|| *opt_key == "standby_temperature_delta"
|
|| opt_key == "standby_temperature_delta"
|
||||||
|| *opt_key == "start_gcode"
|
|| opt_key == "start_gcode"
|
||||||
|| *opt_key == "temperature"
|
|| opt_key == "temperature"
|
||||||
|| *opt_key == "threads"
|
|| opt_key == "threads"
|
||||||
|| *opt_key == "toolchange_gcode"
|
|| opt_key == "toolchange_gcode"
|
||||||
|| *opt_key == "travel_speed"
|
|| opt_key == "travel_speed"
|
||||||
|| *opt_key == "use_firmware_retraction"
|
|| opt_key == "use_firmware_retraction"
|
||||||
|| *opt_key == "use_relative_e_distances"
|
|| opt_key == "use_relative_e_distances"
|
||||||
|| *opt_key == "vibration_limit"
|
|| opt_key == "vibration_limit"
|
||||||
|| *opt_key == "wipe"
|
|| opt_key == "wipe"
|
||||||
|| *opt_key == "z_offset") {
|
|| opt_key == "z_offset") {
|
||||||
// these options only affect G-code export, so nothing to invalidate
|
// these options only affect G-code export, so nothing to invalidate
|
||||||
} else if (*opt_key == "first_layer_extrusion_width") {
|
} else if (opt_key == "first_layer_extrusion_width") {
|
||||||
osteps.insert(posPerimeters);
|
osteps.insert(posPerimeters);
|
||||||
osteps.insert(posInfill);
|
osteps.insert(posInfill);
|
||||||
osteps.insert(posSupportMaterial);
|
osteps.insert(posSupportMaterial);
|
||||||
@ -240,18 +243,31 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||||||
steps.insert(psBrim);
|
steps.insert(psBrim);
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
return this->invalidate_all_steps();
|
all = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!diff.empty())
|
||||||
|
this->config.apply(config, true);
|
||||||
|
|
||||||
bool invalidated = false;
|
bool invalidated = false;
|
||||||
for (std::set<PrintStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
if (all) {
|
||||||
if (this->invalidate_step(*step)) invalidated = true;
|
if (this->invalidate_all_steps())
|
||||||
}
|
invalidated = true;
|
||||||
for (std::set<PrintObjectStep>::const_iterator ostep = osteps.begin(); ostep != osteps.end(); ++ostep) {
|
|
||||||
FOREACH_OBJECT(this, object) {
|
for (PrintObject* object : this->objects)
|
||||||
if ((*object)->invalidate_step(*ostep)) invalidated = true;
|
if (object->invalidate_all_steps())
|
||||||
}
|
invalidated = true;
|
||||||
|
} else {
|
||||||
|
for (const PrintStep &step : steps)
|
||||||
|
if (this->invalidate_step(step))
|
||||||
|
invalidated = true;
|
||||||
|
|
||||||
|
for (const PrintObjectStep &ostep : osteps)
|
||||||
|
for (PrintObject* object : this->objects)
|
||||||
|
if (object->invalidate_step(ostep))
|
||||||
|
invalidated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidated;
|
return invalidated;
|
||||||
@ -481,20 +497,12 @@ Print::apply_config(DynamicPrintConfig config)
|
|||||||
// apply variables to placeholder parser
|
// apply variables to placeholder parser
|
||||||
this->placeholder_parser.apply_config(config);
|
this->placeholder_parser.apply_config(config);
|
||||||
|
|
||||||
bool invalidated = false;
|
|
||||||
|
|
||||||
// handle changes to print config
|
// handle changes to print config
|
||||||
t_config_option_keys print_diff = this->config.diff(config);
|
bool invalidated = this->invalidate_state_by_config(config);
|
||||||
if (!print_diff.empty()) {
|
|
||||||
this->config.apply(config, true);
|
|
||||||
|
|
||||||
if (this->invalidate_state_by_config_options(print_diff))
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle changes to object config defaults
|
// handle changes to object config defaults
|
||||||
this->default_object_config.apply(config, true);
|
this->default_object_config.apply(config, true);
|
||||||
FOREACH_OBJECT(this, obj_ptr) {
|
for (PrintObject* object : this->objects) {
|
||||||
// we don't assume that config contains a full ObjectConfig,
|
// we don't assume that config contains a full ObjectConfig,
|
||||||
// so we base it on the current print-wise default
|
// so we base it on the current print-wise default
|
||||||
PrintObjectConfig new_config = this->default_object_config;
|
PrintObjectConfig new_config = this->default_object_config;
|
||||||
@ -502,19 +510,14 @@ Print::apply_config(DynamicPrintConfig config)
|
|||||||
|
|
||||||
// we override the new config with object-specific options
|
// we override the new config with object-specific options
|
||||||
{
|
{
|
||||||
DynamicPrintConfig model_object_config = (*obj_ptr)->model_object()->config;
|
DynamicPrintConfig model_object_config = object->model_object()->config;
|
||||||
model_object_config.normalize();
|
model_object_config.normalize();
|
||||||
new_config.apply(model_object_config, true);
|
new_config.apply(model_object_config, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the new config is different from the current one
|
// check whether the new config is different from the current one
|
||||||
t_config_option_keys diff = (*obj_ptr)->config.diff(new_config);
|
if (object->invalidate_state_by_config(new_config))
|
||||||
if (!diff.empty()) {
|
invalidated = true;
|
||||||
(*obj_ptr)->config.apply(new_config, true);
|
|
||||||
|
|
||||||
if ((*obj_ptr)->invalidate_state_by_config_options(diff))
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle changes to regions config defaults
|
// handle changes to regions config defaults
|
||||||
@ -563,14 +566,8 @@ Print::apply_config(DynamicPrintConfig config)
|
|||||||
// if we're here and the new region config is different from the old
|
// if we're here and the new region config is different from the old
|
||||||
// one, we need to apply the new config and invalidate all objects
|
// one, we need to apply the new config and invalidate all objects
|
||||||
// (possible optimization: only invalidate objects using this region)
|
// (possible optimization: only invalidate objects using this region)
|
||||||
t_config_option_keys region_config_diff = region->config.diff(new_config);
|
if (region->invalidate_state_by_config(new_config))
|
||||||
if (!region_config_diff.empty()) {
|
invalidated = true;
|
||||||
region->config.apply(new_config);
|
|
||||||
FOREACH_OBJECT(this, o) {
|
|
||||||
if ((*o)->invalidate_state_by_config_options(region_config_diff))
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other_region_configs.insert(other_region_configs.end(), this_region_configs.begin(), this_region_configs.end());
|
other_region_configs.insert(other_region_configs.end(), this_region_configs.begin(), this_region_configs.end());
|
||||||
|
@ -27,8 +27,8 @@ enum PrintStep {
|
|||||||
psSkirt, psBrim,
|
psSkirt, psBrim,
|
||||||
};
|
};
|
||||||
enum PrintObjectStep {
|
enum PrintObjectStep {
|
||||||
posSlice, posPerimeters, posPrepareInfill,
|
posSlice, posPerimeters, posDetectSurfaces,
|
||||||
posInfill, posSupportMaterial,
|
posPrepareInfill, posInfill, posSupportMaterial,
|
||||||
};
|
};
|
||||||
|
|
||||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
// To be instantiated over PrintStep or PrintObjectStep enums.
|
||||||
@ -56,6 +56,7 @@ class PrintRegion
|
|||||||
|
|
||||||
Print* print();
|
Print* print();
|
||||||
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
||||||
|
bool invalidate_state_by_config(const PrintConfigBase &config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Print* _print;
|
Print* _print;
|
||||||
@ -135,7 +136,7 @@ class PrintObject
|
|||||||
void delete_support_layer(int idx);
|
void delete_support_layer(int idx);
|
||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
bool invalidate_state_by_config(const PrintConfigBase &config);
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
|
|
||||||
@ -199,7 +200,7 @@ class Print
|
|||||||
PrintRegion* add_region();
|
PrintRegion* add_region();
|
||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
bool invalidate_state_by_config(const PrintConfigBase &config);
|
||||||
bool invalidate_step(PrintStep step);
|
bool invalidate_step(PrintStep step);
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
bool step_done(PrintObjectStep step) const;
|
bool step_done(PrintObjectStep step) const;
|
||||||
|
@ -40,6 +40,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("avoid_crossing_perimeters", coBool);
|
def = this->add("avoid_crossing_perimeters", coBool);
|
||||||
def->label = "Avoid crossing perimeters";
|
def->label = "Avoid crossing perimeters";
|
||||||
|
def->category = "Layers and Perimeters";
|
||||||
def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation.";
|
def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation.";
|
||||||
def->cli = "avoid-crossing-perimeters!";
|
def->cli = "avoid-crossing-perimeters!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
@ -97,6 +98,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("bridge_acceleration", coFloat);
|
def = this->add("bridge_acceleration", coFloat);
|
||||||
def->label = "Bridge";
|
def->label = "Bridge";
|
||||||
|
def->category = "Speed > Acceleration";
|
||||||
def->tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges.";
|
def->tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges.";
|
||||||
def->sidetext = "mm/s²";
|
def->sidetext = "mm/s²";
|
||||||
def->cli = "bridge-acceleration=f";
|
def->cli = "bridge-acceleration=f";
|
||||||
@ -135,6 +137,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("brim_connections_width", coFloat);
|
def = this->add("brim_connections_width", coFloat);
|
||||||
def->label = "Brim connections width";
|
def->label = "Brim connections width";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "If set to a positive value, straight connections will be built on the first layer between adjacent objects.";
|
def->tooltip = "If set to a positive value, straight connections will be built on the first layer between adjacent objects.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "brim-connections-width=f";
|
def->cli = "brim-connections-width=f";
|
||||||
@ -143,14 +146,20 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("brim_width", coFloat);
|
def = this->add("brim_width", coFloat);
|
||||||
def->label = "Exterior brim width";
|
def->label = "Exterior brim width";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "Horizontal width of the brim that will be printed around each object on the first layer.";
|
def->tooltip = "Horizontal width of the brim that will be printed around each object on the first layer.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "brim-width=f";
|
def->cli = "brim-width=f";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
|
def = this->add("compatible_printers", coStrings);
|
||||||
|
def->label = "Compatible printers";
|
||||||
|
def->default_value = new ConfigOptionStrings();
|
||||||
|
|
||||||
def = this->add("complete_objects", coBool);
|
def = this->add("complete_objects", coBool);
|
||||||
def->label = "Complete individual objects";
|
def->label = "Complete individual objects";
|
||||||
|
def->category = "Advanced";
|
||||||
def->tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware.";
|
def->tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware.";
|
||||||
def->cli = "complete-objects!";
|
def->cli = "complete-objects!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
@ -163,6 +172,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("default_acceleration", coFloat);
|
def = this->add("default_acceleration", coFloat);
|
||||||
def->label = "Default";
|
def->label = "Default";
|
||||||
|
def->category = "Speed > Acceleration";
|
||||||
def->tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all.";
|
def->tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all.";
|
||||||
def->sidetext = "mm/s²";
|
def->sidetext = "mm/s²";
|
||||||
def->cli = "default-acceleration=f";
|
def->cli = "default-acceleration=f";
|
||||||
@ -516,6 +526,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("first_layer_acceleration", coFloat);
|
def = this->add("first_layer_acceleration", coFloat);
|
||||||
def->label = "First layer";
|
def->label = "First layer";
|
||||||
|
def->category = "Speed > Acceleration";
|
||||||
def->tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer.";
|
def->tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer.";
|
||||||
def->sidetext = "mm/s²";
|
def->sidetext = "mm/s²";
|
||||||
def->cli = "first-layer-acceleration=f";
|
def->cli = "first-layer-acceleration=f";
|
||||||
@ -554,6 +565,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("first_layer_speed", coFloatOrPercent);
|
def = this->add("first_layer_speed", coFloatOrPercent);
|
||||||
def->label = "First layer speed";
|
def->label = "First layer speed";
|
||||||
|
def->category = "Speed";
|
||||||
def->tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds.";
|
def->tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds.";
|
||||||
def->sidetext = "mm/s or %";
|
def->sidetext = "mm/s or %";
|
||||||
def->cli = "first-layer-speed=s";
|
def->cli = "first-layer-speed=s";
|
||||||
@ -625,6 +637,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("infill_acceleration", coFloat);
|
def = this->add("infill_acceleration", coFloat);
|
||||||
def->label = "Infill";
|
def->label = "Infill";
|
||||||
|
def->category = "Speed > Acceleration";
|
||||||
def->tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill.";
|
def->tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill.";
|
||||||
def->sidetext = "mm/s²";
|
def->sidetext = "mm/s²";
|
||||||
def->cli = "infill-acceleration=f";
|
def->cli = "infill-acceleration=f";
|
||||||
@ -663,6 +676,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("infill_first", coBool);
|
def = this->add("infill_first", coBool);
|
||||||
def->label = "Infill before perimeters";
|
def->label = "Infill before perimeters";
|
||||||
|
def->category = "Infill";
|
||||||
def->tooltip = "This option will switch the print order of perimeters and infill, making the latter first.";
|
def->tooltip = "This option will switch the print order of perimeters and infill, making the latter first.";
|
||||||
def->cli = "infill-first!";
|
def->cli = "infill-first!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
@ -699,6 +713,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("interior_brim_width", coFloat);
|
def = this->add("interior_brim_width", coFloat);
|
||||||
def->label = "Interior brim width";
|
def->label = "Interior brim width";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "Horizontal width of the brim that will be printed inside object holes on the first layer.";
|
def->tooltip = "Horizontal width of the brim that will be printed inside object holes on the first layer.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "interior-brim-width=f";
|
def->cli = "interior-brim-width=f";
|
||||||
@ -759,6 +774,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("max_print_speed", coFloat);
|
def = this->add("max_print_speed", coFloat);
|
||||||
def->label = "Max print speed";
|
def->label = "Max print speed";
|
||||||
|
def->category = "Speed";
|
||||||
def->tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow.";
|
def->tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow.";
|
||||||
def->sidetext = "mm/s";
|
def->sidetext = "mm/s";
|
||||||
def->cli = "max-print-speed=f";
|
def->cli = "max-print-speed=f";
|
||||||
@ -767,6 +783,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("max_volumetric_speed", coFloat);
|
def = this->add("max_volumetric_speed", coFloat);
|
||||||
def->label = "Max volumetric speed";
|
def->label = "Max volumetric speed";
|
||||||
|
def->category = "Speed";
|
||||||
def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports.";
|
def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports.";
|
||||||
def->sidetext = "mm³/s";
|
def->sidetext = "mm³/s";
|
||||||
def->cli = "max-volumetric-speed=f";
|
def->cli = "max-volumetric-speed=f";
|
||||||
@ -804,6 +821,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("min_skirt_length", coFloat);
|
def = this->add("min_skirt_length", coFloat);
|
||||||
def->label = "Minimum extrusion length";
|
def->label = "Minimum extrusion length";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder.";
|
def->tooltip = "Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "min-skirt-length=f";
|
def->cli = "min-skirt-length=f";
|
||||||
@ -844,12 +862,15 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("only_retract_when_crossing_perimeters", coBool);
|
def = this->add("only_retract_when_crossing_perimeters", coBool);
|
||||||
def->label = "Only retract when crossing perimeters";
|
def->label = "Only retract when crossing perimeters";
|
||||||
|
def->category = "Layers and Perimeters";
|
||||||
def->tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible).";
|
def->tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible).";
|
||||||
def->cli = "only-retract-when-crossing-perimeters!";
|
def->cli = "only-retract-when-crossing-perimeters!";
|
||||||
def->default_value = new ConfigOptionBool(true);
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
def = this->add("ooze_prevention", coBool);
|
def = this->add("ooze_prevention", coBool);
|
||||||
def->label = "Enable";
|
def->label = "Enable";
|
||||||
|
def->full_label = "Ooze Prevention";
|
||||||
|
def->category = "Extruders";
|
||||||
def->tooltip = "During multi-extruder prints, this option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures.";
|
def->tooltip = "During multi-extruder prints, this option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures.";
|
||||||
def->cli = "ooze-prevention!";
|
def->cli = "ooze-prevention!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
@ -868,8 +889,17 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->cli = "overhangs|detect-bridging-perimeters!";
|
def->cli = "overhangs|detect-bridging-perimeters!";
|
||||||
def->default_value = new ConfigOptionBool(true);
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
|
def = this->add("overridable", coStrings);
|
||||||
|
def->label = "Overridable options";
|
||||||
|
{
|
||||||
|
ConfigOptionStrings* opt = new ConfigOptionStrings();
|
||||||
|
opt->values.push_back("support_material");
|
||||||
|
def->default_value = opt;
|
||||||
|
}
|
||||||
|
|
||||||
def = this->add("perimeter_acceleration", coFloat);
|
def = this->add("perimeter_acceleration", coFloat);
|
||||||
def->label = "Perimeters";
|
def->label = "Perimeters";
|
||||||
|
def->category = "Speed > Acceleration";
|
||||||
def->tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters.";
|
def->tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters.";
|
||||||
def->sidetext = "mm/s²";
|
def->sidetext = "mm/s²";
|
||||||
def->cli = "perimeter-acceleration=f";
|
def->cli = "perimeter-acceleration=f";
|
||||||
@ -927,6 +957,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->multiline = true;
|
def->multiline = true;
|
||||||
def->full_width = true;
|
def->full_width = true;
|
||||||
def->height = 60;
|
def->height = 60;
|
||||||
|
def->default_value = new ConfigOptionStrings();
|
||||||
|
|
||||||
def = this->add("print_settings_id", coString);
|
def = this->add("print_settings_id", coString);
|
||||||
def->default_value = new ConfigOptionString("");
|
def->default_value = new ConfigOptionString("");
|
||||||
@ -936,6 +967,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("pressure_advance", coFloat);
|
def = this->add("pressure_advance", coFloat);
|
||||||
def->label = "Pressure advance";
|
def->label = "Pressure advance";
|
||||||
|
def->category = "Extruder";
|
||||||
def->tooltip = "When set to a non-zero value, this experimental option enables pressure regulation. It's the K constant for the advance algorithm that pushes more or less filament upon speed changes. It's useful for Bowden-tube extruders. Reasonable values are in range 0-10.";
|
def->tooltip = "When set to a non-zero value, this experimental option enables pressure regulation. It's the K constant for the advance algorithm that pushes more or less filament upon speed changes. It's useful for Bowden-tube extruders. Reasonable values are in range 0-10.";
|
||||||
def->cli = "pressure-advance=f";
|
def->cli = "pressure-advance=f";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
@ -969,6 +1001,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_before_travel", coFloats);
|
def = this->add("retract_before_travel", coFloats);
|
||||||
def->label = "Minimum travel after retraction";
|
def->label = "Minimum travel after retraction";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "Retraction is not triggered when travel moves are shorter than this length.";
|
def->tooltip = "Retraction is not triggered when travel moves are shorter than this length.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "retract-before-travel=f@";
|
def->cli = "retract-before-travel=f@";
|
||||||
@ -980,6 +1013,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_layer_change", coBools);
|
def = this->add("retract_layer_change", coBools);
|
||||||
def->label = "Retract on layer change";
|
def->label = "Retract on layer change";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "This flag enforces a retraction whenever a Z move is done.";
|
def->tooltip = "This flag enforces a retraction whenever a Z move is done.";
|
||||||
def->cli = "retract-layer-change!";
|
def->cli = "retract-layer-change!";
|
||||||
{
|
{
|
||||||
@ -990,6 +1024,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_length", coFloats);
|
def = this->add("retract_length", coFloats);
|
||||||
def->label = "Length";
|
def->label = "Length";
|
||||||
|
def->category = "Retraction";
|
||||||
def->full_label = "Retraction Length";
|
def->full_label = "Retraction Length";
|
||||||
def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder).";
|
def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder).";
|
||||||
def->sidetext = "mm (zero to disable)";
|
def->sidetext = "mm (zero to disable)";
|
||||||
@ -1014,6 +1049,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_lift", coFloats);
|
def = this->add("retract_lift", coFloats);
|
||||||
def->label = "Lift Z";
|
def->label = "Lift Z";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered.";
|
def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "retract-lift=f@";
|
def->cli = "retract-lift=f@";
|
||||||
@ -1026,6 +1062,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def = this->add("retract_lift_above", coFloats);
|
def = this->add("retract_lift_above", coFloats);
|
||||||
def->label = "Above Z";
|
def->label = "Above Z";
|
||||||
def->full_label = "Only lift Z above";
|
def->full_label = "Only lift Z above";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers.";
|
def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "retract-lift-above=f@";
|
def->cli = "retract-lift-above=f@";
|
||||||
@ -1038,6 +1075,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def = this->add("retract_lift_below", coFloats);
|
def = this->add("retract_lift_below", coFloats);
|
||||||
def->label = "Below Z";
|
def->label = "Below Z";
|
||||||
def->full_label = "Only lift Z below";
|
def->full_label = "Only lift Z below";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers.";
|
def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "retract-lift-below=f@";
|
def->cli = "retract-lift-below=f@";
|
||||||
@ -1049,6 +1087,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_restart_extra", coFloats);
|
def = this->add("retract_restart_extra", coFloats);
|
||||||
def->label = "Extra length on restart";
|
def->label = "Extra length on restart";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed.";
|
def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "retract-restart-extra=f@";
|
def->cli = "retract-restart-extra=f@";
|
||||||
@ -1071,7 +1110,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("retract_speed", coFloats);
|
def = this->add("retract_speed", coFloats);
|
||||||
def->label = "Speed";
|
def->label = "Speed";
|
||||||
def->full_label = "Retraction Speed";
|
def->category = "Retraction";
|
||||||
def->tooltip = "The speed for retractions (it only applies to the extruder motor). If you use the Firmware Retraction option, please note this value still affects the auto-speed pressure regulator.";
|
def->tooltip = "The speed for retractions (it only applies to the extruder motor). If you use the Firmware Retraction option, please note this value still affects the auto-speed pressure regulator.";
|
||||||
def->sidetext = "mm/s";
|
def->sidetext = "mm/s";
|
||||||
def->cli = "retract-speed=f@";
|
def->cli = "retract-speed=f@";
|
||||||
@ -1083,7 +1122,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("seam_position", coEnum);
|
def = this->add("seam_position", coEnum);
|
||||||
def->label = "Seam position";
|
def->label = "Seam position";
|
||||||
def->category = "Layers and perimeters";
|
def->category = "Layers and Perimeters";
|
||||||
def->tooltip = "Position of perimeters starting points.";
|
def->tooltip = "Position of perimeters starting points.";
|
||||||
def->cli = "seam-position=s";
|
def->cli = "seam-position=s";
|
||||||
def->enum_keys_map = ConfigOptionEnum<SeamPosition>::get_enum_values();
|
def->enum_keys_map = ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||||
@ -1114,12 +1153,14 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->cli = "serial-speed=i";
|
def->cli = "serial-speed=i";
|
||||||
def->min = 1;
|
def->min = 1;
|
||||||
def->max = 300000;
|
def->max = 300000;
|
||||||
|
def->enum_values.push_back("57600");
|
||||||
def->enum_values.push_back("115200");
|
def->enum_values.push_back("115200");
|
||||||
def->enum_values.push_back("250000");
|
def->enum_values.push_back("250000");
|
||||||
def->default_value = new ConfigOptionInt(250000);
|
def->default_value = new ConfigOptionInt(250000);
|
||||||
|
|
||||||
def = this->add("skirt_distance", coFloat);
|
def = this->add("skirt_distance", coFloat);
|
||||||
def->label = "Distance from object";
|
def->label = "Distance from object";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";
|
def->tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";
|
||||||
def->sidetext = "mm";
|
def->sidetext = "mm";
|
||||||
def->cli = "skirt-distance=f";
|
def->cli = "skirt-distance=f";
|
||||||
@ -1128,6 +1169,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("skirt_height", coInt);
|
def = this->add("skirt_height", coInt);
|
||||||
def->label = "Skirt height";
|
def->label = "Skirt height";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts.";
|
def->tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts.";
|
||||||
def->sidetext = "layers";
|
def->sidetext = "layers";
|
||||||
def->cli = "skirt-height=i";
|
def->cli = "skirt-height=i";
|
||||||
@ -1135,6 +1177,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("skirts", coInt);
|
def = this->add("skirts", coInt);
|
||||||
def->label = "Loops (minimum)";
|
def->label = "Loops (minimum)";
|
||||||
|
def->category = "Skirt and brim";
|
||||||
def->full_label = "Skirt Loops";
|
def->full_label = "Skirt Loops";
|
||||||
def->tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, the number of loops might be greater than the one configured here. Set this to zero to disable skirt completely.";
|
def->tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, the number of loops might be greater than the one configured here. Set this to zero to disable skirt completely.";
|
||||||
def->cli = "skirts=i";
|
def->cli = "skirts=i";
|
||||||
@ -1229,12 +1272,15 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("spiral_vase", coBool);
|
def = this->add("spiral_vase", coBool);
|
||||||
def->label = "Spiral vase";
|
def->label = "Spiral vase";
|
||||||
|
def->category = "Layers and Perimeters";
|
||||||
def->tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object.";
|
def->tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object.";
|
||||||
def->cli = "spiral-vase!";
|
def->cli = "spiral-vase!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
def = this->add("standby_temperature_delta", coInt);
|
def = this->add("standby_temperature_delta", coInt);
|
||||||
def->label = "Temperature variation";
|
def->label = "Temperature variation";
|
||||||
|
def->full_label = "Standby temperature delta";
|
||||||
|
def->category = "Extruders";
|
||||||
def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
|
def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
|
||||||
def->sidetext = "∆°C";
|
def->sidetext = "∆°C";
|
||||||
def->cli = "standby-temperature-delta=i";
|
def->cli = "standby-temperature-delta=i";
|
||||||
@ -1281,6 +1327,13 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->max = 359;
|
def->max = 359;
|
||||||
def->default_value = new ConfigOptionInt(0);
|
def->default_value = new ConfigOptionInt(0);
|
||||||
|
|
||||||
|
def = this->add("support_material_buildplate_only", coBool);
|
||||||
|
def->label = "Support on build plate only";
|
||||||
|
def->category = "Support material";
|
||||||
|
def->tooltip = "Only create support if it lies on a build plate. Don't create support on a print.";
|
||||||
|
def->cli = "support-material-buildplate-only!";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
def = this->add("support_material_contact_distance", coFloat);
|
def = this->add("support_material_contact_distance", coFloat);
|
||||||
def->gui_type = "f_enum_open";
|
def->gui_type = "f_enum_open";
|
||||||
def->label = "Contact Z distance";
|
def->label = "Contact Z distance";
|
||||||
@ -1353,6 +1406,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("support_material_interface_speed", coFloatOrPercent);
|
def = this->add("support_material_interface_speed", coFloatOrPercent);
|
||||||
def->label = "↳ interface";
|
def->label = "↳ interface";
|
||||||
|
def->label = "Interface Speed";
|
||||||
def->category = "Support material interface speed";
|
def->category = "Support material interface speed";
|
||||||
def->gui_type = "f_enum_open";
|
def->gui_type = "f_enum_open";
|
||||||
def->category = "Support material";
|
def->category = "Support material";
|
||||||
@ -1500,6 +1554,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("travel_speed", coFloat);
|
def = this->add("travel_speed", coFloat);
|
||||||
def->label = "Travel";
|
def->label = "Travel";
|
||||||
|
def->category = "Speed";
|
||||||
def->tooltip = "Speed for travel moves (jumps between distant extrusion points).";
|
def->tooltip = "Speed for travel moves (jumps between distant extrusion points).";
|
||||||
def->sidetext = "mm/s";
|
def->sidetext = "mm/s";
|
||||||
def->cli = "travel-speed=f";
|
def->cli = "travel-speed=f";
|
||||||
@ -1535,6 +1590,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
|
|
||||||
def = this->add("wipe", coBools);
|
def = this->add("wipe", coBools);
|
||||||
def->label = "Wipe while retracting";
|
def->label = "Wipe while retracting";
|
||||||
|
def->category = "Retraction";
|
||||||
def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders.";
|
def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders.";
|
||||||
def->cli = "wipe!";
|
def->cli = "wipe!";
|
||||||
{
|
{
|
||||||
@ -1691,7 +1747,7 @@ PrintConfigBase::_handle_legacy(t_config_option_key &opt_key, std::string &value
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this->def->has(opt_key)) {
|
if (!this->def->has(opt_key)) {
|
||||||
printf("Unknown option %s\n", opt_key.c_str());
|
//printf("Unknown option %s\n", opt_key.c_str());
|
||||||
opt_key = "";
|
opt_key = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
|||||||
ConfigOptionEnum<SeamPosition> seam_position;
|
ConfigOptionEnum<SeamPosition> seam_position;
|
||||||
ConfigOptionBool support_material;
|
ConfigOptionBool support_material;
|
||||||
ConfigOptionInt support_material_angle;
|
ConfigOptionInt support_material_angle;
|
||||||
|
ConfigOptionBool support_material_buildplate_only;
|
||||||
ConfigOptionFloat support_material_contact_distance;
|
ConfigOptionFloat support_material_contact_distance;
|
||||||
ConfigOptionInt support_material_enforce_layers;
|
ConfigOptionInt support_material_enforce_layers;
|
||||||
ConfigOptionInt support_material_extruder;
|
ConfigOptionInt support_material_extruder;
|
||||||
@ -189,6 +190,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
|||||||
OPT_PTR(seam_position);
|
OPT_PTR(seam_position);
|
||||||
OPT_PTR(support_material);
|
OPT_PTR(support_material);
|
||||||
OPT_PTR(support_material_angle);
|
OPT_PTR(support_material_angle);
|
||||||
|
OPT_PTR(support_material_buildplate_only);
|
||||||
OPT_PTR(support_material_contact_distance);
|
OPT_PTR(support_material_contact_distance);
|
||||||
OPT_PTR(support_material_enforce_layers);
|
OPT_PTR(support_material_enforce_layers);
|
||||||
OPT_PTR(support_material_extruder);
|
OPT_PTR(support_material_extruder);
|
||||||
|
@ -11,7 +11,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
|
|||||||
: typed_slices(false),
|
: typed_slices(false),
|
||||||
_print(print),
|
_print(print),
|
||||||
_model_object(model_object),
|
_model_object(model_object),
|
||||||
layer_height_spline(modobj_bbox.size().z)
|
layer_height_spline(model_object->layer_height_spline)
|
||||||
{
|
{
|
||||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
||||||
{
|
{
|
||||||
@ -213,95 +213,66 @@ PrintObject::delete_support_layer(int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PrintObject::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
PrintObject::invalidate_state_by_config(const PrintConfigBase &config)
|
||||||
{
|
{
|
||||||
|
const t_config_option_keys diff = this->config.diff(config);
|
||||||
|
|
||||||
std::set<PrintObjectStep> steps;
|
std::set<PrintObjectStep> steps;
|
||||||
|
bool all = false;
|
||||||
|
|
||||||
// this method only accepts PrintObjectConfig and PrintRegionConfig option keys
|
// this method only accepts PrintObjectConfig and PrintRegionConfig option keys
|
||||||
for (std::vector<t_config_option_key>::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) {
|
for (const t_config_option_key &opt_key : diff) {
|
||||||
if (*opt_key == "perimeters"
|
if (opt_key == "layer_height"
|
||||||
|| *opt_key == "extra_perimeters"
|
|| opt_key == "first_layer_height"
|
||||||
|| *opt_key == "gap_fill_speed"
|
|| opt_key == "xy_size_compensation"
|
||||||
|| *opt_key == "overhangs"
|
|| opt_key == "raft_layers"
|
||||||
|| *opt_key == "first_layer_extrusion_width"
|
|| opt_key == "adaptive_slicing"
|
||||||
|| *opt_key == "perimeter_extrusion_width"
|
|| opt_key == "adaptive_slicing_quality"
|
||||||
|| *opt_key == "thin_walls"
|
|| opt_key == "match_horizontal_surfaces") {
|
||||||
|| *opt_key == "external_perimeters_first") {
|
steps.insert(posSlice);
|
||||||
steps.insert(posPerimeters);
|
} else if (opt_key == "support_material_contact_distance") {
|
||||||
} else if (*opt_key == "layer_height"
|
|
||||||
|| *opt_key == "min_layer_height"
|
|
||||||
|| *opt_key == "max_layer_height"
|
|
||||||
|| *opt_key == "first_layer_height"
|
|
||||||
|| *opt_key == "xy_size_compensation"
|
|
||||||
|| *opt_key == "adaptive_slicing"
|
|
||||||
|| *opt_key == "adaptive_slicing_z_gradation"
|
|
||||||
|| *opt_key == "adaptive_slicing_quality"
|
|
||||||
|| *opt_key == "match_horizontal_surfaces"
|
|
||||||
|| *opt_key == "raft_layers") {
|
|
||||||
steps.insert(posSlice);
|
steps.insert(posSlice);
|
||||||
} else if (*opt_key == "support_material"
|
|
||||||
|| *opt_key == "support_material_angle"
|
|
||||||
|| *opt_key == "support_material_extruder"
|
|
||||||
|| *opt_key == "support_material_extrusion_width"
|
|
||||||
|| *opt_key == "support_material_interface_layers"
|
|
||||||
|| *opt_key == "support_material_interface_extruder"
|
|
||||||
|| *opt_key == "support_material_interface_spacing"
|
|
||||||
|| *opt_key == "support_material_interface_speed"
|
|
||||||
|| *opt_key == "support_material_pattern"
|
|
||||||
|| *opt_key == "support_material_spacing"
|
|
||||||
|| *opt_key == "support_material_threshold"
|
|
||||||
|| *opt_key == "dont_support_bridges"
|
|
||||||
|| *opt_key == "first_layer_extrusion_width") {
|
|
||||||
steps.insert(posSupportMaterial);
|
|
||||||
} else if (*opt_key == "interface_shells"
|
|
||||||
|| *opt_key == "infill_only_where_needed"
|
|
||||||
|| *opt_key == "infill_every_layers"
|
|
||||||
|| *opt_key == "solid_infill_every_layers"
|
|
||||||
|| *opt_key == "bottom_solid_layers"
|
|
||||||
|| *opt_key == "top_solid_layers"
|
|
||||||
|| *opt_key == "solid_infill_below_area"
|
|
||||||
|| *opt_key == "infill_extruder"
|
|
||||||
|| *opt_key == "solid_infill_extruder"
|
|
||||||
|| *opt_key == "infill_extrusion_width") {
|
|
||||||
steps.insert(posPrepareInfill);
|
|
||||||
} else if (*opt_key == "top_infill_pattern"
|
|
||||||
|| *opt_key == "bottom_infill_pattern"
|
|
||||||
|| *opt_key == "fill_angle"
|
|
||||||
|| *opt_key == "fill_pattern"
|
|
||||||
|| *opt_key == "top_infill_extrusion_width"
|
|
||||||
|| *opt_key == "first_layer_extrusion_width"
|
|
||||||
|| *opt_key == "infill_overlap") {
|
|
||||||
steps.insert(posInfill);
|
|
||||||
} else if (*opt_key == "fill_density"
|
|
||||||
|| *opt_key == "solid_infill_extrusion_width") {
|
|
||||||
steps.insert(posPerimeters);
|
|
||||||
steps.insert(posPrepareInfill);
|
|
||||||
} else if (*opt_key == "external_perimeter_extrusion_width"
|
|
||||||
|| *opt_key == "perimeter_extruder") {
|
|
||||||
steps.insert(posPerimeters);
|
steps.insert(posPerimeters);
|
||||||
steps.insert(posSupportMaterial);
|
steps.insert(posSupportMaterial);
|
||||||
} else if (*opt_key == "bridge_flow_ratio") {
|
} else if (opt_key == "support_material") {
|
||||||
steps.insert(posPerimeters);
|
steps.insert(posPerimeters);
|
||||||
steps.insert(posInfill);
|
steps.insert(posSupportMaterial);
|
||||||
} else if (*opt_key == "seam_position"
|
} else if (opt_key == "support_material_angle"
|
||||||
|| *opt_key == "support_material_speed"
|
|| opt_key == "support_material_extruder"
|
||||||
|| *opt_key == "bridge_speed"
|
|| opt_key == "support_material_extrusion_width"
|
||||||
|| *opt_key == "external_perimeter_speed"
|
|| opt_key == "support_material_interface_layers"
|
||||||
|| *opt_key == "infill_speed"
|
|| opt_key == "support_material_interface_extruder"
|
||||||
|| *opt_key == "perimeter_speed"
|
|| opt_key == "support_material_interface_spacing"
|
||||||
|| *opt_key == "small_perimeter_speed"
|
|| opt_key == "support_material_interface_speed"
|
||||||
|| *opt_key == "solid_infill_speed"
|
|| opt_key == "support_material_buildplate_only"
|
||||||
|| *opt_key == "top_solid_infill_speed") {
|
|| opt_key == "support_material_pattern"
|
||||||
|
|| opt_key == "support_material_spacing"
|
||||||
|
|| opt_key == "support_material_threshold"
|
||||||
|
|| opt_key == "dont_support_bridges") {
|
||||||
|
steps.insert(posSupportMaterial);
|
||||||
|
} else if (opt_key == "interface_shells"
|
||||||
|
|| opt_key == "infill_only_where_needed") {
|
||||||
|
steps.insert(posPrepareInfill);
|
||||||
|
} else if (opt_key == "seam_position"
|
||||||
|
|| opt_key == "support_material_speed") {
|
||||||
// these options only affect G-code export, so nothing to invalidate
|
// these options only affect G-code export, so nothing to invalidate
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
return this->invalidate_all_steps();
|
all = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!diff.empty())
|
||||||
|
this->config.apply(config, true);
|
||||||
|
|
||||||
bool invalidated = false;
|
bool invalidated = false;
|
||||||
for (std::set<PrintObjectStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
if (all) {
|
||||||
if (this->invalidate_step(*step)) invalidated = true;
|
invalidated = this->invalidate_all_steps();
|
||||||
|
} else {
|
||||||
|
for (const PrintObjectStep &step : steps)
|
||||||
|
if (this->invalidate_step(step))
|
||||||
|
invalidated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidated;
|
return invalidated;
|
||||||
@ -317,6 +288,8 @@ PrintObject::invalidate_step(PrintObjectStep step)
|
|||||||
this->invalidate_step(posPrepareInfill);
|
this->invalidate_step(posPrepareInfill);
|
||||||
this->_print->invalidate_step(psSkirt);
|
this->_print->invalidate_step(psSkirt);
|
||||||
this->_print->invalidate_step(psBrim);
|
this->_print->invalidate_step(psBrim);
|
||||||
|
} else if (step == posDetectSurfaces) {
|
||||||
|
this->invalidate_step(posPrepareInfill);
|
||||||
} else if (step == posPrepareInfill) {
|
} else if (step == posPrepareInfill) {
|
||||||
this->invalidate_step(posInfill);
|
this->invalidate_step(posInfill);
|
||||||
} else if (step == posInfill) {
|
} else if (step == posInfill) {
|
||||||
@ -324,6 +297,7 @@ PrintObject::invalidate_step(PrintObjectStep step)
|
|||||||
this->_print->invalidate_step(psBrim);
|
this->_print->invalidate_step(psBrim);
|
||||||
} else if (step == posSlice) {
|
} else if (step == posSlice) {
|
||||||
this->invalidate_step(posPerimeters);
|
this->invalidate_step(posPerimeters);
|
||||||
|
this->invalidate_step(posDetectSurfaces);
|
||||||
this->invalidate_step(posSupportMaterial);
|
this->invalidate_step(posSupportMaterial);
|
||||||
} else if (step == posSupportMaterial) {
|
} else if (step == posSupportMaterial) {
|
||||||
this->_print->invalidate_step(psSkirt);
|
this->_print->invalidate_step(psSkirt);
|
||||||
@ -357,11 +331,20 @@ PrintObject::has_support_material() const
|
|||||||
void
|
void
|
||||||
PrintObject::detect_surfaces_type()
|
PrintObject::detect_surfaces_type()
|
||||||
{
|
{
|
||||||
|
// prerequisites
|
||||||
|
// this->slice();
|
||||||
|
|
||||||
|
if (this->state.is_done(posDetectSurfaces)) return;
|
||||||
|
this->state.set_started(posDetectSurfaces);
|
||||||
|
|
||||||
parallelize<Layer*>(
|
parallelize<Layer*>(
|
||||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
||||||
boost::bind(&Slic3r::Layer::detect_surfaces_type, _1),
|
boost::bind(&Slic3r::Layer::detect_surfaces_type, _1),
|
||||||
this->_print->config.threads.value
|
this->_print->config.threads.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this->typed_slices = true;
|
||||||
|
this->state.set_done(posDetectSurfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -625,16 +608,22 @@ std::vector<coordf_t> PrintObject::generate_object_layers(coordf_t first_layer_h
|
|||||||
if(this->config.match_horizontal_surfaces.value) {
|
if(this->config.match_horizontal_surfaces.value) {
|
||||||
coordf_t horizontal_dist = as.horizontal_facet_distance(print_z + height, min_layer_height);
|
coordf_t horizontal_dist = as.horizontal_facet_distance(print_z + height, min_layer_height);
|
||||||
if((horizontal_dist < min_layer_height) && (horizontal_dist > 0)) {
|
if((horizontal_dist < min_layer_height) && (horizontal_dist > 0)) {
|
||||||
// std::cout << "Horizontal feature ahead, distance: " << horizontal_dist << std::endl;
|
#ifdef SLIC3R_DEBUG
|
||||||
|
std::cout << "Horizontal feature ahead, distance: " << horizontal_dist << std::endl;
|
||||||
|
#endif
|
||||||
// can we shrink the current layer a bit?
|
// can we shrink the current layer a bit?
|
||||||
if(height-(min_layer_height - horizontal_dist) > min_layer_height) {
|
if(height-(min_layer_height - horizontal_dist) > min_layer_height) {
|
||||||
// yes we can
|
// yes we can
|
||||||
height -= (min_layer_height - horizontal_dist);
|
height -= (min_layer_height - horizontal_dist);
|
||||||
// std::cout << "Shrink layer height to " << height << std::endl;
|
#ifdef SLIC3R_DEBUG
|
||||||
|
std::cout << "Shrink layer height to " << height << std::endl;
|
||||||
|
#endif
|
||||||
}else{
|
}else{
|
||||||
// no, current layer would become too thin
|
// no, current layer would become too thin
|
||||||
height += horizontal_dist;
|
height += horizontal_dist;
|
||||||
// std::cout << "Widen layer height to " << height << std::endl;
|
#ifdef SLIC3R_DEBUG
|
||||||
|
std::cout << "Widen layer height to " << height << std::endl;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -682,8 +671,9 @@ std::vector<coordf_t> PrintObject::generate_object_layers(coordf_t first_layer_h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store layer vector for interactive manipulation
|
// Store layer vector for interactive manipulation and push back to model
|
||||||
this->layer_height_spline.setLayers(result);
|
this->layer_height_spline.setLayers(result);
|
||||||
|
this->_model_object->layer_height_spline = this->layer_height_spline;
|
||||||
if (this->config.adaptive_slicing.value) { // smoothing after adaptive algorithm
|
if (this->config.adaptive_slicing.value) { // smoothing after adaptive algorithm
|
||||||
result = this->layer_height_spline.getInterpolatedLayers();
|
result = this->layer_height_spline.getInterpolatedLayers();
|
||||||
}
|
}
|
||||||
@ -920,11 +910,15 @@ PrintObject::_make_perimeters()
|
|||||||
this->state.set_started(posPerimeters);
|
this->state.set_started(posPerimeters);
|
||||||
|
|
||||||
// merge slices if they were split into types
|
// merge slices if they were split into types
|
||||||
|
// This is not currently taking place because since merge_slices + detect_surfaces_type
|
||||||
|
// are not truly idempotent we are invalidating posSlice here (see the Perl part of
|
||||||
|
// this method).
|
||||||
if (this->typed_slices) {
|
if (this->typed_slices) {
|
||||||
|
// merge_slices() undoes detect_surfaces_type()
|
||||||
FOREACH_LAYER(this, layer_it)
|
FOREACH_LAYER(this, layer_it)
|
||||||
(*layer_it)->merge_slices();
|
(*layer_it)->merge_slices();
|
||||||
this->typed_slices = false;
|
this->typed_slices = false;
|
||||||
this->state.invalidate(posPrepareInfill);
|
this->state.invalidate(posDetectSurfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare each layer to the one below, and mark those slices needing
|
// compare each layer to the one below, and mark those slices needing
|
||||||
|
@ -65,4 +65,91 @@ PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_la
|
|||||||
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0);
|
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PrintRegion::invalidate_state_by_config(const PrintConfigBase &config)
|
||||||
|
{
|
||||||
|
const t_config_option_keys diff = this->config.diff(config);
|
||||||
|
|
||||||
|
std::set<PrintObjectStep> steps;
|
||||||
|
bool all = false;
|
||||||
|
|
||||||
|
for (const t_config_option_key &opt_key : diff) {
|
||||||
|
if (opt_key == "perimeters"
|
||||||
|
|| opt_key == "extra_perimeters"
|
||||||
|
|| opt_key == "gap_fill_speed"
|
||||||
|
|| opt_key == "overhangs"
|
||||||
|
|| opt_key == "first_layer_extrusion_width"
|
||||||
|
|| opt_key == "perimeter_extrusion_width"
|
||||||
|
|| opt_key == "thin_walls"
|
||||||
|
|| opt_key == "external_perimeters_first") {
|
||||||
|
steps.insert(posPerimeters);
|
||||||
|
} else if (opt_key == "first_layer_extrusion_width") {
|
||||||
|
steps.insert(posSupportMaterial);
|
||||||
|
} else if (opt_key == "infill_every_layers"
|
||||||
|
|| opt_key == "solid_infill_every_layers"
|
||||||
|
|| opt_key == "bottom_solid_layers"
|
||||||
|
|| opt_key == "top_solid_layers"
|
||||||
|
|| opt_key == "solid_infill_below_area"
|
||||||
|
|| opt_key == "infill_extruder"
|
||||||
|
|| opt_key == "solid_infill_extruder"
|
||||||
|
|| opt_key == "infill_extrusion_width") {
|
||||||
|
steps.insert(posPrepareInfill);
|
||||||
|
} else if (opt_key == "top_infill_pattern"
|
||||||
|
|| opt_key == "bottom_infill_pattern"
|
||||||
|
|| opt_key == "fill_angle"
|
||||||
|
|| opt_key == "fill_pattern"
|
||||||
|
|| opt_key == "top_infill_extrusion_width"
|
||||||
|
|| opt_key == "first_layer_extrusion_width"
|
||||||
|
|| opt_key == "infill_overlap") {
|
||||||
|
steps.insert(posInfill);
|
||||||
|
} else if (opt_key == "solid_infill_extrusion_width") {
|
||||||
|
steps.insert(posPerimeters);
|
||||||
|
steps.insert(posPrepareInfill);
|
||||||
|
} else if (opt_key == "fill_density") {
|
||||||
|
const float &cur_value = config.opt<ConfigOptionFloat>("fill_density")->value;
|
||||||
|
const float &new_value = this->config.fill_density.value;
|
||||||
|
if ((cur_value == 0) != (new_value == 0))
|
||||||
|
steps.insert(posPerimeters);
|
||||||
|
|
||||||
|
steps.insert(posPrepareInfill);
|
||||||
|
} else if (opt_key == "external_perimeter_extrusion_width"
|
||||||
|
|| opt_key == "perimeter_extruder") {
|
||||||
|
steps.insert(posPerimeters);
|
||||||
|
steps.insert(posSupportMaterial);
|
||||||
|
} else if (opt_key == "bridge_flow_ratio") {
|
||||||
|
steps.insert(posPerimeters);
|
||||||
|
steps.insert(posInfill);
|
||||||
|
} else if (opt_key == "bridge_speed"
|
||||||
|
|| opt_key == "external_perimeter_speed"
|
||||||
|
|| opt_key == "infill_speed"
|
||||||
|
|| opt_key == "perimeter_speed"
|
||||||
|
|| opt_key == "small_perimeter_speed"
|
||||||
|
|| opt_key == "solid_infill_speed"
|
||||||
|
|| opt_key == "top_solid_infill_speed") {
|
||||||
|
// these options only affect G-code export, so nothing to invalidate
|
||||||
|
} else {
|
||||||
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
|
all = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!diff.empty())
|
||||||
|
this->config.apply(config, true);
|
||||||
|
|
||||||
|
bool invalidated = false;
|
||||||
|
if (all) {
|
||||||
|
for (PrintObject* object : this->print()->objects)
|
||||||
|
if (object->invalidate_all_steps())
|
||||||
|
invalidated = true;
|
||||||
|
} else {
|
||||||
|
for (const PrintObjectStep &step : steps)
|
||||||
|
for (PrintObject* object : this->print()->objects)
|
||||||
|
if (object->invalidate_step(step))
|
||||||
|
invalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ void confess_at(const char *file, int line, const char *func, const char *pat, .
|
|||||||
#define STDMOVE(WHAT) (WHAT)
|
#define STDMOVE(WHAT) (WHAT)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// dummy macro to mark strings for translation for gettext/poedit
|
||||||
|
#define __TRANS(s) s
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
constexpr auto SLIC3R_VERSION = "1.3.0-dev";
|
constexpr auto SLIC3R_VERSION = "1.3.0-dev";
|
||||||
|
@ -19,6 +19,7 @@ REGISTER_CLASS(SpiralVase, "GCode::SpiralVase");
|
|||||||
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
||||||
REGISTER_CLASS(GCode, "GCode");
|
REGISTER_CLASS(GCode, "GCode");
|
||||||
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
||||||
|
REGISTER_CLASS(GCodeTimeEstimator, "GCode::TimeEstimator");
|
||||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||||
REGISTER_CLASS(Layer, "Layer");
|
REGISTER_CLASS(Layer, "Layer");
|
||||||
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
||||||
|
@ -8,10 +8,7 @@ use Test::More tests => 1;
|
|||||||
|
|
||||||
{
|
{
|
||||||
eval {
|
eval {
|
||||||
local $SIG{ALRM} = sub { die "Timed out waiting for exception\n" }; # NB: \n required
|
|
||||||
alarm 30;
|
|
||||||
Slic3r::xspp_test_croak_hangs_on_strawberry();
|
Slic3r::xspp_test_croak_hangs_on_strawberry();
|
||||||
alarm 0;
|
|
||||||
};
|
};
|
||||||
is $@, "xspp_test_croak_hangs_on_strawberry: exception catched\n", 'croak from inside a C++ exception delivered';
|
is $@, "xspp_test_croak_hangs_on_strawberry: exception catched\n", 'croak from inside a C++ exception delivered';
|
||||||
}
|
}
|
||||||
|
@ -27,14 +27,20 @@
|
|||||||
double get_abs_value(t_config_option_key opt_key, double ratio_over);
|
double get_abs_value(t_config_option_key opt_key, double ratio_over);
|
||||||
void apply(DynamicPrintConfig* other)
|
void apply(DynamicPrintConfig* other)
|
||||||
%code{% THIS->apply(*other, true); %};
|
%code{% THIS->apply(*other, true); %};
|
||||||
|
void apply_only(DynamicPrintConfig* other, std::vector<std::string> opt_keys)
|
||||||
|
%code{% THIS->apply_only(*other, opt_keys, true); %};
|
||||||
std::vector<std::string> diff(DynamicPrintConfig* other)
|
std::vector<std::string> diff(DynamicPrintConfig* other)
|
||||||
%code{% RETVAL = THIS->diff(*other); %};
|
%code{% RETVAL = THIS->diff(*other); %};
|
||||||
|
std::vector<std::string> diff_static(StaticPrintConfig* other)
|
||||||
|
%code{% RETVAL = THIS->diff(*other); %};
|
||||||
bool equals(DynamicPrintConfig* other)
|
bool equals(DynamicPrintConfig* other)
|
||||||
%code{% RETVAL = THIS->equals(*other); %};
|
%code{% RETVAL = THIS->equals(*other); %};
|
||||||
void apply_static(StaticPrintConfig* other)
|
void apply_static(StaticPrintConfig* other)
|
||||||
%code{% THIS->apply(*other, true); %};
|
%code{% THIS->apply(*other, true); %};
|
||||||
%name{get_keys} std::vector<std::string> keys();
|
%name{get_keys} std::vector<std::string> keys();
|
||||||
void erase(t_config_option_key opt_key);
|
void erase(t_config_option_key opt_key);
|
||||||
|
void clear();
|
||||||
|
bool empty();
|
||||||
void normalize();
|
void normalize();
|
||||||
%name{setenv} void setenv_();
|
%name{setenv} void setenv_();
|
||||||
double min_object_distance();
|
double min_object_distance();
|
||||||
|
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);
|
||||||
|
};
|
@ -6,11 +6,11 @@
|
|||||||
%}
|
%}
|
||||||
|
|
||||||
%name{Slic3r::LayerHeightSpline} class LayerHeightSpline {
|
%name{Slic3r::LayerHeightSpline} class LayerHeightSpline {
|
||||||
LayerHeightSpline(double object_height);
|
LayerHeightSpline();
|
||||||
~LayerHeightSpline();
|
|
||||||
Clone<LayerHeightSpline> clone()
|
Clone<LayerHeightSpline> clone()
|
||||||
%code%{ RETVAL = THIS; %};
|
%code%{ RETVAL = THIS; %};
|
||||||
|
|
||||||
|
void setObjectHeight(coordf_t object_height);
|
||||||
bool hasData();
|
bool hasData();
|
||||||
bool updateRequired();
|
bool updateRequired();
|
||||||
void suppressUpdate();
|
void suppressUpdate();
|
||||||
|
@ -201,6 +201,9 @@ ModelMaterial::attributes()
|
|||||||
void set_layer_height_ranges(t_layer_height_ranges ranges)
|
void set_layer_height_ranges(t_layer_height_ranges ranges)
|
||||||
%code%{ THIS->layer_height_ranges = ranges; %};
|
%code%{ THIS->layer_height_ranges = ranges; %};
|
||||||
|
|
||||||
|
Ref<LayerHeightSpline> layer_height_spline()
|
||||||
|
%code%{ RETVAL = &THIS->layer_height_spline; %};
|
||||||
|
|
||||||
Ref<Pointf3> origin_translation()
|
Ref<Pointf3> origin_translation()
|
||||||
%code%{ RETVAL = &THIS->origin_translation; %};
|
%code%{ RETVAL = &THIS->origin_translation; %};
|
||||||
void set_origin_translation(Pointf3* point)
|
void set_origin_translation(Pointf3* point)
|
||||||
|
@ -14,6 +14,7 @@ _constant()
|
|||||||
ALIAS:
|
ALIAS:
|
||||||
STEP_SLICE = posSlice
|
STEP_SLICE = posSlice
|
||||||
STEP_PERIMETERS = posPerimeters
|
STEP_PERIMETERS = posPerimeters
|
||||||
|
STEP_DETECT_SURFACES = posDetectSurfaces
|
||||||
STEP_PREPARE_INFILL = posPrepareInfill
|
STEP_PREPARE_INFILL = posPrepareInfill
|
||||||
STEP_INFILL = posInfill
|
STEP_INFILL = posInfill
|
||||||
STEP_SUPPORTMATERIAL = posSupportMaterial
|
STEP_SUPPORTMATERIAL = posSupportMaterial
|
||||||
@ -115,7 +116,6 @@ _constant()
|
|||||||
Ref<SupportLayer> add_support_layer(int id, coordf_t height, coordf_t print_z);
|
Ref<SupportLayer> add_support_layer(int id, coordf_t height, coordf_t print_z);
|
||||||
void delete_support_layer(int idx);
|
void delete_support_layer(int idx);
|
||||||
|
|
||||||
bool invalidate_state_by_config_options(std::vector<std::string> opt_keys);
|
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
bool step_done(PrintObjectStep step)
|
bool step_done(PrintObjectStep step)
|
||||||
@ -125,7 +125,7 @@ _constant()
|
|||||||
void set_step_started(PrintObjectStep step)
|
void set_step_started(PrintObjectStep step)
|
||||||
%code%{ THIS->state.set_started(step); %};
|
%code%{ THIS->state.set_started(step); %};
|
||||||
|
|
||||||
void detect_surfaces_type();
|
%name{_detect_surfaces_type} void detect_surfaces_type();
|
||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
void _slice();
|
void _slice();
|
||||||
@ -191,7 +191,6 @@ _constant()
|
|||||||
size_t region_count()
|
size_t region_count()
|
||||||
%code%{ RETVAL = THIS->regions.size(); %};
|
%code%{ RETVAL = THIS->regions.size(); %};
|
||||||
|
|
||||||
bool invalidate_state_by_config_options(std::vector<std::string> opt_keys);
|
|
||||||
bool invalidate_step(PrintStep step);
|
bool invalidate_step(PrintStep step);
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
bool step_done(PrintStep step)
|
bool step_done(PrintStep step)
|
||||||
@ -260,6 +259,12 @@ _constant()
|
|||||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||||
bool apply_config(DynamicPrintConfig* config)
|
bool apply_config(DynamicPrintConfig* config)
|
||||||
%code%{ RETVAL = THIS->apply_config(*config); %};
|
%code%{ RETVAL = THIS->apply_config(*config); %};
|
||||||
|
bool apply_static_config(StaticPrintConfig* config)
|
||||||
|
%code%{
|
||||||
|
DynamicPrintConfig dpc;
|
||||||
|
dpc.apply(*config);
|
||||||
|
RETVAL = THIS->apply_config(dpc);
|
||||||
|
%};
|
||||||
bool has_infinite_skirt();
|
bool has_infinite_skirt();
|
||||||
bool has_skirt();
|
bool has_skirt();
|
||||||
std::string _validate()
|
std::string _validate()
|
||||||
|
@ -222,6 +222,10 @@ GCodeSender* O_OBJECT_SLIC3R
|
|||||||
Ref<GCodeSender> O_OBJECT_SLIC3R_T
|
Ref<GCodeSender> O_OBJECT_SLIC3R_T
|
||||||
Clone<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
|
GCodeWriter* O_OBJECT_SLIC3R
|
||||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
|
Loading…
x
Reference in New Issue
Block a user