Merge branch 'cppgui' of https://github.com/lordofhyphens/slic3r into cppgui

This commit is contained in:
Joseph Lenox 2018-05-06 12:29:37 -05:00
commit d9844f691c
78 changed files with 17589 additions and 1796 deletions

6
.gitignore vendored
View File

@ -16,3 +16,9 @@ package/osx/Slic3r*.app
*.dmg
*.swp
*.swo
CMakeFiles
*.orig
*.a
core
CMakeCache.txt
*.cmake

View File

@ -1,18 +1,16 @@
language: perl
language: c++
before_install:
- sh package/linux/travis-decrypt-key
install:
- export BOOST_DIR=$HOME/boost_1_63_0
- export SLIC3R_STATIC=1
- export CXX=g++-4.9
- export CC=g++-4.9
- source $HOME/perl5/perlbrew/etc/bashrc
- export CXX=g++-7
- export CC=gcc-7
script:
- bash package/linux/travis-setup.sh
- perlbrew switch slic3r-perl
- perl ./Build.PL
- cmake -DBOOST_ROOT=$BOOST_DIR src/
- make
after_success:
- eval $(perl -Mlocal::lib=$TRAVIS_BUILD_DIR/local-lib)
- LD_LIBRARY_PATH=$WXDIR/lib package/linux/make_archive.sh linux-x64
- package/linux/appimage.sh x86_64
- package/deploy/sftp.sh linux ~/slic3r-upload.rsa *.bz2 Slic3r*.AppImage
@ -22,6 +20,7 @@ branches:
only:
- master
- xsgui
- cppgui
cache:
apt: true
directories:
@ -31,11 +30,15 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
- gcc-4.9
- g++-7
- gcc-7
- libgtk2.0-0
- libgtk2.0-dev
- freeglut3
- cmake
- wx3.0-headers
- libwxgtk3.0-dev
- wx-common
ssh_known_hosts: dl.slic3r.org
notifications:
irc:
@ -44,7 +47,6 @@ notifications:
on_success: change
on_failure: always
use_notice: true
sudo: required
dist: trusty
env:
matrix:

View File

@ -1,4 +1,4 @@
![](var/Slic3r_128px.png) Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.svg?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) [![Build Status](http://osx-build.slic3r.org:8080/buildStatus/icon?job=Slic3r)](http://osx-build.slic3r.org:8080/job/Slic3r)
![](var/Slic3r_128px.png) Slic3r [![Build Status](https://travis-ci.org/slic3r/Slic3r.svg?branch=master)](https://travis-ci.org/slic3r/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) [![Build Status](http://osx-build.slic3r.org:8080/buildStatus/icon?job=Slic3r)](http://osx-build.slic3r.org:8080/job/Slic3r)
======
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!
@ -46,7 +46,7 @@ The core parts of Slic3r are written in C++11, with multithreading. The graphica
You can download a precompiled package from [slic3r.org](http://slic3r.org/) (releases) or from [dl.slicr3r.org](http://dl.slic3r.org/dev/) (automated builds).
If you want to compile the source yourself follow the instructions on one of these wiki pages:
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)
* [Windows](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-Windows)
* [Mac OSX](https://github.com/alexrj/Slic3r/wiki/Running-Slic3r-from-git-on-OS-X)
@ -58,7 +58,7 @@ Sure! You can do the following to find things that are available to help with:
* Development
* [Low Effort tasks](https://github.com/alexrj/Slic3r/labels/Low%20Effort): pick one of them!
* [More available tasks](https://github.com/alexrj/Slic3r/milestone/31): let's discuss together before you start working on them
* Please comment in the related GitHub issue that you are working on it so that other people know.
* Please comment in the related GitHub issue that you are working on it so that other people know.
* Contribute to the [Manual](http://manual.slic3r.org/)! (see its [GitHub repository](https://github.com/alexrj/Slic3r-Manual))
* You can also find us in #slic3r on [FreeNode](https://webchat.freenode.net): talk to _Sound_, _LoH_ or the other members of the Slic3r community.
* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the GitHub tracker if it isn't already present.
@ -84,12 +84,12 @@ The main author of Slic3r is Alessandro Ranellucci (@alexrj, *Sound* in IRC, [@a
Joseph Lenox (@lordofhyphens, *Loh* in IRC) is the current co-maintainer.
Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James.
Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James, stl and gcode file icons designed by Akira Yasuda.
### How can I invoke Slic3r using the command line?
Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
--help Output this usage screen and exit
--version Output the version of Slic3r and exit
--save <file> Save configuration to the specified file
@ -108,16 +108,16 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
them as <name>_upper.stl and <name>_lower.stl
--split Split the shells contained in given STL file into several STL files
--info Output information about the supplied file(s) and exit
-j, --threads <num> Number of threads to use (1+, default: 2)
GUI options:
--gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater)
--no-gui Forces the command line slicing instead of gui.
--no-gui Forces the command line slicing instead of gui.
This takes precedence over --gui if both are present.
--autosave <file> Automatically export current configuration to the specified file
Output options:
--output-filename-format
Output file name format; all config options enclosed in brackets
@ -128,7 +128,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--export-svg Export a SVG file containing slices instead of G-code.
-m, --merge If multiple files are supplied, they will be composed into a single
print rather than processed individually.
Printer options:
--bed-shape Coordinates in mm of the bed's points (default: 0x0,200x0,200x200,0x200)
--has-heatbed This will provide automatic generation of bed heating gcode
@ -149,7 +149,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
default: 0)
--pressure-advance Adjust pressure using the experimental advance algorithm (K constant,
set zero to disable; default: 0)
Filament options:
--filament-diameter Diameter in mm of your raw filament (default: 3)
--extrusion-multiplier
@ -162,7 +162,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--bed-temperature Heated bed temperature in degree Celsius, set 0 to disable (default: 0)
--first-layer-bed-temperature Heated bed temperature for the first layer, in degree Celsius,
set 0 to disable (default: same as --bed-temperature)
Speed options:
--travel-speed Speed of non-print moves in mm/s (default: 130)
--perimeter-speed Speed of print moves for perimeters in mm/s (default: 30)
@ -186,7 +186,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--gap-fill-speed Speed of gap fill print moves in mm/s (default: 20)
--first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute
value or as a percentage over normal speeds (default: 30%)
Acceleration options:
--perimeter-acceleration
Overrides firmware's default acceleration for perimeters. (mm/s^2, set zero
@ -203,7 +203,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--default-acceleration
Acceleration will be reset to this value after the specific settings above
have been applied. (mm/s^2, set zero to disable; default: 0)
Accuracy options:
--layer-height Layer height in mm (default: 0.3)
--first-layer-height Layer height for first layer (mm or %, default: 0.35)
@ -211,7 +211,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
Infill every N layers (default: 1)
--solid-infill-every-layers
Force a solid layer every N layers (default: 0)
Print options:
--perimeters Number of perimeters/horizontal skins (range: 0+, default: 3)
--top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: 3)
@ -242,14 +242,14 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--infill-only-where-needed
Only infill under ceilings (default: no)
--infill-first Make infill before perimeters (default: no)
Quality options (slower slicing):
--extra-perimeters Add more perimeters when needed (default: yes)
--avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
--thin-walls Detect single-width walls (default: yes)
--overhangs Experimental option to use bridge flow, speed and fan for overhangs
(default: yes)
Support material options:
--support-material Generate support material for overhangs
--support-material-threshold
@ -274,7 +274,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
regardless of --support-material and threshold (0+, default: 0)
--dont-support-bridges
Experimental option for preventing support material from being generated under bridged areas (default: yes)
Retraction options:
--retract-length Length of retraction in mm when pausing extrusion (default: 1)
--retract-speed Speed for retraction in mm/s (default: 30)
@ -289,14 +289,14 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--retract-layer-change
Enforce a retraction before each Z move (default: no)
--wipe Wipe the nozzle while doing a retraction (default: no)
Retraction options for multi-extruder setups:
--retract-length-toolchange
Length of retraction in mm when disabling tool (default: 10)
--retract-restart-extra-toolchange
Additional amount of filament in mm to push after
switching tool (default: 0)
Cooling options:
--cooling Enable fan and cooling control
--min-fan-speed Minimum fan speed (default: 35%)
@ -310,7 +310,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--disable-fan-first-layers Disable fan for the first N layers (default: 1)
--fan-always-on Keep fan always on at min fan speed, even for layers that don't need
cooling
Skirt options:
--skirts Number of skirts to draw (0+, default: 1)
--skirt-distance Distance in mm between innermost skirt and object
@ -320,7 +320,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
of filament on the first layer, for each extruder (mm, 0+, default: 0)
--brim-width Width of the brim that will get added to each object to help adhesion
(mm, default: 0)
Transform options:
--scale Factor for scaling input object (default: 1)
--rotate Rotation angle in degrees (0-360, default: 0)
@ -328,11 +328,11 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
--duplicate-grid Number of items with grid arrangement (default: 1,1)
--duplicate-distance Distance in mm between copies (default: 6)
--dont-arrange Don't arrange the objects on the build plate. The model coordinates
define the absolute positions on the build plate.
define the absolute positions on the build plate.
The option --print-center will be ignored.
--xy-size-compensation
Grow/shrink objects by the configured absolute distance (mm, default: 0)
Sequential printing options:
--complete-objects When printing multiple objects and/or copies, complete each one before
starting the next one; watch out for extruder collisions (default: no)
@ -340,11 +340,11 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
(default: 20)
--extruder-clearance-height Maximum vertical extruder depth; i.e. vertical distance from
extruder tip and carriage bottom (default: 20)
Miscellaneous options:
--notes Notes to be added as comments to the output file
--resolution Minimum detail resolution (mm, set zero for full resolution, default: 0)
Flow options (advanced):
--extrusion-width Set extrusion width manually; it accepts either an absolute value in mm
(like 0.65) or a percentage over layer height (like 200%)
@ -364,7 +364,7 @@ Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark
Set a different extrusion width for support material
--infill-overlap Overlap between infill and perimeters (default: 15%)
--bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1)
Multiple extruder options:
--extruder-offset Offset of each extruder, if firmware doesn't handle the displacement
(can be specified multiple times, default: 0x0)

View File

@ -52,6 +52,7 @@ use Slic3r::GUI::Preset;
use Slic3r::GUI::PresetEditor;
use Slic3r::GUI::PresetEditorDialog;
use Slic3r::GUI::SLAPrintOptions;
use Slic3r::GUI::ReloadDialog;
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
our $have_LWP = eval "use LWP::UserAgent; 1";
@ -91,7 +92,9 @@ our $Settings = {
color_toolpaths_by => 'role',
tabbed_preset_editors => 1,
show_host => 0,
nudge_val => 1
nudge_val => 1,
reload_hide_dialog => 0,
reload_behavior => 0
},
};

View File

@ -1177,7 +1177,7 @@ sub add_tin {
sub load_file {
my $self = shift;
my ($input_file, $obj_idx) = @_;
my ($input_file, $obj_idx_to_load) = @_;
$Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
wxTheApp->save_settings;
@ -1203,14 +1203,27 @@ sub load_file {
}
}
if (defined $obj_idx) {
return () if $obj_idx >= $model->objects_count;
@obj_idx = $self->load_model_objects($model->get_object($obj_idx));
for my $obj_idx (0..($model->objects_count-1)) {
my $object = $model->objects->[$obj_idx];
$object->set_input_file($input_file);
for my $vol_idx (0..($object->volumes_count-1)) {
my $volume = $object->get_volume($vol_idx);
$volume->set_input_file($input_file);
$volume->set_input_file_obj_idx($obj_idx);
$volume->set_input_file_obj_idx($vol_idx);
}
}
my $i = 0;
if (defined $obj_idx_to_load) {
return () if $obj_idx_to_load >= $model->objects_count;
@obj_idx = $self->load_model_objects($model->get_object($obj_idx_to_load));
$i = $obj_idx_to_load;
} else {
@obj_idx = $self->load_model_objects(@{$model->objects});
}
my $i = 0;
foreach my $obj_idx (@obj_idx) {
$self->{objects}[$obj_idx]->input_file($input_file);
$self->{objects}[$obj_idx]->input_file_obj_idx($i++);
@ -2306,26 +2319,141 @@ sub reload_from_disk {
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
return if !$object->input_file
|| !-e $object->input_file;
if (!$object->input_file) {
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because it isn't referenced to its input file any more. This is the case after performing operations like cut or split.");
return;
}
if (!-e $object->input_file) {
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the file doesn't exist anymore on the disk.");
return;
}
# Only reload the selected object and not all objects from the input file.
my @new_obj_idx = $self->load_file($object->input_file, $object->input_file_obj_idx);
return if !@new_obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
foreach my $new_obj_idx (@new_obj_idx) {
my $o = $self->{model}->objects->[$new_obj_idx];
$o->clear_instances;
$o->add_instance($_) for @{$model_object->instances};
if ($o->volumes_count == $model_object->volumes_count) {
for my $i (0..($o->volumes_count-1)) {
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
}
if (!@new_obj_idx) {
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the new file doesn't contain the object.");
return;
}
my $org_obj = $self->{model}->objects->[$obj_idx];
# check if the object is dependant of more than one file
my $org_obj_has_modifiers=0;
for my $i (0..($org_obj->volumes_count-1)) {
if ($org_obj->input_file ne $org_obj->get_volume($i)->input_file) {
$org_obj_has_modifiers=1;
last;
}
}
my $reload_behavior = $Slic3r::GUI::Settings->{_}{reload_behavior};
# ask the user how to proceed, if option is selected in preferences
if ($org_obj_has_modifiers && !$Slic3r::GUI::Settings->{_}{reload_hide_dialog}) {
my $dlg = Slic3r::GUI::ReloadDialog->new(undef,$reload_behavior);
my $res = $dlg->ShowModal;
if ($res==wxID_CANCEL) {
$self->remove($_) for @new_obj_idx;
$dlg->Destroy;
return;
}
$reload_behavior = $dlg->GetSelection;
my $save = 0;
if ($reload_behavior != $Slic3r::GUI::Settings->{_}{reload_behavior}) {
$Slic3r::GUI::Settings->{_}{reload_behavior} = $reload_behavior;
$save = 1;
}
if ($dlg->GetHideOnNext) {
$Slic3r::GUI::Settings->{_}{reload_hide_dialog} = 1;
$save = 1;
}
Slic3r::GUI->save_settings if $save;
$dlg->Destroy;
}
my $volume_unmatched=0;
foreach my $new_obj_idx (@new_obj_idx) {
my $new_obj = $self->{model}->objects->[$new_obj_idx];
$new_obj->clear_instances;
$new_obj->add_instance($_) for @{$org_obj->instances};
$new_obj->config->apply($org_obj->config);
my $new_vol_idx = 0;
my $org_vol_idx = 0;
my $new_vol_count=$new_obj->volumes_count;
my $org_vol_count=$org_obj->volumes_count;
while ($new_vol_idx<=$new_vol_count-1) {
if (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) {
# apply config from the matching volumes
$new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume($org_vol_idx++)->config);
} else {
# reload has more volumes than original (first file), apply config from the first volume
$new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume(0)->config);
$volume_unmatched=1;
}
}
$org_vol_idx=$org_vol_count if $reload_behavior==2; # Reload behavior: discard
while (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) {
# original has more volumes (first file), skip those
$org_vol_idx++;
$volume_unmatched=1;
}
while ($org_vol_idx<=$org_vol_count-1) {
if ($reload_behavior==1) { # Reload behavior: copy
my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx));
$new_volume->mesh->translate(@{$org_obj->origin_translation->negative});
$new_volume->mesh->translate(@{$new_obj->origin_translation});
if ($new_volume->name =~ m/link to path\z/) {
my $new_name = $new_volume->name;
$new_name =~ s/ - no link to path$/ - copied/;
$new_volume->set_name($new_name);
}elsif(!($new_volume->name =~ m/copied\z/)) {
$new_volume->set_name($new_volume->name . " - copied");
}
}else{ # Reload behavior: Reload all, also fallback solution if ini was manually edited to a wrong value
if ($org_obj->get_volume($org_vol_idx)->input_file) {
my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($org_vol_idx)->input_file) };
if ($@) {
$org_obj->get_volume($org_vol_idx)->set_input_file("");
}elsif ($org_obj->get_volume($org_vol_idx)->input_file_obj_idx > ($model->objects_count-1)) {
# Object Index for that part / modifier not found in current version of the file
$org_obj->get_volume($org_vol_idx)->set_input_file("");
}else{
my $prt_mod_obj = $model->objects->[$org_obj->get_volume($org_vol_idx)->input_file_obj_idx];
if ($org_obj->get_volume($org_vol_idx)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) {
# Volume Index for that part / modifier not found in current version of the file
$org_obj->get_volume($org_vol_idx)->set_input_file("");
}else{
# all checks passed, load new mesh and copy metadata
my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($org_vol_idx)->input_file_vol_idx));
$new_volume->set_input_file($org_obj->get_volume($org_vol_idx)->input_file);
$new_volume->set_input_file_obj_idx($org_obj->get_volume($org_vol_idx)->input_file_obj_idx);
$new_volume->set_input_file_vol_idx($org_obj->get_volume($org_vol_idx)->input_file_vol_idx);
$new_volume->config->apply($org_obj->get_volume($org_vol_idx)->config);
$new_volume->set_modifier($org_obj->get_volume($org_vol_idx)->modifier);
$new_volume->mesh->translate(@{$new_obj->origin_translation});
}
}
}
if (!$org_obj->get_volume($org_vol_idx)->input_file) {
my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); # error -> copy old mesh
$new_volume->mesh->translate(@{$org_obj->origin_translation->negative});
$new_volume->mesh->translate(@{$new_obj->origin_translation});
if ($new_volume->name =~ m/copied\z/) {
my $new_name = $new_volume->name;
$new_name =~ s/ - copied$/ - no link to path/;
$new_volume->set_name($new_name);
}elsif(!($new_volume->name =~ m/link to path\z/)) {
$new_volume->set_name($new_volume->name . " - no link to path");
}
$volume_unmatched=1;
}
}
$org_vol_idx++;
}
}
$self->remove($obj_idx);
# TODO: refresh object list which contains wrong count and scale
@ -2336,8 +2464,17 @@ sub reload_from_disk {
# When porting to C++ we'll probably have cleaner ways to do this.
$self->make_thumbnail($_-1) for @new_obj_idx;
# update print
$self->stop_background_process;
$self->{print}->reload_object($_-1) for @new_obj_idx;
$self->on_model_change;
# Empty the redo stack
$self->{redo_stack} = [];
if ($volume_unmatched) {
Slic3r::GUI::warning_catcher($self)->("At least 1 volume couldn't be matched between the original object and the reloaded one.");
}
}
sub export_object_stl {

View File

@ -353,11 +353,17 @@ sub on_btn_load {
next;
}
foreach my $object (@{$model->objects}) {
foreach my $volume (@{$object->volumes}) {
my $new_volume = $self->{model_object}->add_volume($volume);
for my $obj_idx (0..($model->objects_count-1)) {
my $object = $model->objects->[$obj_idx];
for my $vol_idx (0..($object->volumes_count-1)) {
my $new_volume = $self->{model_object}->add_volume($object->get_volume($vol_idx));
$new_volume->set_modifier($is_modifier);
$new_volume->set_name(basename($input_file));
# input_file needed to reload / update modifiers' volumes
$new_volume->set_input_file($input_file);
$new_volume->set_input_file_obj_idx($obj_idx);
$new_volume->set_input_file_vol_idx($vol_idx);
# apply the same translation we applied to the object
$new_volume->mesh->translate(@{$self->{model_object}->origin_translation});

View File

@ -92,6 +92,23 @@ sub new {
tooltip => 'In 2D plater, Move objects using keyboard by nudge value of',
default => $Slic3r::GUI::Settings->{_}{nudge_val},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # reload hide dialog
opt_id => 'reload_hide_dialog',
type => 'bool',
label => 'Hide Dialog on Reload',
tooltip => 'When checked, the dialog on reloading files with added parts & modifiers is suppressed. The reload is performed according to the option given in \'Default Reload Behavior\'',
default => $Slic3r::GUI::Settings->{_}{reload_hide_dialog},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # default reload behavior
opt_id => 'reload_behavior',
type => 'select',
label => 'Default Reload Behavior',
tooltip => 'Choose the default behavior of the \'Reload from disk\' function regarding additional parts and modifiers.',
labels => ['Reload all','Reload main, copy added','Reload main, discard added'],
values => [0, 1, 2],
default => $Slic3r::GUI::Settings->{_}{reload_behavior},
width => 180,
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # colorscheme
opt_id => 'colorscheme',
type => 'select',
@ -100,7 +117,7 @@ sub new {
labels => ['Default','Solarized'], # add more schemes, if you want in ColorScheme.pm.
values => ['getDefault','getSolarized'], # add more schemes, if you want - those are the names of the corresponding function in ColorScheme.pm.
default => $Slic3r::GUI::Settings->{_}{colorscheme} // 'getDefault',
width => 130,
width => 180,
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL);

View File

@ -4,7 +4,7 @@ use warnings;
use utf8;
use File::Basename qw(basename);
use List::Util qw(first);
use List::Util qw(first any);
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window
:button wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED EVT_CHECKBOX);
@ -147,12 +147,11 @@ sub on_value_change {
# propagate event to the parent
sub _on_value_change {
my ($self, $opt_key) = @_;
wxTheApp->CallAfter(sub {
$self->current_preset->_dirty_config->apply($self->config);
$self->{on_value_change}->($opt_key) if $self->{on_value_change};
$self->load_presets;
$self->_update;
$self->_update($opt_key);
});
}
@ -456,7 +455,7 @@ sub options {
first_layer_acceleration default_acceleration
skirts skirt_distance skirt_height min_skirt_length
brim_connections_width brim_width interior_brim_width
support_material support_material_threshold support_material_enforce_layers
support_material support_material_threshold support_material_max_layers support_material_enforce_layers
raft_layers
support_material_pattern support_material_spacing support_material_angle
support_material_interface_layers support_material_interface_spacing
@ -600,6 +599,7 @@ sub build {
my $optgroup = $page->new_optgroup('Support material');
$optgroup->append_single_option_line('support_material');
$optgroup->append_single_option_line('support_material_threshold');
$optgroup->append_single_option_line('support_material_max_layers');
$optgroup->append_single_option_line('support_material_enforce_layers');
}
{
@ -793,122 +793,141 @@ sub reload_config {
}
sub _update {
my ($self) = @_;
my ($self, $key) = @_;
my $opt_key = $key;
$opt_key = "all_keys" if (length($key // '') == 0);
my $config = $self->{config};
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0 && $config->support_material == 0)) {
my $dialog = Wx::MessageDialog->new($self,
"The Spiral Vase mode requires:\n"
. "- one perimeter\n"
. "- no top solid layers\n"
. "- 0% fill density\n"
. "- no support material\n"
. "\nShall I adjust those settings in order to enable Spiral Vase?",
'Spiral Vase', wxICON_WARNING | wxYES | wxNO);
if ($dialog->ShowModal() == wxID_YES) {
my $new_conf = Slic3r::Config->new;
$new_conf->set("perimeters", 1);
$new_conf->set("top_solid_layers", 0);
$new_conf->set("fill_density", 0);
$new_conf->set("support_material", 0);
$self->_load_config($new_conf);
} else {
my $new_conf = Slic3r::Config->new;
$new_conf->set("spiral_vase", 0);
$self->_load_config($new_conf);
}
}
if ($config->support_material) {
# Ask only once.
if (! $self->{support_material_overhangs_queried}) {
$self->{support_material_overhangs_queried} = 1;
if ($config->overhangs != 1) {
my $dialog = Wx::MessageDialog->new($self,
"Supports work better, if the following feature is enabled:\n"
. "- Detect bridging perimeters\n"
. "\nShall I adjust those settings for supports?",
'Support Generator', wxICON_WARNING | wxYES | wxNO | wxCANCEL);
my $answer = $dialog->ShowModal();
if (any { /$opt_key/ } qw(all_keys spiral_vase perimeters top_solid_layers fill_density support_material)) {
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0 && $config->support_material == 0)) {
my $dialog = Wx::MessageDialog->new($self,
"The Spiral Vase mode requires:\n"
. "- one perimeter\n"
. "- no top solid layers\n"
. "- 0% fill density\n"
. "- no support material\n"
. "\nShall I adjust those settings in order to enable Spiral Vase?",
'Spiral Vase', wxICON_WARNING | wxYES | wxNO);
if ($dialog->ShowModal() == wxID_YES) {
my $new_conf = Slic3r::Config->new;
if ($answer == wxID_YES) {
# Enable "detect bridging perimeters".
$new_conf->set("overhangs", 1);
} elsif ($answer == wxID_NO) {
# Do nothing, leave supports on and "detect bridging perimeters" off.
} elsif ($answer == wxID_CANCEL) {
# Disable supports.
$new_conf->set("support_material", 0);
$self->{support_material_overhangs_queried} = 0;
}
$new_conf->set("perimeters", 1);
$new_conf->set("top_solid_layers", 0);
$new_conf->set("fill_density", 0);
$new_conf->set("support_material", 0);
$self->_load_config($new_conf);
} else {
my $new_conf = Slic3r::Config->new;
$new_conf->set("spiral_vase", 0);
$self->_load_config($new_conf);
}
}
} else {
$self->{support_material_overhangs_queried} = 0;
}
if (any { /$opt_key/ } qw(all_keys support_material)) {
if ($config->support_material) {
# Ask only once.
if (! $self->{support_material_overhangs_queried}) {
$self->{support_material_overhangs_queried} = 1;
if ($config->overhangs != 1) {
my $dialog = Wx::MessageDialog->new($self,
"Supports work better, if the following feature is enabled:\n"
. "- Detect bridging perimeters\n"
. "\nShall I adjust those settings for supports?",
'Support Generator', wxICON_WARNING | wxYES | wxNO | wxCANCEL);
my $answer = $dialog->ShowModal();
my $new_conf = Slic3r::Config->new;
if ($answer == wxID_YES) {
# Enable "detect bridging perimeters".
$new_conf->set("overhangs", 1);
} elsif ($answer == wxID_NO) {
# Do nothing, leave supports on and "detect bridging perimeters" off.
} elsif ($answer == wxID_CANCEL) {
# Disable supports.
$new_conf->set("support_material", 0);
$self->{support_material_overhangs_queried} = 0;
}
$self->_load_config($new_conf);
}
}
} else {
$self->{support_material_overhangs_queried} = 0;
}
}
if ($config->fill_density == 100
&& !first { $_ eq $config->fill_pattern } @{$Slic3r::Config::Options->{top_infill_pattern}{values}}) {
my $dialog = Wx::MessageDialog->new($self,
"The " . $config->fill_pattern . " infill pattern is not supposed to work at 100% density.\n"
. "\nShall I switch to rectilinear fill pattern?",
'Infill', wxICON_WARNING | wxYES | wxNO);
my $new_conf = Slic3r::Config->new;
if ($dialog->ShowModal() == wxID_YES) {
$new_conf->set("fill_pattern", 'rectilinear');
} else {
$new_conf->set("fill_density", 40);
if (any { /$opt_key/ } qw(all_keys fill_density fill_pattern top_infill_pattern)) {
if ($config->fill_density == 100
&& !first { $_ eq $config->fill_pattern } @{$Slic3r::Config::Options->{top_infill_pattern}{values}}) {
my $dialog = Wx::MessageDialog->new($self,
"The " . $config->fill_pattern . " infill pattern is not supposed to work at 100% density.\n"
. "\nShall I switch to rectilinear fill pattern?",
'Infill', wxICON_WARNING | wxYES | wxNO);
my $new_conf = Slic3r::Config->new;
if ($dialog->ShowModal() == wxID_YES) {
$new_conf->set("fill_pattern", 'rectilinear');
} else {
$new_conf->set("fill_density", 40);
}
$self->_load_config($new_conf);
}
$self->_load_config($new_conf);
}
my $have_perimeters = $config->perimeters > 0;
$self->get_field($_)->toggle($have_perimeters)
for qw(extra_perimeters thin_walls overhangs seam_position external_perimeters_first
external_perimeter_extrusion_width
perimeter_speed small_perimeter_speed external_perimeter_speed);
if (any { /$opt_key/ } qw(all_keys perimeters)) {
$self->get_field($_)->toggle($have_perimeters)
for qw(extra_perimeters thin_walls overhangs seam_position external_perimeters_first
external_perimeter_extrusion_width
perimeter_speed small_perimeter_speed external_perimeter_speed);
}
my $have_adaptive_slicing = $config->adaptive_slicing;
$self->get_field($_)->toggle($have_adaptive_slicing)
for qw(adaptive_slicing_quality match_horizontal_surfaces);
$self->get_field($_)->toggle(!$have_adaptive_slicing)
for qw(layer_height);
if (any { /$opt_key/ } qw(all_keys adaptive_slicing)) {
$self->get_field($_)->toggle($have_adaptive_slicing)
for qw(adaptive_slicing_quality match_horizontal_surfaces);
$self->get_field($_)->toggle(!$have_adaptive_slicing)
for qw(layer_height);
}
my $have_infill = $config->fill_density > 0;
# infill_extruder uses the same logic as in Print::extruders()
$self->get_field($_)->toggle($have_infill)
for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers
solid_infill_below_area infill_extruder);
if (any { /$opt_key/ } qw(all_keys fill_density)) {
# infill_extruder uses the same logic as in Print::extruders()
$self->get_field($_)->toggle($have_infill)
for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers
solid_infill_below_area infill_extruder);
}
my $have_solid_infill = ($config->top_solid_layers > 0) || ($config->bottom_solid_layers > 0);
# solid_infill_extruder uses the same logic as in Print::extruders()
$self->get_field($_)->toggle($have_solid_infill)
for qw(top_infill_pattern bottom_infill_pattern infill_first solid_infill_extruder
solid_infill_extrusion_width solid_infill_speed);
$self->get_field($_)->toggle($have_infill || $have_solid_infill)
for qw(fill_angle infill_extrusion_width infill_speed bridge_speed);
$self->get_field('fill_gaps')->toggle($have_perimeters && $have_infill);
$self->get_field('gap_fill_speed')->toggle($have_perimeters && $have_infill && $config->fill_gaps);
if (any { /$opt_key/ } qw(all_keys top_solid_layers bottom_solid_layers)) {
# solid_infill_extruder uses the same logic as in Print::extruders()
$self->get_field($_)->toggle($have_solid_infill)
for qw(top_infill_pattern bottom_infill_pattern infill_first solid_infill_extruder
solid_infill_extrusion_width solid_infill_speed);
}
if (any { /$opt_key/ } qw(all_keys top_solid_layers bottom_solid_layers fill_density)) {
$self->get_field($_)->toggle($have_infill || $have_solid_infill)
for qw(fill_angle infill_extrusion_width infill_speed bridge_speed);
}
if (any { /$opt_key/ } qw(all_keys fill_density perimeters)) {
$self->get_field('fill_gaps')->toggle($have_perimeters && $have_infill);
$self->get_field('gap_fill_speed')->toggle($have_perimeters && $have_infill && $config->fill_gaps);
}
my $have_top_solid_infill = $config->top_solid_layers > 0;
$self->get_field($_)->toggle($have_top_solid_infill)
for qw(top_infill_extrusion_width top_solid_infill_speed);
my $have_autospeed = any { $config->get("${_}_speed") eq '0' }
qw(perimeter external_perimeter small_perimeter
infill solid_infill top_solid_infill gap_fill support_material
support_material_interface);
qw(perimeter external_perimeter small_perimeter
infill solid_infill top_solid_infill gap_fill support_material
support_material_interface);
$self->get_field('max_print_speed')->toggle($have_autospeed);
my $have_default_acceleration = $config->default_acceleration > 0;
$self->get_field($_)->toggle($have_default_acceleration)
for qw(perimeter_acceleration infill_acceleration bridge_acceleration first_layer_acceleration);
my $have_skirt = $config->skirts > 0 || $config->min_skirt_length > 0;
$self->get_field($_)->toggle($have_skirt)
for qw(skirt_distance skirt_height);
@ -926,6 +945,10 @@ sub _update {
support_material_interface_layers dont_support_bridges
support_material_extrusion_width support_material_interface_extrusion_width
support_material_contact_distance);
# Disable features that need support to be enabled.
$self->get_field($_)->toggle($config->support_material)
for qw(support_material_max_layers);
$self->get_field($_)->toggle($have_support_material && $have_support_interface)
for qw(support_material_interface_spacing support_material_interface_extruder
@ -1217,6 +1240,7 @@ sub options {
retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below
printer_settings_id
printer_notes
use_set_and_wait_bed use_set_and_wait_extruder
);
}
@ -1323,7 +1347,7 @@ sub build {
# look for devices
my $entries;
{
my $res = Net::Bonjour->new('http');
my $res = Net::Bonjour->new('octoprint');
$res->discover;
$entries = [ $res->entries ];
}
@ -1369,6 +1393,8 @@ sub build {
$optgroup->append_single_option_line('pressure_advance');
$optgroup->append_single_option_line('vibration_limit');
$optgroup->append_single_option_line('z_steps_per_mm');
$optgroup->append_single_option_line('use_set_and_wait_extruder');
$optgroup->append_single_option_line('use_set_and_wait_bed');
}
}
{

View File

@ -0,0 +1,60 @@
# A tiny dialog to select how to reload an object that has additional parts or modifiers.
package Slic3r::GUI::ReloadDialog;
use strict;
use warnings;
use utf8;
use Wx qw(:button :dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_CLOSE);
use base 'Wx::Dialog';
sub new {
my $class = shift;
my ($parent,$default_selection) = @_;
my $self = $class->SUPER::new($parent, -1, "Additional parts and modifiers detected", wxDefaultPosition, [350,100], wxDEFAULT_DIALOG_STYLE);
# label
my $text = Wx::StaticText->new($self, -1, "Additional parts and modifiers are loaded in the current model. \n\nHow do you want to proceed?", wxDefaultPosition, wxDefaultSize);
# selector
$self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
$choice->Append("Reload all linked files");
$choice->Append("Reload main file, copy added parts & modifiers");
$choice->Append("Reload main file, discard added parts & modifiers");
$choice->SetSelection($default_selection);
# checkbox
$self->{checkbox} = my $checkbox = Wx::CheckBox->new($self, -1, "Don't ask again");
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
$vsizer->Add($text, 0, wxEXPAND | wxALL, 10);
$vsizer->Add($choice, 0, wxEXPAND | wxALL, 10);
$hsizer->Add($checkbox, 1, wxEXPAND | wxALL, 10);
$hsizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 10);
$vsizer->Add($hsizer, 0, wxEXPAND | wxALL, 0);
$self->SetSizer($vsizer);
$self->SetMinSize($self->GetSize);
$vsizer->SetSizeHints($self);
# needed to actually free memory
EVT_CLOSE($self, sub {
$self->EndModal(wxID_CANCEL);
$self->Destroy;
});
return $self;
}
sub GetSelection {
my ($self) = @_;
return $self->{choice}->GetSelection;
}
sub GetHideOnNext {
my ($self) = @_;
return $self->{checkbox}->GetValue;
}
1;

View File

@ -71,6 +71,34 @@ sub process {
}
}
sub escaped_split {
my ($line) = @_;
# Free up three characters for temporary replacement
$line =~ s/%/%%/g;
$line =~ s/#/##/g;
$line =~ s/\?/\?\?/g;
# replace escaped !'s
$line =~ s/\!\!/%#\?/g;
# split on non-escaped whitespace
my @split = split /(?<=[^\!])\s+/, $line, -1;
for my $part (@split) {
# replace escaped whitespace with the whitespace
$part =~ s/\!(\s+)/$1/g;
# resub temp symbols
$part =~ s/%#\?/\!/g;
$part =~ s/%%/%/g;
$part =~ s/##/#/g;
$part =~ s/\?\?/\?/g;
}
return @split;
}
sub export_gcode {
my $self = shift;
my %params = @_;
@ -121,11 +149,14 @@ sub export_gcode {
$self->config->setenv;
for my $script (@{$self->config->post_process}) {
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
my @parsed_script = escaped_split $script;
my $executable = shift @parsed_script ;
push @parsed_script, $output_file;
# -x doesn't return true on Windows except for .exe files
if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) {
die "The configured post-processing script is not executable: check permissions. ($script)\n";
if (($^O eq 'MSWin32') ? !(-e $executable) : !(-x $executable)) {
die "The configured post-processing script is not executable: check permissions or escape whitespace/exclamation points. ($executable) \n";
}
system($script, $output_file);
system($executable, @parsed_script);
}
}
}

View File

@ -153,6 +153,10 @@ sub export {
foreach my $start_gcode (@{ $self->config->start_filament_gcode }) { # process filament gcode in order
$include_start_extruder_temp = $include_start_extruder_temp && ($start_gcode !~ /M(?:109|104)/i);
}
my $include_end_extruder_temp = $self->config->end_gcode !~ /M(?:109|104)/i;
foreach my $end_gcode (@{ $self->config->end_filament_gcode }) { # process filament gcode in order
$include_end_extruder_temp = $include_end_extruder_temp && ($end_gcode !~ /M(?:109|104)/i);
}
$self->_print_first_layer_temperature(0)
if $include_start_extruder_temp;
printf $fh "%s\n", Slic3r::ConditionalGCode::apply_math($gcodegen->placeholder_parser->process($self->config->start_gcode));
@ -301,6 +305,14 @@ sub export {
printf $fh "%s\n", Slic3r::ConditionalGCode::apply_math($gcodegen->placeholder_parser->process($end_gcode));
}
printf $fh "%s\n", Slic3r::ConditionalGCode::apply_math($gcodegen->placeholder_parser->process($self->config->end_gcode));
$self->_print_off_temperature(0)
if $include_end_extruder_temp;
# set bed temperature
if (($self->config->has_heatbed) && $self->config->end_gcode !~ /M(?:190|140)/i) {
printf $fh $gcodegen->writer->set_bed_temperature(0, 0);
}
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
print $fh $gcodegen->writer->postamble;
@ -356,6 +368,16 @@ sub _print_first_layer_temperature {
}
}
sub _print_off_temperature {
my ($self, $wait) = @_;
for my $t (@{$self->print->extruders}) {
printf {$self->fh} $self->_gcodegen->writer->set_temperature(0, $wait, $t)
}
}
# Called per object's layer.
# First a $gcode string is collected,
# then filtered and finally written to a file $fh.

View File

@ -133,6 +133,8 @@ sub contact_area {
last;
}
my $layer = $object->get_layer($layer_id);
last if $conf->support_material_max_layers
&& $layer_id > $conf->support_material_max_layers;
if ($buildplate_only) {
# Collect the top surfaces up to this layer and merge them.
@ -753,7 +755,9 @@ sub generate_toolpaths {
# interface and contact infill
if (@$interface || @$contact_infill) {
$fillers{interface}->set_angle(deg2rad($interface_angle));
# make interface layers alternate angles by 90 degrees
my $alternate_angle = $interface_angle + (90 * (($layer_id + 1) % 2));
$fillers{interface}->set_angle(deg2rad($alternate_angle));
$fillers{interface}->set_min_spacing($_interface_flow->spacing);
# find centerline of the external loop

View File

@ -32,7 +32,7 @@ cd $WD/${APP}.AppDir
mkdir -p $WD/${APP}.AppDir/usr/bin
# Copy primary Slic3r script here and perl-local, as well as var
for i in {var,slic3r.pl,perl-local}; do
for i in {var,Slic3r}; do
cp -R $srcfolder/$i $WD/${APP}.AppDir/usr/bin/
done
@ -48,8 +48,6 @@ for i in $(cat $WD/libpaths.appimage.txt | grep -v "^#" | awk -F# '{print $1}');
done
cp -R $srcfolder/local-lib ${WD}/${APP}.AppDir/usr/lib/local-lib
cat > $WD/${APP}.AppDir/AppRun << 'EOF'
#!/usr/bin/env bash
# some magic to find out the real location of this script dealing with symlinks

View File

@ -1,8 +1,3 @@
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_baseu-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_adv-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_core-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_gl-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_html-3.0.so.0
/lib/x86_64-linux-gnu/liblzma.so.5
/lib/x86_64-linux-gnu/libpng12.so.0
/usr/lib/x86_64-linux-gnu/libjpeg.so.8

View File

@ -10,7 +10,6 @@ if [ "$#" -ne 1 ]; then
echo "Usage: $(basename $0) arch_name"
exit 1;
fi
libdirs=$(find ./local-lib -iname *.so -exec dirname {} \; | sort -u | paste -sd ";" -)
WD=./$(dirname $0)
source $(dirname $0)/../common/util.sh
# Determine if this is a tagged (release) commit.
@ -22,7 +21,6 @@ set_build_id
set_branch
set_app_name
set_pr_id
install_par
# If we're on a branch, add the branch name to the app name.
if [ "$current_branch" == "master" ]; then
@ -35,8 +33,7 @@ else
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2
fi
rm -rf $WD/_tmp
mkdir -p $WD/_tmp
mkdir -p $WD
# Set the application folder infomation.
appfolder="$WD/${appname}"
@ -46,8 +43,6 @@ resourcefolder=$appfolder
echo "Appfolder: $appfolder, archivefolder: $archivefolder"
# Our slic3r dir and location of perl
PERL_BIN=$(which perl)
PP_BIN=$(which pp)
SLIC3R_DIR="./"
if [[ -d "${appfolder}" ]]; then
@ -67,14 +62,12 @@ echo "Copying resources..."
cp -rf $SLIC3R_DIR/var $resourcefolder/
echo "Copying Slic3r..."
cp $SLIC3R_DIR/slic3r.pl $archivefolder/slic3r.pl
cp -fRP $SLIC3R_DIR/local-lib $archivefolder/local-lib
cp -fRP $SLIC3R_DIR/lib/* $archivefolder/local-lib/lib/perl5/
cp $SLIC3R_DIR/slic3r $archivefolder/Slic3r
mkdir $archivefolder/bin
echo "Installing libraries to $archivefolder/bin ..."
if [ -z ${WXDIR+x} ]; then
for bundle in $(find $archivefolder/local-lib/lib/perl5 -name '*.so' | grep "Wx") $(find $archivefolder/local-lib/lib/perl5 -name '*.so' -type f | grep "wxWidgets"); do
for bundle in $archivefolder/Slic3r; do
echo "$(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}')"
for dylib in $(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}'); do
install -v $dylib $archivefolder/bin
@ -91,37 +84,8 @@ echo "Copying startup script..."
cp -f $WD/startup_script.sh $archivefolder/$appname
chmod +x $archivefolder/$appname
echo "Copying perl from $PERL_BIN"
# Edit package/common/coreperl to add/remove core Perl modules added to this package, one per line.
cp -f $PERL_BIN $archivefolder/perl-local
${PP_BIN} wxextension .0 \
-M $(grep -v "^#" ${WD}/../common/coreperl | xargs | awk 'BEGIN { OFS=" -M "}; {$1=$1; print $0}') \
-B -p -e "print 123" -o $WD/_tmp/test.par
unzip -qq -o $WD/_tmp/test.par -d $WD/_tmp/
cp -rf $WD/_tmp/lib/* $archivefolder/local-lib/lib/perl5/
cp -rf $WD/_tmp/shlib $archivefolder/
rm -rf $WD/_tmp
for i in $(cat $WD/libpaths.txt | grep -v "^#" | awk -F# '{print $1}'); do
install -v $i $archivefolder/bin
done
echo "Cleaning local-lib"
rm -rf $archivefolder/local-lib/bin
rm -rf $archivefolder/local-lib/man
rm -f $archivefolder/local-lib/lib/perl5/Algorithm/*.pl
rm -rf $archivefolder/local-lib/lib/perl5/unicore
rm -rf $archivefolder/local-lib/lib/perl5/App
rm -rf $archivefolder/local-lib/lib/perl5/Devel/CheckLib.pm
rm -rf $archivefolder/local-lib/lib/perl5/ExtUtils
rm -rf $archivefolder/local-lib/lib/perl5/Module/Build*
rm -rf $(pwd)$archivefolder/local-lib/lib/perl5/TAP
rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test*
find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \
-or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \;
rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include
find $archivefolder/local-lib -depth -type d -empty -exec rmdir "{}" \;
tar -C$(pwd)/$(dirname $appfolder) -cjf $(pwd)/$dmgfile "$appname"

View File

@ -3,4 +3,4 @@
BIN=$(readlink "$0")
DIR=$(dirname "$BIN")
export LD_LIBRARY_PATH="$DIR/bin"
exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@
exec "$DIR/Slic3r"

View File

@ -4,27 +4,15 @@ WXVERSION=302
CACHE=$HOME/cache
mkdir -p $CACHE
if [ ! -e $CACHE/slic3r-perlbrew-5.24.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2 => $CACHE/slic3r-perlbrew-5.24.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2" -o $CACHE/slic3r-perlbrew-5.24.tar.bz2;
fi
if [ ! -e $CACHE/boost-compiled.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2 => $CACHE/boost-compiled.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2" -o $CACHE/boost-compiled.tar.bz2
fi
if [ ! -e $CACHE/local-lib-wx${WXVERSION}.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2 => $CACHE/local-lib-wx${WXVERSION}.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2" -o $CACHE/local-lib-wx${WXVERSION}.tar.bz2
fi
if [ ! -e $CACHE/wx${WXVERSION}.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2 => $CACHE/wx${WXVERSION}.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2" -o $CACHE/wx${WXVERSION}.tar.bz2
fi
tar -C$TRAVIS_BUILD_DIR -xjf $CACHE/local-lib-wx${WXVERSION}.tar.bz2
tar -C$HOME/perl5/perlbrew/perls -xjf $CACHE/slic3r-perlbrew-5.24.tar.bz2
tar -C$HOME -xjf $CACHE/boost-compiled.tar.bz2
tar -C$HOME -xjf $CACHE/wx${WXVERSION}.tar.bz2

View File

@ -79,6 +79,8 @@ mkdir -p $resourcefolder
echo "Copying resources..."
cp -rf $SLIC3R_DIR/var $resourcefolder/
mv $resourcefolder/var/Slic3r.icns $resourcefolder
mv $resourcefolder/var/stl.icns $resourcefolder
mv $resourcefolder/var/gcode.icns $resourcefolder
echo "Copying Slic3r..."
cp $SLIC3R_DIR/slic3r.pl $macosfolder/slic3r.pl

View File

@ -36,66 +36,83 @@ cat << EOF >> $plistfile
<key>CFBundleVersion</key>
<string>${SLIC3R_BUILD_ID}</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>stl</string>
<string>STL</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Slic3r.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>obj</string>
<string>OBJ</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Slic3r.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>amf</string>
<string>AMF</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Slic3r.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
<key>LSMinimumSystemVersion</key>
<string>10.7</string>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>stl</string>
<string>STL</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>stl.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>obj</string>
<string>OBJ</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Slic3r.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>amf</string>
<string>AMF</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Slic3r.icns</string>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gcode</string>
<string>GCODE</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>gcode.icns</string>
<key>CFBundleTypeName</key>
<string>GCODE</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LISsAppleDefaultForType</key>
<true/>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
<key>LSMinimumSystemVersion</key>
<string>10.7</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</dict>
</plist>
EOF

View File

@ -1,8 +1,25 @@
cmake_minimum_required (VERSION 2.8)
cmake_minimum_required (VERSION 3.9)
project (slic3r)
# only on newer GCCs: -ftemplate-backtrace-limit=0
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -DM_PI=3.14159265358979323846 -D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DBOOST_ASIO_DISABLE_KQUEUE")
set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS} -Wall -DM_PI=3.14159265358979323846 -D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DBOOST_ASIO_DISABLE_KQUEUE")
if(DEFINED ENV{SLIC3R_VAR_REL})
set(CMAKE_CXX_FLAGS "-DVAR_REL=$ENV{SLIC3R_VAR_REL}")
endif(DEFINED ENV{SLIC3R_VAR_REL})
if(DEFINED ENV{SLIC3R_VAR_ABS})
set(CMAKE_CXX_FLAGS "-DVAR_ABS")
endif(DEFINED ENV{SLIC3R_VAR_ABS})
if(DEFINED ENV{SLIC3R_VAR_ABS_PATH})
set(CMAKE_CXX_FLAGS "-DVAR_ABS_PATH=$ENV{SLIC3R_VAR_ABS_PATH}")
endif(DEFINED ENV{SLIC3R_VAR_ABS_PATH})
execute_process(COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE GIT_VERSION ERROR_QUIET)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -DSLIC3R_DEBUG")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.7.0)
@ -17,14 +34,23 @@ IF(CMAKE_HOST_APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE")
set(CMAKE_EXE_LINKER_FLAGS "-framework IOKit -framework CoreFoundation -lc++")
ELSE(CMAKE_HOST_APPLE)
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
# set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -L.")
ENDIF(CMAKE_HOST_APPLE)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
if(DEFINED ENV{SLIC3R_STATIC})
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
else(DEFINED ENV{SLIC3R_STATIC})
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
endif(DEFINED ENV{SLIC3R_STATIC})
find_package(Threads REQUIRED)
find_package(Boost COMPONENTS system thread filesystem)
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/../xs/src/)
set(GUI_LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/GUI/)
include_directories(${LIBDIR})
include_directories(${LIBDIR}/libslic3r)
@ -37,10 +63,16 @@ include_directories(${LIBDIR}/poly2tri/)
include_directories(${LIBDIR}/poly2tri/sweep)
include_directories(${LIBDIR}/poly2tri/common)
add_library(ZipArchive STATIC
${LIBDIR}/Zip/ZipArchive.cpp
)
add_library(libslic3r STATIC
${LIBDIR}/libslic3r/BoundingBox.cpp
${LIBDIR}/libslic3r/BridgeDetector.cpp
${LIBDIR}/libslic3r/ClipperUtils.cpp
${LIBDIR}/libslic3r/ConfigBase.cpp
${LIBDIR}/libslic3r/Config.cpp
${LIBDIR}/libslic3r/ExPolygon.cpp
${LIBDIR}/libslic3r/ExPolygonCollection.cpp
@ -53,6 +85,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
${LIBDIR}/libslic3r/Fill/FillGyroid.cpp
${LIBDIR}/libslic3r/Flow.cpp
${LIBDIR}/libslic3r/GCode.cpp
${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp
@ -89,8 +122,8 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/SurfaceCollection.cpp
${LIBDIR}/libslic3r/SVG.cpp
${LIBDIR}/libslic3r/TriangleMesh.cpp
${LIBDIR}/libslic3r/Zip/ZipArchive.cpp
)
target_compile_features(libslic3r PUBLIC cxx_std_11)
add_library(BSpline STATIC
${LIBDIR}/BSpline/BSpline.cpp
@ -122,55 +155,86 @@ add_library(poly2tri STATIC
)
add_executable(slic3r slic3r.cpp)
set_target_properties(slic3r PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(slic3r PROPERTIES LINK_SEARCH_END_STATIC 1)
#set_target_properties(slic3r PROPERTIES LINK_SEARCH_START_STATIC 1)
#set_target_properties(slic3r PROPERTIES LINK_SEARCH_END_STATIC 1)
add_executable(extrude-tin utils/extrude-tin.cpp)
set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_END_STATIC 1)
#add_executable(extrude-tin utils/extrude-tin.cpp)
#set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_START_STATIC 1)
#set_target_properties(extrude-tin PROPERTIES LINK_SEARCH_END_STATIC 1)
set(wxWidgets_USE_STATIC)
SET(wxWidgets_USE_LIBS)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_library(bsystem_l boost_system)
add_library(bsystem STATIC IMPORTED)
find_library(bsystem_l boost_system log)
add_library(bsystem SHARED IMPORTED)
set_target_properties(bsystem PROPERTIES IMPORTED_LOCATION ${bsystem_l})
find_library(bthread_l boost_thread)
add_library(bthread STATIC IMPORTED)
add_library(bthread SHARED IMPORTED)
set_target_properties(bthread PROPERTIES IMPORTED_LOCATION ${bthread_l})
include_directories(${Boost_INCLUDE_DIRS})
#find_package(wxWidgets)
#disable wx for the time being - we're not building any of the gui yet
if(DEFINED ENV{SLIC3R_STATIC})
set(wxWidgets_USE_STATIC ON)
ELSE(DEFINED ENV{SLIC3R_STATIC})
set(wxWidgets_USE_STATIC OFF)
ENDIF(DEFINED ENV{SLIC3R_STATIC})
set(wxWidgets_USE_UNICODE ON)
find_package(wxWidgets COMPONENTS base aui core html)
IF(CMAKE_HOST_UNIX)
#set(Boost_LIBRARIES bsystem bthread bfilesystem)
ENDIF(CMAKE_HOST_UNIX)
target_link_libraries (slic3r libslic3r admesh BSpline clipper expat polypartition poly2tri ${Boost_LIBRARIES})
IF(wxWidgets_FOUND)
MESSAGE("wx found!")
INCLUDE("${wxWidgets_USE_FILE}")
add_library(slic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/GUI.cpp)
if (NOT GIT_VERSION STREQUAL "")
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DSLIC3R_BUILD_COMMIT=${GIT_VERSION} ")
else(MSVC)
execute_process(COMMAND git rev-parse --short HEAD OUTPUT_VARIABLE GIT_VERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSLIC3R_BUILD_COMMIT=${GIT_VERSION}")
string(REGEX REPLACE "\n$" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif(MSVC)
endif(NOT GIT_VERSION STREQUAL "")
include_directories(${GUI_LIBDIR})
include_directories(${wxWidgets_INCLUDE})
add_library(slic3r_gui STATIC
${GUI_LIBDIR}/AboutDialog.cpp
${GUI_LIBDIR}/GUI.cpp
${GUI_LIBDIR}/MainFrame.cpp
${GUI_LIBDIR}/Plater.cpp
${GUI_LIBDIR}/Plater/Plate2D.cpp
${GUI_LIBDIR}/Plater/PlaterObject.cpp
${GUI_LIBDIR}/ProgressStatusBar.cpp
${GUI_LIBDIR}/Settings.cpp
${GUI_LIBDIR}/misc_ui.cpp
)
target_compile_features(slic3r_gui PUBLIC cxx_std_11)
#only build GUI lib if building with wx
target_link_libraries (slic3r slic3r-gui ${wxWidgets_LIBRARIES})
target_link_libraries (slic3r slic3r_gui ${wxWidgets_LIBRARIES})
ELSE(wxWidgets_FOUND)
# For convenience. When we cannot continue, inform the user
MESSAGE("wx not found!")
#skip gui when no wx included
ENDIF(wxWidgets_FOUND)
target_link_libraries (slic3r libslic3r admesh BSpline clipper expat polypartition poly2tri ZipArchive ${Boost_LIBRARIES})
# Windows needs a compiled component for Boost.nowide
IF (WIN32)
add_library(boost-nowide STATIC
${LIBDIR}/boost/nowide/iostream.cpp
)
target_link_libraries(slic3r boost-nowide)
target_link_libraries(extrude-tin boost-nowide)
target_link_libraries(slic3r STATIC boost-nowide)
# target_link_libraries(extrude-tin boost-nowide)
ENDIF(WIN32)
target_link_libraries (extrude-tin libslic3r admesh BSpline clipper expat polypartition poly2tri ${Boost_LIBRARIES})
#target_link_libraries (extrude-tin libslic3r admesh BSpline clipper expat polypartition poly2tri ${Boost_LIBRARIES})

98
src/GUI/AboutDialog.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "AboutDialog.hpp"
namespace Slic3r { namespace GUI {
static void link_clicked(wxHtmlLinkEvent& e)
{
wxLaunchDefaultBrowser(e.GetLinkInfo().GetHref());
e.Skip(0);
}
AboutDialog::AboutDialog(wxWindow* parent) : wxDialog(parent, -1, _("About Slic3r"), wxDefaultPosition, wxSize(600, 460), wxCAPTION)
{
auto hsizer { new wxBoxSizer(wxHORIZONTAL) } ;
auto vsizer { new wxBoxSizer(wxVERTICAL) } ;
// logo
auto logo { new AboutDialogLogo(this) };
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
// title
auto title { new wxStaticText(this, -1, "Slic3r", wxDefaultPosition, wxDefaultSize) };
auto title_font { wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
title_font.SetWeight(wxFONTWEIGHT_BOLD);
title_font.SetFamily(wxFONTFAMILY_ROMAN);
title_font.SetPointSize(24);
title->SetFont(title_font);
vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 30);
// version
auto version {new wxStaticText(this, -1, wxString("Version ") + wxString(SLIC3R_VERSION), wxDefaultPosition, wxDefaultSize) };
auto version_font { wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
version_font.SetPointSize((the_os == OS::Windows ? 9 : 11));
version->SetFont(version_font);
vsizer->Add(version, 0, wxALIGN_LEFT | wxBOTTOM, 10);
// text
wxString text {""};
text << "<html>"
<< "<body>"
<< "Copyright &copy; 2011-2017 Alessandro Ranellucci. <br />"
<< "<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the "
<< "<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>."
<< "<br /><br /><br />"
<< "Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, "
<< "Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. "
<< "Manual by Gary Hodgson. Inspired by the RepRap community. <br />"
<< "Slic3r logo designed by Corey Daniels, <a href=\"http://www.famfamfam.com/lab/icons/silk/\">Silk Icon Set</a> designed by Mark James. "
<< "<br /><br />"
<< "Built on " << build_date << " at git version " << git_version << "."
<< "</body>"
<< "</html>";
auto html {new wxHtmlWindow(this, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER)};
html->SetBorders(2);
html->SetPage(text);
html->Bind(wxEVT_HTML_LINK_CLICKED, [=](wxHtmlLinkEvent& e){ link_clicked(e); });
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
// buttons
auto buttons = this->CreateStdDialogButtonSizer(wxOK);
this->SetEscapeId(wxID_CLOSE);
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
hsizer->Add(vsizer, 1, wxEXPAND, 0);
this->SetSizer(hsizer);
};
AboutDialogLogo::AboutDialogLogo(wxWindow* parent) :
wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize)
{
this->logo = wxBitmap(var("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
this->SetMinSize(wxSize(this->logo.GetWidth(), this->logo.GetHeight()));
this->Bind(wxEVT_PAINT, [=](wxPaintEvent& e) { this->repaint(e);});
}
void AboutDialogLogo::repaint(wxPaintEvent& event)
{
auto dc { new wxPaintDC(this) };
dc->SetBackgroundMode(wxPENSTYLE_TRANSPARENT);
const auto size {this->GetSize()};
const auto logo_w {this->logo.GetWidth()};
const auto logo_h {this->logo.GetHeight()};
dc->DrawBitmap(this->logo, (size.GetWidth() - logo_w) / 2, (size.GetHeight() - logo_h) / 2, 1);
event.Skip();
}
}} // namespace Slic3r::GUI

46
src/GUI/AboutDialog.hpp Normal file
View File

@ -0,0 +1,46 @@
#ifndef ABOUTDIALOG_HPP
#define ABOUTDIALOG_HPP
#include <wx/dialog.h>
#include <wx/sizer.h>
#include <wx/settings.h>
#include <wx/colour.h>
#include <wx/html/htmlwin.h>
#include <wx/event.h>
#include <string>
#include "libslic3r.h"
#include "misc_ui.hpp"
#ifndef SLIC3R_BUILD_COMMIT
#define SLIC3R_BUILD_COMMIT (Unknown revision)
#endif
#define VER1_(x) #x
#define VER_(x) VER1_(x)
#define BUILD_COMMIT VER_(SLIC3R_BUILD_COMMIT)
namespace Slic3r { namespace GUI {
const wxString build_date {__DATE__};
const wxString git_version {BUILD_COMMIT};
class AboutDialogLogo : public wxPanel {
private:
wxBitmap logo;
public:
AboutDialogLogo(wxWindow* parent);
void repaint(wxPaintEvent& event);
};
class AboutDialog : public wxDialog {
public:
/// Build and show the About popup.
AboutDialog(wxWindow* parent);
};
}} // namespace Slic3r::GUI
#endif // ABOUTDIALOG_HPP

11
src/GUI/ColorScheme.hpp Normal file
View File

@ -0,0 +1,11 @@
#ifndef COLORSCHEME_HPP
#define COLORSCHEME_HPP
/// Wrapper file to make the color schemes available to using applications
/// without the circular include headaches.
#include "ColorScheme/ColorSchemeBase.hpp"
#include "ColorScheme/Default.hpp"
#include "ColorScheme/Solarized.hpp"
#endif

View File

@ -0,0 +1,47 @@
#ifndef COLOR_SLIC3R_HPP
#define COLOR_SLIC3R_HPP
namespace Slic3r { namespace GUI {
class ColorScheme {
public:
virtual const std::string name() const = 0;
virtual const bool SOLID_BACKGROUNDCOLOR() const = 0;
virtual const wxColour SELECTED_COLOR() const = 0;
virtual const wxColour HOVER_COLOR() const = 0;
virtual const wxColour TOP_COLOR() const = 0;
virtual const wxColour BOTTOM_COLOR() const = 0;
virtual const wxColour BACKGROUND_COLOR() const = 0;
virtual const wxColour GRID_COLOR() const = 0;
virtual const wxColour GROUND_COLOR() const = 0;
virtual const wxColour COLOR_CUTPLANE() const = 0;
virtual const wxColour COLOR_PARTS() const = 0;
virtual const wxColour COLOR_INFILL() const = 0;
virtual const wxColour COLOR_SUPPORT() const = 0;
virtual const wxColour COLOR_UNKNOWN() const = 0;
virtual const wxColour BED_COLOR() const = 0;
virtual const wxColour BED_GRID() const = 0;
virtual const wxColour BED_SELECTED() const = 0;
virtual const wxColour BED_OBJECTS() const = 0;
virtual const wxColour BED_INSTANCE() const = 0;
virtual const wxColour BED_DRAGGED() const = 0;
virtual const wxColour BED_CENTER() const = 0;
virtual const wxColour BED_SKIRT() const = 0;
virtual const wxColour BED_CLEARANCE() const = 0;
virtual const wxColour BACKGROUND255() const = 0;
virtual const wxColour TOOL_DARK() const = 0;
virtual const wxColour TOOL_SUPPORT() const = 0;
virtual const wxColour TOOL_INFILL() const = 0;
virtual const wxColour TOOL_STEPPERIM() const = 0;
virtual const wxColour TOOL_SHADE() const = 0;
virtual const wxColour TOOL_COLOR() const = 0;
virtual const wxColour SPLINE_L_PEN() const = 0;
virtual const wxColour SPLINE_O_PEN() const = 0;
virtual const wxColour SPLINE_I_PEN() const = 0;
virtual const wxColour SPLINE_R_PEN() const = 0;
virtual const wxColour BED_DARK() const = 0;
};
}} // namespace Slic3r::GUI
#endif

View File

@ -0,0 +1,47 @@
#ifndef COLOR_DEFAULT_HPP
#define COLOR_DEFAULT_HPP
namespace Slic3r { namespace GUI {
class DefaultColor : public ColorScheme {
public:
const std::string name() const { return "Default"; }
const bool SOLID_BACKGROUNDCOLOR() const { return false; };
const wxColour SELECTED_COLOR() const { return wxColour(0, 1, 0); };
const wxColour HOVER_COLOR() const { return wxColour(0.4, 0.9, 0); }; //<Hover over Model
const wxColour TOP_COLOR() const { return wxColour(10/255,98/255,144/255); }; //<TOP Backgroud color
const wxColour BOTTOM_COLOR() const { return wxColour(0,0,0); }; //<BOTTOM Backgroud color
const wxColour BACKGROUND_COLOR() const {return TOP_COLOR();} //< SOLID background color
const wxColour GRID_COLOR() const { return wxColour(0.2, 0.2, 0.2, 0.4); }; //<Grid color
const wxColour GROUND_COLOR() const { return wxColour(0.8, 0.6, 0.5, 0.4); }; //<Ground or Plate color
const wxColour COLOR_CUTPLANE() const { return wxColour(.8, .8, .8, 0.5); };
const wxColour COLOR_PARTS() const { return wxColour(1, 0.95, 0.2, 1); }; //<Perimeter color
const wxColour COLOR_INFILL() const { return wxColour(1, 0.45, 0.45, 1); };
const wxColour COLOR_SUPPORT() const { return wxColour(0.5, 1, 0.5, 1); };
const wxColour COLOR_UNKNOWN() const { return wxColour(0.5, 0.5, 1, 1); };
const wxColour BED_COLOR() const { return wxColour(255, 255, 255); };
const wxColour BED_GRID() const { return wxColour(230, 230, 230); };
const wxColour BED_SELECTED() const { return wxColour(255, 166, 128); };
const wxColour BED_OBJECTS() const { return wxColour(210, 210, 210); };
const wxColour BED_INSTANCE() const { return wxColour(255, 128, 128); };
const wxColour BED_DRAGGED() const { return wxColour(128, 128, 255); };
const wxColour BED_CENTER() const { return wxColour(200, 200, 200); };
const wxColour BED_SKIRT() const { return wxColour(150, 150, 150); };
const wxColour BED_CLEARANCE() const { return wxColour(0, 0, 200); };
const wxColour BACKGROUND255() const { return wxColour(255, 255, 255); };
const wxColour TOOL_DARK() const { return wxColour(0, 0, 0); };
const wxColour TOOL_SUPPORT() const { return wxColour(0, 0, 0); };
const wxColour TOOL_INFILL() const { return wxColour(0, 0, 0.7); };
const wxColour TOOL_STEPPERIM() const { return wxColour(0.7, 0, 0); };
const wxColour TOOL_SHADE() const { return wxColour(0.95, 0.95, 0.95); };
const wxColour TOOL_COLOR() const { return wxColour(0.9, 0.9, 0.9); };
const wxColour SPLINE_L_PEN() const { return wxColour(50, 50, 50); };
const wxColour SPLINE_O_PEN() const { return wxColour(200, 200, 200); };
const wxColour SPLINE_I_PEN() const { return wxColour(255, 0, 0); };
const wxColour SPLINE_R_PEN() const { return wxColour(5, 120, 160); };
const wxColour BED_DARK() const { return wxColour(0, 0, 0); };
};
}} // namespace Slic3r::GUI
#endif

View File

@ -0,0 +1,44 @@
#ifndef SOLARIZED_HPP
#define SOLARIZED_HPP
#include <wx/colour.h>
#include "ColorScheme/ColorSchemeBase.hpp"
namespace Slic3r { namespace GUI {
/// S O L A R I Z E
/// http://ethanschoonover.com/solarized
///
/// Implements a colorscheme lookup of wxColors
class Solarized : public ColorScheme {
public:
const wxColour SELECTED_COLOR() { return COLOR_MAGENTA; }
const wxColour HOVER_COLOR() { return COLOR_VIOLET; }
const wxColour SUPPORT_COLOR() { return COLOR_VIOLET; }
const wxColour BACKGROUND_COLOR() { return COLOR_BASE3; }
private:
const wxColour COLOR_BASE0 {wxColour(0.51373,0.58039,0.58824)};
const wxColour COLOR_BASE00 {wxColour(0.39608,0.48235,0.51373)};
const wxColour COLOR_BASE01 {wxColour(0.34510,0.43137,0.45882)};
const wxColour COLOR_BASE02 {wxColour(0.02745,0.21176,0.25882)};
const wxColour COLOR_BASE03 {wxColour(0.00000,0.16863,0.21176)};
const wxColour COLOR_BASE1 {wxColour(0.57647,0.63137,0.63137)};
const wxColour COLOR_BASE2 {wxColour(0.93333,0.90980,0.83529)};
const wxColour COLOR_BASE3 {wxColour(0.99216,0.96471,0.89020)};
const wxColour COLOR_BLUE {wxColour(0.14902,0.54510,0.82353)};
const wxColour COLOR_CYAN {wxColour(0.16471,0.63137,0.59608)};
const wxColour COLOR_GREEN {wxColour(0.52157,0.60000,0.00000)};
const wxColour COLOR_MAGENTA {wxColour(0.82745,0.21176,0.50980)};
const wxColour COLOR_ORANGE {wxColour(0.79608,0.29412,0.08627)};
const wxColour COLOR_RED {wxColour(0.86275,0.19608,0.18431)};
const wxColour COLOR_VIOLET {wxColour(0.42353,0.44314,0.76863)};
const wxColour COLOR_YELLOW {wxColour(0.70980,0.53725,0.00000)};
};
}} // namespace Slic3r::GUI
#endif // SOLARIZED_HPP

15
src/GUI/Controller.hpp Normal file
View File

@ -0,0 +1,15 @@
#ifndef CONTROLLER_UI_HPP
#define CONTROLLER_UI_HPP
namespace Slic3r { namespace GUI {
class Controller : public wxPanel {
public:
Controller(wxWindow* parent, const wxString& title) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, title)
{ }
};
}} // Namespace Slic3r::GUI
#endif // CONTROLLER_UI_HPP

187
src/GUI/GUI.cpp Normal file
View File

@ -0,0 +1,187 @@
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/display.h>
#include <wx/filefn.h>
#include <string>
#include "MainFrame.hpp"
#include "GUI.hpp"
#include "misc_ui.hpp"
#include "Preset.hpp"
// Logging mechanism
#include "Log.hpp"
namespace Slic3r { namespace GUI {
/// Primary initialization and point of entry into the GUI application.
/// Calls MainFrame and handles preset loading, etc.
bool App::OnInit()
{
this->SetAppName("Slic3r");
// TODO: Call a logging function with channel GUI, severity info
this->notifier = std::unique_ptr<Notifier>();
datadir = decode_path(wxStandardPaths::Get().GetUserDataDir());
wxString enc_datadir = encode_path(datadir);
const wxString& slic3r_ini {datadir + "/slic3r.ini"};
const wxString& print_ini {datadir + "/print"};
const wxString& printer_ini {datadir + "/printer"};
const wxString& material_ini {datadir + "/filament"};
// if we don't have a datadir or a slic3r.ini, prompt for wizard.
bool run_wizard = (wxDirExists(datadir) && wxFileExists(slic3r_ini));
/* Check to make sure if datadir exists */
for (auto& dir : std::vector<wxString> { enc_datadir, print_ini, printer_ini, material_ini }) {
if (wxDirExists(dir)) continue;
if (!wxMkdir(dir)) {
Slic3r::Log::fatal_error(LogChannel, (_("Slic3r was unable to create its data directory at ")+ dir).ToStdWstring());
}
}
// TODO: Call a logging function with channel GUI, severity info for datadir path
Slic3r::Log::info(LogChannel, (_("Data dir: ") + datadir).ToStdWstring());
// Load gui settings from slic3r.ini
if (wxFileExists(slic3r_ini)) {
/*
my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") };
if ($ini) {
$last_version = $ini->{_}{version};
$ini->{_}{$_} = $Settings->{_}{$_}
for grep !exists $ini->{_}{$_}, keys %{$Settings->{_}};
$Settings = $ini;
}
delete $Settings->{_}{mode}; # handle legacy
*/
}
this->gui_config->save_settings();
// Load presets
this->load_presets();
wxImage::AddHandler(new wxPNGHandler());
MainFrame *frame = new MainFrame( "Slic3r", wxDefaultPosition, wxDefaultSize, this->gui_config);
this->SetTopWindow(frame);
// Load init bundle
//
// Run the wizard if we don't have an initial config
/*
$self->check_version
if $self->have_version_check
&& ($Settings->{_}{version_check} // 1)
&& (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400);
*/
// run callback functions during idle on the main frame
/*
EVT_IDLE($frame, sub {
while (my $cb = shift @cb) {
$cb->();
}
});
*/
// Handle custom version check event
/*
EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub {
my ($self, $event) = @_;
my ($success, $response, $manual_check) = @{$event->GetData};
if ($success) {
if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) {
my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?",
'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal;
Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES;
} else {
Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check;
}
$Settings->{_}{last_version_check} = time();
$self->save_settings;
} else {
Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check;
}
});
*/
return true;
}
void App::save_window_pos(const wxTopLevelWindow* window, const wxString& name ) {
this->gui_config->window_pos[name] =
std::make_tuple<wxPoint, wxSize, bool>(
window->GetScreenPosition(),
window->GetSize(),
window->IsMaximized());
this->gui_config->save_settings();
}
void App::restore_window_pos(wxTopLevelWindow* window, const wxString& name ) {
try {
auto tmp = gui_config->window_pos[name];
const auto& size = std::get<1>(tmp);
const auto& pos = std::get<0>(tmp);
window->SetSize(size);
auto display = wxDisplay().GetClientArea();
if (((pos.x + size.x / 2) < display.GetRight()) && (pos.y + size.y/2 < display.GetBottom()))
window->Move(pos);
window->Maximize(std::get<2>(tmp));
}
catch (std::out_of_range e) {
// config was empty
}
}
void App::load_presets() {
/*
for my $group (qw(printer filament print)) {
my $presets = $self->{presets}{$group};
# keep external or dirty presets
@$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 -',
);
}
*/
}
}} // namespace Slic3r::GUI

44
src/GUI/GUI.hpp Normal file
View File

@ -0,0 +1,44 @@
#ifndef GUI_HPP
#define GUI_HPP
#include <wx/toplevel.h>
#include "MainFrame.hpp"
#include "Notifier.hpp"
#include <string>
#include <vector>
namespace Slic3r { namespace GUI {
// Friendly indices for the preset lists.
enum class PresetID {
PRINT = 0,
FILAMENT = 1,
PRINTER = 2
};
using preset_list = std::vector<std::string>;
class App: public wxApp
{
public:
virtual bool OnInit();
App(std::shared_ptr<Settings> config) : wxApp(), gui_config(config) {}
/// Save position, size, and maximize state for a TopLevelWindow (includes Frames) by name in Settings.
void save_window_pos(const wxTopLevelWindow* window, const wxString& name );
/// Move/resize a named TopLevelWindow (includes Frames) from Settings
void restore_window_pos(wxTopLevelWindow* window, const wxString& name );
private:
std::shared_ptr<Settings> gui_config; // GUI-specific configuration options
std::unique_ptr<Notifier> notifier {nullptr};
std::vector<preset_list> presets { preset_list(), preset_list(), preset_list() };
void load_presets();
wxString datadir {""};
const std::string LogChannel {"GUI"}; //< Which log these messages should go to.
};
}} // namespace Slic3r::GUI
#endif // GUI_HPP

178
src/GUI/MainFrame.cpp Normal file
View File

@ -0,0 +1,178 @@
#include "MainFrame.hpp"
#include "misc_ui.hpp"
#include <wx/accel.h>
#include <wx/utils.h>
#include "AboutDialog.hpp"
#include "libslic3r.h"
namespace Slic3r { namespace GUI {
wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
wxEND_EVENT_TABLE()
MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: MainFrame(title, pos, size, nullptr) {}
MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, std::shared_ptr<Settings> _gui_config)
: wxFrame(NULL, wxID_ANY, title, pos, size), loaded(false),
tabpanel(nullptr), controller(nullptr), plater(nullptr), gui_config(_gui_config), preset_editor_tabs(std::map<wxWindowID, PresetEditor*>())
{
// Set icon to either the .ico if windows or png for everything else.
if (the_os == OS::Windows)
this->SetIcon(wxIcon(var("Slic3r.ico"), wxBITMAP_TYPE_ICO));
else
this->SetIcon(wxIcon(var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
this->init_tabpanel();
this->init_menubar();
wxToolTip::SetAutoPop(TOOLTIP_TIMER);
// initialize status bar
this->statusbar = new ProgressStatusBar(this, -1);
wxString welcome_text {_("Version SLIC3R_VERSION_REPLACE - Remember to check for updates at http://slic3r.org/")};
welcome_text.Replace("SLIC3R_VERSION_REPLACE", wxString(SLIC3R_VERSION));
this->statusbar->SetStatusText(welcome_text);
this->SetStatusBar(this->statusbar);
this->loaded = 1;
// Initialize layout
{
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(this->tabpanel, 1, wxEXPAND);
sizer->SetSizeHints(this);
this->SetSizer(sizer);
this->Fit();
this->SetMinSize(wxSize(760, 490));
this->SetSize(this->GetMinSize());
wxTheApp->SetTopWindow(this);
gui_config->restore_window_pos(this, "main_frame");
this->Show();
this->Layout();
}
// Set up event handlers.
this->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& e) {
if (e.CanVeto()) {
if (!this->plater->prompt_unsaved_changes()) {
e.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
gui_config->save_window_pos(this, "main_frame");
// Propagate event
e.Skip();
}
});
}
/// Private initialization function for the main frame tab panel.
void MainFrame::init_tabpanel()
{
this->tabpanel = new wxAuiNotebook(this, -1, wxDefaultPosition, wxDefaultSize, wxAUI_NB_TOP);
auto panel = this->tabpanel;
panel->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, ([=](wxAuiNotebookEvent& e)
{
auto tabpanel = this->tabpanel;
// TODO: trigger processing for activation event
if (tabpanel->GetSelection() > 1) {
tabpanel->SetWindowStyle(tabpanel->GetWindowStyleFlag() | wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
} else if (this->gui_config->show_host == false && tabpanel->GetSelection() == 1) {
tabpanel->SetWindowStyle(tabpanel->GetWindowStyleFlag() | wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
} else {
tabpanel->SetWindowStyle(tabpanel->GetWindowStyleFlag() | ~wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
}
}), panel->GetId());
panel->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, ([=](wxAuiNotebookEvent& e)
{
if (typeid(panel) == typeid(Slic3r::GUI::PresetEditor)) {
wxDELETE(this->preset_editor_tabs[panel->GetId()]);
}
wxTheApp->CallAfter([=] { this->tabpanel->SetSelection(0); });
}), panel->GetId());
this->plater = new Slic3r::GUI::Plater(panel, _("Plater"), gui_config);
this->controller = new Slic3r::GUI::Controller(panel, _("Controller"));
panel->AddPage(this->plater, this->plater->GetName());
if (this->gui_config->show_host) panel->AddPage(this->controller, this->controller->GetName());
}
void MainFrame::init_menubar()
{
wxMenu* menuFile = new wxMenu();
{
append_menu_item(menuFile, _(L"Open STL/OBJ/AMF/3MF…"), _("Open a model"), [=](wxCommandEvent& e) { if (this->plater != nullptr) this->plater->add();}, wxID_ANY, "brick_add.png", "Ctrl+O");
}
wxMenu* menuPlater = new wxMenu();
{
}
wxMenu* menuObject = new wxMenu();
{
}
wxMenu* menuSettings = new wxMenu();
{
}
wxMenu* menuView = new wxMenu();
{
}
wxMenu* menuWindow = new wxMenu();
{
}
wxMenu* menuHelp = new wxMenu();
{
// TODO: Reimplement config wizard
//menuHelp->AppendSeparator();
append_menu_item(menuHelp, _("Slic3r &Website"), _("Open the Slic3r website in your browser"), [=](wxCommandEvent& e)
{
wxLaunchDefaultBrowser("http://www.slic3r.org");
});
append_menu_item(menuHelp, _("Check for &Updates..."), _("Check for new Slic3r versions"), [=](wxCommandEvent& e)
{
check_version(true);
});
append_menu_item(menuHelp, _("Slic3r &Manual"), _("Open the Slic3r manual in your browser"), [=](wxCommandEvent& e)
{
wxLaunchDefaultBrowser("http://manual.slic3r.org/");
});
append_menu_item(menuHelp, _("&About Slic3r"), _("Show about dialog"), [=](wxCommandEvent& e)
{
auto about = new AboutDialog(nullptr);
about->ShowModal();
about->Destroy();
}, wxID_ABOUT);
}
wxMenuBar* menubar = new wxMenuBar();
menubar->Append(menuFile, _("&File"));
menubar->Append(menuPlater, _("&Plater"));
menubar->Append(menuObject, _("&Object"));
menubar->Append(menuSettings, _("&Settings"));
menubar->Append(menuView, _("&View"));
menubar->Append(menuWindow, _("&Window"));
menubar->Append(menuHelp, _("&Help"));
this->SetMenuBar(menubar);
}
}} // Namespace Slic3r::GUI

56
src/GUI/MainFrame.hpp Normal file
View File

@ -0,0 +1,56 @@
#ifndef MAINFRAME_HPP
#define MAINFRAME_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/aui/auibook.h>
#include <wx/tooltip.h>
#include <memory>
#include <map>
#include "Controller.hpp"
#include "Plater.hpp"
#include "PresetEditor.hpp"
#include "Settings.hpp"
#include "GUI.hpp"
#include "ProgressStatusBar.hpp"
namespace Slic3r { namespace GUI {
class Plater;
constexpr unsigned int TOOLTIP_TIMER = 32767;
class MainFrame: public wxFrame
{
public:
MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, std::shared_ptr<Settings> _gui_config);
ProgressStatusBar* statusbar {new ProgressStatusBar(this, -1)};
private:
wxDECLARE_EVENT_TABLE();
void init_menubar(); //< Routine to intialize all top-level menu items.
void init_tabpanel(); //< Routine to initialize all of the tabs.
bool loaded; //< Main frame itself has finished loading.
// STUB: preset editor tabs storage
// STUB: Statusbar reference
wxAuiNotebook* tabpanel;
Controller* controller;
Plater* plater;
std::shared_ptr<Settings> gui_config;
std::map<wxWindowID, PresetEditor*> preset_editor_tabs;
};
}} // Namespace Slic3r::GUI
#endif // MAINFRAME_HPP

15
src/GUI/Notifier.hpp Normal file
View File

@ -0,0 +1,15 @@
#ifndef NOTIFIER_HPP
#define NOTIFIER_HPP
namespace Slic3r { namespace GUI {
/// Class to perform window manager notifications using Growl and/or DBus XWindow
class Notifier {
public:
Notifier() { }
};
}} // Namespace Slic3r::GUI
#endif // NOTIFIER_HPP

300
src/GUI/Plater.cpp Normal file
View File

@ -0,0 +1,300 @@
#include <memory>
#include <wx/progdlg.h>
#include <wx/window.h>
#include "Plater.hpp"
#include "ProgressStatusBar.hpp"
#include "Log.hpp"
#include "MainFrame.hpp"
namespace Slic3r { namespace GUI {
const auto PROGRESS_BAR_EVENT = wxNewEventType();
Plater::Plater(wxWindow* parent, const wxString& title, std::shared_ptr<Settings> _settings) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, title), settings(_settings)
{
// Set callback for status event for worker threads
/*
this->print->set_status_cb([=](std::string percent percent, std::wstring message) {
wxPostEvent(this, new wxPlThreadEvent(-1, PROGRESS_BAR_EVENT,
});
*/
auto on_select_object { [=](size_t& obj_idx) {
this->select_object(obj_idx);
} };
/*
# Initialize handlers for canvases
my $on_select_object = sub {
my ($obj_idx) = @_;
$self->select_object($obj_idx);
};
my $on_double_click = sub {
$self->object_settings_dialog if $self->selected_object;
};
my $on_right_click = sub {
my ($canvas, $click_pos) = @_;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $menu = $self->object_menu;
$canvas->PopupMenu($menu, $click_pos);
$menu->Destroy;
};
my $on_instances_moved = sub {
$self->on_model_change;
};
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
$self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click);
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
$self->{canvas3D}->set_on_instances_moved($on_instances_moved);
$self->{canvas3D}->on_viewport_changed(sub {
$self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
});
}
*/
canvas2D = new Plate2D(preview_notebook, wxDefaultSize, objects, model, config, settings);
preview_notebook->AddPage(canvas2D, _("2D"));
/*
# Initialize 2D preview canvas
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
$self->{preview_notebook}->AddPage($self->{canvas}, '2D');
$self->{canvas}->on_select_object($on_select_object);
$self->{canvas}->on_double_click($on_double_click);
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
$self->{canvas}->on_instances_moved($on_instances_moved);
# Initialize 3D toolpaths preview
$self->{preview3D_page_idx} = -1;
if ($Slic3r::GUI::have_OpenGL) {
$self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print});
$self->{preview3D}->canvas->on_viewport_changed(sub {
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
});
$self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview');
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
}
# Initialize toolpaths preview
$self->{toolpaths2D_page_idx} = -1;
if ($Slic3r::GUI::have_OpenGL) {
$self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print});
$self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Layers');
$self->{toolpaths2D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
}
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
wxTheApp->CallAfter(sub {
my $sel = $self->{preview_notebook}->GetSelection;
if ($sel == $self->{preview3D_page_idx} || $sel == $self->{toolpaths2D_page_idx}) {
if (!$Slic3r::GUI::Settings->{_}{background_processing} && !$self->{processed}) {
$self->statusbar->SetCancelCallback(sub {
$self->stop_background_process;
$self->statusbar->SetStatusText("Slicing cancelled");
$self->{preview_notebook}->SetSelection(0);
});
$self->start_background_process;
} else {
$self->{preview3D}->load_print
if $sel == $self->{preview3D_page_idx};
}
}
});
});
*/
}
void Plater::add() {
Log::info(LogChannel, L"Called Add function");
auto& start_object_id = this->object_identifier;
const auto& input_files{open_model(this, *(this->settings), wxTheApp->GetTopWindow())};
for (const auto& f : input_files) {
Log::info(LogChannel, (wxString(L"Calling Load File for ") + f).ToStdWstring());
this->load_file(f);
}
// abort if no objects actually added.
if (start_object_id == this->object_identifier) return;
// save the added objects
auto new_model {this->model};
// get newly added objects count
auto new_objects_count = this->object_identifier - start_object_id;
Slic3r::Log::info(LogChannel, (wxString("Obj id:") << object_identifier).ToStdWstring());
for (auto i = start_object_id; i < new_objects_count + start_object_id; i++) {
const auto& obj_idx {this->get_object_index(i)};
new_model->add_object(*(this->model->objects.at(obj_idx)));
}
Slic3r::Log::info(LogChannel, (wxString("Obj id:") << object_identifier).ToStdWstring());
// Prepare for undo
//this->add_undo_operation("ADD", nullptr, new_model, start_object_id);
}
std::vector<int> Plater::load_file(const wxString& file, const int obj_idx_to_load) {
auto input_file {wxFileName(file)};
settings->skein_directory = input_file.GetPath();
settings->save_settings();
Slic3r::Model model;
bool valid_load {true};
auto obj_idx {std::vector<int>()};
auto progress_dialog {new wxProgressDialog(_(L"Loading…"), _(L"Processing input file…"), 100, this, 0)};
progress_dialog->Pulse();
//TODO: Add a std::wstring so we can handle non-roman characters as file names.
try {
model = Slic3r::Model::read_from_file(file.ToStdString());
} catch (std::runtime_error& e) {
show_error(this, e.what());
Slic3r::Log::error(LogChannel, LOG_WSTRING(file << " failed to load: " << e.what()));
valid_load = false;
}
Slic3r::Log::info(LogChannel, LOG_WSTRING("load_valid is " << valid_load));
if (valid_load) {
if (model.looks_like_multipart_object()) {
auto dialog {new wxMessageDialog(this,
_("This file contains several objects positioned at multiple heights. Instead of considering them as multiple objects, should I consider\n them this file as a single object having multiple parts?\n"), _("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO)};
if (dialog->ShowModal() == wxID_YES) {
model.convert_multipart_object();
}
}
for (auto i = 0U; i < model.objects.size(); i++) {
auto object {model.objects[i]};
object->input_file = file.ToStdString();
for (auto j = 0U; j < object->volumes.size(); j++) {
auto volume {object->volumes.at(j)};
volume->input_file = file.ToStdString();
volume->input_file_obj_idx = i;
volume->input_file_vol_idx = j;
}
}
auto i {0U};
if (obj_idx_to_load > 0) {
Slic3r::Log::info(LogChannel, L"Loading model objects, obj_idx_to_load > 0");
const size_t idx_load = obj_idx_to_load;
if (idx_load >= model.objects.size()) return std::vector<int>();
obj_idx = this->load_model_objects(model.objects.at(idx_load));
i = idx_load;
} else {
Slic3r::Log::info(LogChannel, L"Loading model objects, obj_idx_to_load = 0");
obj_idx = this->load_model_objects(model.objects);
Slic3r::Log::info(LogChannel, LOG_WSTRING("obj_idx size: " << obj_idx.size()));
}
for (const auto &j : obj_idx) {
this->objects[j].input_file = file;
this->objects[j].input_file_obj_idx = i++;
}
GetFrame()->statusbar->SetStatusText(_("Loaded ") + input_file.GetName());
if (this->scaled_down) {
GetFrame()->statusbar->SetStatusText(_("Your object appears to be too large, so it was automatically scaled down to fit your print bed."));
}
if (this->outside_bounds) {
GetFrame()->statusbar->SetStatusText(_("Some of your object(s) appear to be outside the print bed. Use the arrange button to correct this."));
}
}
progress_dialog->Destroy();
this->redo = std::stack<UndoOperation>();
return obj_idx;
}
std::vector<int> Plater::load_model_objects(ModelObject* model_object) {
ModelObjectPtrs tmp {model_object}; // wrap in a std::vector
return load_model_objects(tmp);
}
std::vector<int> Plater::load_model_objects(ModelObjectPtrs model_objects) {
auto bed_center {this->bed_centerf()};
auto bed_shape {Slic3r::Polygon::new_scale(this->config->get<ConfigOptionPoints>("bed_shape").values)};
auto bed_size {bed_shape.bounding_box().size()};
bool need_arrange {false};
auto obj_idx {std::vector<int>()};
Slic3r::Log::info(LogChannel, LOG_WSTRING("Objects: " << model_objects.size()));
for (auto& obj : model_objects) {
auto o {this->model->add_object(*obj)};
o->repair();
auto tmpobj {PlaterObject()};
const auto objfile {wxFileName::FileName( obj->input_file )};
tmpobj.name = wxString(std::string() == obj->name ? obj->name : objfile.GetName());
tmpobj.identifier = (this->object_identifier)++;
this->objects.push_back(tmpobj);
obj_idx.push_back(this->objects.size());
Slic3r::Log::info(LogChannel, LOG_WSTRING("Object array new size: " << this->objects.size()));
Slic3r::Log::info(LogChannel, LOG_WSTRING("Instances: " << obj->instances.size()));
if (obj->instances.size() == 0) {
if (settings->autocenter) {
need_arrange = true;
o->center_around_origin();
o->add_instance();
o->instances.back()->offset = this->bed_centerf();
} else {
need_arrange = false;
if (settings->autoalignz) {
o->align_to_ground();
}
o->add_instance();
}
} else {
if (settings->autoalignz) {
o->align_to_ground();
}
}
{
// If the object is too large (more than 5x the bed) scale it down.
auto size {o->bounding_box().size()};
double ratio {0.0f};
if (ratio > 5) {
for (auto& instance : o->instances) {
instance->scaling_factor = (1.0f/ratio);
this->scaled_down = true;
}
}
}
{
// Provide a warning if downscaling by 5x still puts it over the bed size.
}
}
return obj_idx;
}
MainFrame* Plater::GetFrame() { return dynamic_cast<MainFrame*>(wxGetTopLevelParent(this)); }
int Plater::get_object_index(size_t object_id) {
for (size_t i = 0U; i < this->objects.size(); i++) {
if (this->objects.at(i).identifier == object_id) return static_cast<int>(i);
}
return -1;
}
}} // Namespace Slic3r::GUI

88
src/GUI/Plater.hpp Normal file
View File

@ -0,0 +1,88 @@
#ifndef PLATER_HPP
#define PLATER_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/notebook.h>
#include <stack>
#include "libslic3r.h"
#include "Model.hpp"
#include "Print.hpp"
#include "Config.hpp"
#include "Plater/PlaterObject.hpp"
#include "Plater/Plate2D.hpp"
#include "Settings.hpp"
#include "MainFrame.hpp"
namespace Slic3r { namespace GUI {
using UndoOperation = int;
using obj_index = unsigned int;
class PlaterObject;
class Plate2D;
class MainFrame;
class Plater : public wxPanel
{
public:
Plater(wxWindow* parent, const wxString& title, std::shared_ptr<Settings> _settings);
void add();
/// Ask if there are any unsaved changes.
bool prompt_unsaved_changes() { return true; }
private:
std::shared_ptr<Slic3r::Print> print {std::make_shared<Print>(Slic3r::Print())};
std::shared_ptr<Slic3r::Model> model {std::make_shared<Model>(Slic3r::Model())};
std::shared_ptr<Settings> settings {};
std::shared_ptr<Slic3r::Config> config { Slic3r::Config::new_from_defaults(
{"bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance",
"brim_width", "serial_port", "serial_speed", "host_type", "print_host", "octoprint_apikey",
"shortcuts", "filament_colour"})};
bool processed {false};
std::vector<PlaterObject> objects {}; //< Main object vector.
size_t object_identifier {0U}; //< Counter for adding objects to Slic3r
std::stack<UndoOperation> undo {};
std::stack<UndoOperation> redo {};
wxNotebook* preview_notebook {new wxNotebook(this, -1, wxDefaultPosition, wxSize(335,335), wxNB_BOTTOM)};
Plate2D* canvas2D {}; //< 2D plater canvas
/// Handles the actual load of the file from the dialog handoff.
std::vector<int> load_file(const wxString& file, const int obj_idx_to_load = -1);
const std::string LogChannel {"GUI_Plater"}; //< Which log these messages should go to.
std::vector<int> load_model_objects(ModelObject* model_object);
std::vector<int> load_model_objects(ModelObjectPtrs model_objects);
bool scaled_down {false};
bool outside_bounds {false};
MainFrame* GetFrame();
void select_object(size_t& obj_idx) { };
int get_object_index(size_t object_id);
Slic3r::Pointf bed_centerf() {
auto bed_shape { Slic3r::Polygon::new_scale(this->config->get<ConfigOptionPoints>("bed_shape").values) };
return Slic3r::Pointf();
}
};
} } // Namespace Slic3r::GUI
#endif // PLATER_HPP

261
src/GUI/Plater/Plate2D.cpp Normal file
View File

@ -0,0 +1,261 @@
#include "Plater/Plate2D.hpp"
// libslic3r includes
#include "Geometry.hpp"
#include "Point.hpp"
#include "Log.hpp"
#include "ClipperUtils.hpp"
// wx includes
#include <wx/colour.h>
#include <wx/dcbuffer.h>
namespace Slic3r { namespace GUI {
Plate2D::Plate2D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config, std::shared_ptr<Settings> _settings) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, size, wxTAB_TRAVERSAL), objects(_objects), model(_model), config(_config), settings(_settings)
{
this->Bind(wxEVT_PAINT, [=](wxPaintEvent &e) { this->repaint(e); });
this->Bind(wxEVT_MOTION, [=](wxMouseEvent &e) { this->mouse_drag(e); });
this->Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent &e) { this->mouse_down(e); });
this->Bind(wxEVT_LEFT_UP, [=](wxMouseEvent &e) { this->mouse_up(e); });
this->Bind(wxEVT_LEFT_DCLICK, [=](wxMouseEvent &e) { this->mouse_dclick(e); });
if (user_drawn_background) {
this->Bind(wxEVT_ERASE_BACKGROUND, [=](wxEraseEvent& e){ });
}
this->Bind(wxEVT_SIZE, [=](wxSizeEvent &e) { this->update_bed_size(); this->Refresh(); });
// Bind the varying mouse events
// Set the brushes
set_colors();
this->SetBackgroundStyle(wxBG_STYLE_PAINT);
}
void Plate2D::repaint(wxPaintEvent& e) {
// Need focus to catch keyboard events.
this->SetFocus();
auto dc {new wxAutoBufferedPaintDC(this)};
const auto& size {wxSize(this->GetSize().GetWidth(), this->GetSize().GetHeight())};
if (this->user_drawn_background) {
// On all systems the AutoBufferedPaintDC() achieves double buffering.
// On MacOS the background is erased, on Windows the background is not erased
// and on Linux/GTK the background is erased to gray color.
// Fill DC with the background on Windows & Linux/GTK.
const auto& brush_background {wxBrush(this->settings->color->BACKGROUND255(), wxBRUSHSTYLE_SOLID)};
const auto& pen_background {wxPen(this->settings->color->BACKGROUND255(), 1, wxPENSTYLE_SOLID)};
dc->SetPen(pen_background);
dc->SetBrush(brush_background);
const auto& rect {this->GetUpdateRegion().GetBox()};
dc->DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
}
// Draw bed
{
dc->SetPen(this->print_center_pen);
dc->SetBrush(this->bed_brush);
auto tmp {scaled_points_to_pixel(this->bed_polygon, true)};
dc->DrawPolygon(this->bed_polygon.points.size(), tmp.data(), 0, 0);
}
// draw print center
{
if (this->objects.size() > 0 && settings->autocenter) {
const auto center = this->unscaled_point_to_pixel(this->print_center);
dc->SetPen(print_center_pen);
dc->DrawLine(center.x, 0, center.x, size.y);
dc->DrawLine(0, center.y, size.x, center.y);
dc->SetTextForeground(wxColor(0,0,0));
dc->SetFont(wxFont(10, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
wxString val {};
val.Printf("X = %.0f", this->print_center.x);
dc->DrawLabel(val , wxRect(0,0, center.x*2, this->GetSize().GetHeight()), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM);
val.Printf("Y = %.0f", this->print_center.y);
dc->DrawRotatedText(val, 0, center.y + 15, 90);
}
}
// draw text if plate is empty
if (this->objects.size() == 0) {
dc->SetTextForeground(settings->color->BED_OBJECTS());
dc->SetFont(wxFont(14, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
dc->DrawLabel(CANVAS_TEXT, wxRect(0,0, this->GetSize().GetWidth(), this->GetSize().GetHeight()), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
} else {
// draw grid
dc->SetPen(grid_pen);
// Assumption: grid of lines is arranged
// as adjacent pairs of wxPoints
for (auto i = 0U; i < grid.size(); i+=2) {
dc->DrawLine(grid[i], grid[i+1]);
}
}
}
void Plate2D::mouse_drag(wxMouseEvent& e) {
const auto pos {e.GetPosition()};
const auto& point {this->point_to_model_units(e.GetPosition())};
if (e.Dragging()) {
Slic3r::Log::info(LogChannel, L"Mouse dragging");
} else {
auto cursor = wxSTANDARD_CURSOR;
/*
if (find_first_of(this->objects.begin(), this->objects.end(); [=](const PlaterObject& o) { return o.contour->contains_point(point);} ) == this->object.end()) {
cursor = wxCursor(wxCURSOR_HAND);
}
*/
this->SetCursor(*cursor);
}
}
void Plate2D::mouse_down(wxMouseEvent& e) {
}
void Plate2D::mouse_up(wxMouseEvent& e) {
}
void Plate2D::mouse_dclick(wxMouseEvent& e) {
}
void Plate2D::set_colors() {
this->SetBackgroundColour(settings->color->BACKGROUND255());
this->objects_brush.SetColour(settings->color->BED_OBJECTS());
this->objects_brush.SetStyle(wxBRUSHSTYLE_SOLID);
this->instance_brush.SetColour(settings->color->BED_INSTANCE());
this->instance_brush.SetStyle(wxBRUSHSTYLE_SOLID);
this->selected_brush.SetColour(settings->color->BED_SELECTED());
this->selected_brush.SetStyle(wxBRUSHSTYLE_SOLID);
this->dragged_brush.SetColour(settings->color->BED_DRAGGED());
this->dragged_brush.SetStyle(wxBRUSHSTYLE_SOLID);
this->bed_brush.SetColour(settings->color->BED_COLOR());
this->bed_brush.SetStyle(wxBRUSHSTYLE_SOLID);
this->transparent_brush.SetColour(wxColour(0,0,0));
this->transparent_brush.SetStyle(wxBRUSHSTYLE_TRANSPARENT);
this->grid_pen.SetColour(settings->color->BED_GRID());
this->grid_pen.SetWidth(1);
this->grid_pen.SetStyle(wxPENSTYLE_SOLID);
this->print_center_pen.SetColour(settings->color->BED_CENTER());
this->print_center_pen.SetWidth(1);
this->print_center_pen.SetStyle(wxPENSTYLE_SOLID);
this->clearance_pen.SetColour(settings->color->BED_CLEARANCE());
this->clearance_pen.SetWidth(1);
this->clearance_pen.SetStyle(wxPENSTYLE_SOLID);
this->skirt_pen.SetColour(settings->color->BED_SKIRT());
this->skirt_pen.SetWidth(1);
this->skirt_pen.SetStyle(wxPENSTYLE_SOLID);
this->dark_pen.SetColour(settings->color->BED_DARK());
this->dark_pen.SetWidth(1);
this->dark_pen.SetStyle(wxPENSTYLE_SOLID);
}
void Plate2D::nudge_key(wxKeyEvent& e) {
const auto key = e.GetKeyCode();
switch( key ) {
case WXK_LEFT:
this->nudge(MoveDirection::Left);
case WXK_RIGHT:
this->nudge(MoveDirection::Right);
case WXK_DOWN:
this->nudge(MoveDirection::Down);
case WXK_UP:
this->nudge(MoveDirection::Up);
default:
break; // do nothing
}
}
void Plate2D::nudge(MoveDirection dir) {
if (this->selected_instance < this->objects.size()) {
auto i = 0U;
for (auto& obj : this->objects) {
if (obj.selected) {
if (obj.selected_instance != -1) {
}
}
i++;
}
}
if (selected_instance >= this->objects.size()) {
Slic3r::Log::warn(LogChannel, L"Nudge failed because there is no selected instance.");
return; // Abort
}
}
void Plate2D::update_bed_size() {
const auto& canvas_size {this->GetSize()};
const auto& canvas_w {canvas_size.GetWidth()};
const auto& canvas_h {canvas_size.GetHeight()};
if (canvas_w == 0) return; // Abort early if we haven't drawn canvas yet.
this->bed_polygon = Slic3r::Polygon(scale(dynamic_cast<ConfigOptionPoints*>(config->optptr("bed_shape"))->values));
const auto& polygon = bed_polygon;
const auto& bb = bed_polygon.bounding_box();
const auto& size = bb.size();
this->scaling_factor = std::min(canvas_w / unscale(size.x), canvas_h / unscale(size.y));
this->bed_origin = wxPoint(
canvas_w / 2 - (unscale(bb.max.x + bb.min.x)/2 * this->scaling_factor),
canvas_h - (canvas_h / 2 - (unscale(bb.max.y + bb.min.y)/2 * this->scaling_factor))
);
const auto& center = bb.center();
this->print_center = wxPoint(unscale(center.x), unscale(center.y));
// Cache bed contours and grid
{
const auto& step { scale_(10) };
auto grid {Polylines()};
for (coord_t x = (bb.min.x - (bb.min.x % step) + step); x < bb.max.x; x += step) {
grid.push_back(Polyline());
grid.back().append(Point(x, bb.min.y));
grid.back().append(Point(x, bb.max.y));
};
for (coord_t y = (bb.min.y - (bb.min.y % step) + step); y < bb.max.y; y += step) {
grid.push_back(Polyline());
grid.back().append(Point(bb.min.x, y));
grid.back().append(Point(bb.max.x, y));
};
grid = intersection_pl(grid, polygon);
for (auto& i : grid) {
const auto& tmpline { this->scaled_points_to_pixel(i, 1) };
this->grid.insert(this->grid.end(), tmpline.begin(), tmpline.end());
}
}
}
std::vector<wxPoint> Plate2D::scaled_points_to_pixel(const Slic3r::Polygon& poly, bool unscale) {
return this->scaled_points_to_pixel(Polyline(poly), unscale);
}
std::vector<wxPoint> Plate2D::scaled_points_to_pixel(const Slic3r::Polyline& poly, bool unscale) {
std::vector<wxPoint> result;
for (const auto& pt : poly.points) {
const auto tmp {wxPoint(pt.x, pt.y)};
result.push_back( (unscale ? unscaled_point_to_pixel(tmp) : tmp) );
}
return result;
}
} } // Namespace Slic3r::GUI

125
src/GUI/Plater/Plate2D.hpp Normal file
View File

@ -0,0 +1,125 @@
#ifndef PLATE2D_HPP
#define PLATE2D_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/datetime.h>
#include <vector>
#include <functional>
#include "Plater.hpp"
#include "ColorScheme.hpp"
#include "Settings.hpp"
#include "Plater/PlaterObject.hpp"
#include "misc_ui.hpp"
#include "Log.hpp"
namespace Slic3r { namespace GUI {
// Setup for an Easter Egg with the canvas text.
const wxDateTime today_date {wxDateTime().GetDateOnly()};
const wxDateTime special_date {13, wxDateTime::Month::Sep, 2006, 0, 0, 0, 0};
const bool today_is_special = {today_date.GetDay() == special_date.GetDay() && today_date.GetMonth() == special_date.GetMonth()};
enum class MoveDirection {
Up, Down, Left, Right
};
class Plate2D : public wxPanel {
public:
Plate2D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config, std::shared_ptr<Settings> _settings);
// std::function<> on_select_object {};
private:
std::vector<PlaterObject>& objects; //< reference to parent vector
std::shared_ptr<Slic3r::Model> model;
std::shared_ptr<Slic3r::Config> config;
std::shared_ptr<Settings> settings;
// Different brushes to draw with, initialized from settings->Color during the constructor
wxBrush objects_brush {};
wxBrush instance_brush {};
wxBrush selected_brush {};
wxBrush bed_brush {};
wxBrush dragged_brush {};
wxBrush transparent_brush {};
wxPen grid_pen {};
wxPen print_center_pen {};
wxPen clearance_pen {};
wxPen skirt_pen {};
wxPen dark_pen {};
bool user_drawn_background {(the_os == OS::Mac ? false : true)};
size_t selected_instance;
/// Handle mouse-move events
void mouse_drag(wxMouseEvent& e);
void mouse_down(wxMouseEvent& e);
void mouse_up(wxMouseEvent& e);
void mouse_dclick(wxMouseEvent& e);
/// Handle repaint events
void repaint(wxPaintEvent& e);
void nudge_key(wxKeyEvent& e);
void nudge(MoveDirection dir);
/// Set/Update all of the colors used by the various brushes in the panel.
void set_colors();
/// Convert a scale point array to a pixel polygon suitable for DrawPolygon
std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polygon& poly, bool unscale);
std::vector<wxPoint> scaled_points_to_pixel(const Slic3r::Polyline& poly, bool unscale);
/// For a specific point, unscale it relative to the origin
wxPoint unscaled_point_to_pixel(const wxPoint& in) {
const auto& canvas_height {this->GetSize().GetHeight()};
const auto& zero = this->bed_origin;
return wxPoint(in.x * this->scaling_factor + zero.x,
in.y * this->scaling_factor + (zero.y - canvas_height));
}
/// Read print bed size from config and calculate the scaled rendition of the bed given the draw canvas.
void update_bed_size();
/// private class variables to stash bits for drawing the print bed area.
wxPoint bed_origin {};
wxPoint print_center {};
Slic3r::Polygon bed_polygon {};
std::vector<wxPoint> grid {};
/// Set up the 2D canvas blank canvas text.
/// Easter egg: Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
const wxString CANVAS_TEXT { today_is_special ? _(L"What do you want to print today?™") : _("Drag your objects here") };
/// How much to scale the points to fit in the draw bounding box area.
/// Expressed as pixel / mm
double scaling_factor {1.0};
const std::string LogChannel {"GUI_2D"};
Slic3r::Point point_to_model_units(coordf_t x, coordf_t y) {
const auto& zero {this->bed_origin};
return Slic3r::Point(
scale_(x - zero.x) / this->scaling_factor,
scale_(y - zero.y) / this->scaling_factor
);
}
Slic3r::Point point_to_model_units(const wxPoint& pt) {
return this->point_to_model_units(pt.x, pt.y);
}
Slic3r::Point point_to_model_units(const Pointf& pt) {
return this->point_to_model_units(pt.x, pt.y);
}
};
} } // Namespace Slic3r::GUI
#endif // PLATE2D_HPP

View File

@ -0,0 +1,51 @@
#include "Plater/PlaterObject.hpp"
#include "Geometry.hpp"
#include "ExPolygon.hpp"
#include "libslic3r.h"
namespace Slic3r { namespace GUI {
Slic3r::ExPolygonCollection& PlaterObject::make_thumbnail(const Slic3r::Model& model, int obj_idx) {
// make method idempotent
this->thumbnail.expolygons.clear();
auto mesh {model.objects[obj_idx]->raw_mesh()};
auto model_instance {model.objects[obj_idx]->instances[0]};
// Apply any x/y rotations and scaling vector if this came from a 3MF object.
mesh.rotate_x(model_instance->x_rotation);
mesh.rotate_y(model_instance->y_rotation);
mesh.scale(model_instance->scaling_vector);
if (mesh.facets_count() <= 5000) {
auto area_threshold {scale_(1.0)};
ExPolygons tmp {};
std::copy_if(tmp.end(), mesh.horizontal_projection().begin(), mesh.horizontal_projection().end(), [=](const ExPolygon& p) { return p.area() >= area_threshold; } ); // return all polys bigger than the area
this->thumbnail.append(tmp);
this->thumbnail.simplify(0.5);
} else {
auto convex_hull {Slic3r::ExPolygon(mesh.convex_hull())};
this->thumbnail.append(convex_hull);
}
return this->thumbnail;
}
Slic3r::ExPolygonCollection& PlaterObject::transform_thumbnail(const Slic3r::Model& model, int obj_idx) {
if (this->thumbnail.expolygons.size() == 0) return this->thumbnail;
const auto& model_object {model.objects[obj_idx] };
const auto& model_instance {model_object->instances[0]};
// the order of these transformations MUST be the same everywhere, including
// in Slic3r::Print->add_model_object()
auto t {this->thumbnail};
t.rotate(model_instance->rotation, Slic3r::Point(0,0));
t.scale(model_instance->scaling_factor);
this->transformed_thumbnail = t;
return this->transformed_thumbnail;
}
} } // Namespace Slic3r::GUI

View File

@ -0,0 +1,35 @@
#ifndef PLATEROBJECT_HPP
#define PLATEROBJECT_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "ExPolygonCollection.hpp"
#include "Model.hpp"
namespace Slic3r { namespace GUI {
class PlaterObject {
public:
wxString name {L""};
size_t identifier {0U};
wxString input_file {L""};
int input_file_obj_idx {-1};
Slic3r::ExPolygonCollection thumbnail;
Slic3r::ExPolygonCollection transformed_thumbnail;
// read only
std::vector<Slic3r::ExPolygonCollection> instance_thumbnails;
bool selected {false};
int selected_instance {-1};
Slic3r::ExPolygonCollection& make_thumbnail(const Slic3r::Model& model, int obj_idx);
Slic3r::ExPolygonCollection& transform_thumbnail(const Slic3r::Model& model, int obj_idx);
};
}} // Namespace Slic3r::GUI
#endif

17
src/GUI/Preset.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef PRESET_HPP
#define PRESET_HPP
#include "PrintConfig.hpp"
namespace Slic3r { namespace GUI {
class Preset {
private:
Slic3r::DynamicPrintConfig config { Slic3r::DynamicPrintConfig() };
};
}} // namespace Slic3r::GUI
#endif // PRESET_HPP

11
src/GUI/PresetEditor.hpp Normal file
View File

@ -0,0 +1,11 @@
#ifndef PRESETEDITOR_HPP
#define PRESETEDITOR_HPP
namespace Slic3r { namespace GUI {
class PresetEditor : public wxPanel {
};
}} // namespace Slic3r::GUI
#endif // PRESETEDITOR_HPP

View File

@ -0,0 +1,74 @@
#include "ProgressStatusBar.hpp"
#include "misc_ui.hpp"
namespace Slic3r { namespace GUI {
ProgressStatusBar::ProgressStatusBar(wxWindow* parent, int id) : wxStatusBar(parent, id) {
this->prog->Hide();
this->cancelbutton->Hide();
this->SetFieldsCount(3);
const int tmpWidths[] {-1, 150, 155}; // need to make the array ahead of time in C++
this->SetStatusWidths(3, tmpWidths);
// Assign events.
this->Bind(wxEVT_TIMER, [=](wxTimerEvent& e){this->OnTimer(e);});
this->Bind(wxEVT_SIZE, [=](wxSizeEvent& e){this->OnSize(e);});
this->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { this->cancel_cb(); this->cancelbutton->Hide();});
}
ProgressStatusBar::~ProgressStatusBar() {
if (this->timer != nullptr) {
if (this->timer->IsRunning())
this->timer->Stop();
}
}
/// wxPerl version of this used a impromptu hashmap and a loop
/// which more impractical here.
/// Opportunity to refactor here.
void ProgressStatusBar::OnSize(wxSizeEvent &e) {
// position 0 is reserved for status text
// position 1 is cancel button
// position 2 is prog
{
wxRect rect;
this->GetFieldRect(1, rect);
const auto& offset = ( wxGTK ? 1 : 0); // cosmetic 1px offset on wxgtk
const auto& pos {wxPoint(rect.x + offset, rect.y + offset)};
this->cancelbutton->Move(pos);
this->cancelbutton->SetSize(rect.GetWidth() - offset, rect.GetHeight());
}
{
wxRect rect;
this->GetFieldRect(2, rect);
const auto& offset = ( wxGTK ? 1 : 0); // cosmetic 1px offset on wxgtk
const auto& pos {wxPoint(rect.x + offset, rect.y + offset)};
this->prog->Move(pos);
this->prog->SetSize(rect.GetWidth() - offset, rect.GetHeight());
}
e.Skip();
}
void ProgressStatusBar::SetProgress(size_t val) {
if (!this->prog->IsShown()) {
this->ShowProgress(true);
}
if (val == this->prog->GetRange()) {
this->prog->SetValue(0);
this->ShowProgress(false);
} else {
this->prog->SetValue(val);
}
}
void ProgressStatusBar::OnTimer(wxTimerEvent& e) {
if (this->prog->IsShown())
this->timer->Stop();
if (this->busy)
this->prog->Pulse();
}
}} // Namespace Slic3r::GUI

View File

@ -0,0 +1,67 @@
#ifndef PROGRESSSTATUSBAR_HPP
#define PROGRESSSTATUSBAR_HPP
#include <wx/event.h>
#include <wx/statusbr.h>
#include <wx/timer.h>
#include <wx/gauge.h>
#include <wx/button.h>
#include <functional>
namespace Slic3r { namespace GUI {
class ProgressStatusBar : public wxStatusBar {
public:
/// Constructor stub from parent
ProgressStatusBar(wxWindow* parent, int id);
/// Stop any running timers before destruction.
~ProgressStatusBar();
///
wxTimer* timer {new wxTimer(this)};
/// Progress bar
wxGauge* prog {new wxGauge(this, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize)};
/// General cancel button. Using applications can assign functions to it.
wxButton* cancelbutton {new wxButton(this, -1, _("Cancel"), wxDefaultPosition, wxDefaultSize)};
/// Set callback function for cancel button press.
void SetCancelCallback(std::function<void()> cb) {
this->cancel_cb = cb;
cb == nullptr ? this->cancelbutton->Hide() : this->cancelbutton->Show();
}
/// Show the progress bar.
void ShowProgress(bool show = true) { this->prog->Show(show); this->prog->Pulse(); }
/// Accessor function for the current value of the progress bar
inline size_t GetProgress() {return this->prog->GetValue();}
/// Accessor set function for the current value of the progress bar
void SetProgress(size_t val);
void SetRange(int range) { if (range != this->prog->GetRange() ) this->prog->SetRange(range);}
/// Start the timer.
void Run(int rate = 100) { if (this->timer->IsRunning()) this->timer->Start(rate);};
void StartBusy(int rate = 100) { this->busy = true; this->ShowProgress(true); if (!this->timer->IsRunning()) this->timer->Start(rate); }
void StopBusy() { this->timer->Stop(); this->ShowProgress(false); this->prog->SetValue(0); this->busy = false;}
/// Accessor function for busy state
bool IsBusy() {return this->busy;}
private:
void OnSize(wxSizeEvent& e);
void OnTimer(wxTimerEvent& e);
// Cancel callback function
std::function<void()> cancel_cb {[](){;}};
bool busy {false};
};
}} // Namespace Slic3r::GUI
#endif // PROGRESSSTATUSBAR_HPP

21
src/GUI/Settings.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "Settings.hpp"
namespace Slic3r { namespace GUI {
void Settings::save_settings() {
/*
sub save_settings {
my ($self) = @_;
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
}
*/
}
void Settings::save_window_pos(wxWindow* ref, wxString name) {
}
void Settings::restore_window_pos(wxWindow* ref, wxString name) {
}
}} // namespace Slic3r::GUI

67
src/GUI/Settings.hpp Normal file
View File

@ -0,0 +1,67 @@
#ifndef SETTINGS_HPP
#define SETTINGS_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <map>
#include <tuple>
#include "libslic3r.h"
#include "ColorScheme.hpp"
namespace Slic3r { namespace GUI {
enum class PathColor {
role
};
enum class ReloadBehavior {
all, copy, discard
};
/// Stub class to hold onto GUI-specific settings options.
/// TODO: Incorporate the system from libslic3r
class Settings {
public:
bool show_host {false};
bool version_check {true};
bool autocenter {true};
bool autoalignz {true};
bool invert_zoom {false};
bool background_processing {false};
bool preset_editor_tabs {false};
bool hide_reload_dialog {false};
ReloadBehavior reload {ReloadBehavior::all};
std::unique_ptr<ColorScheme> color {new Slic3r::GUI::DefaultColor};
PathColor color_toolpaths_by {PathColor::role};
float nudge {1.0}; //< 2D plater nudge amount in mm
unsigned int threads {1}; //< Number of threads to use when slicing
const wxString version { wxString(SLIC3R_VERSION) };
wxString skein_directory {}; //< Recently-opened skien directory.
void save_settings();
void load_settings();
/// Storage for window positions
std::map<wxString, std::tuple<wxPoint, wxSize, bool> > window_pos { std::map<wxString, std::tuple<wxPoint, wxSize, bool> >() };
void save_window_pos(wxWindow* ref, wxString name);
void restore_window_pos(wxWindow* ref, wxString name);
private:
const std::string LogChannel {"GUI_Settings"}; //< Which log these messages should go to.
};
}} //namespace Slic3r::GUI
#endif // SETTINGS_HPP

133
src/GUI/misc_ui.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "misc_ui.hpp"
#include <wx/stdpaths.h>
#include <wx/msgdlg.h>
#include <wx/arrstr.h>
#include <exception>
#include <stdexcept>
namespace Slic3r { namespace GUI {
#ifdef SLIC3R_DEV
void check_version(bool manual) {
}
#else
void check_version(bool manual) {
}
#endif
const wxString var(const wxString& in) {
// TODO replace center string with path to VAR in actual distribution later
if (VAR_ABS) {
return wxString(VAR_ABS_PATH) + "/" + in;
} else {
return bin() + wxString(VAR_REL) + "/" + in;
}
}
const wxString bin() {
wxFileName f(wxStandardPaths::Get().GetExecutablePath());
wxString appPath(f.GetPath());
return appPath;
}
/// Returns the path to Slic3r's default user data directory.
const wxString home(const wxString& in) {
if (the_os == OS::Windows)
return wxGetHomeDir() + "/" + in + "/";
return wxGetHomeDir() + "/." + in + "/";
}
wxString decode_path(const wxString& in) {
// TODO Stub
return in;
}
wxString encode_path(const wxString& in) {
// TODO Stub
return in;
}
void show_error(wxWindow* parent, const wxString& message) {
wxMessageDialog(parent, message, _("Error"), wxOK | wxICON_ERROR).ShowModal();
}
void show_info(wxWindow* parent, const wxString& message, const wxString& title = _("Notice")) {
wxMessageDialog(parent, message, title, wxOK | wxICON_INFORMATION).ShowModal();
}
void fatal_error(wxWindow* parent, const wxString& message) {
show_error(parent, message);
throw std::runtime_error(message.ToStdString());
}
/*
sub append_submenu {
my ($self, $menu, $string, $description, $submenu, $id, $icon) = @_;
$id //= &Wx::NewId();
my $item = Wx::MenuItem->new($menu, $id, $string, $description // '');
$self->set_menu_item_icon($item, $icon);
$item->SetSubMenu($submenu);
$menu->Append($item);
return $item;
}
*/
/*
sub scan_serial_ports {
my ($self) = @_;
my @ports = ();
if ($^O eq 'MSWin32') {
# Windows
if (eval "use Win32::TieRegistry; 1") {
my $ts = Win32::TieRegistry->new("HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM",
{ Access => 'KEY_READ' });
if ($ts) {
# when no serial ports are available, the registry key doesn't exist and
# TieRegistry->new returns undef
$ts->Tie(\my %reg);
push @ports, sort values %reg;
}
}
} else {
# UNIX and OS X
push @ports, glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*';
}
return grep !/Bluetooth|FireFly/, @ports;
}
*/
/*
sub show_error {
my ($parent, $message) = @_;
Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal;
}
*/
std::vector<wxString> open_model(wxWindow* parent, const Settings& settings, wxWindow* top) {
auto dialog {new wxFileDialog((parent != nullptr ? parent : top), _("Choose one or more files") + wxString(" (STL/OBJ/AMF/3MF):"), ".", "",
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST)};
if (dialog->ShowModal() != wxID_OK) {
dialog->Destroy();
return std::vector<wxString>();
}
std::vector<wxString> tmp;
wxArrayString tmpout;
dialog->GetPaths(tmpout);
for (const auto& i : tmpout) {
tmp.push_back(i);
}
dialog->Destroy();
return tmp;
}
}} // namespace Slic3r::GUI

136
src/GUI/misc_ui.hpp Normal file
View File

@ -0,0 +1,136 @@
#ifndef MISC_UI_HPP
#define MISC_UI_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <map>
#include <utility>
#include "Settings.hpp"
#include "Log.hpp"
/// Macro to build std::wstring that slic3r::log expects using << syntax of wxString
/// Avoids wx pollution of libslic3r
#define LOG_WSTRING(...) ((wxString("") << __VA_ARGS__).ToStdWstring())
/// Common static (that is, free-standing) functions, not part of an object hierarchy.
namespace Slic3r { namespace GUI {
enum class OS { Linux, Mac, Windows } ;
// constants to reduce the proliferation of macros in the rest of the code
#ifdef __WIN32
constexpr OS the_os = OS::Windows;
#elif __APPLE__
constexpr OS the_os = OS::Mac;
#elif __linux__
constexpr OS the_os = OS::Linux;
#ifdef __WXGTK__
constexpr bool wxGTK {true};
#else
constexpr bool wxGTK {false};
#endif
#endif
#ifdef SLIC3R_DEV
constexpr bool isDev = true;
#else
constexpr bool isDev = false;
#endif
// hopefully the compiler is smart enough to figure this out
const std::map<const std::string, const std::string> FILE_WILDCARDS {
std::make_pair("known", "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf)|*.3mf;*.3MF;*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML"),
std::make_pair("stl", "STL files (*.stl)|*.stl;*.STL"),
std::make_pair("obj", "OBJ files (*.obj)|*.obj;*.OBJ"),
std::make_pair("amf", "AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML"),
std::make_pair("tmf", "3MF files (*.3mf)|*.3mf;*.3MF"),
std::make_pair("ini", "INI files *.ini|*.ini;*.INI"),
std::make_pair("gcode", "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC"),
std::make_pair("svg", "SVG files *.svg|*.svg;*.SVG")
};
const std::string MODEL_WILDCARD { FILE_WILDCARDS.at("known") + std::string("|") + FILE_WILDCARDS.at("stl")+ std::string("|") + FILE_WILDCARDS.at("obj") + std::string("|") + FILE_WILDCARDS.at("amf")+ std::string("|") + FILE_WILDCARDS.at("tmf")};
const std::string STL_MODEL_WILDCARD { FILE_WILDCARDS.at("stl") };
const std::string AMF_MODEL_WILDCARD { FILE_WILDCARDS.at("amf") };
const std::string TMF_MODEL_WILDCARD { FILE_WILDCARDS.at("tmf") };
/// Mostly useful for Linux distro maintainers, this will change where Slic3r assumes
/// its ./var directory lives (where its art assets are).
/// Define VAR_ABS and VAR_ABS_PATH
#ifndef VAR_ABS
#define VAR_ABS false
#else
#define VAR_ABS true
#endif
#ifndef VAR_ABS_PATH
#define VAR_ABS_PATH "/usr/share/Slic3r/var"
#endif
#ifndef VAR_REL // Redefine on compile
#define VAR_REL L"/../var"
#endif
/// Performs a check via the Internet for a new version of Slic3r.
/// If this version of Slic3r was compiled with -DSLIC3R_DEV, check the development
/// space instead of release.
void check_version(bool manual = false);
/// Provides a path to Slic3r's var dir.
const wxString var(const wxString& in);
/// Provide a path to where Slic3r exec'd from.
const wxString bin();
/// Always returns path to home directory.
const wxString home(const wxString& in = "Slic3r");
/// Shows an error messagebox
void show_error(wxWindow* parent, const wxString& message);
/// Shows an info messagebox.
void show_info(wxWindow* parent, const wxString& message, const wxString& title);
/// Show an error messagebox and then throw an exception.
void fatal_error(wxWindow* parent, const wxString& message);
template <typename T>
void append_menu_item(wxMenu* menu, const wxString& name,const wxString& help, T lambda, int id = wxID_ANY, const wxString& icon = "", const wxString& accel = "") {
wxMenuItem* tmp = menu->Append(wxID_ANY, name, help);
wxAcceleratorEntry* a = new wxAcceleratorEntry();
if (!accel.IsEmpty()) {
a->FromString(accel);
tmp->SetAccel(a); // set the accelerator if and only if the accelerator is fine
}
tmp->SetHelp(help);
if (!icon.IsEmpty())
tmp->SetBitmap(wxBitmap(var(icon)));
if (typeid(lambda) != typeid(nullptr))
menu->Bind(wxEVT_MENU, lambda, tmp->GetId(), tmp->GetId());
}
/*
sub CallAfter {
my ($self, $cb) = @_;
push @cb, $cb;
}
*/
wxString decode_path(const wxString& in);
wxString encode_path(const wxString& in);
std::vector<wxString> open_model(wxWindow* parent, const Settings& settings, wxWindow* top);
}} // namespace Slic3r::GUI
#endif // MISC_UI_HPP

View File

@ -1,4 +1,4 @@
#include "Config.hpp"
#include "ConfigBase.hpp"
#include "Geometry.hpp"
#include "IO.hpp"
#include "Model.hpp"
@ -13,6 +13,7 @@
#include <boost/filesystem.hpp>
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
#include "GUI/GUI.hpp"
using namespace Slic3r;
@ -39,6 +40,15 @@ main(int argc, char **argv)
cli_config.apply(config, true);
DynamicPrintConfig print_config;
std::shared_ptr<GUI::Settings> gui_config = std::make_shared<GUI::Settings>();
GUI::App *gui = new GUI::App(gui_config);
gui_config->show_host = true;
GUI::App::SetInstance(gui);
wxEntry(argc, argv);
// load config files supplied via --load
for (const std::string &file : cli_config.load.values) {

13050
src/test/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

BIN
var/gcode.icns Executable file

Binary file not shown.

BIN
var/gcode.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
var/slt.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
var/stl.icns Executable file

Binary file not shown.

View File

@ -1,709 +1,31 @@
#include "Config.hpp"
#include <assert.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <exception> // std::runtime_error
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/config.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <string.h>
#include "Log.hpp"
namespace Slic3r {
std::string escape_string_cstyle(const std::string &str)
{
// Allocate a buffer twice the input string length,
// so the output will fit even if all input characters get escaped.
std::vector<char> out(str.size() * 2, 0);
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\n' || c == '\r') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else if (c == '\\'){
(*outptr ++) = '\\';
(*outptr ++) = '\\';
} else
(*outptr ++) = c;
}
return std::string(out.data(), outptr - out.data());
std::shared_ptr<Config>
Config::new_from_defaults()
{
return std::make_shared<Config>();
}
std::string escape_strings_cstyle(const std::vector<std::string> &strs)
std::shared_ptr<Config>
Config::new_from_defaults(std::initializer_list<std::string> init)
{
// 1) Estimate the output buffer size to avoid buffer reallocation.
size_t outbuflen = 0;
for (size_t i = 0; i < strs.size(); ++ i)
// Reserve space for every character escaped + quotes + semicolon.
outbuflen += strs[i].size() * 2 + 3;
// 2) Fill in the buffer.
std::vector<char> out(outbuflen, 0);
char *outptr = out.data();
for (size_t j = 0; j < strs.size(); ++ j) {
if (j > 0)
// Separate the strings.
(*outptr ++) = ';';
const std::string &str = strs[j];
// Is the string simple or complex? Complex string contains spaces, tabs, new lines and other
// escapable characters. Empty string shall be quoted as well, if it is the only string in strs.
bool should_quote = strs.size() == 1 && str.empty();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
should_quote = true;
break;
}
}
if (should_quote) {
(*outptr ++) = '"';
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\\' || c == '"') {
(*outptr ++) = '\\';
(*outptr ++) = c;
} else if (c == '\n' || c == '\r') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else
(*outptr ++) = c;
}
(*outptr ++) = '"';
} else {
memcpy(outptr, str.data(), str.size());
outptr += str.size();
auto my_config(std::make_shared<Config>());
for (auto& opt_key : init) {
if (print_config_def.has(opt_key)) {
const std::string value { print_config_def.get(opt_key)->default_value->serialize() };
my_config->set_deserialize(opt_key, value);
}
}
return std::string(out.data(), outptr - out.data());
return my_config;
}
bool unescape_string_cstyle(const std::string &str, std::string &str_out)
std::shared_ptr<Config>
new_from_cli(const int& argc, const char* argv[])
{
std::vector<char> out(str.size(), 0);
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\\') {
if (++ i == str.size())
return false;
c = str[i];
if (c == 'n')
(*outptr ++) = '\n';
} else
(*outptr ++) = c;
}
str_out.assign(out.data(), outptr - out.data());
return true;
return std::make_shared<Config>();
}
bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out)
{
if (str.empty())
return true;
size_t i = 0;
for (;;) {
// Skip white spaces.
char c = str[i];
while (c == ' ' || c == '\t') {
if (++ i == str.size())
return true;
c = str[i];
}
// Start of a word.
std::vector<char> buf;
buf.reserve(16);
// Is it enclosed in quotes?
c = str[i];
if (c == '"') {
// Complex case, string is enclosed in quotes.
for (++ i; i < str.size(); ++ i) {
c = str[i];
if (c == '"') {
// End of string.
break;
}
if (c == '\\') {
if (++ i == str.size())
return false;
c = str[i];
if (c == 'n')
c = '\n';
}
buf.push_back(c);
}
if (i == str.size())
return false;
++ i;
} else {
for (; i < str.size(); ++ i) {
c = str[i];
if (c == ';')
break;
buf.push_back(c);
}
}
// Store the string into the output vector.
out.push_back(std::string(buf.data(), buf.size()));
if (i == str.size())
return true;
// Skip white spaces.
c = str[i];
while (c == ' ' || c == '\t') {
if (++ i == str.size())
// End of string. This is correct.
return true;
c = str[i];
}
if (c != ';')
return false;
if (++ i == str.size()) {
// Emit one additional empty string.
out.push_back(std::string());
return true;
}
}
}
bool
operator== (const ConfigOption &a, const ConfigOption &b)
{
return a.serialize().compare(b.serialize()) == 0;
}
bool
operator!= (const ConfigOption &a, const ConfigOption &b)
{
return !(a == b);
}
ConfigOptionDef::ConfigOptionDef(const ConfigOptionDef &other)
: type(other.type), default_value(NULL),
gui_type(other.gui_type), gui_flags(other.gui_flags), label(other.label),
full_label(other.full_label), category(other.category), tooltip(other.tooltip),
sidetext(other.sidetext), cli(other.cli), ratio_over(other.ratio_over),
multiline(other.multiline), full_width(other.full_width), readonly(other.readonly),
height(other.height), width(other.width), min(other.min), max(other.max),
aliases(other.aliases), shortcut(other.shortcut), enum_values(other.enum_values),
enum_labels(other.enum_labels), enum_keys_map(other.enum_keys_map)
{
if (other.default_value != NULL)
this->default_value = other.default_value->clone();
}
ConfigOptionDef::~ConfigOptionDef()
{
if (this->default_value != NULL)
delete this->default_value;
}
ConfigOptionDef*
ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
ConfigOptionDef*
ConfigDef::add(const t_config_option_key &opt_key, const ConfigOptionDef &def)
{
this->options.insert(std::make_pair(opt_key, def));
return &this->options[opt_key];
}
bool
ConfigDef::has(const t_config_option_key &opt_key) const
{
return this->options.count(opt_key) > 0;
}
const ConfigOptionDef*
ConfigDef::get(const t_config_option_key &opt_key) const
{
if (this->options.count(opt_key) == 0) return NULL;
return &const_cast<ConfigDef*>(this)->options[opt_key];
}
void
ConfigDef::merge(const ConfigDef &other)
{
this->options.insert(other.options.begin(), other.options.end());
}
bool
ConfigBase::has(const t_config_option_key &opt_key) const {
return this->option(opt_key) != NULL;
}
void
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
// apply all options
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
for (const t_config_option_key &opt_key : opt_keys) {
ConfigOption* my_opt = this->option(opt_key, true);
if (my_opt == NULL) {
if (ignore_nonexistent == false) throw UnknownOptionException();
continue;
}
// not the most efficient way, but easier than casting pointers to subclasses
bool res = my_opt->deserialize( other.option(opt_key)->serialize() );
if (!res) {
std::string error = "Unexpected failure when deserializing serialized value for " + opt_key;
CONFESS(error.c_str());
}
}
}
bool
ConfigBase::equals(const ConfigBase &other) const {
return this->diff(other).empty();
}
// this will *ignore* options not present in both configs
t_config_option_keys
ConfigBase::diff(const ConfigBase &other) const {
t_config_option_keys diff;
for (const t_config_option_key &opt_key : this->keys())
if (other.has(opt_key) && other.serialize(opt_key) != this->serialize(opt_key))
diff.push_back(opt_key);
return diff;
}
std::string
ConfigBase::serialize(const t_config_option_key &opt_key) const {
const ConfigOption* opt = this->option(opt_key);
assert(opt != NULL);
return opt->serialize();
}
bool
ConfigBase::set_deserialize(t_config_option_key opt_key, std::string str, bool append) {
const ConfigOptionDef* optdef = this->def->get(opt_key);
if (optdef == NULL) {
// If we didn't find an option, look for any other option having this as an alias.
for (const auto &opt : this->def->options) {
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) {
opt_key = opt_key2;
optdef = &opt.second;
break;
}
}
if (optdef != NULL) break;
}
if (optdef == NULL)
throw UnknownOptionException();
}
if (!optdef->shortcut.empty()) {
for (const t_config_option_key &shortcut : optdef->shortcut) {
if (!this->set_deserialize(shortcut, str)) return false;
}
return true;
}
ConfigOption* opt = this->option(opt_key, true);
assert(opt != NULL);
return opt->deserialize(str, append);
}
// Return an absolute value of a possibly relative config variable.
// For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height.
double
ConfigBase::get_abs_value(const t_config_option_key &opt_key) const {
const ConfigOption* opt = this->option(opt_key);
if (const ConfigOptionFloatOrPercent* optv = dynamic_cast<const ConfigOptionFloatOrPercent*>(opt)) {
// get option definition
const ConfigOptionDef* def = this->def->get(opt_key);
assert(def != NULL);
// compute absolute value over the absolute value of the base option
return optv->get_abs_value(this->get_abs_value(def->ratio_over));
} else if (const ConfigOptionFloat* optv = dynamic_cast<const ConfigOptionFloat*>(opt)) {
return optv->value;
} else {
throw std::runtime_error("Not a valid option type for get_abs_value()");
}
}
// Return an absolute value of a possibly relative config variable.
// For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value.
double
ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const {
// get stored option value
const ConfigOptionFloatOrPercent* opt = dynamic_cast<const ConfigOptionFloatOrPercent*>(this->option(opt_key));
assert(opt != NULL);
// compute absolute value
return opt->get_abs_value(ratio_over);
}
void
ConfigBase::setenv_()
{
t_config_option_keys opt_keys = this->keys();
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
// prepend the SLIC3R_ prefix
std::ostringstream ss;
ss << "SLIC3R_";
ss << *it;
std::string envname = ss.str();
// capitalize environment variable name
for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
}
}
const ConfigOption*
ConfigBase::option(const t_config_option_key &opt_key) const {
return const_cast<ConfigBase*>(this)->option(opt_key, false);
}
ConfigOption*
ConfigBase::option(const t_config_option_key &opt_key, bool create) {
return this->optptr(opt_key, create);
}
void
ConfigBase::load(const std::string &file)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(file);
pt::read_ini(ifs, tree);
BOOST_FOREACH(const pt::ptree::value_type &v, tree) {
try {
t_config_option_key opt_key = v.first;
std::string value = v.second.get_value<std::string>();
this->set_deserialize(opt_key, value);
} catch (UnknownOptionException &e) {
// ignore
}
}
}
void
ConfigBase::save(const std::string &file) const
{
using namespace std;
boost::nowide::ofstream c;
c.open(file, ios::out | ios::trunc);
{
time_t now;
time(&now);
char buf[sizeof "0000-00-00 00:00:00"];
strftime(buf, sizeof buf, "%F %T", gmtime(&now));
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl;
}
t_config_option_keys my_keys = this->keys();
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key)
c << *opt_key << " = " << this->serialize(*opt_key) << endl;
c.close();
}
DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
{
this->swap(other);
return *this;
}
void
DynamicConfig::swap(DynamicConfig &other)
{
std::swap(this->options, other.options);
}
DynamicConfig::~DynamicConfig () {
for (t_options_map::iterator it = this->options.begin(); it != this->options.end(); ++it) {
ConfigOption* opt = it->second;
if (opt != NULL) delete opt;
}
}
DynamicConfig::DynamicConfig (const DynamicConfig& other) {
this->def = other.def;
this->apply(other, false);
}
ConfigOption*
DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
if (this->options.count(opt_key) == 0) {
if (create) {
const ConfigOptionDef* optdef = this->def->get(opt_key);
if (optdef == NULL) return NULL;
ConfigOption* opt;
if (optdef->default_value != NULL) {
opt = optdef->default_value->clone();
} else if (optdef->type == coFloat) {
opt = new ConfigOptionFloat ();
} else if (optdef->type == coFloats) {
opt = new ConfigOptionFloats ();
} else if (optdef->type == coInt) {
opt = new ConfigOptionInt ();
} else if (optdef->type == coInts) {
opt = new ConfigOptionInts ();
} else if (optdef->type == coString) {
opt = new ConfigOptionString ();
} else if (optdef->type == coStrings) {
opt = new ConfigOptionStrings ();
} else if (optdef->type == coPercent) {
opt = new ConfigOptionPercent ();
} else if (optdef->type == coFloatOrPercent) {
opt = new ConfigOptionFloatOrPercent ();
} else if (optdef->type == coPoint) {
opt = new ConfigOptionPoint ();
} else if (optdef->type == coPoint3) {
opt = new ConfigOptionPoint3 ();
} else if (optdef->type == coPoints) {
opt = new ConfigOptionPoints ();
} else if (optdef->type == coBool) {
opt = new ConfigOptionBool ();
} else if (optdef->type == coBools) {
opt = new ConfigOptionBools ();
} else if (optdef->type == coEnum) {
ConfigOptionEnumGeneric* optv = new ConfigOptionEnumGeneric ();
optv->keys_map = &optdef->enum_keys_map;
opt = static_cast<ConfigOption*>(optv);
} else {
throw std::runtime_error("Unknown option type");
}
this->options[opt_key] = opt;
return opt;
} else {
return NULL;
}
}
return this->options[opt_key];
}
t_config_option_keys
DynamicConfig::keys() const {
t_config_option_keys keys;
for (t_options_map::const_iterator it = this->options.begin(); it != this->options.end(); ++it)
keys.push_back(it->first);
return keys;
}
void
DynamicConfig::erase(const t_config_option_key &opt_key) {
this->options.erase(opt_key);
}
void
DynamicConfig::clear() {
this->options.clear();
}
bool
DynamicConfig::empty() const {
return this->options.empty();
}
void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{
std::vector<char*> _argv;
// push a bogus executable name (argv[0])
_argv.push_back(const_cast<char*>(""));
for (size_t i = 0; i < tokens.size(); ++i)
_argv.push_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(_argv.size(), &_argv[0], extra);
}
void
DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
{
// cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts;
for (const auto &oit : this->def->options) {
std::string cli = oit.second.cli;
cli = cli.substr(0, cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
std::vector<std::string> tokens;
boost::split(tokens, cli, boost::is_any_of("|"));
for (const std::string &t : tokens)
opts[t] = oit.first;
}
bool parse_options = true;
for (int i = 1; i < argc; ++i) {
std::string token = argv[i];
// Store non-option arguments in the provided vector.
if (!parse_options || !boost::starts_with(token, "-")) {
extra->push_back(token);
continue;
}
// Stop parsing tokens as options when -- is supplied.
if (token == "--") {
parse_options = false;
continue;
}
// Remove leading dashes
boost::trim_left_if(token, boost::is_any_of("-"));
// Remove the "no-" prefix used to negate boolean options.
bool no = false;
if (boost::starts_with(token, "no-")) {
no = true;
boost::replace_first(token, "no-", "");
}
// Read value when supplied in the --key=value form.
std::string value;
{
size_t equals_pos = token.find("=");
if (equals_pos != std::string::npos) {
value = token.substr(equals_pos+1);
token.erase(equals_pos);
}
}
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
printf("Warning: unknown option --%s\n", token.c_str());
continue;
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def->options.at(opt_key);
// If the option type expects a value and it was not already provided,
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
continue;
}
value = argv[++i];
}
// Store the option value.
const bool existing = this->has(opt_key);
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->values.push_back(!no);
} else if (ConfigOptionStrings* opt = this->opt<ConfigOptionStrings>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionFloats* opt = this->opt<ConfigOptionFloats>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionPoints* opt = this->opt<ConfigOptionPoints>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else {
this->set_deserialize(opt_key, value, true);
}
}
}
void
StaticConfig::set_defaults()
{
// use defaults from definition
if (this->def == NULL) return;
t_config_option_keys keys = this->keys();
for (t_config_option_keys::const_iterator it = keys.begin(); it != keys.end(); ++it) {
const ConfigOptionDef* def = this->def->get(*it);
if (def->default_value != NULL)
this->option(*it)->set(*def->default_value);
}
}
t_config_option_keys
StaticConfig::keys() const {
t_config_option_keys keys;
for (t_optiondef_map::const_iterator it = this->def->options.begin(); it != this->def->options.end(); ++it) {
const ConfigOption* opt = this->option(it->first);
if (opt != NULL) keys.push_back(it->first);
}
return keys;
}
bool
ConfigOptionPoint::deserialize(std::string str, bool append) {
std::vector<std::string> tokens(2);
boost::split(tokens, str, boost::is_any_of(",x"));
try {
this->value.x = boost::lexical_cast<coordf_t>(tokens[0]);
this->value.y = boost::lexical_cast<coordf_t>(tokens[1]);
} catch (boost::bad_lexical_cast &e){
std::cout << "Exception caught : " << e.what() << std::endl;
return false;
}
return true;
};
bool
ConfigOptionPoint3::deserialize(std::string str, bool append) {
std::vector<std::string> tokens(3);
boost::split(tokens, str, boost::is_any_of(",x"));
try {
this->value.x = boost::lexical_cast<coordf_t>(tokens[0]);
this->value.y = boost::lexical_cast<coordf_t>(tokens[1]);
this->value.z = boost::lexical_cast<coordf_t>(tokens[2]);
} catch (boost::bad_lexical_cast &e){
std::cout << "Exception caught : " << e.what() << std::endl;
return false;
}
return true;
};
bool
ConfigOptionPoints::deserialize(std::string str, bool append) {
if (!append) this->values.clear();
std::vector<std::string> tokens;
boost::split(tokens, str, boost::is_any_of("x,"));
if (tokens.size() % 2) return false;
try {
for (size_t i = 0; i < tokens.size(); ++i) {
Pointf point;
point.x = boost::lexical_cast<coordf_t>(tokens[i]);
point.y = boost::lexical_cast<coordf_t>(tokens[++i]);
this->values.push_back(point);
}
} catch (boost::bad_lexical_cast &e) {
printf("%s\n", e.what());
return false;
}
return true;
}
}
} // namespace Slic3r

View File

@ -1,754 +1,30 @@
#ifndef slic3r_Config_hpp_
#define slic3r_Config_hpp_
#ifndef CONFIG_HPP
#define CONFIG_HPP
#include <map>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "libslic3r.h"
#include "Point.hpp"
#include <initializer_list>
#include <memory>
#include "PrintConfig.hpp"
namespace Slic3r {
/// Name of the configuration option.
typedef std::string t_config_option_key;
typedef std::vector<std::string> t_config_option_keys;
class Config : public DynamicPrintConfig {
public:
static std::shared_ptr<Config> new_from_defaults();
static std::shared_ptr<Config> new_from_defaults(std::initializer_list<std::string> init);
static std::shared_ptr<Config> new_from_cli(const int& argc, const char* argv[]);
extern std::string escape_string_cstyle(const std::string &str);
extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
extern bool unescape_string_cstyle(const std::string &str, std::string &out);
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
void write_ini(const std::string& file) { save(file); }
void read_ini(const std::string& file) { load(file); }
/// \brief Public interface for configuration options.
///
/// Defines get/set for all supported data types.
/// Default value for output values is 0 for numeric/boolean types and "" for string types.
/// Subclasses override the appropriate functions in the interface and return real data.
class ConfigOption {
public:
virtual ~ConfigOption() {};
virtual ConfigOption* clone() const = 0;
virtual std::string serialize() const = 0;
virtual bool deserialize(std::string str, bool append = false) = 0;
virtual void set(const ConfigOption &option) = 0;
virtual int getInt() const { return 0; };
virtual double getFloat() const { return 0; };
virtual bool getBool() const { return false; };
virtual void setInt(int val) {};
virtual std::string getString() const { return ""; };
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
/// Template function to retrieve and cast in hopefully a slightly nicer
/// format than longwinded dynamic_cast<>
template <class T>
T& get(const t_config_option_key& opt_key, bool create=false) {
return *(dynamic_cast<T*>(this->optptr(opt_key, create)));
}
};
/// Value of a single valued option (bool, int, float, string, point, enum)
template <class T>
class ConfigOptionSingle : public ConfigOption {
public:
T value;
ConfigOptionSingle(T _value) : value(_value) {};
operator T() const { return this->value; };
void set(const ConfigOption &option) {
const ConfigOptionSingle<T>* other = dynamic_cast< const ConfigOptionSingle<T>* >(&option);
if (other != NULL) this->value = other->value;
};
};
} // namespace Slic3r
/// Virtual base class, represents value of a vector valued option (bools, ints, floats, strings, points)
class ConfigOptionVectorBase : public ConfigOption {
public:
virtual ~ConfigOptionVectorBase() {};
virtual std::vector<std::string> vserialize() const = 0;
};
/// Value of a vector valued option (bools, ints, floats, strings, points), template
template <class T>
class ConfigOptionVector : public ConfigOptionVectorBase
{
public:
std::vector<T> values;
ConfigOptionVector() {};
ConfigOptionVector(const std::vector<T> _values) : values(_values) {};
virtual ~ConfigOptionVector() {};
void set(const ConfigOption &option) {
const ConfigOptionVector<T>* other = dynamic_cast< const ConfigOptionVector<T>* >(&option);
if (other != NULL) this->values = other->values;
};
T get_at(size_t i) const {
try {
return this->values.at(i);
} catch (const std::out_of_range& oor) {
return this->values.front();
}
};
};
/// Template specialization for a single ConfigOption
/// Internally resolves to a double.
class ConfigOptionFloat : public ConfigOptionSingle<double>
{
public:
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
ConfigOptionFloat* clone() const { return new ConfigOptionFloat(this->value); };
double getFloat() const { return this->value; };
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
return ss.str();
};
bool deserialize(std::string str, bool append = false) {
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// Vector form of template specialization for floating point numbers.
class ConfigOptionFloats : public ConfigOptionVector<double>
{
public:
ConfigOptionFloats() {};
ConfigOptionFloats(const std::vector<double> _values) : ConfigOptionVector<double>(_values) {};
ConfigOptionFloats* clone() const { return new ConfigOptionFloats(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << *it;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
std::istringstream iss(item_str);
double value;
iss >> value;
this->values.push_back(value);
}
return true;
};
};
class ConfigOptionInt : public ConfigOptionSingle<int>
{
public:
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
ConfigOptionInt* clone() const { return new ConfigOptionInt(this->value); };
int getInt() const { return this->value; };
void setInt(int val) { this->value = val; };
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
return ss.str();
};
bool deserialize(std::string str, bool append = false) {
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
class ConfigOptionInts : public ConfigOptionVector<int>
{
public:
ConfigOptionInts() {};
ConfigOptionInts(const std::vector<int> _values) : ConfigOptionVector<int>(_values) {};
ConfigOptionInts* clone() const { return new ConfigOptionInts(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << *it;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
std::istringstream iss(item_str);
int value;
iss >> value;
this->values.push_back(value);
}
return true;
};
};
class ConfigOptionString : public ConfigOptionSingle<std::string>
{
public:
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {};
ConfigOptionString* clone() const { return new ConfigOptionString(this->value); };
std::string getString() const { return this->value; };
std::string serialize() const {
return escape_string_cstyle(this->value);
};
bool deserialize(std::string str, bool append = false) {
return unescape_string_cstyle(str, this->value);
};
};
/// semicolon-separated strings
class ConfigOptionStrings : public ConfigOptionVector<std::string>
{
public:
ConfigOptionStrings() {};
ConfigOptionStrings(const std::vector<std::string> _values) : ConfigOptionVector<std::string>(_values) {};
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
std::string serialize() const {
return escape_strings_cstyle(this->values);
};
std::vector<std::string> vserialize() const {
return this->values;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
return unescape_strings_cstyle(str, this->values);
};
};
/// \brief Specialized floating point class to represent some percentage value of
/// another numeric configuration option.
class ConfigOptionPercent : public ConfigOptionFloat
{
public:
ConfigOptionPercent() : ConfigOptionFloat(0) {};
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->value); };
/// Calculate the value of this option as it relates to some
/// other numerical value.
double get_abs_value(double ratio_over) const {
return ratio_over * this->value / 100;
};
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
std::string s(ss.str());
s += "%";
return s;
};
bool deserialize(std::string str, bool append = false) {
// don't try to parse the trailing % since it's optional
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// Combination class that can store a raw float or a percentage
/// value. Includes a flag to indicate how it should be interpreted.
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{
public:
bool percent;
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
ConfigOptionFloatOrPercent(double _value, bool _percent)
: ConfigOptionPercent(_value), percent(_percent) {};
ConfigOptionFloatOrPercent* clone() const { return new ConfigOptionFloatOrPercent(this->value, this->percent); };
void set(const ConfigOption &option) {
const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option);
if (other != NULL) {
this->value = other->value;
this->percent = other->percent;
}
};
double get_abs_value(double ratio_over) const {
if (this->percent) {
return ratio_over * this->value / 100;
} else {
return this->value;
}
};
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
std::string s(ss.str());
if (this->percent) s += "%";
return s;
};
bool deserialize(std::string str, bool append = false) {
this->percent = str.find_first_of("%") != std::string::npos;
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// \brief Configuration option to store a 2D (x,y) tuple.
class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
{
public:
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
ConfigOptionPoint(Pointf _value) : ConfigOptionSingle<Pointf>(_value) {};
ConfigOptionPoint* clone() const { return new ConfigOptionPoint(this->value); };
std::string serialize() const {
std::ostringstream ss;
ss << this->value.x;
ss << ",";
ss << this->value.y;
return ss.str();
};
bool deserialize(std::string str, bool append = false);
};
/// \brief Configuration option to store a 3D (x,y,z) tuple.
class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
{
public:
ConfigOptionPoint3() : ConfigOptionSingle<Pointf3>(Pointf3(0,0,0)) {};
ConfigOptionPoint3(Pointf3 _value) : ConfigOptionSingle<Pointf3>(_value) {};
ConfigOptionPoint3* clone() const { return new ConfigOptionPoint3(this->value); };
std::string serialize() const {
std::ostringstream ss;
ss << this->value.x;
ss << ",";
ss << this->value.y;
ss << ",";
ss << this->value.z;
return ss.str();
};
bool deserialize(std::string str, bool append = false);
bool is_positive_volume () {
return this->value.x > 0 && this->value.y > 0 && this->value.z > 0;
};
};
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
{
public:
ConfigOptionPoints() {};
ConfigOptionPoints(const std::vector<Pointf> _values) : ConfigOptionVector<Pointf>(_values) {};
ConfigOptionPoints* clone() const { return new ConfigOptionPoints(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << it->x;
ss << "x";
ss << it->y;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false);
};
/// \brief Represents a boolean flag
class ConfigOptionBool : public ConfigOptionSingle<bool>
{
public:
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
ConfigOptionBool* clone() const { return new ConfigOptionBool(this->value); };
bool getBool() const { return this->value; };
std::string serialize() const {
return std::string(this->value ? "1" : "0");
};
bool deserialize(std::string str, bool append = false) {
this->value = (str.compare("1") == 0);
return true;
};
};
class ConfigOptionBools : public ConfigOptionVector<bool>
{
public:
ConfigOptionBools() {};
ConfigOptionBools(const std::vector<bool> _values) : ConfigOptionVector<bool>(_values) {};
ConfigOptionBools* clone() const { return new ConfigOptionBools(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<bool>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << (*it ? "1" : "0");
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
for (std::vector<bool>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << (*it ? "1" : "0");
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
this->values.push_back(item_str.compare("1") == 0);
}
return true;
};
};
/// Map from an enum name to an enum integer value.
typedef std::map<std::string,int> t_config_enum_values;
/// \brief Templated enumeration representation.
template <class T>
class ConfigOptionEnum : public ConfigOptionSingle<T>
{
public:
// by default, use the first value (0) of the T enum type
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
ConfigOptionEnum<T>* clone() const { return new ConfigOptionEnum<T>(this->value); };
std::string serialize() const {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) {
if (it->second == static_cast<int>(this->value)) return it->first;
}
return "";
};
bool deserialize(std::string str, bool append = false) {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
if (enum_keys_map.count(str) == 0) return false;
this->value = static_cast<T>(enum_keys_map[str]);
return true;
};
/// Map from an enum name to an enum integer value.
//FIXME The map is called often, it shall be initialized statically.
static t_config_enum_values get_enum_values();
};
/// \brief Generic enum configuration value.
///
/// We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum.
/// In the StaticConfig, it is better to use the specialized ConfigOptionEnum<T> containers.
class ConfigOptionEnumGeneric : public ConfigOptionInt
{
public:
const t_config_enum_values* keys_map;
std::string serialize() const {
for (t_config_enum_values::const_iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) {
if (it->second == this->value) return it->first;
}
return "";
};
bool deserialize(std::string str, bool append = false) {
if (this->keys_map->count(str) == 0) return false;
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
return true;
};
};
/// Type of a configuration value.
enum ConfigOptionType {
coNone,
/// single float
coFloat,
/// vector of floats
coFloats,
/// single int
coInt,
/// vector of ints
coInts,
/// single string
coString,
/// vector of strings
coStrings,
/// percent value. Currently only used for infill.
coPercent,
/// a fraction or an absolute value
coFloatOrPercent,
/// single 2d point. Currently not used.
coPoint,
/// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
coPoints,
coPoint3,
/// single boolean value
coBool,
/// vector of boolean values
coBools,
/// a generic enum
coEnum,
};
/// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{
public:
/// \brief Type of option referenced.
///
/// The following (and any vector version) are supported:
/// \sa ConfigOptionFloat
/// \sa ConfigOptionInt
/// \sa ConfigOptionString
/// \sa ConfigOptionPercent
/// \sa ConfigOptionFloatOrPercent
/// \sa ConfigOptionPoint
/// \sa ConfigOptionBool
/// \sa ConfigOptionPoint3
/// \sa ConfigOptionBool
ConfigOptionType type;
/// \brief Default value of this option.
///
/// The default value object is owned by ConfigDef, it is released in its destructor.
ConfigOption* default_value;
/// \brief Specialization to indicate to the GUI what kind of control is more appropriate.
///
/// Usually empty.
/// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
/// "select_open" - to open a selection dialog (currently only a serial port selection).
std::string gui_type;
/// The flags may be combined.
/// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
/// "align_label_right" - align label to right
std::string gui_flags;
/// Label of the GUI input field.
/// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
/// while full_label contains a label of a stand-alone field.
/// The full label is shown, when adding an override parameter for an object or a modified object.
std::string label;
std::string full_label;
/// Category of a configuration field, from the GUI perspective.
/// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
std::string category;
/// A tooltip text shown in the GUI.
std::string tooltip;
/// Text right from the input field, usually a unit of measurement.
std::string sidetext;
/// Format of this parameter on a command line.
std::string cli;
/// Set for type == coFloatOrPercent.
/// It provides a link to a configuration value, of which this option provides a ratio.
/// For example,
/// For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
t_config_option_key ratio_over;
/// True for multiline strings.
bool multiline;
/// For text input: If true, the GUI text box spans the complete page width.
bool full_width;
/// This configuration item is not editable.
/// Currently only used for the display of the number of threads.
bool readonly;
/// Height of a multiline GUI text box.
int height;
/// Optional width of an input field.
int width;
/// <min, max> limit of a numeric input.
/// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
/// By setting min=0, only nonnegative input is allowed.
int min;
int max;
/// Legacy names for this configuration option.
/// Used when parsing legacy configuration file.
std::vector<t_config_option_key> aliases;
/// Sometimes a single value may well define multiple values in a "beginner" mode.
/// Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
std::vector<t_config_option_key> shortcut;
/// Definition of values / labels for a combo box.
/// Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open".
std::vector<std::string> enum_values;
std::vector<std::string> enum_labels;
/// For enums (when type == coEnum). Maps enum_values to enums.
/// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
t_config_enum_values enum_keys_map;
ConfigOptionDef() : type(coNone), default_value(NULL),
multiline(false), full_width(false), readonly(false),
height(-1), width(-1), min(INT_MIN), max(INT_MAX) {};
ConfigOptionDef(const ConfigOptionDef &other);
~ConfigOptionDef();
private:
ConfigOptionDef& operator= (ConfigOptionDef other);
};
/// Map from a config option name to its definition.
//i The definition does not carry an actual value of the config option, only its constant default value.
//i t_config_option_key is std::string
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
class ConfigDef
{
public:
t_optiondef_map options;
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
ConfigOptionDef* add(const t_config_option_key &opt_key, const ConfigOptionDef &def);
bool has(const t_config_option_key &opt_key) const;
const ConfigOptionDef* get(const t_config_option_key &opt_key) const;
void merge(const ConfigDef &other);
};
/// An abstract configuration store.
class ConfigBase
{
public:
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
/// ConfigBase does not own ConfigDef, it only references it.
const ConfigDef* def;
ConfigBase() : def(NULL) {};
ConfigBase(const ConfigDef* def) : def(def) {};
virtual ~ConfigBase() {};
bool has(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);
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 t_config_option_keys keys() const = 0;
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
void apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent = false);
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;
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, double ratio_over) const;
void setenv_();
void load(const std::string &file);
void save(const std::string &file) const;
};
/// Configuration store with dynamic number of configuration values.
/// In Slic3r, the dynamic config is mostly used at the user interface layer.
class DynamicConfig : public virtual ConfigBase
{
public:
DynamicConfig() {};
DynamicConfig(const ConfigDef* def) : ConfigBase(def) {};
DynamicConfig(const DynamicConfig& other);
DynamicConfig& operator= (DynamicConfig other);
void swap(DynamicConfig &other);
virtual ~DynamicConfig();
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
t_config_option_keys keys() const;
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(int argc, char** argv, t_config_option_keys* extra);
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;
};
/// Configuration store with a static definition of configuration values.
/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
/// because the configuration values could be accessed directly.
class StaticConfig : public virtual ConfigBase
{
public:
StaticConfig() : ConfigBase() {};
/// Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object
/// and which could be resolved by this->optptr(key) call.
t_config_option_keys keys() const;
/// Set all statically defined config options to their defaults defined by this->def.
void set_defaults();
/// The derived class has to implement optptr to resolve a static configuration value.
/// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
};
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::exception {};
}
#endif
#endif // CONFIG_HPP

View File

@ -0,0 +1,709 @@
#include "ConfigBase.hpp"
#include <assert.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <exception> // std::runtime_error
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/config.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <string.h>
namespace Slic3r {
std::string escape_string_cstyle(const std::string &str)
{
// Allocate a buffer twice the input string length,
// so the output will fit even if all input characters get escaped.
std::vector<char> out(str.size() * 2, 0);
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\n' || c == '\r') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else if (c == '\\'){
(*outptr ++) = '\\';
(*outptr ++) = '\\';
} else
(*outptr ++) = c;
}
return std::string(out.data(), outptr - out.data());
}
std::string escape_strings_cstyle(const std::vector<std::string> &strs)
{
// 1) Estimate the output buffer size to avoid buffer reallocation.
size_t outbuflen = 0;
for (size_t i = 0; i < strs.size(); ++ i)
// Reserve space for every character escaped + quotes + semicolon.
outbuflen += strs[i].size() * 2 + 3;
// 2) Fill in the buffer.
std::vector<char> out(outbuflen, 0);
char *outptr = out.data();
for (size_t j = 0; j < strs.size(); ++ j) {
if (j > 0)
// Separate the strings.
(*outptr ++) = ';';
const std::string &str = strs[j];
// Is the string simple or complex? Complex string contains spaces, tabs, new lines and other
// escapable characters. Empty string shall be quoted as well, if it is the only string in strs.
bool should_quote = strs.size() == 1 && str.empty();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
should_quote = true;
break;
}
}
if (should_quote) {
(*outptr ++) = '"';
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\\' || c == '"') {
(*outptr ++) = '\\';
(*outptr ++) = c;
} else if (c == '\n' || c == '\r') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else
(*outptr ++) = c;
}
(*outptr ++) = '"';
} else {
memcpy(outptr, str.data(), str.size());
outptr += str.size();
}
}
return std::string(out.data(), outptr - out.data());
}
bool unescape_string_cstyle(const std::string &str, std::string &str_out)
{
std::vector<char> out(str.size(), 0);
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
if (c == '\\') {
if (++ i == str.size())
return false;
c = str[i];
if (c == 'n')
(*outptr ++) = '\n';
} else
(*outptr ++) = c;
}
str_out.assign(out.data(), outptr - out.data());
return true;
}
bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out)
{
if (str.empty())
return true;
size_t i = 0;
for (;;) {
// Skip white spaces.
char c = str[i];
while (c == ' ' || c == '\t') {
if (++ i == str.size())
return true;
c = str[i];
}
// Start of a word.
std::vector<char> buf;
buf.reserve(16);
// Is it enclosed in quotes?
c = str[i];
if (c == '"') {
// Complex case, string is enclosed in quotes.
for (++ i; i < str.size(); ++ i) {
c = str[i];
if (c == '"') {
// End of string.
break;
}
if (c == '\\') {
if (++ i == str.size())
return false;
c = str[i];
if (c == 'n')
c = '\n';
}
buf.push_back(c);
}
if (i == str.size())
return false;
++ i;
} else {
for (; i < str.size(); ++ i) {
c = str[i];
if (c == ';')
break;
buf.push_back(c);
}
}
// Store the string into the output vector.
out.push_back(std::string(buf.data(), buf.size()));
if (i == str.size())
return true;
// Skip white spaces.
c = str[i];
while (c == ' ' || c == '\t') {
if (++ i == str.size())
// End of string. This is correct.
return true;
c = str[i];
}
if (c != ';')
return false;
if (++ i == str.size()) {
// Emit one additional empty string.
out.push_back(std::string());
return true;
}
}
}
bool
operator== (const ConfigOption &a, const ConfigOption &b)
{
return a.serialize().compare(b.serialize()) == 0;
}
bool
operator!= (const ConfigOption &a, const ConfigOption &b)
{
return !(a == b);
}
ConfigOptionDef::ConfigOptionDef(const ConfigOptionDef &other)
: type(other.type), default_value(NULL),
gui_type(other.gui_type), gui_flags(other.gui_flags), label(other.label),
full_label(other.full_label), category(other.category), tooltip(other.tooltip),
sidetext(other.sidetext), cli(other.cli), ratio_over(other.ratio_over),
multiline(other.multiline), full_width(other.full_width), readonly(other.readonly),
height(other.height), width(other.width), min(other.min), max(other.max),
aliases(other.aliases), shortcut(other.shortcut), enum_values(other.enum_values),
enum_labels(other.enum_labels), enum_keys_map(other.enum_keys_map)
{
if (other.default_value != NULL)
this->default_value = other.default_value->clone();
}
ConfigOptionDef::~ConfigOptionDef()
{
if (this->default_value != NULL)
delete this->default_value;
}
ConfigOptionDef*
ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
ConfigOptionDef*
ConfigDef::add(const t_config_option_key &opt_key, const ConfigOptionDef &def)
{
this->options.insert(std::make_pair(opt_key, def));
return &this->options[opt_key];
}
bool
ConfigDef::has(const t_config_option_key &opt_key) const
{
return this->options.count(opt_key) > 0;
}
const ConfigOptionDef*
ConfigDef::get(const t_config_option_key &opt_key) const
{
if (this->options.count(opt_key) == 0) return NULL;
return &const_cast<ConfigDef*>(this)->options[opt_key];
}
void
ConfigDef::merge(const ConfigDef &other)
{
this->options.insert(other.options.begin(), other.options.end());
}
bool
ConfigBase::has(const t_config_option_key &opt_key) const {
return this->option(opt_key) != NULL;
}
void
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
// apply all options
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
for (const t_config_option_key &opt_key : opt_keys) {
ConfigOption* my_opt = this->option(opt_key, true);
if (my_opt == NULL) {
if (ignore_nonexistent == false) throw UnknownOptionException();
continue;
}
// not the most efficient way, but easier than casting pointers to subclasses
bool res = my_opt->deserialize( other.option(opt_key)->serialize() );
if (!res) {
std::string error = "Unexpected failure when deserializing serialized value for " + opt_key;
CONFESS(error.c_str());
}
}
}
bool
ConfigBase::equals(const ConfigBase &other) const {
return this->diff(other).empty();
}
// this will *ignore* options not present in both configs
t_config_option_keys
ConfigBase::diff(const ConfigBase &other) const {
t_config_option_keys diff;
for (const t_config_option_key &opt_key : this->keys())
if (other.has(opt_key) && other.serialize(opt_key) != this->serialize(opt_key))
diff.push_back(opt_key);
return diff;
}
std::string
ConfigBase::serialize(const t_config_option_key &opt_key) const {
const ConfigOption* opt = this->option(opt_key);
assert(opt != NULL);
return opt->serialize();
}
bool
ConfigBase::set_deserialize(t_config_option_key opt_key, std::string str, bool append) {
const ConfigOptionDef* optdef = this->def->get(opt_key);
if (optdef == NULL) {
// If we didn't find an option, look for any other option having this as an alias.
for (const auto &opt : this->def->options) {
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) {
opt_key = opt_key2;
optdef = &opt.second;
break;
}
}
if (optdef != NULL) break;
}
if (optdef == NULL)
throw UnknownOptionException();
}
if (!optdef->shortcut.empty()) {
for (const t_config_option_key &shortcut : optdef->shortcut) {
if (!this->set_deserialize(shortcut, str)) return false;
}
return true;
}
ConfigOption* opt = this->option(opt_key, true);
assert(opt != NULL);
return opt->deserialize(str, append);
}
// Return an absolute value of a possibly relative config variable.
// For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height.
double
ConfigBase::get_abs_value(const t_config_option_key &opt_key) const {
const ConfigOption* opt = this->option(opt_key);
if (const ConfigOptionFloatOrPercent* optv = dynamic_cast<const ConfigOptionFloatOrPercent*>(opt)) {
// get option definition
const ConfigOptionDef* def = this->def->get(opt_key);
assert(def != NULL);
// compute absolute value over the absolute value of the base option
return optv->get_abs_value(this->get_abs_value(def->ratio_over));
} else if (const ConfigOptionFloat* optv = dynamic_cast<const ConfigOptionFloat*>(opt)) {
return optv->value;
} else {
throw std::runtime_error("Not a valid option type for get_abs_value()");
}
}
// Return an absolute value of a possibly relative config variable.
// For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value.
double
ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const {
// get stored option value
const ConfigOptionFloatOrPercent* opt = dynamic_cast<const ConfigOptionFloatOrPercent*>(this->option(opt_key));
assert(opt != NULL);
// compute absolute value
return opt->get_abs_value(ratio_over);
}
void
ConfigBase::setenv_()
{
t_config_option_keys opt_keys = this->keys();
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
// prepend the SLIC3R_ prefix
std::ostringstream ss;
ss << "SLIC3R_";
ss << *it;
std::string envname = ss.str();
// capitalize environment variable name
for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
}
}
const ConfigOption*
ConfigBase::option(const t_config_option_key &opt_key) const {
return const_cast<ConfigBase*>(this)->option(opt_key, false);
}
ConfigOption*
ConfigBase::option(const t_config_option_key &opt_key, bool create) {
return this->optptr(opt_key, create);
}
void
ConfigBase::load(const std::string &file)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(file);
pt::read_ini(ifs, tree);
BOOST_FOREACH(const pt::ptree::value_type &v, tree) {
try {
t_config_option_key opt_key = v.first;
std::string value = v.second.get_value<std::string>();
this->set_deserialize(opt_key, value);
} catch (UnknownOptionException &e) {
// ignore
}
}
}
void
ConfigBase::save(const std::string &file) const
{
using namespace std;
boost::nowide::ofstream c;
c.open(file, ios::out | ios::trunc);
{
time_t now;
time(&now);
char buf[sizeof "0000-00-00 00:00:00"];
strftime(buf, sizeof buf, "%F %T", gmtime(&now));
c << "# generated by Slic3r " << SLIC3R_VERSION << " on " << buf << endl;
}
t_config_option_keys my_keys = this->keys();
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key)
c << *opt_key << " = " << this->serialize(*opt_key) << endl;
c.close();
}
DynamicConfig& DynamicConfig::operator= (DynamicConfig other)
{
this->swap(other);
return *this;
}
void
DynamicConfig::swap(DynamicConfig &other)
{
std::swap(this->options, other.options);
}
DynamicConfig::~DynamicConfig () {
for (t_options_map::iterator it = this->options.begin(); it != this->options.end(); ++it) {
ConfigOption* opt = it->second;
if (opt != NULL) delete opt;
}
}
DynamicConfig::DynamicConfig (const DynamicConfig& other) {
this->def = other.def;
this->apply(other, false);
}
ConfigOption*
DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
if (this->options.count(opt_key) == 0) {
if (create) {
const ConfigOptionDef* optdef = this->def->get(opt_key);
if (optdef == NULL) return NULL;
ConfigOption* opt;
if (optdef->default_value != NULL) {
opt = optdef->default_value->clone();
} else if (optdef->type == coFloat) {
opt = new ConfigOptionFloat ();
} else if (optdef->type == coFloats) {
opt = new ConfigOptionFloats ();
} else if (optdef->type == coInt) {
opt = new ConfigOptionInt ();
} else if (optdef->type == coInts) {
opt = new ConfigOptionInts ();
} else if (optdef->type == coString) {
opt = new ConfigOptionString ();
} else if (optdef->type == coStrings) {
opt = new ConfigOptionStrings ();
} else if (optdef->type == coPercent) {
opt = new ConfigOptionPercent ();
} else if (optdef->type == coFloatOrPercent) {
opt = new ConfigOptionFloatOrPercent ();
} else if (optdef->type == coPoint) {
opt = new ConfigOptionPoint ();
} else if (optdef->type == coPoint3) {
opt = new ConfigOptionPoint3 ();
} else if (optdef->type == coPoints) {
opt = new ConfigOptionPoints ();
} else if (optdef->type == coBool) {
opt = new ConfigOptionBool ();
} else if (optdef->type == coBools) {
opt = new ConfigOptionBools ();
} else if (optdef->type == coEnum) {
ConfigOptionEnumGeneric* optv = new ConfigOptionEnumGeneric ();
optv->keys_map = &optdef->enum_keys_map;
opt = static_cast<ConfigOption*>(optv);
} else {
throw std::runtime_error("Unknown option type");
}
this->options[opt_key] = opt;
return opt;
} else {
return NULL;
}
}
return this->options[opt_key];
}
t_config_option_keys
DynamicConfig::keys() const {
t_config_option_keys keys;
for (t_options_map::const_iterator it = this->options.begin(); it != this->options.end(); ++it)
keys.push_back(it->first);
return keys;
}
void
DynamicConfig::erase(const t_config_option_key &opt_key) {
this->options.erase(opt_key);
}
void
DynamicConfig::clear() {
this->options.clear();
}
bool
DynamicConfig::empty() const {
return this->options.empty();
}
void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{
std::vector<char*> _argv;
// push a bogus executable name (argv[0])
_argv.push_back(const_cast<char*>(""));
for (size_t i = 0; i < tokens.size(); ++i)
_argv.push_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(_argv.size(), &_argv[0], extra);
}
void
DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
{
// cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts;
for (const auto &oit : this->def->options) {
std::string cli = oit.second.cli;
cli = cli.substr(0, cli.find("="));
boost::trim_right_if(cli, boost::is_any_of("!"));
std::vector<std::string> tokens;
boost::split(tokens, cli, boost::is_any_of("|"));
for (const std::string &t : tokens)
opts[t] = oit.first;
}
bool parse_options = true;
for (int i = 1; i < argc; ++i) {
std::string token = argv[i];
// Store non-option arguments in the provided vector.
if (!parse_options || !boost::starts_with(token, "-")) {
extra->push_back(token);
continue;
}
// Stop parsing tokens as options when -- is supplied.
if (token == "--") {
parse_options = false;
continue;
}
// Remove leading dashes
boost::trim_left_if(token, boost::is_any_of("-"));
// Remove the "no-" prefix used to negate boolean options.
bool no = false;
if (boost::starts_with(token, "no-")) {
no = true;
boost::replace_first(token, "no-", "");
}
// Read value when supplied in the --key=value form.
std::string value;
{
size_t equals_pos = token.find("=");
if (equals_pos != std::string::npos) {
value = token.substr(equals_pos+1);
token.erase(equals_pos);
}
}
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
printf("Warning: unknown option --%s\n", token.c_str());
continue;
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def->options.at(opt_key);
// If the option type expects a value and it was not already provided,
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
printf("No value supplied for --%s\n", token.c_str());
continue;
}
value = argv[++i];
}
// Store the option value.
const bool existing = this->has(opt_key);
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->values.push_back(!no);
} else if (ConfigOptionStrings* opt = this->opt<ConfigOptionStrings>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionFloats* opt = this->opt<ConfigOptionFloats>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionPoints* opt = this->opt<ConfigOptionPoints>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else {
this->set_deserialize(opt_key, value, true);
}
}
}
void
StaticConfig::set_defaults()
{
// use defaults from definition
if (this->def == NULL) return;
t_config_option_keys keys = this->keys();
for (t_config_option_keys::const_iterator it = keys.begin(); it != keys.end(); ++it) {
const ConfigOptionDef* def = this->def->get(*it);
if (def->default_value != NULL)
this->option(*it)->set(*def->default_value);
}
}
t_config_option_keys
StaticConfig::keys() const {
t_config_option_keys keys;
for (t_optiondef_map::const_iterator it = this->def->options.begin(); it != this->def->options.end(); ++it) {
const ConfigOption* opt = this->option(it->first);
if (opt != NULL) keys.push_back(it->first);
}
return keys;
}
bool
ConfigOptionPoint::deserialize(std::string str, bool append) {
std::vector<std::string> tokens(2);
boost::split(tokens, str, boost::is_any_of(",x"));
try {
this->value.x = boost::lexical_cast<coordf_t>(tokens[0]);
this->value.y = boost::lexical_cast<coordf_t>(tokens[1]);
} catch (boost::bad_lexical_cast &e){
std::cout << "Exception caught : " << e.what() << std::endl;
return false;
}
return true;
};
bool
ConfigOptionPoint3::deserialize(std::string str, bool append) {
std::vector<std::string> tokens(3);
boost::split(tokens, str, boost::is_any_of(",x"));
try {
this->value.x = boost::lexical_cast<coordf_t>(tokens[0]);
this->value.y = boost::lexical_cast<coordf_t>(tokens[1]);
this->value.z = boost::lexical_cast<coordf_t>(tokens[2]);
} catch (boost::bad_lexical_cast &e){
std::cout << "Exception caught : " << e.what() << std::endl;
return false;
}
return true;
};
bool
ConfigOptionPoints::deserialize(std::string str, bool append) {
if (!append) this->values.clear();
std::vector<std::string> tokens;
boost::split(tokens, str, boost::is_any_of("x,"));
if (tokens.size() % 2) return false;
try {
for (size_t i = 0; i < tokens.size(); ++i) {
Pointf point;
point.x = boost::lexical_cast<coordf_t>(tokens[i]);
point.y = boost::lexical_cast<coordf_t>(tokens[++i]);
this->values.push_back(point);
}
} catch (boost::bad_lexical_cast &e) {
printf("%s\n", e.what());
return false;
}
return true;
}
}

View File

@ -0,0 +1,754 @@
#ifndef slic3r_ConfigBase_hpp_
#define slic3r_ConfigBase_hpp_
#include <map>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "libslic3r.h"
#include "Point.hpp"
namespace Slic3r {
/// Name of the configuration option.
typedef std::string t_config_option_key;
typedef std::vector<std::string> t_config_option_keys;
extern std::string escape_string_cstyle(const std::string &str);
extern std::string escape_strings_cstyle(const std::vector<std::string> &strs);
extern bool unescape_string_cstyle(const std::string &str, std::string &out);
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
/// \brief Public interface for configuration options.
///
/// Defines get/set for all supported data types.
/// Default value for output values is 0 for numeric/boolean types and "" for string types.
/// Subclasses override the appropriate functions in the interface and return real data.
class ConfigOption {
public:
virtual ~ConfigOption() {};
virtual ConfigOption* clone() const = 0;
virtual std::string serialize() const = 0;
virtual bool deserialize(std::string str, bool append = false) = 0;
virtual void set(const ConfigOption &option) = 0;
virtual int getInt() const { return 0; };
virtual double getFloat() const { return 0; };
virtual bool getBool() const { return false; };
virtual void setInt(int val) {};
virtual std::string getString() const { return ""; };
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
};
/// Value of a single valued option (bool, int, float, string, point, enum)
template <class T>
class ConfigOptionSingle : public ConfigOption {
public:
T value;
ConfigOptionSingle(T _value) : value(_value) {};
operator T() const { return this->value; };
void set(const ConfigOption &option) {
const ConfigOptionSingle<T>* other = dynamic_cast< const ConfigOptionSingle<T>* >(&option);
if (other != NULL) this->value = other->value;
};
};
/// Virtual base class, represents value of a vector valued option (bools, ints, floats, strings, points)
class ConfigOptionVectorBase : public ConfigOption {
public:
virtual ~ConfigOptionVectorBase() {};
virtual std::vector<std::string> vserialize() const = 0;
};
/// Value of a vector valued option (bools, ints, floats, strings, points), template
template <class T>
class ConfigOptionVector : public ConfigOptionVectorBase
{
public:
std::vector<T> values;
ConfigOptionVector() {};
ConfigOptionVector(const std::vector<T> _values) : values(_values) {};
virtual ~ConfigOptionVector() {};
void set(const ConfigOption &option) {
const ConfigOptionVector<T>* other = dynamic_cast< const ConfigOptionVector<T>* >(&option);
if (other != NULL) this->values = other->values;
};
T get_at(size_t i) const {
try {
return this->values.at(i);
} catch (const std::out_of_range& oor) {
return this->values.front();
}
};
};
/// Template specialization for a single ConfigOption
/// Internally resolves to a double.
class ConfigOptionFloat : public ConfigOptionSingle<double>
{
public:
ConfigOptionFloat() : ConfigOptionSingle<double>(0) {};
ConfigOptionFloat(double _value) : ConfigOptionSingle<double>(_value) {};
ConfigOptionFloat* clone() const { return new ConfigOptionFloat(this->value); };
double getFloat() const { return this->value; };
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
return ss.str();
};
bool deserialize(std::string str, bool append = false) {
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// Vector form of template specialization for floating point numbers.
class ConfigOptionFloats : public ConfigOptionVector<double>
{
public:
ConfigOptionFloats() {};
ConfigOptionFloats(const std::vector<double> _values) : ConfigOptionVector<double>(_values) {};
ConfigOptionFloats* clone() const { return new ConfigOptionFloats(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << *it;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (std::vector<double>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
std::istringstream iss(item_str);
double value;
iss >> value;
this->values.push_back(value);
}
return true;
};
};
class ConfigOptionInt : public ConfigOptionSingle<int>
{
public:
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
ConfigOptionInt* clone() const { return new ConfigOptionInt(this->value); };
int getInt() const { return this->value; };
void setInt(int val) { this->value = val; };
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
return ss.str();
};
bool deserialize(std::string str, bool append = false) {
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
class ConfigOptionInts : public ConfigOptionVector<int>
{
public:
ConfigOptionInts() {};
ConfigOptionInts(const std::vector<int> _values) : ConfigOptionVector<int>(_values) {};
ConfigOptionInts* clone() const { return new ConfigOptionInts(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << *it;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
vv.reserve(this->values.size());
for (std::vector<int>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
std::istringstream iss(item_str);
int value;
iss >> value;
this->values.push_back(value);
}
return true;
};
};
class ConfigOptionString : public ConfigOptionSingle<std::string>
{
public:
ConfigOptionString() : ConfigOptionSingle<std::string>("") {};
ConfigOptionString(std::string _value) : ConfigOptionSingle<std::string>(_value) {};
ConfigOptionString* clone() const { return new ConfigOptionString(this->value); };
std::string getString() const { return this->value; };
std::string serialize() const {
return escape_string_cstyle(this->value);
};
bool deserialize(std::string str, bool append = false) {
return unescape_string_cstyle(str, this->value);
};
};
/// semicolon-separated strings
class ConfigOptionStrings : public ConfigOptionVector<std::string>
{
public:
ConfigOptionStrings() {};
ConfigOptionStrings(const std::vector<std::string> _values) : ConfigOptionVector<std::string>(_values) {};
ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); };
std::string serialize() const {
return escape_strings_cstyle(this->values);
};
std::vector<std::string> vserialize() const {
return this->values;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
return unescape_strings_cstyle(str, this->values);
};
};
/// \brief Specialized floating point class to represent some percentage value of
/// another numeric configuration option.
class ConfigOptionPercent : public ConfigOptionFloat
{
public:
ConfigOptionPercent() : ConfigOptionFloat(0) {};
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->value); };
/// Calculate the value of this option as it relates to some
/// other numerical value.
double get_abs_value(double ratio_over) const {
return ratio_over * this->value / 100;
};
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
std::string s(ss.str());
s += "%";
return s;
};
bool deserialize(std::string str, bool append = false) {
// don't try to parse the trailing % since it's optional
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// Combination class that can store a raw float or a percentage
/// value. Includes a flag to indicate how it should be interpreted.
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{
public:
bool percent;
ConfigOptionFloatOrPercent() : ConfigOptionPercent(0), percent(false) {};
ConfigOptionFloatOrPercent(double _value, bool _percent)
: ConfigOptionPercent(_value), percent(_percent) {};
ConfigOptionFloatOrPercent* clone() const { return new ConfigOptionFloatOrPercent(this->value, this->percent); };
void set(const ConfigOption &option) {
const ConfigOptionFloatOrPercent* other = dynamic_cast< const ConfigOptionFloatOrPercent* >(&option);
if (other != NULL) {
this->value = other->value;
this->percent = other->percent;
}
};
double get_abs_value(double ratio_over) const {
if (this->percent) {
return ratio_over * this->value / 100;
} else {
return this->value;
}
};
std::string serialize() const {
std::ostringstream ss;
ss << this->value;
std::string s(ss.str());
if (this->percent) s += "%";
return s;
};
bool deserialize(std::string str, bool append = false) {
this->percent = str.find_first_of("%") != std::string::npos;
std::istringstream iss(str);
iss >> this->value;
return !iss.fail();
};
};
/// \brief Configuration option to store a 2D (x,y) tuple.
class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
{
public:
ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {};
ConfigOptionPoint(Pointf _value) : ConfigOptionSingle<Pointf>(_value) {};
ConfigOptionPoint* clone() const { return new ConfigOptionPoint(this->value); };
std::string serialize() const {
std::ostringstream ss;
ss << this->value.x;
ss << ",";
ss << this->value.y;
return ss.str();
};
bool deserialize(std::string str, bool append = false);
};
/// \brief Configuration option to store a 3D (x,y,z) tuple.
class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
{
public:
ConfigOptionPoint3() : ConfigOptionSingle<Pointf3>(Pointf3(0,0,0)) {};
ConfigOptionPoint3(Pointf3 _value) : ConfigOptionSingle<Pointf3>(_value) {};
ConfigOptionPoint3* clone() const { return new ConfigOptionPoint3(this->value); };
std::string serialize() const {
std::ostringstream ss;
ss << this->value.x;
ss << ",";
ss << this->value.y;
ss << ",";
ss << this->value.z;
return ss.str();
};
bool deserialize(std::string str, bool append = false);
bool is_positive_volume () {
return this->value.x > 0 && this->value.y > 0 && this->value.z > 0;
};
};
class ConfigOptionPoints : public ConfigOptionVector<Pointf>
{
public:
ConfigOptionPoints() {};
ConfigOptionPoints(const std::vector<Pointf> _values) : ConfigOptionVector<Pointf>(_values) {};
ConfigOptionPoints* clone() const { return new ConfigOptionPoints(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << it->x;
ss << "x";
ss << it->y;
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << *it;
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false);
};
/// \brief Represents a boolean flag
class ConfigOptionBool : public ConfigOptionSingle<bool>
{
public:
ConfigOptionBool() : ConfigOptionSingle<bool>(false) {};
ConfigOptionBool(bool _value) : ConfigOptionSingle<bool>(_value) {};
ConfigOptionBool* clone() const { return new ConfigOptionBool(this->value); };
bool getBool() const { return this->value; };
std::string serialize() const {
return std::string(this->value ? "1" : "0");
};
bool deserialize(std::string str, bool append = false) {
this->value = (str.compare("1") == 0);
return true;
};
};
class ConfigOptionBools : public ConfigOptionVector<bool>
{
public:
ConfigOptionBools() {};
ConfigOptionBools(const std::vector<bool> _values) : ConfigOptionVector<bool>(_values) {};
ConfigOptionBools* clone() const { return new ConfigOptionBools(this->values); };
std::string serialize() const {
std::ostringstream ss;
for (std::vector<bool>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
ss << (*it ? "1" : "0");
}
return ss.str();
};
std::vector<std::string> vserialize() const {
std::vector<std::string> vv;
for (std::vector<bool>::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
std::ostringstream ss;
ss << (*it ? "1" : "0");
vv.push_back(ss.str());
}
return vv;
};
bool deserialize(std::string str, bool append = false) {
if (!append) this->values.clear();
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
this->values.push_back(item_str.compare("1") == 0);
}
return true;
};
};
/// Map from an enum name to an enum integer value.
typedef std::map<std::string,int> t_config_enum_values;
/// \brief Templated enumeration representation.
template <class T>
class ConfigOptionEnum : public ConfigOptionSingle<T>
{
public:
// by default, use the first value (0) of the T enum type
ConfigOptionEnum() : ConfigOptionSingle<T>(static_cast<T>(0)) {};
ConfigOptionEnum(T _value) : ConfigOptionSingle<T>(_value) {};
ConfigOptionEnum<T>* clone() const { return new ConfigOptionEnum<T>(this->value); };
std::string serialize() const {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) {
if (it->second == static_cast<int>(this->value)) return it->first;
}
return "";
};
bool deserialize(std::string str, bool append = false) {
t_config_enum_values enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
if (enum_keys_map.count(str) == 0) return false;
this->value = static_cast<T>(enum_keys_map[str]);
return true;
};
/// Map from an enum name to an enum integer value.
//FIXME The map is called often, it shall be initialized statically.
static t_config_enum_values get_enum_values();
};
/// \brief Generic enum configuration value.
///
/// We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum.
/// In the StaticConfig, it is better to use the specialized ConfigOptionEnum<T> containers.
class ConfigOptionEnumGeneric : public ConfigOptionInt
{
public:
const t_config_enum_values* keys_map;
std::string serialize() const {
for (t_config_enum_values::const_iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) {
if (it->second == this->value) return it->first;
}
return "";
};
bool deserialize(std::string str, bool append = false) {
if (this->keys_map->count(str) == 0) return false;
this->value = (*const_cast<t_config_enum_values*>(this->keys_map))[str];
return true;
};
};
/// Type of a configuration value.
enum ConfigOptionType {
coNone,
/// single float
coFloat,
/// vector of floats
coFloats,
/// single int
coInt,
/// vector of ints
coInts,
/// single string
coString,
/// vector of strings
coStrings,
/// percent value. Currently only used for infill.
coPercent,
/// a fraction or an absolute value
coFloatOrPercent,
/// single 2d point. Currently not used.
coPoint,
/// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
coPoints,
coPoint3,
/// single boolean value
coBool,
/// vector of boolean values
coBools,
/// a generic enum
coEnum,
};
/// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{
public:
/// \brief Type of option referenced.
///
/// The following (and any vector version) are supported:
/// \sa ConfigOptionFloat
/// \sa ConfigOptionInt
/// \sa ConfigOptionString
/// \sa ConfigOptionPercent
/// \sa ConfigOptionFloatOrPercent
/// \sa ConfigOptionPoint
/// \sa ConfigOptionBool
/// \sa ConfigOptionPoint3
/// \sa ConfigOptionBool
ConfigOptionType type;
/// \brief Default value of this option.
///
/// The default value object is owned by ConfigDef, it is released in its destructor.
ConfigOption* default_value;
/// \brief Specialization to indicate to the GUI what kind of control is more appropriate.
///
/// Usually empty.
/// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
/// "select_open" - to open a selection dialog (currently only a serial port selection).
std::string gui_type;
/// The flags may be combined.
/// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
/// "align_label_right" - align label to right
std::string gui_flags;
/// Label of the GUI input field.
/// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
/// while full_label contains a label of a stand-alone field.
/// The full label is shown, when adding an override parameter for an object or a modified object.
std::string label;
std::string full_label;
/// Category of a configuration field, from the GUI perspective.
/// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
std::string category;
/// A tooltip text shown in the GUI.
std::string tooltip;
/// Text right from the input field, usually a unit of measurement.
std::string sidetext;
/// Format of this parameter on a command line.
std::string cli;
/// Set for type == coFloatOrPercent.
/// It provides a link to a configuration value, of which this option provides a ratio.
/// For example,
/// For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
t_config_option_key ratio_over;
/// True for multiline strings.
bool multiline;
/// For text input: If true, the GUI text box spans the complete page width.
bool full_width;
/// This configuration item is not editable.
/// Currently only used for the display of the number of threads.
bool readonly;
/// Height of a multiline GUI text box.
int height;
/// Optional width of an input field.
int width;
/// <min, max> limit of a numeric input.
/// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
/// By setting min=0, only nonnegative input is allowed.
int min;
int max;
/// Legacy names for this configuration option.
/// Used when parsing legacy configuration file.
std::vector<t_config_option_key> aliases;
/// Sometimes a single value may well define multiple values in a "beginner" mode.
/// Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
std::vector<t_config_option_key> shortcut;
/// Definition of values / labels for a combo box.
/// Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open".
std::vector<std::string> enum_values;
std::vector<std::string> enum_labels;
/// For enums (when type == coEnum). Maps enum_values to enums.
/// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
t_config_enum_values enum_keys_map;
ConfigOptionDef() : type(coNone), default_value(NULL),
multiline(false), full_width(false), readonly(false),
height(-1), width(-1), min(INT_MIN), max(INT_MAX) {};
ConfigOptionDef(const ConfigOptionDef &other);
~ConfigOptionDef();
private:
ConfigOptionDef& operator= (ConfigOptionDef other);
};
/// Map from a config option name to its definition.
//i The definition does not carry an actual value of the config option, only its constant default value.
//i t_config_option_key is std::string
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
class ConfigDef
{
public:
t_optiondef_map options;
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
ConfigOptionDef* add(const t_config_option_key &opt_key, const ConfigOptionDef &def);
bool has(const t_config_option_key &opt_key) const;
const ConfigOptionDef* get(const t_config_option_key &opt_key) const;
void merge(const ConfigDef &other);
};
/// An abstract configuration store.
class ConfigBase
{
public:
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
/// ConfigBase does not own ConfigDef, it only references it.
const ConfigDef* def;
ConfigBase() : def(NULL) {};
ConfigBase(const ConfigDef* def) : def(def) {};
virtual ~ConfigBase() {};
bool has(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);
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 t_config_option_keys keys() const = 0;
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
void apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent = false);
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;
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, double ratio_over) const;
void setenv_();
void load(const std::string &file);
void save(const std::string &file) const;
};
/// Configuration store with dynamic number of configuration values.
/// In Slic3r, the dynamic config is mostly used at the user interface layer.
class DynamicConfig : public virtual ConfigBase
{
public:
DynamicConfig() {};
DynamicConfig(const ConfigDef* def) : ConfigBase(def) {};
DynamicConfig(const DynamicConfig& other);
DynamicConfig& operator= (DynamicConfig other);
void swap(DynamicConfig &other);
virtual ~DynamicConfig();
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
t_config_option_keys keys() const;
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(int argc, char** argv, t_config_option_keys* extra);
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;
};
/// Configuration store with a static definition of configuration values.
/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
/// because the configuration values could be accessed directly.
class StaticConfig : public virtual ConfigBase
{
public:
StaticConfig() : ConfigBase() {};
/// Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object
/// and which could be resolved by this->optptr(key) call.
t_config_option_keys keys() const;
/// Set all statically defined config options to their defaults defined by this->def.
void set_defaults();
/// The derived class has to implement optptr to resolve a static configuration value.
/// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
};
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::exception {};
}
#endif

View File

@ -135,5 +135,10 @@ ExPolygonCollection::append(const ExPolygons &expp)
{
this->expolygons.insert(this->expolygons.end(), expp.begin(), expp.end());
}
void
ExPolygonCollection::append(const ExPolygon &expp)
{
this->expolygons.push_back(expp);
}
}

View File

@ -33,6 +33,7 @@ class ExPolygonCollection
Polygons contours() const;
Polygons holes() const;
void append(const ExPolygons &expolygons);
void append(const ExPolygon &expolygons);
};
inline ExPolygonCollection&

View File

@ -2,7 +2,7 @@
#define slic3r_Flow_hpp_
#include "libslic3r.h"
#include "Config.hpp"
#include "ConfigBase.hpp"
#include "ExtrusionEntity.hpp"
namespace Slic3r {

View File

@ -503,8 +503,8 @@ std::string
GCode::_extrude(ExtrusionPath path, std::string description, double speed)
{
path.simplify(SCALED_RESOLUTION);
std::string gcode;
description = path.is_bridge() ? description + " (bridge)" : description;
// go to first point of extrusion path
if (!this->_last_pos_defined || !this->_last_pos.coincides_with(path.first_point())) {

View File

@ -107,7 +107,7 @@ GCodeWriter::postamble() const
std::string
GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const
{
wait = this->config.use_set_and_wait_extruder ? true : wait;
std::string code, comment;
if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfMakerWare) && FLAVOR_IS_NOT(gcfSailfish)) {
code = "M109";
@ -143,6 +143,7 @@ std::string
GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait) const
{
std::string code, comment;
wait = this->config.use_set_and_wait_bed ? true : wait;
if (wait && FLAVOR_IS_NOT(gcfTeacup)) {
if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) {
code = "M109";

33
xs/src/libslic3r/Log.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef slic3r_LOG_HPP
#define slic3r_LOG_HPP
#include <string>
namespace Slic3r {
class Log {
public:
static void fatal_error(std::string topic, std::wstring message) {
std::cerr << topic << " FERR" << ": ";
std::wcerr << message << std::endl;
}
static void error(std::string topic, std::wstring message) {
std::cerr << topic << " ERR" << ": ";
std::wcerr << message << std::endl;
}
static void info(std::string topic, std::wstring message) {
std::clog << topic << " INFO" << ": ";
std::wclog << message << std::endl;
}
static void warn(std::string topic, std::wstring message) {
std::cerr << topic << " WARN" << ": ";
std::wcerr << message << std::endl;
}
};
}
#endif // slic3r_LOG_HPP

View File

@ -936,12 +936,18 @@ ModelObject::print_info() const
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
: mesh(mesh), modifier(false), object(object)
: mesh(mesh), input_file(""), modifier(false), object(object)
{}
ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other)
: name(other.name), mesh(other.mesh), config(other.config),
modifier(other.modifier), object(object)
: name(other.name),
mesh(other.mesh),
config(other.config),
input_file(other.input_file),
input_file_obj_idx(other.input_file_obj_idx),
input_file_vol_idx(other.input_file_vol_idx),
modifier(other.modifier),
object(object)
{
this->material_id(other.material_id());
}
@ -959,6 +965,10 @@ ModelVolume::swap(ModelVolume &other)
std::swap(this->mesh, other.mesh);
std::swap(this->config, other.config);
std::swap(this->modifier, other.modifier);
std::swap(this->input_file, other.input_file);
std::swap(this->input_file_obj_idx, other.input_file_obj_idx);
std::swap(this->input_file_vol_idx, other.input_file_vol_idx);
}
t_model_material_id

View File

@ -457,7 +457,12 @@ class ModelVolume
DynamicPrintConfig config;
///< Configuration parameters specific to an object model geometry or a modifier volume,
///< overriding the global Slic3r settings and the ModelObject settings.
/// Input file path needed for reloading the volume from disk
std::string input_file; ///< Input file path
int input_file_obj_idx; ///< Input file object index
int input_file_vol_idx; ///< Input file volume index
bool modifier; ///< Is it an object to be printed, or a modifier volume?
/// Get the parent object owning this modifier volume.

View File

@ -152,6 +152,15 @@ to_points(const std::vector<T> &items)
return pp;
}
inline Points
scale(const std::vector<Pointf>&in ) {
Points out;
for (const auto& p : in) {out.push_back(Point(scale_(p.x), scale_(p.y))); }
return out;
}
}
// start Boost

View File

@ -293,4 +293,15 @@ Polygon::convex_points(double angle) const
return convex;
}
Polygon Polygon::new_scale(const Pointfs& p) {
Points scaled_p;
for (auto i : p) {
// scale each individual point and append to a new array
scaled_p.push_back(Slic3r::Point(scale_(i.x), scale_(i.y)));
}
return Slic3r::Polygon(scaled_p);
};
}

View File

@ -48,6 +48,8 @@ class Polygon : public MultiPoint {
std::string wkt() const;
Points concave_points(double angle = PI) const;
Points convex_points(double angle = PI) const;
static Polygon new_scale(const Pointfs& p);
};
inline Polygons

View File

@ -288,7 +288,7 @@ Print::invalidate_step(PrintStep step)
// propagate to dependent steps
if (step == psSkirt) {
this->invalidate_step(psBrim);
invalidated |= this->invalidate_step(psBrim);
}
return invalidated;

View File

@ -75,20 +75,20 @@ class PrintObject
friend class Print;
public:
// map of (vectors of volume ids), indexed by region_id
/* (we use map instead of vector so that we don't have to worry about
resizing it and the [] operator adds new items automagically) */
/// map of (vectors of volume ids), indexed by region_id
/// (we use map instead of vector so that we don't have to worry about
/// resizing it and the [] operator adds new items automagically)
std::map< size_t,std::vector<int> > region_volumes;
PrintObjectConfig config;
PrintObjectConfig config; //< Configuration
t_layer_height_ranges layer_height_ranges;
LayerHeightSpline layer_height_spline;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
/// this is set to true when LayerRegion->slices is split in top/internal/bottom
/// so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices;
Point3 size; // XYZ in scaled coordinates
Point3 size; //< XYZ in scaled coordinates
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API

View File

@ -221,7 +221,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("end_gcode", coString);
def->label = "End G-code";
def->tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings.";
def->tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings. If Slic3r detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions.";
def->cli = "end-gcode=s";
def->multiline = true;
def->full_width = true;
@ -230,7 +230,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("end_filament_gcode", coStrings);
def->label = "End G-code";
def->tooltip = "This end procedure is inserted at the end of the output file, before the printer end gcode. Note that you can use placeholder variables for all Slic3r settings. If you have multiple extruders, the gcode is processed in extruder order.";
def->tooltip = "This end procedure is inserted at the end of the output file, before the printer end gcode. Note that you can use placeholder variables for all Slic3r settings. If you have multiple extruders, the gcode is processed in extruder order. If Slic3r detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions.";
def->cli = "end-filament-gcode=s@";
def->multiline = true;
def->full_width = true;
@ -1394,6 +1394,16 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("0.2 (detachable)");
def->default_value = new ConfigOptionFloat(0.2);
def = this->add("support_material_max_layers", coInt);
def->label = "Max layer count for supports";
def->category = "Support material";
def->tooltip = "Disable support generation above this layer. Setting this to 0 will disable this feature.";
def->sidetext = "layers";
def->cli = "support-material-max-layers=f";
def->full_label = "Maximum layer count for support generation";
def->min = 0;
def->default_value = new ConfigOptionInt(0);
def = this->add("support_material_enforce_layers", coInt);
def->label = "Enforce support for the first";
def->category = "Support material";
@ -1631,6 +1641,19 @@ PrintConfigDef::PrintConfigDef()
def->cli = "use-relative-e-distances!";
def->default_value = new ConfigOptionBool(false);
def = this->add("use_set_and_wait_extruder", coBool);
def->label = "Use Set-and-Wait GCode (Extruder)";
def->tooltip = "If your firmware supports a set and wait gcode for temperature changes, use it for automatically inserted temperature gcode for all extruders. Does not affect custom gcode.";
def->cli = "use-set-and-wait-extruder!";
def->default_value = new ConfigOptionBool(false);
def = this->add("use_set_and_wait_bed", coBool);
def->label = "Use Set-and-Wait GCode (Bed)";
def->tooltip = "If your firmware supports a set and wait gcode for temperature changes, use it for automatically inserted temperature gcode for the heatbed. Does not affect custom gcode.";
def->cli = "use-set-and-wait-heatbed!";
def->default_value = new ConfigOptionBool(false);
def = this->add("use_volumetric_e", coBool);
def->label = "Use volumetric E";
def->tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. If your firmware doesn't already know filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] T0' in your start G-code in order to turn volumetric mode on and use the filament diameter associated to the filament selected in Slic3r. This is only supported in recent Marlin.";

View File

@ -19,7 +19,7 @@
#define slic3r_PrintConfig_hpp_
#include "libslic3r.h"
#include "Config.hpp"
#include "ConfigBase.hpp"
#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY
@ -171,6 +171,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
ConfigOptionInt support_material_angle;
ConfigOptionBool support_material_buildplate_only;
ConfigOptionFloat support_material_contact_distance;
ConfigOptionInt support_material_max_layers;
ConfigOptionInt support_material_enforce_layers;
ConfigOptionInt support_material_extruder;
ConfigOptionFloatOrPercent support_material_extrusion_width;
@ -208,6 +209,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
OPT_PTR(support_material_angle);
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
OPT_PTR(support_material_max_layers);
OPT_PTR(support_material_enforce_layers);
OPT_PTR(support_material_extruder);
OPT_PTR(support_material_extrusion_width);
@ -349,6 +351,8 @@ class GCodeConfig : public virtual StaticPrintConfig
ConfigOptionBool use_firmware_retraction;
ConfigOptionBool use_relative_e_distances;
ConfigOptionBool use_volumetric_e;
ConfigOptionBool use_set_and_wait_extruder;
ConfigOptionBool use_set_and_wait_bed;
GCodeConfig(bool initialize = true) : StaticPrintConfig() {
if (initialize)
@ -390,6 +394,8 @@ class GCodeConfig : public virtual StaticPrintConfig
OPT_PTR(use_firmware_retraction);
OPT_PTR(use_relative_e_distances);
OPT_PTR(use_volumetric_e);
OPT_PTR(use_set_and_wait_extruder);
OPT_PTR(use_set_and_wait_bed);
return NULL;
};

View File

@ -8,10 +8,10 @@
namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
: typed_slices(false),
: layer_height_spline(model_object->layer_height_spline),
typed_slices(false),
_print(print),
_model_object(model_object),
layer_height_spline(model_object->layer_height_spline)
_model_object(model_object)
{
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
{
@ -288,25 +288,25 @@ PrintObject::invalidate_step(PrintObjectStep step)
// propagate to dependent steps
if (step == posPerimeters) {
this->invalidate_step(posPrepareInfill);
this->_print->invalidate_step(psSkirt);
this->_print->invalidate_step(psBrim);
invalidated |= this->invalidate_step(posPrepareInfill);
invalidated |= this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
} else if (step == posDetectSurfaces) {
this->invalidate_step(posPrepareInfill);
invalidated |= this->invalidate_step(posPrepareInfill);
} else if (step == posPrepareInfill) {
this->invalidate_step(posInfill);
invalidated |= this->invalidate_step(posInfill);
} else if (step == posInfill) {
this->_print->invalidate_step(psSkirt);
this->_print->invalidate_step(psBrim);
invalidated |= this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
} else if (step == posSlice) {
this->invalidate_step(posPerimeters);
this->invalidate_step(posDetectSurfaces);
this->invalidate_step(posSupportMaterial);
invalidated |= this->invalidate_step(posPerimeters);
invalidated |= this->invalidate_step(posDetectSurfaces);
invalidated |= this->invalidate_step(posSupportMaterial);
}else if (step == posLayers) {
this->invalidate_step(posSlice);
invalidated |= this->invalidate_step(posSlice);
} else if (step == posSupportMaterial) {
this->_print->invalidate_step(psSkirt);
this->_print->invalidate_step(psBrim);
invalidated |= this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
}
return invalidated;

View File

@ -19,16 +19,16 @@ class SVG
bool flipY;
SVG(const char* afilename) :
arrows(false), fill("grey"), stroke("black"), filename(afilename), flipY(false)
arrows(false), fill("grey"), stroke("black"), flipY(false), filename(afilename)
{ open(filename); }
SVG(const char* afilename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
arrows(false), fill("grey"), stroke("black"), filename(afilename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
arrows(false), fill("grey"), stroke("black"), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY), filename(afilename)
{ open(filename, bbox, bbox_offset, aflipY); }
SVG(const std::string &filename) :
arrows(false), fill("grey"), stroke("black"), filename(filename), flipY(false)
arrows(false), fill("grey"), stroke("black"), flipY(false), filename(filename)
{ open(filename); }
SVG(const std::string &filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool aflipY = false) :
arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY)
arrows(false), fill("grey"), stroke("black"), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY), filename(filename)
{ open(filename, bbox, bbox_offset, aflipY); }
~SVG() { if (f != NULL) Close(); }

View File

@ -4,15 +4,30 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 5;
use Test::More tests => 6;
{
my $print = Slic3r::Print->new;
isa_ok $print, 'Slic3r::Print';
isa_ok $print->config, 'Slic3r::Config::Static::Ref';
isa_ok $print->default_object_config, 'Slic3r::Config::Static::Ref';
isa_ok $print->default_region_config, 'Slic3r::Config::Static::Ref';
isa_ok $print->placeholder_parser, 'Slic3r::GCode::PlaceholderParser::Ref';
{
my $print = Slic3r::Print->new;
isa_ok $print, 'Slic3r::Print';
isa_ok $print->config, 'Slic3r::Config::Static::Ref';
isa_ok $print->default_object_config, 'Slic3r::Config::Static::Ref';
isa_ok $print->default_region_config, 'Slic3r::Config::Static::Ref';
isa_ok $print->placeholder_parser, 'Slic3r::GCode::PlaceholderParser::Ref';
}
{
my $print = Slic3r::Print->new;
my $config = Slic3r::Config->new;
$config->set('skirts', 0);
$print->apply_config($config);
$config->set('skirts', 1);
$print->set_step_started(Slic3r::Print::State::STEP_SKIRT);
$print->set_step_done(Slic3r::Print::State::STEP_SKIRT);
my $invalid = $print->apply_config($config);
ok $invalid, 'applying skirt config invalidates skirt step';
}
}
__END__

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 7;
use Test::More tests => 10;
{
{
my $test_string = "{if{3 == 4}} string";
@ -25,6 +25,13 @@ use Test::More tests => 7;
my $result = Slic3r::ConditionalGCode::apply_math($test_string);
is $result, " string", 'If statement with nested bracket removes itself only on resulting true, does not strip text outside of brackets.';
}
{
my $test_string = "{if 3 > 2} string";
my $result = Slic3r::ConditionalGCode::apply_math($test_string);
is $result, " string", 'If statement with nested bracket removes itself only on resulting true, does not strip text outside of brackets.';
}
{
my $test_string = "{if{3 == 3}}string";
@ -49,4 +56,18 @@ use Test::More tests => 7;
my $result = Slic3r::ConditionalGCode::apply_math($test_string);
is $result, "M104 S{a}; Sets temp to 20", 'string (minus brackets) on failure to parse.';
}
{
my $config = Slic3r::Config->new;
$config->set('infill_extruder', 2);
$config->normalize;
my $test_string = "{if [infill_extruder] == 2}M104 S210";
my $pp = Slic3r::GCode::PlaceholderParser->new;
$pp->apply_config($config);
my $interim = $pp->process($test_string);
is $interim, "{if 2 == 2}M104 S210", 'Placeholder parser works inside conditional gcode.';
my $result = Slic3r::ConditionalGCode::apply_math($interim);
is $result, "M104 S210", 'If statement with nested bracket removes itself only on resulting true, does not strip text outside of brackets.';
}
}

View File

@ -258,6 +258,20 @@ ModelMaterial::attributes()
%code%{ RETVAL = THIS->name; %};
void set_name(std::string value)
%code%{ THIS->name = value; %};
std::string input_file()
%code%{ RETVAL = THIS->input_file; %};
void set_input_file(std::string value)
%code%{ THIS->input_file = value; %};
int input_file_obj_idx()
%code%{ RETVAL = THIS->input_file_obj_idx; %};
void set_input_file_obj_idx(int obj_idx)
%code%{ THIS->input_file_obj_idx = obj_idx; %};
int input_file_vol_idx()
%code%{ RETVAL = THIS->input_file_vol_idx; %};
void set_input_file_vol_idx(int vol_idx)
%code%{ THIS->input_file_vol_idx = vol_idx; %};
t_model_material_id material_id();
void set_material_id(t_model_material_id material_id)
%code%{ THIS->material_id(material_id); %};