Merge branch 'master' into adaptive-slicing

This commit is contained in:
Florens Wasserfall 2017-06-21 15:49:24 +02:00
commit c0dfffe0ff
117 changed files with 7471 additions and 1268 deletions

View File

@ -1,7 +1,9 @@
### Version ### Version
_Version of Slic3r used goes here_ _Version of Slic3r used goes here_
_Use `About->About Slic3r` for release versions_ _Use `About->About Slic3r` for release versions._
_Do not report Prusa3D Slic3r bugs here without confirming it is a problem on a development release of Slic3r, or your issue will be closed. *Only* use normal Slic3r version IDs._
_For -dev versions, use `git describe --tag` or get the hash value for the version you downloaded or `git rev-parse HEAD`_ _For -dev versions, use `git describe --tag` or get the hash value for the version you downloaded or `git rev-parse HEAD`_

View File

@ -2,7 +2,6 @@ language: perl
before_install: before_install:
- sh package/linux/travis-decrypt-key - sh package/linux/travis-decrypt-key
install: install:
- export LDLOADLIBS=-lstdc++
- export BOOST_DIR=$HOME/boost_1_63_0 - export BOOST_DIR=$HOME/boost_1_63_0
- export SLIC3R_STATIC=1 - export SLIC3R_STATIC=1
- export CXX=g++-4.9 - export CXX=g++-4.9

View File

@ -58,8 +58,7 @@ Sure! You can do the following to find things that are available to help with:
* Development * Development
* [Low Effort tasks](https://github.com/alexrj/Slic3r/labels/Low%20Effort): pick one of them! * [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 * [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.
* 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)) * 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. * 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. * Add an [issue](https://github.com/alexrj/Slic3r/issues) to the GitHub tracker if it isn't already present.

View File

@ -7,6 +7,7 @@ environment:
SLIC3R_STATIC: 1 SLIC3R_STATIC: 1
SLIC3R_VERSION: 1.3.0 SLIC3R_VERSION: 1.3.0
BOOST_DIR: C:\dev\boost_1_63_0 BOOST_DIR: C:\dev\boost_1_63_0
WXDIR: C:\dev\wxwidgets
WXSHARED: SHARED=0 WXSHARED: SHARED=0
FORCE_WX_BUILD: 0 FORCE_WX_BUILD: 0
FORCE_BOOST_REINSTALL: 0 FORCE_BOOST_REINSTALL: 0
@ -14,32 +15,37 @@ environment:
secure: QfeTOSKXz1uFCEACqFKLNw== secure: QfeTOSKXz1uFCEACqFKLNw==
UPLOAD_USER: UPLOAD_USER:
secure: fYPwnI3p6HNR+eMRJR3JfmyNolFn+Uc0MUn2bBXp9uU= secure: fYPwnI3p6HNR+eMRJR3JfmyNolFn+Uc0MUn2bBXp9uU=
matrix:
- ARCH: 64bit
- ARCH: 32bit
install: install:
- IF DEFINED ENC_SECRET nuget install secure-file -ExcludeVersion - IF DEFINED ENC_SECRET nuget install secure-file -ExcludeVersion
- IF DEFINED ENC_SECRET secure-file\tools\secure-file -decrypt package/deploy/slic3r-upload.ppk.enc -secret %ENC_SECRET% - IF DEFINED ENC_SECRET secure-file\tools\secure-file -decrypt package/deploy/slic3r-upload.ppk.enc -secret %ENC_SECRET%
- ps: "& package/win/appveyor_preinstall.ps1" - ps: "& package/win/appveyor_preinstall.ps1"
cache: cache:
- C:\Users\appveyor\boost.1.63.0.7z - C:\Users\appveyor\local-lib-64bit.7z
- C:\Users\appveyor\local-lib.7z - C:\Users\appveyor\local-lib-32bit.7z
- C:\Strawberry\perl\site - C:\Users\appveyor\freeglut.64bit.7z
- C:\Users\appveyor\freeglut.7z - C:\Users\appveyor\freeglut.32bit.7z
- C:\users\appveyor\strawberry.msi - C:\users\appveyor\strawberry.64bit.msi
- C:\users\appveyor\strawberry.32bit.msi
- C:\Users\appveyor\winscp.zip - C:\Users\appveyor\winscp.zip
- C:\Users\appveyor\extra_perl.7z - C:\Users\appveyor\extra_perl.7z
- C:\Users\appveyor\wxwidgets.7z - C:\Users\appveyor\wxwidgets-64bit.7z
- C:\Users\appveyor\wxwidgets-32bit.7z
- C:\Users\appveyor\boost.1.63.0.32bit.7z
- C:\Users\appveyor\boost.1.63.0.64bit.7z
build_script: build_script:
- ps: "& package/win/appveyor_buildscript.ps1" - ps: "& package/win/appveyor_buildscript.ps1"
test_script:
- ps: "mkdir C:\\Andrés\nwget \"http://www.thingiverse.com/download:73351\" -o\"C:\\Andrés\\5mm.stl\"\necho \"bed_temperature=60\" > C:\\Andrés\\test.ini\n\ncd C:\\projects\\slic3r\nperl slic3r.pl --load \"C:\\Andrés\\test.ini\" \"C:\\Andrés\\5mm.stl\"\n\nif (!(Test-Path \"C:\\Andrés\\5mm.gcode\")) {\necho \"IS IT HERE\"\n}"
artifacts: artifacts:
- path: .\slic3r*zip - path: .\slic3r*zip
name: slic3r-dev name: slic3r-dev
deploy_script: deploy_script:
- ps: "& package/win/appveyor_deploy.ps1" - ps: "cd C:/projects/slic3r; & package/win/appveyor_deploy.ps1"
on_success: on_success:
- ps: - ps:
on_failure: on_failure:
- ps: - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
on_finish: on_finish:
- ps: - ps:

View File

@ -301,6 +301,8 @@ sub resume_all_threads {
sub encode_path { sub encode_path {
my ($path) = @_; my ($path) = @_;
return undef if !defined $path;
$path = Unicode::Normalize::NFC($path); $path = Unicode::Normalize::NFC($path);
$path = Encode::encode(locale_fs => $path); $path = Encode::encode(locale_fs => $path);
@ -311,6 +313,8 @@ sub encode_path {
sub decode_path { sub decode_path {
my ($path) = @_; my ($path) = @_;
return undef if !defined $path;
$path = Encode::decode(locale_fs => $path) $path = Encode::decode(locale_fs => $path)
unless utf8::is_utf8($path); unless utf8::is_utf8($path);

View File

@ -95,7 +95,8 @@ sub load {
# legacy syntax of load() # legacy syntax of load()
my $config = $class->new; my $config = $class->new;
$config->_load(Slic3r::encode_path($file));
$config->_load($file);
return $config; return $config;
} }
@ -103,7 +104,7 @@ sub save {
my $self = shift; my $self = shift;
my ($file) = @_; my ($file) = @_;
return $self->_save(Slic3r::encode_path($file)); return $self->_save($file);
} }
# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class, # Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class,
@ -269,12 +270,6 @@ sub validate {
qw(perimeter infill solid_infill top_infill support_material first_layer); qw(perimeter infill solid_infill top_infill support_material first_layer);
} }
# support material
if ($self->support_material) {
die "Value of 0 is illegal. Use some % value instead (e.g. 150%) for auto.\n"
if $self->support_material_threshold =~ /^0+/;
}
# general validation, quick and dirty # general validation, quick and dirty
foreach my $opt_key (@{$self->get_keys}) { foreach my $opt_key (@{$self->get_keys}) {

View File

@ -5,6 +5,7 @@ use utf8;
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
:filedialog :font); :filedialog :font);
use Wx::Event qw(EVT_MENU);
BEGIN { BEGIN {
# Wrap the Wx::_load_plugin() function which doesn't work with non-ASCII paths # Wrap the Wx::_load_plugin() function which doesn't work with non-ASCII paths
@ -73,7 +74,6 @@ use constant AMF_MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(amf)};
our $datadir; our $datadir;
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
our $no_controller;
our $autosave; our $autosave;
our $threads; our $threads;
our @cb; our @cb;
@ -84,23 +84,26 @@ our $Settings = {
autocenter => 1, autocenter => 1,
invert_zoom => 0, invert_zoom => 0,
background_processing => 0, background_processing => 0,
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
no_controller => 0,
threads => $Slic3r::Config::Options->{threads}{default}, threads => $Slic3r::Config::Options->{threads}{default},
color_toolpaths_by => 'role', color_toolpaths_by => 'role',
tabbed_preset_editors => 1,
}, },
}; };
our $have_button_icons = &Wx::wxVERSION_STRING =~ / (?:2\.9\.[1-9]|3\.)/; our $have_button_icons = &Wx::wxVERSION_STRING =~ / (?:2\.9\.[1-9]|3\.)/;
our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$small_font->SetPointSize(11) if !&Wx::wxMSW; $small_font->SetPointSize(11) if &Wx::wxMAC;
our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$small_bold_font->SetPointSize(11) if !&Wx::wxMSW; $small_bold_font->SetPointSize(11) if &Wx::wxMAC;
$small_bold_font->SetWeight(wxFONTWEIGHT_BOLD); $small_bold_font->SetWeight(wxFONTWEIGHT_BOLD);
our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$medium_font->SetPointSize(12); $medium_font->SetPointSize(12);
our $grey = Wx::Colour->new(200,200,200); our $grey = Wx::Colour->new(200,200,200);
# to use in ScrolledWindow::SetScrollRate(xstep, ystep)
# step related to system font point size
our $scroll_step = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)->GetPointSize;
our $VERSION_CHECK_EVENT : shared = Wx::NewEventType; our $VERSION_CHECK_EVENT : shared = Wx::NewEventType;
our $DLP_projection_screen; our $DLP_projection_screen;
@ -437,6 +440,30 @@ sub scan_serial_ports {
return grep !/Bluetooth|FireFly/, @ports; return grep !/Bluetooth|FireFly/, @ports;
} }
sub append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
$id //= &Wx::NewId();
my $item = Wx::MenuItem->new($menu, $id, $string, $description // '', $kind // 0);
$self->set_menu_item_icon($item, $icon);
$menu->Append($item);
EVT_MENU($self, $id, $cb);
return $item;
}
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 set_menu_item_icon { sub set_menu_item_icon {
my ($self, $menuItem, $icon) = @_; my ($self, $menuItem, $icon) = @_;

View File

@ -330,6 +330,19 @@ sub set_viewport_from_scene {
$self->_dirty(1); $self->_dirty(1);
} }
sub zoom{
my ($self, $direction) = @_;
if( $direction eq 'in'){
$self->_zoom($self->_zoom / (1-0.3));
}
elsif($direction eq 'out'){
$self->_zoom($self->_zoom / (1+0.3));
}
$self->on_viewport_changed->() if $self->on_viewport_changed;
$self->_dirty(1);
$self->Refresh;
}
# Set the camera to a default orientation, # Set the camera to a default orientation,
# zoom to volumes. # zoom to volumes.
sub select_view { sub select_view {
@ -1441,7 +1454,7 @@ sub load_print_object_toolpaths {
} }
} }
$add->($non_solid, $top_z, $copy, $color); $add->($non_solid, $top_z, $copy, $color);
$color = $self->colors->[ ($layerm->region->config->solid_infill_extruder-1) % @{&COLORS} ]; $color = $self->colors->[ ($layerm->region->config->solid_infill_extruder-1) % @{$self->colors} ];
$add->($solid, $top_z, $copy, $color); $add->($solid, $top_z, $copy, $color);
} else { } else {
$add->($layerm->fills, $top_z, $copy, $color); $add->($layerm->fills, $top_z, $copy, $color);

View File

@ -40,20 +40,20 @@ sub new {
EVT_LEFT_DOWN($btn, sub { EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new; my $menu = Wx::Menu->new;
my %presets = map { $_->name => $_ } wxTheApp->presets('printer'); my %presets = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
# remove printers that already exist # remove printers that already exist
my @panels = $self->print_panels; my @panels = $self->print_panels;
delete $presets{$_} for map $_->printer_name, @panels; delete $presets{$_} for map $_->printer_name, @panels;
foreach my $preset_name (sort keys %presets) { foreach my $preset_name (sort keys %presets) {
my $config = $presets{$preset_name}->dirty_config; my $preset = $presets{$preset_name};
next if !$config->serial_port; next if !$preset->dirty_config->serial_port;
my $id = &Wx::NewId(); my $id = &Wx::NewId();
$menu->Append($id, $preset_name); $menu->Append($id, $preset_name);
EVT_MENU($menu, $id, sub { EVT_MENU($menu, $id, sub {
$self->add_printer($preset_name, $config); $self->add_printer($preset);
}); });
} }
$self->PopupMenu($menu, $btn->GetPosition); $self->PopupMenu($menu, $btn->GetPosition);
@ -100,10 +100,10 @@ sub OnActivate {
# get all available presets # get all available presets
my %presets = (); my %presets = ();
{ foreach my $preset (@{wxTheApp->presets->{printer}}) {
my %all = map { $_->name => $_ } @{wxTheApp->presets->{printer}}; $preset->load_config;
my %configs = map { my $name = $_; $name => $all{$name}->load_config } keys %all; next if !$preset->dirty_config->serial_port;
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all; $presets{$preset->name} = $preset;
} }
# decide which ones we want to keep # decide which ones we want to keep
@ -124,7 +124,7 @@ sub OnActivate {
# enable printers whose port is available # enable printers whose port is available
my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports; my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports;
$active{$_} = 1 $active{$_} = 1
for grep exists $ports{$presets{$_}->serial_port}, keys %presets; for grep exists $ports{$presets{$_}->dirty_config->serial_port}, keys %presets;
} }
if (!%active && $self->_selected_printer_preset) { if (!%active && $self->_selected_printer_preset) {
# enable currently selected printer if it is configured # enable currently selected printer if it is configured
@ -140,7 +140,7 @@ sub OnActivate {
$self->{sizer}->DetachWindow($panel); $self->{sizer}->DetachWindow($panel);
$panel->Destroy; $panel->Destroy;
} }
$self->add_printer($_, $presets{$_}) for sort keys %active; $self->add_printer($presets{$_}) for sort keys %active;
# show/hide the warning about no printers # show/hide the warning about no printers
$self->{text_no_printers}->Show(!%presets); $self->{text_no_printers}->Show(!%presets);
@ -156,16 +156,16 @@ sub OnActivate {
} }
sub add_printer { sub add_printer {
my ($self, $printer_name, $config) = @_; my ($self, $preset) = @_;
# check that printer doesn't exist already # check that printer doesn't exist already
foreach my $panel ($self->print_panels) { foreach my $panel ($self->print_panels) {
if ($panel->printer_name eq $printer_name) { if ($panel->printer_name eq $preset->name) {
return $panel; return $panel;
} }
} }
my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $printer_name, $config); my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $preset);
$self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); $self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
$self->Layout; $self->Layout;

View File

@ -3,6 +3,7 @@ use strict;
use warnings; use warnings;
use utf8; use utf8;
use List::Util qw(first);
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer
:textctrl :font :systemsettings); :textctrl :font :systemsettings);
use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER EVT_SCROLLWIN); use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER EVT_SCROLLWIN);
@ -16,11 +17,11 @@ use constant STATUS_TIMER_INTERVAL => 1000; # milliseconds
use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds
sub new { sub new {
my ($class, $parent, $printer_name, $config) = @_; my ($class, $parent, $preset) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]); my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]);
$self->printer_name($printer_name || 'Printer'); $self->printer_name($preset->name);
$self->config($config); $self->config($preset->dirty_config);
$self->manual_control_config({ $self->manual_control_config({
xy_travel_speed => 130, xy_travel_speed => 130,
z_travel_speed => 10, z_travel_speed => 10,
@ -103,7 +104,7 @@ sub new {
} }
my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL); my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
{ {
$self->{serial_port_combobox} = Wx::ComboBox->new($self, -1, $config->serial_port, wxDefaultPosition, wxDefaultSize, []); $self->{serial_port_combobox} = Wx::ComboBox->new($self, -1, $self->config->serial_port, wxDefaultPosition, wxDefaultSize, []);
$self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font); $self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font);
$self->update_serial_ports; $self->update_serial_ports;
$serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1); $serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
@ -125,7 +126,7 @@ sub new {
} }
my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL); my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
{ {
$self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize, $self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, $self->config->serial_speed, wxDefaultPosition, wxDefaultSize,
["57600", "115200", "250000"]); ["57600", "115200", "250000"]);
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font); $self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
$serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0); $serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0);
@ -336,6 +337,17 @@ sub connect {
# request temperature now, without waiting for the timer # request temperature now, without waiting for the timer
$self->sender->send("M105", 1); $self->sender->send("M105", 1);
# Update the printer preset with the new connection info
{
my $preset = first { $_->name eq $self->printer_name } @{wxTheApp->presets->{printer}};
if ($preset) {
$preset->load_config;
$preset->_dirty_config->set('serial_port', $self->{serial_port_combobox}->GetValue);
$preset->_dirty_config->set('serial_speed', $self->{serial_speed_combobox}->GetValue);
$preset->save([ 'serial_port', 'serial_speed' ]);
}
}
} else { } else {
$self->set_status("Connection failed. Check serial port and speed."); $self->set_status("Connection failed. Check serial port and speed.");
} }
@ -543,17 +555,26 @@ use Wx::Event qw(EVT_BUTTON EVT_TIMER EVT_ERASE_BACKGROUND);
use base qw(Wx::Panel Class::Accessor); use base qw(Wx::Panel Class::Accessor);
__PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print __PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print
on_abort_print blink_timer)); on_abort_print blink_timer duration queued));
sub new { sub new {
my ($class, $parent, $job) = @_; my ($class, $parent, $job) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
# Estimate print duration
{
my $estimator = Slic3r::GCode::TimeEstimator->new;
$estimator->parse_file($job->gcode_file);
$self->duration($estimator->time);
}
$self->job($job); $self->job($job);
$self->SetBackgroundColour(wxWHITE); $self->queued(scalar localtime);
$self->SetBackgroundColour(Wx::SystemSettings::GetColour(Wx::wxSYS_COLOUR_LISTBOX));
{ {
my $white_brush = Wx::Brush->new(wxWHITE, wxSOLID); my $white_brush = Wx::Brush->new($self->GetBackgroundColour, wxSOLID);
my $pen = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); my $pen = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID);
EVT_ERASE_BACKGROUND($self, sub { EVT_ERASE_BACKGROUND($self, sub {
my ($self, $event) = @_; my ($self, $event) = @_;
@ -574,13 +595,18 @@ sub new {
if ($job->printed) { if ($job->printed) {
$text->SetForegroundColour($Slic3r::GUI::grey); $text->SetForegroundColour($Slic3r::GUI::grey);
} }
$text->SetToolTipString("Queued on " . $self->queued)
if $text->can('SetToolTipString');
$left_sizer->Add($text, 0, wxEXPAND, 0); $left_sizer->Add($text, 0, wxEXPAND, 0);
} }
{ {
my $filament_stats = join "\n", my $stats = join "\n",
map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)", map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)",
sort keys %{$job->filament_stats}; sort keys %{$job->filament_stats};
my $text = Wx::StaticText->new($self, -1, $filament_stats, wxDefaultPosition, wxDefaultSize); $stats .= sprintf "\nEstimated time: %d hours and %d minutes",
int($self->duration/3600), int($self->duration/60) % 60;
my $text = Wx::StaticText->new($self, -1, $stats, wxDefaultPosition, wxDefaultSize);
$text->SetFont($Slic3r::GUI::small_font); $text->SetFont($Slic3r::GUI::small_font);
if ($job->printed && !$job->printing) { if ($job->printed && !$job->printing) {
$text->SetForegroundColour($Slic3r::GUI::grey); $text->SetForegroundColour($Slic3r::GUI::grey);

View File

@ -8,9 +8,10 @@ use utf8;
use File::Basename qw(basename dirname); use File::Basename qw(basename dirname);
use List::Util qw(min); use List::Util qw(min);
use Slic3r::Geometry qw(X Y Z); use Slic3r::Geometry qw(X Y Z);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog use Wx qw(:frame :bitmap :id :misc :panel :sizer :menu :dialog :filedialog
:font :icon wxTheApp); :font :icon :aui wxTheApp);
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED); use Wx::AUI;
use Wx::Event qw(EVT_CLOSE EVT_AUINOTEBOOK_PAGE_CHANGED EVT_AUINOTEBOOK_PAGE_CLOSE);
use base 'Wx::Frame'; use base 'Wx::Frame';
our $qs_last_input_file; our $qs_last_input_file;
@ -28,6 +29,7 @@ sub new {
} }
$self->{loaded} = 0; $self->{loaded} = 0;
$self->{preset_editor_tabs} = {}; # group => panel
# initialize tabpanel and menubar # initialize tabpanel and menubar
$self->_init_tabpanel; $self->_init_tabpanel;
@ -92,15 +94,28 @@ sub new {
sub _init_tabpanel { sub _init_tabpanel {
my ($self) = @_; my ($self) = @_;
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); $self->{tabpanel} = my $panel = Wx::AuiNotebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxAUI_NB_TOP);
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub { EVT_AUINOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage; my $panel = $self->{tabpanel}->GetPage($self->{tabpanel}->GetSelection);
$panel->OnActivate if $panel->can('OnActivate'); $panel->OnActivate if $panel->can('OnActivate');
if ($self->{tabpanel}->GetSelection > 1) {
$self->{tabpanel}->SetWindowStyle($self->{tabpanel}->GetWindowStyleFlag | wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
} else {
$self->{tabpanel}->SetWindowStyle($self->{tabpanel}->GetWindowStyleFlag & ~wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
}
});
EVT_AUINOTEBOOK_PAGE_CLOSE($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetPage($self->{tabpanel}->GetSelection);
if ($panel->isa('Slic3r::GUI::PresetEditor')) {
delete $self->{preset_editor_tabs}{$panel->name};
}
wxTheApp->CallAfter(sub {
$self->{tabpanel}->SetSelection(0);
});
}); });
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater"); $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller") $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
unless ($Slic3r::GUI::Settings->{_}{no_controller});
} }
sub _init_menubar { sub _init_menubar {
@ -109,60 +124,60 @@ sub _init_menubar {
# File menu # File menu
my $fileMenu = Wx::Menu->new; my $fileMenu = Wx::Menu->new;
{ {
$self->_append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub { wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub {
$self->{plater}->add if $self->{plater}; $self->{plater}->add if $self->{plater};
}, undef, 'brick_add.png'); }, undef, 'brick_add.png');
$self->_append_menu_item($fileMenu, "Open 2.5D TIN mesh…", 'Import a 2.5D TIN mesh', sub { wxTheApp->append_menu_item($fileMenu, "Open 2.5D TIN mesh…", 'Import a 2.5D TIN mesh', sub {
$self->{plater}->add_tin if $self->{plater}; $self->{plater}->add_tin if $self->{plater};
}, undef, 'map_add.png'); }, undef, 'map_add.png');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub { wxTheApp->append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
$self->load_config_file; $self->load_config_file;
}, undef, 'plugin_add.png'); }, undef, 'plugin_add.png');
$self->_append_menu_item($fileMenu, "&Export Config…\tCtrl+E", 'Export current configuration to file', sub { wxTheApp->append_menu_item($fileMenu, "&Export Config…\tCtrl+E", 'Export current configuration to file', sub {
$self->export_config; $self->export_config;
}, undef, 'plugin_go.png'); }, undef, 'plugin_go.png');
$self->_append_menu_item($fileMenu, "&Load Config Bundle…", 'Load presets from a bundle', sub { wxTheApp->append_menu_item($fileMenu, "&Load Config Bundle…", 'Load presets from a bundle', sub {
$self->load_configbundle; $self->load_configbundle;
}, undef, 'lorry_add.png'); }, undef, 'lorry_add.png');
$self->_append_menu_item($fileMenu, "&Export Config Bundle…", 'Export all presets to file', sub { wxTheApp->append_menu_item($fileMenu, "&Export Config Bundle…", 'Export all presets to file', sub {
$self->export_configbundle; $self->export_configbundle;
}, undef, 'lorry_go.png'); }, undef, 'lorry_go.png');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
my $repeat; my $repeat;
$self->_append_menu_item($fileMenu, "Q&uick Slice…\tCtrl+U", 'Slice file', sub { wxTheApp->append_menu_item($fileMenu, "Q&uick Slice…\tCtrl+U", 'Slice file', sub {
wxTheApp->CallAfter(sub { wxTheApp->CallAfter(sub {
$self->quick_slice; $self->quick_slice;
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file); $repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
}); });
}, undef, 'cog_go.png'); }, undef, 'cog_go.png');
$self->_append_menu_item($fileMenu, "Quick Slice and Save &As…\tCtrl+Alt+U", 'Slice file and save as', sub { wxTheApp->append_menu_item($fileMenu, "Quick Slice and Save &As…\tCtrl+Alt+U", 'Slice file and save as', sub {
wxTheApp->CallAfter(sub { wxTheApp->CallAfter(sub {
$self->quick_slice(save_as => 1); $self->quick_slice(save_as => 1);
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file); $repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
}); });
}, undef, 'cog_go.png'); }, undef, 'cog_go.png');
$repeat = $self->_append_menu_item($fileMenu, "&Repeat Last Quick Slice\tCtrl+Shift+U", 'Repeat last quick slice', sub { $repeat = wxTheApp->append_menu_item($fileMenu, "&Repeat Last Quick Slice\tCtrl+Shift+U", 'Repeat last quick slice', sub {
wxTheApp->CallAfter(sub { wxTheApp->CallAfter(sub {
$self->quick_slice(reslice => 1); $self->quick_slice(reslice => 1);
}); });
}, undef, 'cog_go.png'); }, undef, 'cog_go.png');
$repeat->Enable(0); $repeat->Enable(0);
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub { wxTheApp->append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub {
$self->quick_slice(save_as => 1, export_svg => 1); $self->quick_slice(save_as => 1, export_svg => 1);
}, undef, 'shape_handles.png'); }, undef, 'shape_handles.png');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub { wxTheApp->append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub {
$self->repair_stl; $self->repair_stl;
}, undef, 'wrench.png'); }, undef, 'wrench.png');
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
# Cmd+, is standard on OS X - what about other operating systems? # Cmd+, is standard on OS X - what about other operating systems?
$self->_append_menu_item($fileMenu, "Preferences…\tCtrl+,", 'Application preferences', sub { wxTheApp->append_menu_item($fileMenu, "Preferences…\tCtrl+,", 'Application preferences', sub {
Slic3r::GUI::Preferences->new($self)->ShowModal; Slic3r::GUI::Preferences->new($self)->ShowModal;
}, wxID_PREFERENCES); }, wxID_PREFERENCES);
$fileMenu->AppendSeparator(); $fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, "&Quit", 'Quit Slic3r', sub { wxTheApp->append_menu_item($fileMenu, "&Quit", 'Quit Slic3r', sub {
$self->Close(0); $self->Close(0);
}, wxID_EXIT); }, wxID_EXIT);
} }
@ -174,40 +189,43 @@ sub _init_menubar {
$self->{plater_menu} = Wx::Menu->new; $self->{plater_menu} = Wx::Menu->new;
{ {
my $selectMenu = $self->{plater_select_menu} = Wx::Menu->new; my $selectMenu = $self->{plater_select_menu} = Wx::Menu->new;
my $selectMenuItem = $self->{plater_menu}->AppendSubMenu($selectMenu, "Select", 'Select an object in the plater'); wxTheApp->append_submenu($self->{plater_menu}, "Select", 'Select an object in the plater', $selectMenu, undef, 'brick.png');
wxTheApp->set_menu_item_icon($selectMenuItem, 'brick.png');
} }
$self->_append_menu_item($self->{plater_menu}, "Select Next Object\tCtrl+Right", 'Select Next Object in the plater', sub { wxTheApp->append_menu_item($self->{plater_menu}, "Select Next Object\tCtrl+Right", 'Select Next Object in the plater', sub {
$plater->select_next; $plater->select_next;
}, undef, 'arrow_right.png'); }, undef, 'arrow_right.png');
$self->_append_menu_item($self->{plater_menu}, "Select Prev Object\tCtrl+Left", 'Select Previous Object in the plater', sub { wxTheApp->append_menu_item($self->{plater_menu}, "Select Prev Object\tCtrl+Left", 'Select Previous Object in the plater', sub {
$plater->select_prev; $plater->select_prev;
}, undef, 'arrow_left.png'); }, undef, 'arrow_left.png');
wxTheApp->append_menu_item($self->{plater_menu}, "Zoom In\tCtrl+up", 'Zoom In',
sub { $self->{plater}->zoom('in') }, undef, 'zoom_in.png');
wxTheApp->append_menu_item($self->{plater_menu}, "Zoom Out\tCtrl+down", 'Zoom Out',
sub { $self->{plater}->zoom('out') }, undef, 'zoom_out.png');
$self->{plater_menu}->AppendSeparator(); $self->{plater_menu}->AppendSeparator();
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub { wxTheApp->append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
$plater->export_gcode; $plater->export_gcode;
}, undef, 'cog_go.png'); }, undef, 'cog_go.png');
$self->_append_menu_item($self->{plater_menu}, "Export plate as STL...", 'Export current plate as STL', sub { wxTheApp->append_menu_item($self->{plater_menu}, "Export plate as STL...", 'Export current plate as STL', sub {
$plater->export_stl; $plater->export_stl;
}, undef, 'brick_go.png'); }, undef, 'brick_go.png');
$self->_append_menu_item($self->{plater_menu}, "Export plate with modifiers as AMF...", 'Export current plate as AMF, including all modifier meshes', sub { wxTheApp->append_menu_item($self->{plater_menu}, "Export plate with modifiers as AMF...", 'Export current plate as AMF, including all modifier meshes', sub {
$plater->export_amf; $plater->export_amf;
}, undef, 'brick_go.png'); }, undef, 'brick_go.png');
$self->{object_menu} = $self->{plater}->object_menu; $self->{object_menu} = $self->{plater}->object_menu;
$self->on_plater_object_list_changed(0);
$self->on_plater_selection_changed(0); $self->on_plater_selection_changed(0);
} }
# Settings menu # Settings menu
my $settingsMenu = Wx::Menu->new; my $settingsMenu = Wx::Menu->new;
{ {
$self->_append_menu_item($settingsMenu, "P&rint Settings…\tCtrl+1", 'Show the print settings editor', sub { wxTheApp->append_menu_item($settingsMenu, "P&rint Settings…\tCtrl+1", 'Show the print settings editor', sub {
$self->{plater}->show_preset_editor('print'); $self->{plater}->show_preset_editor('print');
}, undef, 'cog.png'); }, undef, 'cog.png');
$self->_append_menu_item($settingsMenu, "&Filament Settings…\tCtrl+2", 'Show the filament settings editor', sub { wxTheApp->append_menu_item($settingsMenu, "&Filament Settings…\tCtrl+2", 'Show the filament settings editor', sub {
$self->{plater}->show_preset_editor('filament'); $self->{plater}->show_preset_editor('filament');
}, undef, 'spool.png'); }, undef, 'spool.png');
$self->_append_menu_item($settingsMenu, "Print&er Settings…\tCtrl+3", 'Show the printer settings editor', sub { wxTheApp->append_menu_item($settingsMenu, "Print&er Settings…\tCtrl+3", 'Show the printer settings editor', sub {
$self->{plater}->show_preset_editor('printer'); $self->{plater}->show_preset_editor('printer');
}, undef, 'printer_empty.png'); }, undef, 'printer_empty.png');
} }
@ -215,15 +233,15 @@ sub _init_menubar {
# View menu # View menu
{ {
$self->{viewMenu} = Wx::Menu->new; $self->{viewMenu} = Wx::Menu->new;
$self->_append_menu_item($self->{viewMenu}, "Top\tCtrl+4" , 'Top View' , sub { $self->select_view('top' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Top\tCtrl+4" , 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, "Bottom\tCtrl+5" , 'Bottom View' , sub { $self->select_view('bottom' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Bottom\tCtrl+5" , 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, "Left\tCtrl+6" , 'Left View' , sub { $self->select_view('left' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Left\tCtrl+6" , 'Left View' , sub { $self->select_view('left' ); });
$self->_append_menu_item($self->{viewMenu}, "Right\tCtrl+7" , 'Right View' , sub { $self->select_view('right' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Right\tCtrl+7" , 'Right View' , sub { $self->select_view('right' ); });
$self->_append_menu_item($self->{viewMenu}, "Front\tCtrl+8" , 'Front View' , sub { $self->select_view('front' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Front\tCtrl+8" , 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, "Back\tCtrl+9" , 'Back View' , sub { $self->select_view('back' ); }); wxTheApp->append_menu_item($self->{viewMenu}, "Back\tCtrl+9" , 'Back View' , sub { $self->select_view('back' ); });
$self->_append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); }); wxTheApp->append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); });
$self->{viewMenu}->AppendSeparator(); $self->{viewMenu}->AppendSeparator();
$self->{color_toolpaths_by_role} = $self->_append_menu_item($self->{viewMenu}, $self->{color_toolpaths_by_role} = wxTheApp->append_menu_item($self->{viewMenu},
"Color Toolpaths by Role", "Color Toolpaths by Role",
'Color toolpaths according to perimeter/infill/support material', 'Color toolpaths according to perimeter/infill/support material',
sub { sub {
@ -233,7 +251,7 @@ sub _init_menubar {
}, },
undef, undef, wxITEM_RADIO undef, undef, wxITEM_RADIO
); );
$self->{color_toolpaths_by_extruder} = $self->_append_menu_item($self->{viewMenu}, $self->{color_toolpaths_by_extruder} = wxTheApp->append_menu_item($self->{viewMenu},
"Color Toolpaths by Filament", "Color Toolpaths by Filament",
'Color toolpaths using the configured extruder/filament color', 'Color toolpaths using the configured extruder/filament color',
sub { sub {
@ -253,13 +271,13 @@ sub _init_menubar {
# Window menu # Window menu
my $windowMenu = Wx::Menu->new; my $windowMenu = Wx::Menu->new;
{ {
$self->_append_menu_item($windowMenu, "&Plater\tCtrl+T", 'Show the plater', sub { wxTheApp->append_menu_item($windowMenu, "&Plater\tCtrl+T", 'Show the plater', sub {
$self->select_tab(0); $self->select_tab(0);
}, undef, 'application_view_tile.png'); }, undef, 'application_view_tile.png');
$self->_append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub { wxTheApp->append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub {
$self->select_tab(1); $self->select_tab(1);
}, undef, 'printer_empty.png') unless ($Slic3r::GUI::Settings->{_}{no_controller}); }, undef, 'printer_empty.png');
$self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub { wxTheApp->append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
$self->{plater}->pause_background_process; $self->{plater}->pause_background_process;
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal; Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
$self->{plater}->resume_background_process; $self->{plater}->resume_background_process;
@ -269,22 +287,22 @@ sub _init_menubar {
# Help menu # Help menu
my $helpMenu = Wx::Menu->new; my $helpMenu = Wx::Menu->new;
{ {
$self->_append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub { wxTheApp->append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub {
$self->config_wizard; $self->config_wizard;
}); });
$helpMenu->AppendSeparator(); $helpMenu->AppendSeparator();
$self->_append_menu_item($helpMenu, "Slic3r &Website", 'Open the Slic3r website in your browser', sub { wxTheApp->append_menu_item($helpMenu, "Slic3r &Website", 'Open the Slic3r website in your browser', sub {
Wx::LaunchDefaultBrowser('http://slic3r.org/'); Wx::LaunchDefaultBrowser('http://slic3r.org/');
}); });
my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub { my $versioncheck = wxTheApp->append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
wxTheApp->check_version(1); wxTheApp->check_version(1);
}); });
$versioncheck->Enable(wxTheApp->have_version_check); $versioncheck->Enable(wxTheApp->have_version_check);
$self->_append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub { wxTheApp->append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub {
Wx::LaunchDefaultBrowser('http://manual.slic3r.org/'); Wx::LaunchDefaultBrowser('http://manual.slic3r.org/');
}); });
$helpMenu->AppendSeparator(); $helpMenu->AppendSeparator();
$self->_append_menu_item($helpMenu, "&About Slic3r", 'Show about dialog', sub { wxTheApp->append_menu_item($helpMenu, "&About Slic3r", 'Show about dialog', sub {
wxTheApp->about; wxTheApp->about;
}); });
} }
@ -310,6 +328,14 @@ sub is_loaded {
return $self->{loaded}; return $self->{loaded};
} }
sub on_plater_object_list_changed {
my ($self, $have_objects) = @_;
return if !defined $self->{plater_menu};
$self->{plater_menu}->Enable($_->GetId, $have_objects)
for $self->{plater_menu}->GetMenuItems;
}
sub on_plater_selection_changed { sub on_plater_selection_changed {
my ($self, $have_selection) = @_; my ($self, $have_selection) = @_;
@ -458,9 +484,9 @@ sub repair_stl {
} }
my $tmesh = Slic3r::TriangleMesh->new; my $tmesh = Slic3r::TriangleMesh->new;
$tmesh->ReadSTLFile(Slic3r::encode_path($input_file)); $tmesh->ReadSTLFile($input_file);
$tmesh->repair; $tmesh->repair;
$tmesh->WriteOBJFile(Slic3r::encode_path($output_file)); $tmesh->WriteOBJFile($output_file);
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair"); Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
} }
@ -631,15 +657,4 @@ sub select_view {
$self->{plater}->select_view($direction); $self->{plater}->select_view($direction);
} }
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description, $kind);
wxTheApp->set_menu_item_icon($item, $icon);
EVT_MENU($self, $id, $cb);
return $item;
}
1; 1;

View File

@ -50,7 +50,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config->new_from_defaults(qw( $self->{config} = Slic3r::Config->new_from_defaults(qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width
serial_port serial_speed octoprint_host octoprint_apikey overridable filament_colour serial_port serial_speed host_type print_host octoprint_apikey shortcuts filament_colour
)); ));
$self->{model} = Slic3r::Model->new; $self->{model} = Slic3r::Model->new;
$self->{print} = Slic3r::Print->new; $self->{print} = Slic3r::Print->new;
@ -329,7 +329,6 @@ sub new {
if ($self->{preview3D}) { if ($self->{preview3D}) {
$self->{preview3D}->set_bed_shape($self->{config}->bed_shape); $self->{preview3D}->set_bed_shape($self->{config}->bed_shape);
} }
$self->on_model_change;
{ {
my $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 3, 1, 2); my $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 3, 1, 2);
@ -373,9 +372,37 @@ sub new {
{ {
my $o = $self->{settings_override_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, my $o = $self->{settings_override_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self,
on_change => sub { on_change => sub {
my ($opt_key) = @_;
my ($preset) = $self->selected_presets('print');
$preset->load_config;
# If this option is not in the override panel it means it was manually deleted,
# so let's restore the profile value.
if (!$self->{settings_override_config}->has($opt_key)) {
$preset->_dirty_config->set($opt_key, $preset->_config->get($opt_key));
} else {
# Apply the overrides to the current Print preset, potentially making it dirty
$preset->_dirty_config->apply($self->{settings_override_config});
# If this is a configured shortcut (and not just a dirty option),
# save it now.
if (any { $_ eq $opt_key } @{$preset->dirty_config->shortcuts}) {
$preset->save([$opt_key]);
}
}
$self->load_presets;
$self->config_changed; $self->config_changed;
# Reload the open tab if any
if (my $print_tab = $self->GetFrame->{preset_editor_tabs}{print}) {
$print_tab->load_presets;
$print_tab->reload_preset;
}
}); });
$o->set_editable(1); $o->can_add(0);
$o->can_delete(1);
$o->set_opt_keys([ Slic3r::GUI::PresetEditor::Print->options ]); $o->set_opt_keys([ Slic3r::GUI::PresetEditor::Print->options ]);
$self->{settings_override_config} = Slic3r::Config->new; $self->{settings_override_config} = Slic3r::Config->new;
$o->set_default_config($self->{settings_override_config}); $o->set_default_config($self->{settings_override_config});
@ -541,6 +568,14 @@ sub _on_change_combobox {
return 0 if !$self->prompt_unsaved_changes; return 0 if !$self->prompt_unsaved_changes;
} }
wxTheApp->CallAfter(sub { wxTheApp->CallAfter(sub {
# Close the preset editor tab if any
if (exists $self->GetFrame->{preset_editor_tabs}{$group}) {
my $tabpanel = $self->GetFrame->{tabpanel};
$tabpanel->DeletePage($tabpanel->GetPageIndex($self->GetFrame->{preset_editor_tabs}{$group}));
delete $self->GetFrame->{preset_editor_tabs}{$group};
$tabpanel->SetSelection(0); # without this, a newly created tab will not be selected by wx
}
$self->_on_select_preset($group); $self->_on_select_preset($group);
# This will remove the "(modified)" mark from any dirty preset handled here. # This will remove the "(modified)" mark from any dirty preset handled here.
@ -569,31 +604,19 @@ sub _on_select_preset {
my $o_config = $self->{settings_override_config}; my $o_config = $self->{settings_override_config};
my $o_panel = $self->{settings_override_panel}; my $o_panel = $self->{settings_override_panel};
if ($changed) { my $shortcuts = $config->get('shortcuts');
# Preserve current options if re-selecting the same preset
$o_config->clear;
}
my $overridable = $config->get('overridable'); # Re-populate the override panel with the configured shortcuts
# and the dirty options.
# Add/remove options (we do it this way for preserving current options) $o_config->clear;
foreach my $opt_key (@$overridable) { foreach my $opt_key (@$shortcuts, $presets[0]->dirty_options) {
# Populate option with the default value taken from configuration # Don't add shortcut for shortcuts!
# (re-set the override always, because if we here it means user next if $opt_key eq 'shortcuts';
# switched to this preset or opened/closed the editor, so he expects
# the new values set in the editor to be used).
$o_config->set($opt_key, $config->get($opt_key)); $o_config->set($opt_key, $config->get($opt_key));
} }
foreach my $opt_key (@{$o_config->get_keys}) {
# Keep options listed among overridable and options added on the fly
if ((none { $_ eq $opt_key } @$overridable)
&& (any { $_ eq $opt_key } $o_panel->fixed_options)) {
$o_config->erase($opt_key);
}
}
$o_panel->set_default_config($config); $o_panel->set_default_config($config);
$o_panel->set_fixed_options(\@$overridable); $o_panel->set_fixed_options(\@$shortcuts);
$o_panel->update_optgroup; $o_panel->update_optgroup;
} elsif ($group eq 'printer') { } elsif ($group eq 'printer') {
# reload print and filament settings to honor their compatible_printer options # reload print and filament settings to honor their compatible_printer options
@ -706,7 +729,7 @@ sub load_presets {
} }
} }
$self->{print}->placeholder_parser->set("${group}_preset", [ @preset_names ]); $self->{print}->placeholder_parser->set_multiple("${group}_preset", [ @preset_names ]);
} }
} }
@ -749,19 +772,54 @@ sub selected_presets {
sub show_preset_editor { sub show_preset_editor {
my ($self, $group, $i) = @_; my ($self, $group, $i) = @_;
my $class = "Slic3r::GUI::PresetEditorDialog::" . ucfirst($group); wxTheApp->CallAfter(sub {
my $dlg = $class->new($self); my @presets = $self->selected_presets($group);
my @presets = $self->selected_presets($group); my $preset_editor;
$dlg->preset_editor->select_preset_by_name($presets[$i // 0]->name); my $dlg;
$dlg->ShowModal; my $mainframe = $self->GetFrame;
my $tabpanel = $mainframe->{tabpanel};
if (exists $mainframe->{preset_editor_tabs}{$group}) {
# we already have an open editor
$tabpanel->SetSelection($tabpanel->GetPageIndex($mainframe->{preset_editor_tabs}{$group}));
return;
} elsif ($Slic3r::GUI::Settings->{_}{tabbed_preset_editors}) {
my $class = "Slic3r::GUI::PresetEditor::" . ucfirst($group);
$mainframe->{preset_editor_tabs}{$group} = $preset_editor = $class->new($self->GetFrame);
$tabpanel->AddPage($preset_editor, ucfirst($group) . " Settings", 1);
} else {
my $class = "Slic3r::GUI::PresetEditorDialog::" . ucfirst($group);
$dlg = $class->new($self);
$preset_editor = $dlg->preset_editor;
}
# Re-load the presets as they might have changed. $preset_editor->select_preset_by_name($presets[$i // 0]->name);
$self->load_presets; $preset_editor->on_value_change(sub {
# Re-load the presets in order to toggle the (modified) suffix
$self->load_presets;
# Select the preset that was last selected in the editor. # Update shortcuts
$self->select_preset_by_name $self->_on_select_preset($group);
($dlg->preset_editor->current_preset->name, $group, $i, 1);
# Use the new config wherever we actually use its contents
$self->config_changed;
});
my $cb = sub {
my ($group, $preset) = @_;
# Re-load the presets as they might have changed.
$self->load_presets;
# Select the preset in plater too
$self->select_preset_by_name($preset->name, $group, $i, 1);
};
$preset_editor->on_select_preset($cb);
$preset_editor->on_save_preset($cb);
if ($dlg) {
$dlg->Show;
}
});
} }
# Returns the current config by merging the selected presets and the overrides. # Returns the current config by merging the selected presets and the overrides.
@ -772,7 +830,7 @@ sub config {
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new_from_defaults;
# get defaults also for the values tracked by the Plater's config # get defaults also for the values tracked by the Plater's config
# (for example 'overridable') # (for example 'shortcuts')
$config->apply(Slic3r::Config->new_from_defaults(@{$self->{config}->get_keys})); $config->apply(Slic3r::Config->new_from_defaults(@{$self->{config}->get_keys}));
my %classes = map { $_ => "Slic3r::GUI::PresetEditor::".ucfirst($_) } my %classes = map { $_ => "Slic3r::GUI::PresetEditor::".ucfirst($_) }
@ -893,6 +951,11 @@ sub load_file {
sub load_model_objects { sub load_model_objects {
my ($self, @model_objects) = @_; my ($self, @model_objects) = @_;
# Always restart background process when adding new objects.
# This prevents lack of processing in some circumstances when background process is
# running but adding a new object does not invalidate anything.
$self->stop_background_process;
my $bed_centerf = $self->bed_centerf; my $bed_centerf = $self->bed_centerf;
my $bed_shape = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape}); my $bed_shape = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape});
my $bed_size = $bed_shape->bounding_box->size; my $bed_size = $bed_shape->bounding_box->size;
@ -1353,8 +1416,8 @@ sub config_changed {
$self->{btn_print}->Hide; $self->{btn_print}->Hide;
} }
$self->Layout; $self->Layout;
} elsif ($opt_key eq 'octoprint_host') { } elsif ($opt_key eq 'print_host') {
if ($config->get('octoprint_host')) { if ($config->get('print_host')) {
$self->{btn_send_gcode}->Show; $self->{btn_send_gcode}->Show;
} else { } else {
$self->{btn_send_gcode}->Hide; $self->{btn_send_gcode}->Hide;
@ -1506,7 +1569,7 @@ sub pause_background_process {
return 1; return 1;
} elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) { } elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) {
$self->{apply_config_timer}->Stop; $self->{apply_config_timer}->Stop;
return 1; return 0; # we didn't actually pause any running thread; need to reschedule
} }
return 0; return 0;
@ -1677,7 +1740,7 @@ sub on_export_completed {
$message = "File added to print queue"; $message = "File added to print queue";
$do_print = 1; $do_print = 1;
} elsif ($self->{send_gcode_file}) { } elsif ($self->{send_gcode_file}) {
$message = "Sending G-code file to the OctoPrint server..."; $message = "Sending G-code file to the " . $self->{config}->host_type . " server...";
$send_gcode = 1; $send_gcode = 1;
} else { } else {
$message = "G-code file exported to " . $self->{export_gcode_output_file}; $message = "G-code file exported to " . $self->{export_gcode_output_file};
@ -1721,8 +1784,7 @@ sub do_print {
my %current_presets = $self->selected_presets; my %current_presets = $self->selected_presets;
my $printer_name = $current_presets{printer}->[0]->name; my $printer_panel = $controller->add_printer($current_presets{printer}->[0], $self->config);
my $printer_panel = $controller->add_printer($printer_name, $self->config);
my $filament_stats = $self->{print}->filament_stats; my $filament_stats = $self->{print}->filament_stats;
$filament_stats = { map { $current_presets{filament}[$_]->name => $filament_stats->{$_} } keys %$filament_stats }; $filament_stats = { map { $current_presets{filament}[$_]->name => $filament_stats->{$_} } keys %$filament_stats };
@ -1751,23 +1813,33 @@ sub prepare_send {
my $ua = LWP::UserAgent->new; my $ua = LWP::UserAgent->new;
$ua->timeout(5); $ua->timeout(5);
my $res = $ua->get( my $res;
"http://" . $self->{config}->octoprint_host . "/api/files/local", if ($self->{config}->print_host) {
'X-Api-Key' => $self->{config}->octoprint_apikey, if($self->{config}->host_type eq 'octoprint'){
); $res = $ua->get(
"http://" . $self->{config}->print_host . "/api/files/local",
'X-Api-Key' => $self->{config}->octoprint_apikey,
);
}else {
$res = $ua->get(
"http://" . $self->{config}->print_host . "/rr_files",
);
}
}
$progress->Destroy; $progress->Destroy;
if ($res->is_success) { if ($res->is_success) {
if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { my $searchterm = ($self->{config}->host_type eq 'octoprint') ? '/"name":\s*"\Q$filename\E"/' : '"'.$filename.'"';
if ($res->decoded_content =~ $searchterm) {
my $dialog = Wx::MessageDialog->new($self, my $dialog = Wx::MessageDialog->new($self,
"It looks like a file with the same name already exists in the server. " "It looks like a file with the same name already exists in the server. "
. "Shall I overwrite it?", . "Shall I overwrite it?",
'OctoPrint', wxICON_WARNING | wxYES | wxNO); $self->{config}->host_type, wxICON_WARNING | wxYES | wxNO);
if ($dialog->ShowModal() == wxID_NO) { if ($dialog->ShowModal() == wxID_NO) {
return; return;
} }
} }
} else { } else {
my $message = "Error while connecting to the OctoPrint server: " . $res->status_line; my $message = "Error while connecting to the " . $self->{config}->host_type . " server: " . $res->status_line;
Slic3r::GUI::show_error($self, $message); Slic3r::GUI::show_error($self, $message);
return; return;
} }
@ -1786,24 +1858,52 @@ sub send_gcode {
$ua->timeout(180); $ua->timeout(180);
my $path = Slic3r::encode_path($self->{send_gcode_file}); my $path = Slic3r::encode_path($self->{send_gcode_file});
my $res = $ua->post( my $filename = basename($self->{print}->output_filepath($main::opt{output} // ''));
"http://" . $self->{config}->octoprint_host . "/api/files/local", my $res;
Content_Type => 'form-data', if($self->{config}->print_host){
'X-Api-Key' => $self->{config}->octoprint_apikey, if($self->{config}->host_type eq 'octoprint'){
Content => [ $res = $ua->post(
# OctoPrint doesn't like Windows paths so we use basename() "http://" . $self->{config}->print_host . "/api/files/local",
# Also, since we need to read from filesystem we process it through encode_path() Content_Type => 'form-data',
file => [ $path, basename($path) ], 'X-Api-Key' => $self->{config}->octoprint_apikey,
print => $self->{send_gcode_file_print} ? 1 : 0, Content => [
], # OctoPrint doesn't like Windows paths so we use basename()
); # Also, since we need to read from filesystem we process it through encode_path()
file => [ $path, basename($path) ],
print => $self->{send_gcode_file_print} ? 1 : 0,
],
);
}else{
# slurp the file we would send into a string - should be someplace to reference this but could not find it?
local $/=undef;
open my $gch,$path;
my $gcode=<$gch>;
close($gch);
# get the time string
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my $t = sprintf("%4d-%02d-%02dT%02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec);
my $req = HTTP::Request->new(POST => "http://" . $self->{config}->print_host . "/rr_upload?name=0:/gcodes/" . basename($path) . "&time=$t",);
$req->content( $gcode );
$res = $ua->request($req);
if ($res->is_success) {
if ($self->{send_gcode_file_print}) {
$res = $ua->get(
"http://" . $self->{config}->print_host . "/rr_gcode?gcode=M32%20" . basename($path),
);
}
}
}
}
$self->statusbar->StopBusy; $self->statusbar->StopBusy;
if ($res->is_success) { if ($res->is_success) {
$self->statusbar->SetStatusText("G-code file successfully uploaded to the OctoPrint server"); $self->statusbar->SetStatusText("G-code file successfully uploaded to the " . $self->{config}->host_type . " server");
} else { } else {
my $message = "Error while uploading to the OctoPrint server: " . $res->status_line; my $message = "Error while uploading to the " . $self->{config}->host_type . " server: " . $res->status_line;
Slic3r::GUI::show_error($self, $message); Slic3r::GUI::show_error($self, $message);
$self->statusbar->SetStatusText($message); $self->statusbar->SetStatusText($message);
} }
@ -1971,7 +2071,7 @@ sub on_model_change {
if ($count > 1) { if ($count > 1) {
$name .= " (${count}x)"; $name .= " (${count}x)";
} }
my $item = $self->GetFrame->_append_menu_item($menu, $name, 'Select object', sub { my $item = wxTheApp->append_menu_item($menu, $name, 'Select object', sub {
$self->select_object($i); $self->select_object($i);
$self->refresh_canvases; $self->refresh_canvases;
}, undef, undef, wxITEM_CHECK); }, undef, undef, wxITEM_CHECK);
@ -2187,6 +2287,9 @@ sub object_list_changed {
$self->{htoolbar}->EnableTool($_, $have_objects) $self->{htoolbar}->EnableTool($_, $have_objects)
for (TB_RESET, TB_ARRANGE); for (TB_RESET, TB_ARRANGE);
} }
# prepagate the event to the frame (a custom Wx event would be cleaner)
$self->GetFrame->on_plater_object_list_changed($have_objects);
} }
sub selection_changed { sub selection_changed {
@ -2335,108 +2438,112 @@ sub object_menu {
my $frame = $self->GetFrame; my $frame = $self->GetFrame;
my $menu = Wx::Menu->new; my $menu = Wx::Menu->new;
$frame->_append_menu_item($menu, "Delete\tCtrl+Del", 'Remove the selected object', sub { wxTheApp->append_menu_item($menu, "Delete\tCtrl+Del", 'Remove the selected object', sub {
$self->remove; $self->remove;
}, undef, 'brick_delete.png'); }, undef, 'brick_delete.png');
$frame->_append_menu_item($menu, "Increase copies\tCtrl++", 'Place one more copy of the selected object', sub { wxTheApp->append_menu_item($menu, "Increase copies\tCtrl++", 'Place one more copy of the selected object', sub {
$self->increase; $self->increase;
}, undef, 'add.png'); }, undef, 'add.png');
$frame->_append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub { wxTheApp->append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub {
$self->decrease; $self->decrease;
}, undef, 'delete.png'); }, undef, 'delete.png');
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub { wxTheApp->append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
$self->set_number_of_copies; $self->set_number_of_copies;
}, undef, 'textfield.png'); }, undef, 'textfield.png');
$menu->AppendSeparator(); $menu->AppendSeparator();
$frame->_append_menu_item($menu, "Move to bed center", 'Center object around bed center', sub { wxTheApp->append_menu_item($menu, "Move to bed center", 'Center object around bed center', sub {
$self->center_selected_object_on_bed; $self->center_selected_object_on_bed;
}, undef, 'arrow_in.png'); }, undef, 'arrow_in.png');
$frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub { wxTheApp->append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
$self->rotate(-45); $self->rotate(-45);
}, undef, 'arrow_rotate_clockwise.png'); }, undef, 'arrow_rotate_clockwise.png');
$frame->_append_menu_item($menu, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub { wxTheApp->append_menu_item($menu, "Rotate 45° counter-clockwise", 'Rotate the selected object by 45° counter-clockwise', sub {
$self->rotate(+45); $self->rotate(+45);
}, undef, 'arrow_rotate_anticlockwise.png'); }, undef, 'arrow_rotate_anticlockwise.png');
my $rotateMenu = Wx::Menu->new; {
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle'); my $rotateMenu = Wx::Menu->new;
wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png'); wxTheApp->append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub {
$frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub { $self->rotate(undef, X);
$self->rotate(undef, X); }, undef, 'bullet_red.png');
}, undef, 'bullet_red.png'); wxTheApp->append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub {
$frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub { $self->rotate(undef, Y);
$self->rotate(undef, Y); }, undef, 'bullet_green.png');
}, undef, 'bullet_green.png'); wxTheApp->append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
$frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub { $self->rotate(undef, Z);
$self->rotate(undef, Z); }, undef, 'bullet_blue.png');
}, undef, 'bullet_blue.png'); wxTheApp->append_submenu($menu, "Rotate", 'Rotate the selected object by an arbitrary angle', $rotateMenu, undef, 'textfield.png');
}
my $mirrorMenu = Wx::Menu->new; {
my $mirrorMenuItem = $menu->AppendSubMenu($mirrorMenu, "Mirror", 'Mirror the selected object'); my $mirrorMenu = Wx::Menu->new;
wxTheApp->set_menu_item_icon($mirrorMenuItem, 'shape_flip_horizontal.png'); wxTheApp->append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
$frame->_append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub { $self->mirror(X);
$self->mirror(X); }, undef, 'bullet_red.png');
}, undef, 'bullet_red.png'); wxTheApp->append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub {
$frame->_append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub { $self->mirror(Y);
$self->mirror(Y); }, undef, 'bullet_green.png');
}, undef, 'bullet_green.png'); wxTheApp->append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub {
$frame->_append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub { $self->mirror(Z);
$self->mirror(Z); }, undef, 'bullet_blue.png');
}, undef, 'bullet_blue.png'); wxTheApp->append_submenu($menu, "Mirror", 'Mirror the selected object', $mirrorMenu, undef, 'shape_flip_horizontal.png');
}
my $scaleMenu = Wx::Menu->new; {
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis'); my $scaleMenu = Wx::Menu->new;
wxTheApp->set_menu_item_icon($scaleMenuItem, 'arrow_out.png'); wxTheApp->append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$frame->_append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub { $self->changescale(undef);
$self->changescale(undef); });
}); wxTheApp->append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { $self->changescale(X);
$self->changescale(X); }, undef, 'bullet_red.png');
}, undef, 'bullet_red.png'); wxTheApp->append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$frame->_append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub { $self->changescale(Y);
$self->changescale(Y); }, undef, 'bullet_green.png');
}, undef, 'bullet_green.png'); wxTheApp->append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$frame->_append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub { $self->changescale(Z);
$self->changescale(Z); }, undef, 'bullet_blue.png');
}, undef, 'bullet_blue.png'); wxTheApp->append_submenu($menu, "Scale", 'Scale the selected object by a given factor', $scaleMenu, undef, 'arrow_out.png');
}
my $scaleToSizeMenu = Wx::Menu->new; {
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis'); my $scaleToSizeMenu = Wx::Menu->new;
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png'); wxTheApp->append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub { $self->changescale(undef, 1);
$self->changescale(undef, 1); });
}); wxTheApp->append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { $self->changescale(X, 1);
$self->changescale(X, 1); }, undef, 'bullet_red.png');
}, undef, 'bullet_red.png'); wxTheApp->append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub { $self->changescale(Y, 1);
$self->changescale(Y, 1); }, undef, 'bullet_green.png');
}, undef, 'bullet_green.png'); wxTheApp->append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub { $self->changescale(Z, 1);
$self->changescale(Z, 1); }, undef, 'bullet_blue.png');
}, undef, 'bullet_blue.png'); wxTheApp->append_submenu($menu, "Scale to size", 'Scale the selected object to match a given size', $scaleToSizeMenu, undef, 'arrow_out.png');
}
$frame->_append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub { wxTheApp->append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub {
$self->split_object; $self->split_object;
}, undef, 'shape_ungroup.png'); }, undef, 'shape_ungroup.png');
$frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub { wxTheApp->append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub {
$self->object_cut_dialog; $self->object_cut_dialog;
}, undef, 'package.png'); }, undef, 'package.png');
$frame->_append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub { $frame->_append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub {
$self->object_layers_dialog; $self->object_layers_dialog;
}, undef, 'cog.png'); }, undef, 'cog.png');
$menu->AppendSeparator(); $menu->AppendSeparator();
$frame->_append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub { wxTheApp->append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub {
$self->object_settings_dialog; $self->object_settings_dialog;
}, undef, 'cog.png'); }, undef, 'cog.png');
$menu->AppendSeparator(); $menu->AppendSeparator();
$frame->_append_menu_item($menu, "Reload from Disk", 'Reload the selected file from Disk', sub { wxTheApp->append_menu_item($menu, "Reload from Disk", 'Reload the selected file from Disk', sub {
$self->reload_from_disk; $self->reload_from_disk;
}, undef, 'arrow_refresh.png'); }, undef, 'arrow_refresh.png');
$frame->_append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub { wxTheApp->append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub {
$self->export_object_stl; $self->export_object_stl;
}, undef, 'brick_go.png'); }, undef, 'brick_go.png');
$frame->_append_menu_item($menu, "Export object and modifiers as AMF…", 'Export this single object and all associated modifiers as AMF file', sub { wxTheApp->append_menu_item($menu, "Export object and modifiers as AMF…", 'Export this single object and all associated modifiers as AMF file', sub {
$self->export_object_amf; $self->export_object_amf;
}, undef, 'brick_go.png'); }, undef, 'brick_go.png');
@ -2457,6 +2564,21 @@ sub select_view {
} }
} }
sub zoom{
my ($self, $direction) = @_;
#Apply Zoom to the current active tab
my ($currentSelection) = $self->{preview_notebook}->GetSelection;
if($currentSelection == 0){
$self->{canvas3D}->zoom($direction) if($self->{canvas3D});
}
elsif($currentSelection == 2){ #3d Preview tab
$self->{preview3D}->canvas->zoom($direction) if($self->{preview3D});
}
elsif($currentSelection == 3) { #2D toolpaths tab
$self->{toolpaths2D}->{canvas}->zoom($direction) if($self->{toolpaths2D});
}
}
package Slic3r::GUI::Plater::DropTarget; package Slic3r::GUI::Plater::DropTarget;
use Wx::DND; use Wx::DND;
use base 'Wx::FileDropTarget'; use base 'Wx::FileDropTarget';
@ -2546,7 +2668,7 @@ use base 'Wx::Dialog';
sub new { sub new {
my $class = shift; my $class = shift;
my ($parent, $filename) = @_; my ($parent, $filename) = @_;
my $self = $class->SUPER::new($parent, -1, "Send to OctoPrint", wxDefaultPosition, my $self = $class->SUPER::new($parent, -1, "Send to Server", wxDefaultPosition,
[400, -1]); [400, -1]);
$self->{filename} = $filename; $self->{filename} = $filename;
@ -2555,7 +2677,7 @@ sub new {
my $optgroup; my $optgroup;
$optgroup = Slic3r::GUI::OptionsGroup->new( $optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $self, parent => $self,
title => 'Send to OctoPrint', title => 'Send to Server',
on_change => sub { on_change => sub {
my ($opt_id) = @_; my ($opt_id) = @_;

View File

@ -60,6 +60,8 @@ sub new {
} elsif ($key == 68 || $key == 317) { } elsif ($key == 68 || $key == 317) {
$slider->SetValue($slider->GetValue - 1); $slider->SetValue($slider->GetValue - 1);
$self->set_z($self->{layers_z}[$slider->GetValue]); $self->set_z($self->{layers_z}[$slider->GetValue]);
} else {
$event->Skip;
} }
}); });
@ -217,6 +219,20 @@ sub new {
return $self; return $self;
} }
sub zoom{
my($self, $direction) = @_;
if( $direction eq 'in'){
$self->_zoom($self->_zoom / (1+0.3));
}
elsif($direction eq 'out'){
$self->_zoom($self->_zoom / (1-0.3));
$self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
}
#apply changes
$self->_dirty(1);
$self->Refresh;
}
sub mouse_event { sub mouse_event {
my ($self, $e) = @_; my ($self, $e) = @_;

View File

@ -58,6 +58,8 @@ sub new {
} elsif ($key == 68 || $key == 317) { } elsif ($key == 68 || $key == 317) {
$slider->SetValue($slider->GetValue - 1); $slider->SetValue($slider->GetValue - 1);
$self->set_z($self->{layers_z}[$slider->GetValue]); $self->set_z($self->{layers_z}[$slider->GetValue]);
} else {
$event->Skip;
} }
}); });

View File

@ -164,34 +164,43 @@ sub new {
my $frame = $self->GetFrame; my $frame = $self->GetFrame;
my $menu = Wx::Menu->new; my $menu = Wx::Menu->new;
my $scaleToSizeMenu = Wx::Menu->new; {
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis'); my $scaleMenu = Wx::Menu->new;
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png'); wxTheApp->append_menu_item($scaleMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes',
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes', sub { sub { $self->changescale(undef, 0) });
$self->changescale(undef, 1); wxTheApp->append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis',
}); sub { $self->changescale(X, 0) }, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub { wxTheApp->append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis',
$self->changescale(X, 1); sub { $self->changescale(Y, 0) }, undef, 'bullet_green.png');
}, undef, 'bullet_red.png'); wxTheApp->append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis',
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub { sub { $self->changescale(Z, 0) }, undef, 'bullet_blue.png');
$self->changescale(Y, 1); wxTheApp->append_submenu($menu, "Scale", 'Scale the selected object by a given factor',
}, undef, 'bullet_green.png'); $scaleMenu, undef, 'arrow_out.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub { }
$self->changescale(Z, 1); {
}, undef, 'bullet_blue.png'); my $scaleToSizeMenu = Wx::Menu->new;
my $rotateMenu = Wx::Menu->new; wxTheApp->append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes',
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle'); sub { $self->changescale(undef, 1) });
wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png'); wxTheApp->append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis',
$frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub { sub { $self->changescale(X, 1) }, undef, 'bullet_red.png');
$self->rotate(undef, X); wxTheApp->append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis',
}, undef, 'bullet_red.png'); sub { $self->changescale(Y, 1) }, undef, 'bullet_green.png');
$frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub { wxTheApp->append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis',
$self->rotate(undef, Y); sub { $self->changescale(Z, 1) }, undef, 'bullet_blue.png');
}, undef, 'bullet_green.png'); wxTheApp->append_submenu($menu, "Scale to size", 'Scale the selected object to match a given size',
$frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub { $scaleToSizeMenu, undef, 'arrow_out.png');
$self->rotate(undef, Z); }
}, undef, 'bullet_blue.png'); {
my $rotateMenu = Wx::Menu->new;
wxTheApp->append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis',
sub { $self->rotate(undef, X) }, undef, 'bullet_red.png');
wxTheApp->append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis',
sub { $self->rotate(undef, Y) }, undef, 'bullet_green.png');
wxTheApp->append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis',
sub { $self->rotate(undef, Z) }, undef, 'bullet_blue.png');
wxTheApp->append_submenu($menu, "Rotate", 'Rotate the selected object by an arbitrary angle',
$rotateMenu, undef, 'arrow_rotate_anticlockwise.png');
}
$frame->PopupMenu($menu, $event->GetPoint); $frame->PopupMenu($menu, $event->GetPoint);
}); });
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) }); EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
@ -409,7 +418,6 @@ sub on_btn_lambda {
# set a default extruder value, since user can't add it manually # set a default extruder value, since user can't add it manually
$new_volume->config->set_ifndef('extruder', 0); $new_volume->config->set_ifndef('extruder', 0);
$self->{parts_changed} = 1;
$self->_parts_changed($self->{model_object}->volumes_count-1); $self->_parts_changed($self->{model_object}->volumes_count-1);
} }
@ -436,6 +444,7 @@ sub on_btn_delete {
sub _parts_changed { sub _parts_changed {
my ($self, $selected_volume_idx) = @_; my ($self, $selected_volume_idx) = @_;
$self->{parts_changed} = 1;
$self->reload_tree($selected_volume_idx); $self->reload_tree($selected_volume_idx);
if ($self->{canvas}) { if ($self->{canvas}) {
$self->{canvas}->reset_objects; $self->{canvas}->reset_objects;

View File

@ -35,6 +35,9 @@ sub new {
# notify tabs # notify tabs
$self->{layers}->Closing; $self->{layers}->Closing;
# save window size
wxTheApp->save_window_pos($self, "object_settings");
$self->EndModal(wxID_OK); $self->EndModal(wxID_OK);
$self->Destroy; $self->Destroy;
}); });
@ -46,6 +49,8 @@ sub new {
$self->SetSizer($sizer); $self->SetSizer($sizer);
$self->SetMinSize($self->GetSize); $self->SetMinSize($self->GetSize);
wxTheApp->restore_window_pos($self, "object_settings");
return $self; return $self;
} }
@ -58,16 +63,6 @@ sub PartSettingsChanged {
my ($self) = @_; my ($self) = @_;
return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged; return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged;
} }
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description, $kind);
wxTheApp->set_menu_item_icon($item, $icon);
EVT_MENU($self, $id, $cb);
return $item;
}
package Slic3r::GUI::Plater::ObjectDialog::BaseTab; package Slic3r::GUI::Plater::ObjectDialog::BaseTab;

View File

@ -34,7 +34,8 @@ sub new {
$self->{default_config} = Slic3r::Config->new; $self->{default_config} = Slic3r::Config->new;
$self->{config} = Slic3r::Config->new; $self->{config} = Slic3r::Config->new;
$self->{on_change} = $params{on_change}; $self->{on_change} = $params{on_change};
$self->{editable} = 1; $self->{can_add} = 1;
$self->{can_delete} = 1;
$self->{fixed_options} = {}; $self->{fixed_options} = {};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
@ -52,27 +53,32 @@ sub new {
EVT_LEFT_DOWN($btn, sub { EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new; my $menu = Wx::Menu->new;
my $last_cat = ''; my $last_cat = '';
# create category submenus
my %categories = (); # category => submenu
foreach my $opt_key (@{$self->{options}}) { foreach my $opt_key (@{$self->{options}}) {
my $id = &Wx::NewId();
# add icon, if we have one for this category
my $icon;
if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) { if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) {
if ($last_cat && $cat ne $last_cat) { $categories{$cat} //= Wx::Menu->new;
$menu->AppendSeparator;
}
$last_cat = $cat;
$icon = $icons{$cat};
} }
}
my $menuItem = $menu->Append($id, $self->{option_labels}{$opt_key}); # append submenus to main menu
wxTheApp->set_menu_item_icon($menuItem, $icon) if $icon; foreach my $cat (sort keys %categories) {
wxTheApp->append_submenu($menu, $cat, "", $categories{$cat}, undef, $icons{$cat});
}
EVT_MENU($menu, $id, sub { # append options to submenus
foreach my $opt_key (@{$self->{options}}) {
my $cat = $Slic3r::Config::Options->{$opt_key}{category} or next;
my $cb = sub {
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key)); $self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
$self->update_optgroup; $self->update_optgroup;
$self->{on_change}->($opt_key) if $self->{on_change}; $self->{on_change}->($opt_key) if $self->{on_change};
}); };
wxTheApp->append_menu_item($categories{$cat}, $self->{option_labels}{$opt_key},
$Slic3r::Config::Options->{$opt_key}{tooltip}, $cb);
} }
$self->PopupMenu($menu, $btn->GetPosition); $self->PopupMenu($menu, $btn->GetPosition);
$menu->Destroy; $menu->Destroy;
@ -84,7 +90,9 @@ sub new {
} }
$self->SetSizer($self->{sizer}); $self->SetSizer($self->{sizer});
$self->SetScrollbars(0, 1, 0, 1);
# http://docs.wxwidgets.org/3.0/classwx_scrolled.html#details
$self->SetScrollRate(0, $Slic3r::GUI::scroll_step);
$self->set_opt_keys($params{opt_keys}) if $params{opt_keys}; $self->set_opt_keys($params{opt_keys}) if $params{opt_keys};
$self->update_optgroup; $self->update_optgroup;
@ -109,17 +117,11 @@ sub set_config {
sub set_opt_keys { sub set_opt_keys {
my ($self, $opt_keys) = @_; my ($self, $opt_keys) = @_;
# sort options by category+label # sort options by label
$self->{option_labels} = {}; $self->{option_labels} = {};
foreach my $opt_key (@$opt_keys) { foreach my $opt_key (@$opt_keys) {
my $def = $Slic3r::Config::Options->{$opt_key} or next; my $def = $Slic3r::Config::Options->{$opt_key} or next;
if (!$def->{category}) { $self->{option_labels}{$opt_key} = $def->{full_label} // $def->{label};
#printf "Skipping %s\n", $opt_key;
next;
}
$self->{option_labels}{$opt_key} = sprintf '%s > %s',
$def->{category},
$def->{full_label} // $def->{label};
}; };
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } keys %{$self->{option_labels}} ]; $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } keys %{$self->{option_labels}} ];
} }
@ -143,7 +145,7 @@ sub update_optgroup {
$self->{options_sizer}->Clear(1); $self->{options_sizer}->Clear(1);
return if !defined $self->{config}; return if !defined $self->{config};
$self->{btn_add}->Show($self->{editable}); $self->{btn_add}->Show($self->{can_add});
my %categories = (); my %categories = ();
foreach my $opt_key (@{$self->{config}->get_keys}) { foreach my $opt_key (@{$self->{config}->get_keys}) {
@ -172,7 +174,7 @@ sub update_optgroup {
my ($opt_key, $opt_index) = @{ $optgroup->_opt_map->{$opt_id} }; my ($opt_key, $opt_index) = @{ $optgroup->_opt_map->{$opt_id} };
# disallow deleting fixed options # disallow deleting fixed options
return undef if $self->{fixed_options}{$opt_key} || !$self->{editable}; return undef if $self->{fixed_options}{$opt_key} || !$self->{can_delete};
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE); wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
@ -209,11 +211,27 @@ sub disable {
$self->Disable; $self->Disable;
} }
# Shows or hides the Add button. # Shows or hides the Add/Delete buttons.
sub set_editable { sub set_editable {
my ($self, $editable) = @_; my ($self, $editable) = @_;
$self->{editable} = $editable; $self->{can_add} = $self->{can_delete} = $editable;
}
# Shows or hides the Add button.
sub can_add {
my ($self, $can) = @_;
$self->{can_add} = $can if defined $can;
return $can;
}
# Shows or hides the Delete button.
sub can_delete {
my ($self, $can) = @_;
$self->{can_delete} = $can if defined $can;
return $can;
} }
1; 1;

View File

@ -65,11 +65,11 @@ sub new {
default => $Slic3r::GUI::Settings->{_}{threads}, default => $Slic3r::GUI::Settings->{_}{threads},
)); ));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_controller', opt_id => 'tabbed_preset_editors',
type => 'bool', type => 'bool',
label => 'Disable USB/serial connection', label => 'Display profile editors as tabs',
tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.', tooltip => 'When opening a profile editor, it will be shown in a dialog or in a tab according to this option.',
default => $Slic3r::GUI::Settings->{_}{no_controller}, default => $Slic3r::GUI::Settings->{_}{tabbed_preset_editors},
)); ));
my $sizer = Wx::BoxSizer->new(wxVERTICAL); my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@ -88,7 +88,7 @@ sub new {
sub _accept { sub _accept {
my $self = shift; my $self = shift;
if ($self->{values}{mode} || defined($self->{values}{no_controller})) { if ($self->{values}{mode}) {
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective."); Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
} }

View File

@ -94,7 +94,7 @@ sub prompt_unsaved_changes {
if ($res == wxID_CANCEL) { if ($res == wxID_CANCEL) {
return 0; return 0;
} elsif ($res == wxID_YES) { } elsif ($res == wxID_YES) {
return $self->save($self->default ? undef : $self->name); return $self->default ? $self->save_prompt($parent) : $self->save;
} elsif ($res == wxID_NO) { } elsif ($res == wxID_NO) {
$self->dismiss_changes; $self->dismiss_changes;
return 1; return 1;
@ -104,20 +104,29 @@ sub prompt_unsaved_changes {
return 1; return 1;
} }
sub save_prompt {
my ($self, $parent) = @_;
my $default_name = $self->default ? 'Untitled' : $self->name;
$default_name =~ s/\.ini$//i;
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
default => $default_name,
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
);
return 0 unless $dlg->ShowModal == wxID_OK;
$self->save_as($dlg->get_name);
}
sub save { sub save {
my ($self, $name, $parent) = @_; my ($self, $opt_keys) = @_;
if (!$name) { return $self->save_as($self->name, $opt_keys);
my $default_name = $self->default ? 'Untitled' : $self->name; }
$default_name =~ s/\.ini$//i;
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent, sub save_as {
default => $default_name, my ($self, $name, $opt_keys) = @_;
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
);
return 0 unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name;
}
$self->rename($name); $self->rename($name);
@ -125,8 +134,15 @@ sub save {
die "Calling save() without setting filename"; die "Calling save() without setting filename";
} }
$self->_config->clear; if ($opt_keys) {
$self->_config->apply($self->_dirty_config); $self->_config->apply_only($self->_dirty_config, $opt_keys);
} else {
$self->_config->clear;
$self->_config->apply($self->_dirty_config);
}
# unlink the file first to avoid problems on case-insensitive file systems
unlink Slic3r::encode_path($self->file);
$self->_config->save($self->file); $self->_config->save($self->file);
wxTheApp->load_presets; wxTheApp->load_presets;
@ -167,6 +183,8 @@ sub dirty_config {
sub load_config { sub load_config {
my ($self) = @_; my ($self) = @_;
return $self->_config if $self->_loaded;
my @keys = $self->_group_class->options; my @keys = $self->_group_class->options;
my @extra_keys = $self->_group_class->overriding_options; my @extra_keys = $self->_group_class->overriding_options;

View File

@ -87,7 +87,7 @@ sub new {
}); });
EVT_CHOICE($parent, $self->{presets_choice}, sub { EVT_CHOICE($parent, $self->{presets_choice}, sub {
$self->on_select_preset; $self->_on_select_preset;
}); });
EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset }); EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset });
@ -123,13 +123,20 @@ sub save_preset {
$self->{treectrl}->SetFocus; $self->{treectrl}->SetFocus;
my $preset = $self->current_preset; my $preset = $self->current_preset;
$preset->save(undef, $self); $preset->save_prompt($self);
$self->load_presets; $self->load_presets;
$self->select_preset_by_name($preset->name); $self->select_preset_by_name($preset->name);
$self->{on_save_preset}->($self->name, $preset) if $self->{on_save_preset};
return 1; return 1;
} }
sub on_save_preset {
my ($self, $cb) = @_;
$self->{on_save_preset} = $cb;
}
sub on_value_change { sub on_value_change {
my ($self, $cb) = @_; my ($self, $cb) = @_;
$self->{on_value_change} = $cb; $self->{on_value_change} = $cb;
@ -141,10 +148,12 @@ sub on_value_change {
sub _on_value_change { sub _on_value_change {
my ($self, $opt_key) = @_; my ($self, $opt_key) = @_;
$self->current_preset->_dirty_config->apply($self->config); wxTheApp->CallAfter(sub {
$self->{on_value_change}->($opt_key) if $self->{on_value_change}; $self->current_preset->_dirty_config->apply($self->config);
$self->load_presets; $self->{on_value_change}->($opt_key) if $self->{on_value_change};
$self->_update; $self->load_presets;
$self->_update;
});
} }
sub _update {} sub _update {}
@ -155,7 +164,7 @@ sub select_preset {
my ($self, $i, $force) = @_; my ($self, $i, $force) = @_;
$self->{presets_choice}->SetSelection($i); $self->{presets_choice}->SetSelection($i);
$self->on_select_preset($force); $self->_on_select_preset($force);
} }
sub select_preset_by_name { sub select_preset_by_name {
@ -163,8 +172,12 @@ sub select_preset_by_name {
my $presets = wxTheApp->presets->{$self->name}; my $presets = wxTheApp->presets->{$self->name};
my $i = first { $presets->[$_]->name eq $name } 0..$#$presets; my $i = first { $presets->[$_]->name eq $name } 0..$#$presets;
if (!defined $i) {
warn "No preset named $name";
return 0;
}
$self->{presets_choice}->SetSelection($i); $self->{presets_choice}->SetSelection($i);
$self->on_select_preset($force); $self->_on_select_preset($force);
} }
sub prompt_unsaved_changes { sub prompt_unsaved_changes {
@ -175,6 +188,11 @@ sub prompt_unsaved_changes {
} }
sub on_select_preset { sub on_select_preset {
my ($self, $cb) = @_;
$self->{on_select_preset} = $cb;
}
sub _on_select_preset {
my ($self, $force) = @_; my ($self, $force) = @_;
# This method is called: # This method is called:
@ -186,7 +204,12 @@ sub on_select_preset {
my $preset = wxTheApp->presets->{$self->name}->[$self->{presets_choice}->GetSelection]; my $preset = wxTheApp->presets->{$self->name}->[$self->{presets_choice}->GetSelection];
# If selection didn't change, do nothing. # If selection didn't change, do nothing.
return if defined $self->current_preset && $preset->name eq $self->current_preset->name; # (But still reset current_preset because it might contain an older object of the
# current preset)
if (defined $self->current_preset && $preset->name eq $self->current_preset->name) {
$self->current_preset($preset);
return;
}
# If we have unsaved changes, prompt user. # If we have unsaved changes, prompt user.
if (!$force && !$self->prompt_unsaved_changes) { if (!$force && !$self->prompt_unsaved_changes) {
@ -203,9 +226,7 @@ sub on_select_preset {
# prompted and chose to discard changes. # prompted and chose to discard changes.
$self->load_presets; $self->load_presets;
$preset->load_config if !$preset->_loaded; $self->reload_preset;
$self->config->clear;
$self->config->apply($preset->dirty_config);
eval { eval {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
@ -215,12 +236,13 @@ sub on_select_preset {
$self->_update; $self->_update;
$self->on_preset_loaded; $self->on_preset_loaded;
$self->reload_config;
}; };
if ($@) { if ($@) {
$@ = "I was unable to load the selected config file: $@"; $@ = "I was unable to load the selected config file: $@";
Slic3r::GUI::catch_error($self); Slic3r::GUI::catch_error($self);
} }
$self->{on_select_preset}->($self->name, $preset) if $self->{on_select_preset};
} }
sub add_options_page { sub add_options_page {
@ -240,6 +262,15 @@ sub add_options_page {
return $page; return $page;
} }
sub reload_preset {
my ($self) = @_;
$self->current_preset->load_config if !$self->current_preset->_loaded;
$self->config->clear;
$self->config->apply($self->current_preset->dirty_config);
$self->reload_config;
}
sub reload_config { sub reload_config {
my $self = shift; my $self = shift;
@ -437,12 +468,12 @@ sub options {
perimeter_extruder infill_extruder solid_infill_extruder perimeter_extruder infill_extruder solid_infill_extruder
support_material_extruder support_material_interface_extruder support_material_extruder support_material_interface_extruder
ooze_prevention standby_temperature_delta ooze_prevention standby_temperature_delta
interface_shells interface_shells regions_overlap
extrusion_width first_layer_extrusion_width perimeter_extrusion_width extrusion_width first_layer_extrusion_width perimeter_extrusion_width
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
top_infill_extrusion_width support_material_extrusion_width top_infill_extrusion_width support_material_extrusion_width
infill_overlap bridge_flow_ratio infill_overlap bridge_flow_ratio
xy_size_compensation resolution overridable compatible_printers xy_size_compensation resolution shortcuts compatible_printers
print_settings_id print_settings_id
) )
} }
@ -450,7 +481,7 @@ sub options {
sub build { sub build {
my $self = shift; my $self = shift;
my $overridable_widget = sub { my $shortcuts_widget = sub {
my ($parent) = @_; my ($parent) = @_;
my $Options = $Slic3r::Config::Options; my $Options = $Slic3r::Config::Options;
@ -459,15 +490,15 @@ sub build {
grep { exists $Options->{$_} && $Options->{$_}{category} } $self->options grep { exists $Options->{$_} && $Options->{$_}{category} } $self->options
); );
my @opt_keys = sort { $options{$a} cmp $options{$b} } keys %options; my @opt_keys = sort { $options{$a} cmp $options{$b} } keys %options;
$self->{overridable_opt_keys} = [ @opt_keys ]; $self->{shortcuts_opt_keys} = [ @opt_keys ];
my $listbox = $self->{overridable_list} = Wx::CheckListBox->new($parent, -1, my $listbox = $self->{shortcuts_list} = Wx::CheckListBox->new($parent, -1,
wxDefaultPosition, [-1, 320], [ map $options{$_}, @opt_keys ]); wxDefaultPosition, [-1, 320], [ map $options{$_}, @opt_keys ]);
EVT_CHECKLISTBOX($self, $listbox, sub { EVT_CHECKLISTBOX($self, $listbox, sub {
my $value = [ map $opt_keys[$_], grep $listbox->IsChecked($_), 0..$#opt_keys ]; my $value = [ map $opt_keys[$_], grep $listbox->IsChecked($_), 0..$#opt_keys ];
$self->config->set('overridable', $value); $self->config->set('shortcuts', $value);
$self->_on_value_change('overridable'); $self->_on_value_change('shortcuts');
}); });
my $sizer = Wx::BoxSizer->new(wxVERTICAL); my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@ -639,6 +670,7 @@ sub build {
} }
{ {
my $optgroup = $page->new_optgroup('Advanced'); my $optgroup = $page->new_optgroup('Advanced');
$optgroup->append_single_option_line('regions_overlap');
$optgroup->append_single_option_line('interface_shells'); $optgroup->append_single_option_line('interface_shells');
} }
} }
@ -720,7 +752,7 @@ sub build {
} }
{ {
my $page = $self->add_options_page('Overrides', 'wrench.png'); my $page = $self->add_options_page('Shortcuts', 'wrench.png');
{ {
my $optgroup = $page->new_optgroup('Profile preferences'); my $optgroup = $page->new_optgroup('Profile preferences');
{ {
@ -732,10 +764,10 @@ sub build {
} }
} }
{ {
my $optgroup = $page->new_optgroup('Overridable settings (they will be displayed in the plater for quick changes)'); my $optgroup = $page->new_optgroup('Show shortcuts for the following settings');
{ {
my $line = Slic3r::GUI::OptionsGroup::Line->new( my $line = Slic3r::GUI::OptionsGroup::Line->new(
widget => $overridable_widget, widget => $shortcuts_widget,
full_width => 1, full_width => 1,
); );
$optgroup->append_line($line); $optgroup->append_line($line);
@ -750,9 +782,9 @@ sub reload_config {
$self->_reload_compatible_printers_widget; $self->_reload_compatible_printers_widget;
{ {
my %overridable = map { $_ => 1 } @{ $self->config->get('overridable') }; my %shortcuts = map { $_ => 1 } @{ $self->config->get('shortcuts') };
for my $i (0..$#{$self->{overridable_opt_keys}}) { for my $i (0..$#{$self->{shortcuts_opt_keys}}) {
$self->{overridable_list}->Check($i, $overridable{ $self->{overridable_opt_keys}[$i] }); $self->{shortcuts_list}->Check($i, $shortcuts{ $self->{shortcuts_opt_keys}[$i] });
} }
} }
@ -1174,11 +1206,10 @@ sub options {
bed_shape z_offset z_steps_per_mm has_heatbed bed_shape z_offset z_steps_per_mm has_heatbed
gcode_flavor use_relative_e_distances gcode_flavor use_relative_e_distances
serial_port serial_speed serial_port serial_speed
octoprint_host octoprint_apikey host_type print_host octoprint_apikey
use_firmware_retraction pressure_advance vibration_limit use_firmware_retraction pressure_advance vibration_limit
use_volumetric_e use_volumetric_e
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode
notes
nozzle_diameter extruder_offset nozzle_diameter extruder_offset
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below
@ -1243,7 +1274,7 @@ sub build {
} }
}); });
} }
unless ($Slic3r::GUI::Settings->{_}{no_controller}) { {
my $optgroup = $page->new_optgroup('USB/Serial connection'); my $optgroup = $page->new_optgroup('USB/Serial connection');
my $line = Slic3r::GUI::OptionsGroup::Line->new( my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Serial port', label => 'Serial port',
@ -1277,9 +1308,11 @@ sub build {
$optgroup->append_line($line); $optgroup->append_line($line);
} }
{ {
my $optgroup = $page->new_optgroup('OctoPrint upload'); my $optgroup = $page->new_optgroup('Print server upload');
my $host_line = $optgroup->create_single_option_line('octoprint_host'); $optgroup->append_single_option_line('host_type');
my $host_line = $optgroup->create_single_option_line('print_host');
$host_line->append_button("Browse…", "zoom.png", sub { $host_line->append_button("Browse…", "zoom.png", sub {
# look for devices # look for devices
my $entries; my $entries;
@ -1292,19 +1325,19 @@ sub build {
my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
if ($dlg->ShowModal == wxID_OK) { if ($dlg->ShowModal == wxID_OK) {
my $value = $dlg->GetValue . ":" . $dlg->GetPort; my $value = $dlg->GetValue . ":" . $dlg->GetPort;
$self->config->set('octoprint_host', $value); $self->config->set('print_host', $value);
$self->_on_value_change('octoprint_host'); $self->_on_value_change('print_host');
} }
} else { } else {
Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal;
} }
}, undef, !eval "use Net::Bonjour; 1"); }, \$self->{print_host_browse_btn}, !eval "use Net::Bonjour; 1");
$host_line->append_button("Test", "wrench.png", sub { $host_line->append_button("Test", "wrench.png", sub {
my $ua = LWP::UserAgent->new; my $ua = LWP::UserAgent->new;
$ua->timeout(10); $ua->timeout(10);
my $res = $ua->get( my $res = $ua->get(
"http://" . $self->config->octoprint_host . "/api/version", "http://" . $self->config->print_host . "/api/version",
'X-Api-Key' => $self->config->octoprint_apikey, 'X-Api-Key' => $self->config->octoprint_apikey,
); );
if ($res->is_success) { if ($res->is_success) {
@ -1314,7 +1347,7 @@ sub build {
"I wasn't able to connect to OctoPrint (" . $res->status_line . "). " "I wasn't able to connect to OctoPrint (" . $res->status_line . "). "
. "Check hostname and OctoPrint version (at least 1.1.0 is required)."); . "Check hostname and OctoPrint version (at least 1.1.0 is required).");
} }
}, \$self->{octoprint_host_test_btn}); }, \$self->{print_host_test_btn});
$optgroup->append_line($host_line); $optgroup->append_line($host_line);
$optgroup->append_single_option_line('octoprint_apikey'); $optgroup->append_single_option_line('octoprint_apikey');
} }
@ -1398,13 +1431,13 @@ sub build {
my $optgroup = $page->new_optgroup('Notes', my $optgroup = $page->new_optgroup('Notes',
label_width => 0, label_width => 0,
); );
my $option = $optgroup->get_option('notes'); my $option = $optgroup->get_option('printer_notes');
$option->full_width(1); $option->full_width(1);
$option->height(250); $option->height(250);
$optgroup->append_single_option_line($option); $optgroup->append_single_option_line($option);
} }
} }
$self->_update_serial_ports unless $Slic3r::GUI::Settings->{_}{no_controller}; $self->_update_serial_ports;
} }
sub _update_serial_ports { sub _update_serial_ports {
@ -1520,12 +1553,17 @@ sub _update {
$self->{serial_test_btn}->Disable; $self->{serial_test_btn}->Disable;
} }
} }
if ($config->get('octoprint_host') && eval "use LWP::UserAgent; 1") { if (($config->get('host_type') eq 'octoprint')) {
$self->{octoprint_host_test_btn}->Enable; $self->{print_host_browse_btn}->Enable;
} else { }else{
$self->{octoprint_host_test_btn}->Disable; $self->{print_host_browse_btn}->Disable;
} }
$self->get_field('octoprint_apikey')->toggle($config->get('octoprint_host')); if (($config->get('host_type') eq 'octoprint') && eval "use LWP::UserAgent; 1") {
$self->{print_host_test_btn}->Enable;
} else {
$self->{print_host_test_btn}->Disable;
}
$self->get_field('octoprint_apikey')->toggle($config->get('print_host'));
my $have_multiple_extruders = $self->{extruders_count} > 1; my $have_multiple_extruders = $self->{extruders_count} > 1;
$self->get_field('toolchange_gcode')->toggle($have_multiple_extruders); $self->get_field('toolchange_gcode')->toggle($have_multiple_extruders);
@ -1618,11 +1656,12 @@ sub new {
$self->{title} = $title; $self->{title} = $title;
$self->{iconID} = $iconID; $self->{iconID} = $iconID;
$self->SetScrollbars(1, 1, 1, 1);
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->SetSizer($self->{vsizer}); $self->SetSizer($self->{vsizer});
# http://docs.wxwidgets.org/3.0/classwx_scrolled.html#details
$self->SetScrollRate($Slic3r::GUI::scroll_step, $Slic3r::GUI::scroll_step);
return $self; return $self;
} }

View File

@ -9,7 +9,7 @@ use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER);
use base qw(Wx::Dialog Class::Accessor); use base qw(Wx::Dialog Class::Accessor);
use utf8; use utf8;
__PACKAGE__->mk_accessors(qw(config config2 screen controller _optgroups)); __PACKAGE__->mk_accessors(qw(config config2 manual_control_config screen controller _optgroups));
sub new { sub new {
my ($class, $parent) = @_; my ($class, $parent) = @_;
@ -27,6 +27,12 @@ sub new {
z_lift_speed => 8, z_lift_speed => 8,
offset => [0,0], offset => [0,0],
}); });
$self->manual_control_config({
xy_travel_speed => 130,
z_travel_speed => 10,
temperature => '',
bed_temperature => '',
});
my $ini = eval { Slic3r::Config->read_ini("$Slic3r::GUI::datadir/DLP.ini") }; my $ini = eval { Slic3r::Config->read_ini("$Slic3r::GUI::datadir/DLP.ini") };
if ($ini) { if ($ini) {
@ -286,7 +292,7 @@ sub new {
return; return;
} }
my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new
($self, $self->config, $sender); ($self, $self->config, $sender, $self->manual_control_config);
$dlg->ShowModal; $dlg->ShowModal;
$sender->disconnect; $sender->disconnect;
}); });
@ -605,8 +611,12 @@ sub start_print {
} }
Slic3r::debugf "connected to " . $self->config->serial_port . "\n"; Slic3r::debugf "connected to " . $self->config->serial_port . "\n";
# TODO: this wait should be handled by GCodeSender
sleep 4;
# send custom start G-code # send custom start G-code
$self->sender->send($_, 1) for grep !/^;/, split /\n/, $self->config->start_gcode; $self->sender->send($_, 1) for grep !/^;/, split /\n/, $self->config->start_gcode;
$self->sender->("G90", 1); # set absolute positioning
} }
$self->is_printing(1); $self->is_printing(1);

View File

@ -42,8 +42,10 @@ sub size {
sub process { sub process {
my ($self) = @_; my ($self) = @_;
$self->status_cb->(20, "Generating perimeters"); ### No need to call this as we call it as part of prepare_infill()
$_->make_perimeters for @{$self->objects}; ### until we fix the idempotency issue.
###$self->status_cb->(20, "Generating perimeters");
###$_->make_perimeters for @{$self->objects};
$self->status_cb->(70, "Infilling layers"); $self->status_cb->(70, "Infilling layers");
$_->infill for @{$self->objects}; $_->infill for @{$self->objects};

View File

@ -72,9 +72,8 @@ sub export {
my @lt = localtime; my @lt = localtime;
printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n", printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
$lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0]; $lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0];
# Write notes (content of the Print Settings tab -> Notes) # Write notes (content of all Settings tabs -> Notes)
print $fh "; $_\n" foreach split /\R/, $self->config->notes; print $fh $gcodegen->notes;
print $fh "\n" if $self->config->notes;
# Write some terse information on the slicing parameters. # Write some terse information on the slicing parameters.
my $first_object = $self->objects->[0]; my $first_object = $self->objects->[0];
my $layer_height = $first_object->config->layer_height; my $layer_height = $first_object->config->layer_height;
@ -136,6 +135,10 @@ sub export {
# prepare the helper object for replacing placeholders in custom G-code and output filename # prepare the helper object for replacing placeholders in custom G-code and output filename
$self->placeholder_parser->update_timestamp; $self->placeholder_parser->update_timestamp;
# GCode sets this automatically whenever we call change_layer(),
# but we need it for skirt/brim too
$gcodegen->set_first_layer(1);
# disable fan # disable fan
print $fh $gcodegen->writer->set_fan(0, 1) print $fh $gcodegen->writer->set_fan(0, 1)
if $self->config->cooling && $self->config->disable_fan_first_layers; if $self->config->cooling && $self->config->disable_fan_first_layers;
@ -403,8 +406,8 @@ sub process_layer {
push @mm3_per_mm, $layer->support_interface_fills->min_mm3_per_mm; push @mm3_per_mm, $layer->support_interface_fills->min_mm3_per_mm;
} }
} }
# filter out 0-width segments # ignore too thin segments
@mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm; @mm3_per_mm = grep $_ > 0.01, @mm3_per_mm;
if (@mm3_per_mm) { if (@mm3_per_mm) {
my $min_mm3_per_mm = min(@mm3_per_mm); my $min_mm3_per_mm = min(@mm3_per_mm);
# In order to honor max_print_speed we need to find a target volumetric # In order to honor max_print_speed we need to find a target volumetric

View File

@ -213,7 +213,11 @@ sub generate_support_material {
$self->clear_support_layers; $self->clear_support_layers;
if ((!$self->config->support_material && $self->config->raft_layers == 0) || scalar(@{$self->layers}) < 2) { if ((!$self->config->support_material
&& $self->config->raft_layers == 0
&& $self->config->support_material_enforce_layers == 0)
|| scalar(@{$self->layers}) < 2
) {
$self->set_step_done(STEP_SUPPORTMATERIAL); $self->set_step_done(STEP_SUPPORTMATERIAL);
return; return;
} }

View File

@ -93,18 +93,21 @@ sub generate {
sub contact_area { sub contact_area {
# $object is Slic3r::Print::Object # $object is Slic3r::Print::Object
my ($self, $object) = @_; my ($self, $object) = @_;
my $conf = $self->object_config;
# if user specified a custom angle threshold, convert it to radians # if user specified a custom angle threshold, convert it to radians
my $threshold_rad; my $threshold_rad;
if (!($self->object_config->support_material_threshold =~ /%$/)) { if (!($conf->support_material_threshold =~ /%$/)) {
$threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive $threshold_rad = deg2rad($conf->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
} }
# Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces # Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
# and subtract $buildplate_only_top_surfaces from the contact surfaces, so # and subtract $buildplate_only_top_surfaces from the contact surfaces, so
# there is no contact surface supported by a top surface. # there is no contact surface supported by a top surface.
my $buildplate_only = $self->object_config->support_material && $self->object_config->support_material_buildplate_only; my $buildplate_only =
( $conf->support_material || $conf->support_material_enforce_layers)
&& $conf->support_material_buildplate_only;
my $buildplate_only_top_surfaces = []; my $buildplate_only_top_surfaces = [];
# determine contact areas # determine contact areas
@ -115,12 +118,19 @@ sub contact_area {
# so $layer_id == 0 means first object layer # so $layer_id == 0 means first object layer
# and $layer->id == 0 means first print layer (including raft) # and $layer->id == 0 means first print layer (including raft)
if ($self->object_config->raft_layers == 0) { # if no raft, and we're at layer 0, skip to layer 1
next if $layer_id == 0; if ( $conf->raft_layers == 0 && $layer_id == 0 ) {
} elsif (!$self->object_config->support_material) { next;
}
# with or without raft, if we're above layer 1, we need to quit
# support generation if supports are disabled, or if we're at a high
# enough layer that enforce-supports no longer applies
if ( $layer_id > 0
&& !$conf->support_material
&& ($layer_id >= $conf->support_material_enforce_layers) ) {
# if we are only going to generate raft just check # if we are only going to generate raft just check
# the 'overhangs' of the first object layer # the 'overhangs' of the first object layer
last if $layer_id > 0; last;
} }
my $layer = $object->get_layer($layer_id); my $layer = $object->get_layer($layer_id);
@ -154,12 +164,19 @@ sub contact_area {
my $diff; my $diff;
# If a threshold angle was specified, use a different logic for detecting overhangs. # If a threshold angle was specified, use a different logic for detecting overhangs.
if (defined $threshold_rad if (($conf->support_material && defined $threshold_rad)
|| $layer_id < $self->object_config->support_material_enforce_layers || $layer_id <= $conf->support_material_enforce_layers
|| ($self->object_config->raft_layers > 0 && $layer_id == 0)) { || ($conf->raft_layers > 0 && $layer_id == 0)) {
my $d = defined $threshold_rad my $d = 0;
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) my $layer_threshold_rad = $threshold_rad;
: 0; if ($layer_id <= $conf->support_material_enforce_layers) {
# Use ~45 deg number for enforced supports if we are in auto
$layer_threshold_rad = deg2rad(89);
}
if (defined $layer_threshold_rad) {
$d = scale $lower_layer->height
* ((cos $layer_threshold_rad) / (sin $layer_threshold_rad));
}
$diff = diff( $diff = diff(
offset([ map $_->p, @{$layerm->slices} ], -$d), offset([ map $_->p, @{$layerm->slices} ], -$d),
@ -177,7 +194,7 @@ sub contact_area {
} else { } else {
$diff = diff( $diff = diff(
[ map $_->p, @{$layerm->slices} ], [ map $_->p, @{$layerm->slices} ],
offset([ map @$_, @{$lower_layer->slices} ], +$self->object_config->get_abs_value_over('support_material_threshold', $fw)), offset([ map @$_, @{$lower_layer->slices} ], +$conf->get_abs_value_over('support_material_threshold', $fw)),
); );
# collapse very tiny spots # collapse very tiny spots
@ -189,22 +206,31 @@ sub contact_area {
# outside the lower slice boundary, thus no overhang # outside the lower slice boundary, thus no overhang
} }
if ($self->object_config->dont_support_bridges) { if ($conf->dont_support_bridges) {
# compute the area of bridging perimeters # compute the area of bridging perimeters
# Note: this is duplicate code from GCode.pm, we need to refactor
my $bridged_perimeters; # Polygons my $bridged_perimeters; # Polygons
{ {
my $bridge_flow = $layerm->flow(FLOW_ROLE_PERIMETER, 1); my $bridge_flow = $layerm->flow(FLOW_ROLE_PERIMETER, 1);
my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $layerm->region->config->perimeter_extruder-1); # Get the lower layer's slices and grow them by half the nozzle diameter
my $lower_grown_slices = offset([ map @$_, @{$lower_layer->slices} ], +scale($nozzle_diameter/2)); # because we will consider the upper perimeters supported even if half nozzle
# falls outside the lower slices.
my $lower_grown_slices;
{
my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $layerm->region->config->perimeter_extruder-1);
$lower_grown_slices = offset(
[ map @$_, @{$lower_layer->slices} ],
+scale($nozzle_diameter/2),
);
}
# TODO: split_at_first_point() could split a bridge mid-way # Get all perimeters as polylines.
my @overhang_perimeters = # TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops)
map { $_->isa('Slic3r::ExtrusionLoop') ? $_->polygon->split_at_first_point : $_->polyline->clone } # could split a bridge mid-way
map @$_, @{$layerm->perimeters}; my @overhang_perimeters = map $_->as_polyline, @{$layerm->perimeters->flatten};
# Only consider the overhang parts of such perimeters,
# overhangs being those parts not supported by
# workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() # workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
$_->[0]->translate(1,0) for @overhang_perimeters; $_->[0]->translate(1,0) for @overhang_perimeters;
@overhang_perimeters = @{diff_pl( @overhang_perimeters = @{diff_pl(
@ -226,11 +252,16 @@ sub contact_area {
# convert bridging polylines into polygons by inflating them with their thickness # convert bridging polylines into polygons by inflating them with their thickness
{ {
# since we're dealing with bridges, we can't assume width is larger than spacing, # For bridges we can't assume width is larger than spacing because they
# so we take the largest value and also apply safety offset to be ensure no gaps # are positioned according to non-bridging perimeters spacing.
# are left in between my $w = max(
my $w = max($bridge_flow->scaled_width, $bridge_flow->scaled_spacing); $bridge_flow->scaled_width,
$bridge_flow->scaled_spacing,
$fw, # width of external perimeters
$layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width,
);
$bridged_perimeters = union([ $bridged_perimeters = union([
# Also apply safety offset to ensure no gaps are left in between.
map @{$_->grow($w/2 + 10)}, @overhang_perimeters map @{$_->grow($w/2 + 10)}, @overhang_perimeters
]); ]);
} }
@ -264,7 +295,7 @@ sub contact_area {
1, 1,
); );
} }
} # if ($self->object_config->dont_support_bridges) } # if ($conf->dont_support_bridges)
if ($buildplate_only) { if ($buildplate_only) {
# Don't support overhangs above the top surfaces. # Don't support overhangs above the top surfaces.
@ -309,7 +340,7 @@ sub contact_area {
my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter); my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter);
# ignore this contact area if it's too low # ignore this contact area if it's too low
next if $contact_z < $self->object_config->get_value('first_layer_height') - epsilon; next if $contact_z < $conf->get_value('first_layer_height') - epsilon;
$contact{$contact_z} = [ @contact ]; $contact{$contact_z} = [ @contact ];
$overhang{$contact_z} = [ @overhang ]; $overhang{$contact_z} = [ @overhang ];

View File

@ -133,6 +133,13 @@ sub mesh {
$facets = [ $facets = [
[0,1,2],[1,0,3],[2,1,4],[2,5,0],[0,6,3],[1,3,7],[1,8,4],[4,9,2],[10,5,2],[5,6,0],[6,11,3],[3,12,7],[7,8,1],[4,8,11],[4,11,9],[9,10,2],[10,13,5],[14,6,5],[9,11,6],[11,12,3],[12,8,7],[11,8,15],[13,10,9],[5,13,14],[14,13,6],[6,13,9],[15,12,11],[15,8,12] [0,1,2],[1,0,3],[2,1,4],[2,5,0],[0,6,3],[1,3,7],[1,8,4],[4,9,2],[10,5,2],[5,6,0],[6,11,3],[3,12,7],[7,8,1],[4,8,11],[4,11,9],[9,10,2],[10,13,5],[14,6,5],[9,11,6],[11,12,3],[12,8,7],[11,8,15],[13,10,9],[5,13,14],[14,13,6],[6,13,9],[15,12,11],[15,8,12]
]; ];
} elsif ($name eq 'bridge_with_hole') {
$vertices = [
[75,69.5,8],[80,76.9091644287109,8],[75,94.5,8],[125,69.5,8],[120,76.9091644287109,8],[120,87.0908355712891,8],[80,87.0908355712891,8],[125,94.5,8],[80,87.0908355712891,5],[120,87.0908355712891,5],[125,94.5,0],[120,69.5,0],[120,94.5,0],[125,69.5,0],[120,94.5,5],[80,94.5,5],[80,94.5,0],[75,94.5,0],[80,69.5,5],[80,69.5,0],[80,76.9091644287109,5],[120,69.5,5],[75,69.5,0],[120,76.9091644287109,5]
];
$facets = [
[0,1,2],[1,0,3],[1,3,4],[4,3,5],[2,6,7],[6,2,1],[7,6,5],[7,5,3],[5,8,9],[8,5,6],[10,11,12],[11,10,13],[14,8,15],[8,14,9],[2,16,17],[16,2,15],[15,2,14],[14,10,12],[10,14,7],[7,14,2],[16,18,19],[18,16,20],[20,16,1],[1,16,8],[8,16,15],[6,1,8],[3,11,13],[11,3,21],[21,3,18],[18,22,19],[22,18,0],[0,18,3],[16,22,17],[22,16,19],[2,22,0],[22,2,17],[5,23,4],[23,11,21],[11,23,12],[12,23,9],[9,23,5],[12,9,14],[23,18,20],[18,23,21],[10,3,13],[3,10,7],[1,23,20],[23,1,4]
];
} elsif ($name eq 'step') { } elsif ($name eq 'step') {
$vertices = [ $vertices = [
[0,20,5],[0,20,0],[0,0,5],[0,0,0],[20,0,0],[20,0,5],[1,19,5],[1,1,5],[19,1,5],[20,20,5],[19,19,5],[20,20,0],[19,19,10],[1,19,10],[1,1,10],[19,1,10] [0,20,5],[0,20,0],[0,0,5],[0,0,0],[20,0,0],[20,0,5],[1,19,5],[1,1,5],[19,1,5],[20,20,5],[19,19,5],[20,20,0],[19,19,10],[1,19,10],[1,1,10],[19,1,10]

View File

@ -19,8 +19,16 @@ fi
if [ -s $KEY ]; then if [ -s $KEY ]; then
for i in $FILES; do for i in $FILES; do
filepath=$(readlink -f "$i") filepath=$(readlink -f "$i")
echo put $filepath | sftp -i$KEY "${UPLOAD_USER}@dl.slic3r.org:$DIR/" tmpfile=$(mktemp)
echo put $filepath > $tmpfile
sftp -b $tmpfile -i$KEY "${UPLOAD_USER}@dl.slic3r.org:$DIR/"
result=$?
if [ $? -eq 1 ]; then
echo "Error with SFTP"
exit $result;
fi
done done
else else
echo "$KEY is not available, not deploying." echo "$KEY is not available, not deploying."
fi fi
exit $result

View File

@ -1,28 +1,10 @@
/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_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_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_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/liblzma.so.5
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libpcre.so.3
/lib/x86_64-linux-gnu/libpng12.so.0 /lib/x86_64-linux-gnu/libpng12.so.0
/lib/x86_64-linux-gnu/libresolv.so.2
/lib/x86_64-linux-gnu/libuuid.so.1
/usr/lib/x86_64-linux-gnu/libSM.so.6
/usr/lib/x86_64-linux-gnu/libatk-1.0.so.0
/usr/lib/x86_64-linux-gnu/libdatrie.so.1
/usr/lib/x86_64-linux-gnu/libfreetype.so.6
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgraphite2.so.3
/usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0
/usr/lib/x86_64-linux-gnu/libharfbuzz.so.0
/usr/lib/x86_64-linux-gnu/libjbig.so.0
/usr/lib/x86_64-linux-gnu/libjpeg.so.8 /usr/lib/x86_64-linux-gnu/libjpeg.so.8
/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 /usr/lib/x86_64-linux-gnu/libjbig.so.0
/usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0
/usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0
/usr/lib/x86_64-linux-gnu/libpixman-1.so.0
/usr/lib/x86_64-linux-gnu/libtiff.so.5 /usr/lib/x86_64-linux-gnu/libtiff.so.5

View File

@ -96,7 +96,7 @@ cp -f $PERL_BIN $archivefolder/perl-local
${PP_BIN} wxextension .0 \ ${PP_BIN} wxextension .0 \
-M attributes -M base -M bytes -M B -M POSIX \ -M attributes -M base -M bytes -M B -M POSIX \
-M FindBin -M Unicode::Normalize -M Tie::Handle \ -M FindBin -M Unicode::Normalize -M Tie::Handle \
-M Time::Local -M Math::Trig \ -M Time::Local -M Math::Trig -M IO::Socket -M Errno \
-M lib -M overload \ -M lib -M overload \
-M warnings -M local::lib \ -M warnings -M local::lib \
-M strict -M utf8 -M parent \ -M strict -M utf8 -M parent \
@ -122,7 +122,7 @@ rm -rf $(pwd)$archivefolder/local-lib/lib/perl5/TAP
rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test* rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test*
find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \ find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \ -or -name DocView -or -name STC -or -name IPC \
-or -name AUI -or -name Calendar -or -name DataView \ -or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \ -or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \; -or -name Ribbon \) -exec rm -rf "{}" \;
rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
DIR=$(dirname "$0") DIR=$(dirname "$0")
export LD_LIBRARY_PATH=./shlib/x86_64-linux-thread-multi/ export LD_LIBRARY_PATH=./bin
exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@ exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@

View File

@ -101,8 +101,8 @@ echo "Copying perl from $PERL_BIN"
cp -f $PERL_BIN $macosfolder/perl-local cp -f $PERL_BIN $macosfolder/perl-local
${PP_BIN} -M attributes -M base -M bytes -M B -M POSIX \ ${PP_BIN} -M attributes -M base -M bytes -M B -M POSIX \
-M FindBin -M Unicode::Normalize -M Tie::Handle \ -M FindBin -M Unicode::Normalize -M Tie::Handle \
-M Time::Local -M Math::Trig \ -M Time::Local -M Math::Trig -M IO::Socket -M Errno \
-M lib -M overload \ -M Storable -M lib -M overload \
-M warnings -M local::lib \ -M warnings -M local::lib \
-M strict -M utf8 -M parent \ -M strict -M utf8 -M parent \
-B -p -e "print 123" -o $WD/_tmp/test.par -B -p -e "print 123" -o $WD/_tmp/test.par
@ -128,11 +128,10 @@ find -d $macosfolder/local-lib -name '*.h' -delete
find -d $macosfolder/local-lib -name wxPerl.app -exec rm -rf "{}" \; find -d $macosfolder/local-lib -name wxPerl.app -exec rm -rf "{}" \;
find -d $macosfolder/local-lib -type d -path '*/Wx/*' \( -name WebView \ find -d $macosfolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \ -or -name DocView -or -name STC -or -name IPC \
-or -name AUI -or -name Calendar -or -name DataView \ -or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \ -or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \; -or -name Ribbon \) -exec rm -rf "{}" \;
find -d $macosfolder/local-lib -name libwx_osx_cocoau_ribbon-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_ribbon-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_aui-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_stc-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_stc-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_webview-3.0.0.2.0.dylib -delete find -d $macosfolder/local-lib -name libwx_osx_cocoau_webview-3.0.0.2.0.dylib -delete
rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include

View File

@ -1,21 +1,27 @@
if (!(Test-Path "C:\users\appveyor\local-lib.7z")) { if (!(Test-Path "C:\users\appveyor\local-lib-$env:ARCH.7z")) {
wget "http://www.siusgs.com/slic3r/buildserver/win/slic3r-perl-dependencies-5.24.0-win-seh-gcc6.3.0-x64.7z" -o "C:\users\appveyor\local-lib.7z" | Write-Output wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=local-lib-$env:ARCH.7z" -o "C:\users\appveyor\local-lib-$env:ARCH.7z" | Write-Output
} }
if (Test-Path "C:\users\appveyor\local-lib.7z") { if (Test-Path "C:\users\appveyor\local-lib-$env:ARCH.7z") {
cmd /c "7z x C:\Users\appveyor\local-lib.7z -oC:\projects\slic3r" -y | Write-Output cmd /c "7z x C:\Users\appveyor\local-lib-$env:ARCH.7z -oC:\projects\slic3r" -y | Write-Output
rm -r 'C:\projects\slic3r\local-lib\Slic3r*' rm -r 'C:\projects\slic3r\local-lib\Slic3r*'
} }
$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;" + $env:Path $env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;" + $env:Path
cd C:\projects\slic3r cd C:\projects\slic3r
rm -r 'C:\Program Files (x86)\Microsoft Vis*\bin' -Force rm -r 'C:\Program Files (x86)\Microsoft Vis*\bin' -Force
Add-AppveyorCompilationMessage -Message "Building Slic3r XS" Add-AppveyorCompilationMessage -Message "Building Slic3r XS"
perl Build.pl perl ./Build.pl
if (-NOT ($LASTEXITCODE -eq 0)) {
if ($LastExitCode -ne 0) {
Add-AppveyorCompilationMessage -Message "XS Failed to Build" -Category Error Add-AppveyorCompilationMessage -Message "XS Failed to Build" -Category Error
$host.SetShouldExit($LastExitCode)
exit
} }
Add-AppveyorCompilationMessage -Message "Making ZIP package" Add-AppveyorCompilationMessage -Message "Making ZIP package"
cd package/win cd package/win
./compile_wrapper.ps1 524| Write-Output ./compile_wrapper.ps1 524 | Write-Output
./package_win32.ps1 524| Write-Output ./package_win32.ps1 524| Write-Output

View File

@ -1,8 +1,3 @@
Add-AppveyorCompilationMessage -Message "Making ZIP package"
cd package/win
./compile_wrapper.ps1 524 | Write-Output
./package_win32.ps1 524| Write-Output
cd ../../
Add-AppveyorCompilationMessage -Message "Uploading to server." Add-AppveyorCompilationMessage -Message "Uploading to server."
& ./package/deploy/winscp.ps1 -DIR win -KEY $env:APPVEYOR_BUILD_FOLDER/package/deploy/slic3r-upload.ppk -FILE *.zip *>> ./sftplog.txt & ./package/deploy/winscp.ps1 -DIR win -KEY $env:APPVEYOR_BUILD_FOLDER/package/deploy/slic3r-upload.ppk -FILE *.zip *>> ./sftplog.txt

View File

@ -1,19 +1,28 @@
mkdir C:\projects\slic3r\FreeGLUT mkdir C:\projects\slic3r\FreeGLUT
if (!(Test-Path "C:\users\appveyor\freeglut.7z")) if (!(Test-Path "C:\users\appveyor\freeglut.$env:ARCH.7z"))
{ {
wget "http://www.siusgs.com/slic3r/buildserver/win/freeglut-mingw-3.0.0.win64.7z" -o C:\users\appveyor\freeglut.7z wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=freeglut-mingw-3.0.0.$env:ARCH.7z" -o C:\users\appveyor\freeglut.$env:ARCH.7z
} }
cmd /c "7z x C:\Users\appveyor\freeglut.7z -oC:\projects\slic3r\FreeGLUT" cmd /c "7z x C:\Users\appveyor\freeglut.$env:ARCH.7z -oC:\projects\slic3r\FreeGLUT"
if (!(Test-Path "C:\users\appveyor\strawberry.msi")) { if (!(Test-Path "C:\users\appveyor\strawberry.$env:ARCH.msi")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=slic3r-perl-5.24.1.4-64bit.msi" -o "C:\users\appveyor\strawberry.msi" | Write-Output if ($env:ARCH -eq "64bit") {
} wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=slic3r-perl-5.24.1.4-64bit.msi" -o "C:\users\appveyor\strawberry.$env:ARCH.msi" | Write-Output
if (!(Test-Path "C:\users\appveyor\extra_perl.7z")) { } else {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=Strawberry-6.3.0-seg-archive.7z" -o "C:\users\appveyor\extra_perl.7z" | Write-Output wget "http://strawberryperl.com/download/5.24.1.1/strawberry-perl-5.24.1.1-32bit.msi" -o "C:\users\appveyor\strawberry.$env:ARCH.msi" | Write-Output
}
} }
msiexec.exe /i "C:\users\appveyor\strawberry.msi" /quiet if (!($env:ARCH -eq "32bit")) {
cmd /c "7z x -aoa C:\Users\appveyor\extra_perl.7z -oC:\" if (!(Test-Path "C:\users\appveyor\extra_perl.7z")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=Strawberry-6.3.0-seg-archive.7z" -o "C:\users\appveyor\extra_perl.7z" | Write-Output
}
}
msiexec.exe /i "C:\users\appveyor\strawberry.$env:ARCH.msi" /quiet
if (!($env:ARCH -eq "32bit")) {
cmd /c "7z x -aoa C:\Users\appveyor\extra_perl.7z -oC:\"
}
if (!(Test-Path "C:\users\appveyor\winscp.zip")) { if (!(Test-Path "C:\users\appveyor\winscp.zip")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=WinSCP-5.9.4-Portable.zip" -o "C:\users\appveyor\winscp.zip" | Write-Output wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=WinSCP-5.9.4-Portable.zip" -o "C:\users\appveyor\winscp.zip" | Write-Output
@ -32,11 +41,15 @@ if(Test-Path -Path 'C:\Strawberry' ) {
copy C:\Strawberry\c\bin\gcc.exe C:\Strawberry\c\bin\cc.exe copy C:\Strawberry\c\bin\gcc.exe C:\Strawberry\c\bin\cc.exe
cmd /c mklink /D C:\Perl C:\Strawberry\perl cmd /c mklink /D C:\Perl C:\Strawberry\perl
mkdir C:\dev mkdir C:\dev
if (!(Test-Path "C:\users\appveyor\boost.1.63.0.7z") -Or $env:FORCE_BOOST_REINSTALL -eq 1) { if (!(Test-Path "C:\users\appveyor\boost.1.63.0.$env:ARCH.7z") -Or $env:FORCE_BOOST_REINSTALL -eq 1) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=boost_1_63_0-x64-gcc-6.3.0-seh.7z" -O "C:\users\appveyor\boost.1.63.0.7z" | Write-Output if ($env:ARCH -eq "64bit") {
wget "http://www.siusgs.com/slic3r/buildserver/win/boost_1_63_0-x64-gcc-6.3.0-seh.7z" -O "C:\users\appveyor\boost.1.63.0.$env:ARCH.7z" | Write-Output
} else {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=boost_1_63_0-x32-gcc-4.9.2-sjlj.7z" -O "C:\users\appveyor\boost.1.63.0.$env:ARCH.7z" | Write-Output
}
} }
Add-AppveyorCompilationMessage -Message "Extracting cached archive." Add-AppveyorCompilationMessage -Message "Extracting cached archive."
cmd /c "7z x C:\Users\appveyor\boost.1.63.0.7z -oC:\dev" cmd /c "7z x C:\Users\appveyor\boost.1.63.0.$env:ARCH.7z -oC:\dev"
mkdir C:\dev\CitrusPerl mkdir C:\dev\CitrusPerl
cmd /C mklink /D C:\dev\CitrusPerl\mingw32 C:\Strawberry\c cmd /C mklink /D C:\dev\CitrusPerl\mingw32 C:\Strawberry\c
@ -53,19 +66,14 @@ if(Test-Path -Path 'C:\Strawberry' ) {
Add-AppveyorCompilationMessage -Message "Installing wxWidgets (xsgui dependency))" Add-AppveyorCompilationMessage -Message "Installing wxWidgets (xsgui dependency))"
if ($env:FORCE_WX_BUILD -eq 1) { if ($env:FORCE_WX_BUILD -eq 1) {
rm "C:\Users\appveyor\wxwidgets.7z" -Force rm "C:\Users\appveyor\wxwidgets-$env:ARCH.7z" -Force
} }
if (!(Test-Path "C:\Users\appveyor\wxwidgets.7z")) {
Add-AppveyorCompilationMessage -Message "Compiling wxWidgets" if (!(Test-Path "C:\Users\appveyor\wxwidgets-$env:ARCH.7z")) {
git clone https://github.com/wxWidgets/wxWidgets -b "v3.1.0" -q C:\dev\wxWidgets Add-AppveyorCompilationMessage -Message "Extracting wxWidgets for $env:ARCH"
cd C:\dev\wxwidgets wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=wxwidgets-$env:ARCH.7z" -o C:\users\appveyor\wxwidgets-$env:ARCH.7z
cp .\include\wx\msw\setup0.h include/wx/msw/setup.h 7z x C:\users\appveyor\wxwidgets-$env:ARCH.7z -oC:\dev
cd build\msw
mingw32-make -f makefile.gcc CXXFLAGS="-std=gnu++11" BUILD=release VENDOR=Slic3r
cd C:\dev
7z a C:\Users\appveyor\wxwidgets.7z wxwidgets
cd C:\projects\slic3r
} else { } else {
Add-AppveyorCompilationMessage -Message "Extracting prebuilt wxWidgets." Add-AppveyorCompilationMessage -Message "Extracting prebuilt wxWidgets."
7z x "C:\Users\appveyor\wxwidgets.7z" -oC:\dev 7z x "C:\Users\appveyor\wxwidgets-$env:ARCH.7z" -oC:\dev
} }

View File

@ -1,30 +1,38 @@
# Short Powershell script to build a wrapper exec # Short Powershell script to build a wrapper exec
if ($args[0]) Param
{ (
$perlver = $args[0] [string]$perlVersion = "524",
} else [string]$STRAWBERRY_PATH = "C:\Strawberry",
{ # Path to C++ compiler, or just name if it is in path
$perlver = 524 [string]$cxx = "g++"
} )
$perllib = "-lperl$perlver" function Get-ScriptDirectory
$shell_loc = "..\common\shell.cpp" {
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
$scriptDir = Get-ScriptDirectory
$perllib = "-lperl$perlVersion"
$shell_loc = "${scriptDir}\..\common\shell.cpp"
# Build the resource file (used to load icon, etc) # Build the resource file (used to load icon, etc)
windres slic3r.rc -O coff -o slic3r.res windres ${scriptDir}\slic3r.rc -O coff -o ${scriptDir}\slic3r.res
# Compile an object file that does not have gui forced. # Compile an object file that does not have gui forced.
g++ -c -I'C:\strawberry\perl\lib\CORE\' $shell_loc -o slic3r.o Invoke-Expression "$cxx -c -I'${STRAWBERRY_PATH}\perl\lib\CORE\' $shell_loc -o ${scriptDir}/slic3r.o"
# Compile an object file with --gui automatically passed as an argument # Compile an object file with --gui automatically passed as an argument
g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI $shell_loc -o slic3r-gui.o Invoke-Expression "$cxx -c -I'${STRAWBERRY_PATH}\perl\lib\CORE\' -DFORCE_GUI $shell_loc -o ${scriptDir}/slic3r-gui.o"
# Build the EXE for the unforced version as slic3r-console # Build the EXE for the unforced version as slic3r-console
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host Invoke-Expression "$cxx -static-libgcc -static-libstdc++ -L'${STRAWBERRY_PATH}\c\lib' -L'${STRAWBERRY_PATH}\perl\bin' -L'${STRAWBERRY_PATH}\perl\lib\CORE\' $perllib ${scriptDir}/slic3r.o ${scriptDir}/slic3r.res -o ${scriptDir}/slic3r-console.exe | Write-Host"
# Build the EXE for the forced GUI # Build the EXE for the forced GUI
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -mwindows -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host Invoke-Expression "$cxx -static-libgcc -static-libstdc++ -L'${STRAWBERRY_PATH}\c\lib' -mwindows -L'${STRAWBERRY_PATH}\perl\bin' -L'${STRAWBERRY_PATH}\perl\lib\CORE\' $perllib ${scriptDir}/slic3r-gui.o ${scriptDir}/slic3r.res -o ${scriptDir}/slic3r.exe | Write-Host"
# Build an extra copy of the GUI version that creates a console window # Build an extra copy of the GUI version that creates a console window
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r-debug-console.exe | Write-Host Invoke-Expression "$cxx -static-libgcc -static-libstdc++ -L'${STRAWBERRY_PATH}\c\lib' -L'${STRAWBERRY_PATH}\perl\bin' -L'${STRAWBERRY_PATH}\perl\lib\CORE\' $perllib ${scriptDir}/slic3r-gui.o ${scriptDir}/slic3r.res -o ${scriptDir}/slic3r-debug-console.exe | Write-Host"

View File

@ -2,62 +2,119 @@
# Licensed under the same license as the rest of Slic3r. # Licensed under the same license as the rest of Slic3r.
# ------------------------ # ------------------------
# You need to have Strawberry Perl 5.24.0.1 (or slic3r-perl) installed for this to work, # You need to have Strawberry Perl 5.24.0.1 (or slic3r-perl) installed for this to work,
param ( Param
[switch]$exe = $false (
# Perl version major/minor number. Slic3r perl uses 524
[string]$perlVersion = "524",
# Override the output file name.
[string]$outputFile = "",
[string]$currentDate = "$(Get-Date -UFormat '%Y.%m.%d')",
# Override the branch name used in the output. Otherwise autodetect based on git.
[string]$branch = "",
#This is "32bit" or "64bit". It will detect based on presence of libglut.
[string]$arch = $env:ARCH,
# Change this to where you have Strawberry Perl installed.
[string]$STRAWBERRY_PATH = "C:\Strawberry",
[switch]$skipInstallPAR
) )
function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
$scriptDir = Get-ScriptDirectory
echo "Make this is run from the perl command window." echo "Make this is run from the perl command window."
echo "Requires PAR." echo "Requires PAR."
New-Variable -Name "current_branch" -Value "" $perldll = "perl$perlVersion"
New-Variable -Name "current_date" -Value "$(Get-Date -UFormat '%Y.%m.%d')"
New-Variable -Name "output_file" -Value ""
if ($args[0]) { if ($branch -eq "") {
$perlversion = $args[0] git branch | foreach {
if ($env:APPVEYOR) {
if ($_ -match "` (.*)") {
$branch += $matches[1]
}
} else {
if ($_ -match "\*` (.*)"){
$branch += $matches[1]
}
}
}
}
if ($outputFile -eq "") {
$outputFile = $output_zip
}
if (!($arch -eq "64bit" -Or $arch -eq "32bit")) {
# detect current version through libglut
if (Test-Path "${STRAWBERRY_PATH}\c\bin\libglut-0__.dll") {
$arch = "64bit"
} else {
$arch = "32bit"
}
echo "Arch: $arch"
}
if ($env:APPVEYOR) {
$output_zip = "${scriptDir}\..\..\Slic3r-${branch}.${currentDate}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).${arch}.zip"
} else { } else {
$perlversion = "524" $output_zip = "${scriptDir}\..\..\Slic3r-${branch}.${currentDate}.$(git rev-parse --short HEAD).${arch}.zip"
} }
$perldll = "perl$perlversion" if ($outputFile -eq "") {
$outputFile = $output_zip
git branch | foreach {
if ($env:APPVEYOR) {
if ($_ -match "` (.*)") {
$current_branch += $matches[1]
}
} else {
if ($_ -match "\*` (.*)"){
$current_branch += $matches[1]
}
}
} }
if ($exe) {
$output_file = "slic3r.exe" if (-Not $skipInstallPAR) {
cpanm "PAR::Packer"
}
# Some file names change based on 64bit/32bit. Set them here.
if ($arch -eq "32bit") {
$perlarch = "sjlj"
$glut = "libglut-0_.dll"
$pthread= "pthreadGC2-w32.dll"
} else { } else {
$output_file = "slic3r.par" $perlarch = "seh"
$glut = "libglut-0__.dll"
$pthread= "pthreadGC2-w64.dll"
} }
# Change this to where you have Strawberry Perl installed. if (!( (Test-Path -Path "${scriptDir}\slic3r.exe") -And (Test-Path -Path "${scriptDir}\slic3r-console.exe") -And (Test-Path -Path "${scriptDir}\slic3r-debug-console.exe") ) ) {
New-Variable -Name "STRAWBERRY_PATH" -Value "C:\Strawberry" echo "Compiling Slic3r binaries"
& ${scriptDir}\compile_wrapper.ps1 -perlVersion=$perlVersion -STRAWBERRY_PATH=$STRAWBERRY_PATH
}
cpanm "PAR::Packer" # remove all static libraries, they just take up space.
if ($env:APPVEYOR) {
gci ${scriptDir}\..\..\ -recurse | ? {$_.Name -match ".*\.a$"} | ri
gci -recurse ${scriptDir}\..\..\local-lib | ? {$_.PSIsContainer -And $_.Name -match "DocView|IPC|DataView|Media|Ribbon|Calendar|STC|PerlTest|WebView"} | ri
gci -recurse ${scriptDir}\..\..\local-lib| ? {$_.Name -match ".*(webview|ribbon|stc).*\.dll"} | ri
gci -recurse ${scriptDir}\..\..\local-lib| ? {$_.Name -match ".*(webview|ribbon|stc).*\.dll"} | ri
gci -recurse ${scriptDir}\..\..\local-lib| ? {$_.Name -match "^ExtUtils$"} | ri
gci -recurse ${scriptDir}\..\..\local-lib\lib\perl5\Module ? {$_.Name -match "^Build"} | ri
gci -recurse ${scriptDir}\..\..\local-lib ? {$_.Name -match "\.pod$"} | ri
gci -recurse ${scriptDir}\..\..\local-lib ? {$_.Name -match "\.h$"} | ri
}
pp ` pp `
-a "slic3r.exe;slic3r.exe" ` -a "${scriptDir}/slic3r.exe;Slic3r.exe" `
-a "slic3r-console.exe;slic3r-console.exe" ` -a "${scriptDir}/slic3r-console.exe;Slic3r-console.exe" `
-a "slic3r-debug-console.exe;slic3r-debug-console.exe" ` -a "${scriptDir}/slic3r-debug-console.exe;Slic3r-debug-console.exe" `
-a "../../lib;lib" ` -a "${scriptDir}/../../lib;lib" `
-a "../../local-lib;local-lib" ` -a "${scriptDir}/../../local-lib;local-lib" `
-a "../../slic3r.pl;slic3r.pl" ` -a "${scriptDir}/../../slic3r.pl;slic3r.pl" `
-a "../../utils;utils" ` -a "${scriptDir}/../../var;var" `
-a "../../var;var" ` -a "${scriptDir}/../../FreeGLUT/freeglut.dll;freeglut.dll" `
-a "../../FreeGLUT/freeglut.dll;freeglut.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\perl${perlVersion}.dll;perl${perlVersion}.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\perl${perlversion}.dll;perl${perlversion}.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_seh-1.dll;libgcc_s_seh-1.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_${perlarch}-1.dll;libgcc_s_${perlarch}-1.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" `
-a "${STRAWBERRY_PATH}\c\bin\pthreadGC2-w64.dll;pthreadGC2-w64.dll" ` -a "${STRAWBERRY_PATH}\c\bin\${pthread};${pthread}" `
-a "${STRAWBERRY_PATH}\c\bin\libglut-0__.dll;libglut-0__.dll" ` -a "${STRAWBERRY_PATH}\c\bin\${glut};${glut}" `
-M AutoLoader ` -M AutoLoader `
-M B ` -M B `
-M Carp ` -M Carp `
@ -93,6 +150,7 @@ pp `
-M IO ` -M IO `
-M IO::Handle ` -M IO::Handle `
-M IO::Select ` -M IO::Select `
-M IO::Socket `
-M LWP ` -M LWP `
-M LWP::MediaTypes ` -M LWP::MediaTypes `
-M LWP::MemberMixin ` -M LWP::MemberMixin `
@ -137,23 +195,11 @@ pp `
-M XSLoader ` -M XSLoader `
-B ` -B `
-M lib ` -M lib `
-p ..\..\slic3r.pl -o ..\..\${output_file} -p ${scriptDir}\..\..\slic3r.pl -o ${scriptDir}\..\..\slic3r.par
# switch renaming based on whether or not using packaged exe or zip # switch renaming based on whether or not using packaged exe or zip
if ($exe) {
if ($env:APPVEYOR) {
copy ..\..\slic3r.exe "..\..\slic3r-${current_branch}.${current_date}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).exe"
del ..\slic3r.exe
} else {
copy ..\..\slic3r.exe "..\..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).exe"
del ..\..\slic3r.exe
}
} else {
# make this more useful for not being on the appveyor server # make this more useful for not being on the appveyor server
if ($env:APPVEYOR) { copy ${scriptDir}\..\..\slic3r.par ${outputFile}
copy ..\..\slic3r.par "..\..\slic3r-${current_branch}.${current_date}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).zip" echo "Package saved as ${outputFile}"
} else { del ${scriptDir}\..\..\slic3r.par
copy ..\..\slic3r.par "..\..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).zip"
del ..\..\slic3r.par
}
}

View File

@ -18,6 +18,7 @@ use Slic3r::Geometry qw(epsilon X Y Z deg2rad);
use Time::HiRes qw(gettimeofday tv_interval); use Time::HiRes qw(gettimeofday tv_interval);
$|++; $|++;
binmode STDOUT, ':utf8'; binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
our %opt = (); our %opt = ();
my %cli_options = (); my %cli_options = ();
@ -35,7 +36,6 @@ my %cli_options = ();
'load=s@' => \$opt{load}, 'load=s@' => \$opt{load},
'autosave=s' => \$opt{autosave}, 'autosave=s' => \$opt{autosave},
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config}, 'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
'no-controller' => \$opt{no_controller},
'datadir=s' => \$opt{datadir}, 'datadir=s' => \$opt{datadir},
'export-svg' => \$opt{export_svg}, 'export-svg' => \$opt{export_svg},
'merge|m' => \$opt{merge}, 'merge|m' => \$opt{merge},
@ -74,9 +74,9 @@ my @external_configs = ();
if ($opt{load}) { if ($opt{load}) {
foreach my $configfile (@{$opt{load}}) { foreach my $configfile (@{$opt{load}}) {
$configfile = Slic3r::decode_path($configfile); $configfile = Slic3r::decode_path($configfile);
if (-e $configfile) { if (-e Slic3r::encode_path($configfile)) {
push @external_configs, Slic3r::Config->load($configfile); push @external_configs, Slic3r::Config->load($configfile);
} elsif (-e "$FindBin::Bin/$configfile") { } elsif (-e Slic3r::encode_path("$FindBin::Bin/$configfile")) {
printf STDERR "Loading $FindBin::Bin/$configfile\n"; printf STDERR "Loading $FindBin::Bin/$configfile\n";
push @external_configs, Slic3r::Config->load("$FindBin::Bin/$configfile"); push @external_configs, Slic3r::Config->load("$FindBin::Bin/$configfile");
} else { } else {
@ -99,7 +99,7 @@ if ($opt{save}) {
if (@{$config->get_keys} > 0) { if (@{$config->get_keys} > 0) {
$config->save($opt{save}); $config->save($opt{save});
} else { } else {
Slic3r::Config->new_from_defaults->save($opt{save}); Slic3r::Config->new_from_defaults->save(Slic3r::decode_path($opt{save}));
} }
} }
@ -109,7 +109,6 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
{ {
no warnings 'once'; no warnings 'once';
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
$Slic3r::GUI::no_controller = $opt{no_controller};
$Slic3r::GUI::autosave = Slic3r::decode_path($opt{autosave} // ''); $Slic3r::GUI::autosave = Slic3r::decode_path($opt{autosave} // '');
$Slic3r::GUI::threads = $opt{threads}; $Slic3r::GUI::threads = $opt{threads};
} }
@ -266,7 +265,7 @@ if (@ARGV) { # slicing from command line
my ($percent, $message) = @_; my ($percent, $message) = @_;
printf "=> %s\n", $message; printf "=> %s\n", $message;
}, },
output_file => $opt{output}, output_file => Slic3r::decode_path($opt{output}),
); );
$sprint->apply_config($config); $sprint->apply_config($config);
@ -316,6 +315,9 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
--save <file> Save configuration to the specified file --save <file> Save configuration to the specified file
--load <file> Load configuration from the specified file. It can be used --load <file> Load configuration from the specified file. It can be used
more than once to load options from multiple files. more than once to load options from multiple files.
--datadir <path> Load and store settings at the given directory.
This is useful for maintaining different profiles or including
configurations from a network storage.
-o, --output <file> File to output gcode to (by default, the file will be saved -o, --output <file> File to output gcode to (by default, the file will be saved
into the same directory as the input file using the into the same directory as the input file using the
--output-filename-format to generate the filename.) If a --output-filename-format to generate the filename.) If a

View File

@ -139,17 +139,29 @@ include_directories(${Boost_INCLUDE_DIRS})
IF(CMAKE_HOST_UNIX) IF(CMAKE_HOST_UNIX)
#set(Boost_LIBRARIES bsystem bthread bfilesystem) #set(Boost_LIBRARIES bsystem bthread bfilesystem)
ENDIF(CMAKE_HOST_UNIX) ENDIF(CMAKE_HOST_UNIX)
target_link_libraries (slic3r libslic3r admesh clipper expat polypartition poly2tri ${Boost_LIBRARIES})
IF(wxWidgets_FOUND) IF(wxWidgets_FOUND)
MESSAGE("wx found!") MESSAGE("wx found!")
INCLUDE("${wxWidgets_USE_FILE}") INCLUDE("${wxWidgets_USE_FILE}")
add_library(slic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/GUI.cpp) add_library(slic3r_gui STATIC ${LIBDIR}/slic3r/GUI/3DScene.cpp ${LIBDIR}/slic3r/GUI/GUI.cpp)
#only build GUI lib if building with wx #only build GUI lib if building with wx
target_link_libraries (slic3r slic3r_gui libslic3r admesh clipper expat polypartition poly2tri ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES}) target_link_libraries (slic3r slic3r-gui ${wxWidgets_LIBRARIES})
ELSE(wxWidgets_FOUND) ELSE(wxWidgets_FOUND)
# For convenience. When we cannot continue, inform the user # For convenience. When we cannot continue, inform the user
MESSAGE("wx not found!") MESSAGE("wx not found!")
target_link_libraries (slic3r libslic3r admesh clipper expat polypartition poly2tri ${Boost_LIBRARIES})
#skip gui when no wx included #skip gui when no wx included
ENDIF(wxWidgets_FOUND) ENDIF(wxWidgets_FOUND)
# 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)
ENDIF(WIN32)
target_link_libraries (extrude-tin libslic3r admesh clipper expat polypartition poly2tri ${Boost_LIBRARIES}) target_link_libraries (extrude-tin libslic3r admesh clipper expat polypartition poly2tri ${Boost_LIBRARIES})

View File

@ -10,15 +10,22 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <math.h> #include <math.h>
#include "boost/filesystem.hpp" #include <boost/filesystem.hpp>
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
using namespace Slic3r; using namespace Slic3r;
void confess_at(const char *file, int line, const char *func, const char *pat, ...){} void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
int int
main(const int argc, const char **argv) main(int argc, char **argv)
{ {
// Convert arguments to UTF-8 (needed on Windows).
// argv then points to memory owned by a.
boost::nowide::args a(argc, argv);
// parse all command line options into a DynamicConfig // parse all command line options into a DynamicConfig
ConfigDef config_def; ConfigDef config_def;
config_def.merge(cli_config_def); config_def.merge(cli_config_def);
@ -34,18 +41,17 @@ main(const int argc, const char **argv)
DynamicPrintConfig print_config; DynamicPrintConfig print_config;
// load config files supplied via --load // load config files supplied via --load
for (std::vector<std::string>::const_iterator file = cli_config.load.values.begin(); for (const std::string &file : cli_config.load.values) {
file != cli_config.load.values.end(); ++file) { if (!boost::filesystem::exists(file)) {
if (!boost::filesystem::exists(*file)) { boost::nowide::cout << "No such file: " << file << std::endl;
std::cout << "No such file: " << *file << std::endl;
exit(1); exit(1);
} }
DynamicPrintConfig c; DynamicPrintConfig c;
try { try {
c.load(*file); c.load(file);
} catch (std::exception &e) { } catch (std::exception &e) {
std::cout << "Error while reading config file: " << e.what() << std::endl; boost::nowide::cout << "Error while reading config file: " << e.what() << std::endl;
exit(1); exit(1);
} }
c.normalize(); c.normalize();
@ -62,85 +68,85 @@ main(const int argc, const char **argv)
// read input file(s) if any // read input file(s) if any
std::vector<Model> models; std::vector<Model> models;
for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) { for (const t_config_option_key &file : input_files) {
if (!boost::filesystem::exists(*it)) { if (!boost::filesystem::exists(file)) {
std::cout << "No such file: " << *it << std::endl; boost::nowide::cerr << "No such file: " << file << std::endl;
exit(1); exit(1);
} }
Model model; Model model;
try { try {
model = Model::read_from_file(*it); model = Model::read_from_file(file);
} catch (std::exception &e) { } catch (std::exception &e) {
std::cout << *it << ": " << e.what() << std::endl; boost::nowide::cerr << file << ": " << e.what() << std::endl;
exit(1); exit(1);
} }
if (model.objects.empty()) { if (model.objects.empty()) {
printf("Error: file is empty: %s\n", it->c_str()); boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
continue; continue;
} }
model.add_default_instances(); model.add_default_instances();
// apply command line transform options // apply command line transform options
for (ModelObjectPtrs::iterator o = model.objects.begin(); o != model.objects.end(); ++o) { for (ModelObject* o : model.objects) {
if (cli_config.scale_to_fit.is_positive_volume()) if (cli_config.scale_to_fit.is_positive_volume())
(*o)->scale_to_fit(cli_config.scale_to_fit.value); o->scale_to_fit(cli_config.scale_to_fit.value);
// TODO: honor option order? // TODO: honor option order?
(*o)->scale(cli_config.scale.value); o->scale(cli_config.scale.value);
(*o)->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X); o->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X);
(*o)->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y); o->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y);
(*o)->rotate(Geometry::deg2rad(cli_config.rotate.value), Z); o->rotate(Geometry::deg2rad(cli_config.rotate.value), Z);
} }
// TODO: handle --merge // TODO: handle --merge
models.push_back(model); models.push_back(model);
} }
for (std::vector<Model>::iterator model = models.begin(); model != models.end(); ++model) { for (Model &model : models) {
if (cli_config.info) { if (cli_config.info) {
// --info works on unrepaired model // --info works on unrepaired model
model->print_info(); model.print_info();
} else if (cli_config.export_obj) { } else if (cli_config.export_obj) {
std::string outfile = cli_config.output.value; std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model->objects.front()->input_file + ".obj"; if (outfile.empty()) outfile = model.objects.front()->input_file + ".obj";
TriangleMesh mesh = model->mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); mesh.repair();
IO::OBJ::write(mesh, outfile); IO::OBJ::write(mesh, outfile);
printf("File exported to %s\n", outfile.c_str()); boost::nowide::cout << "File exported to " << outfile << std::endl;
} else if (cli_config.export_pov) { } else if (cli_config.export_pov) {
std::string outfile = cli_config.output.value; std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model->objects.front()->input_file + ".pov"; if (outfile.empty()) outfile = model.objects.front()->input_file + ".pov";
TriangleMesh mesh = model->mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); mesh.repair();
IO::POV::write(mesh, outfile); IO::POV::write(mesh, outfile);
printf("File exported to %s\n", outfile.c_str()); boost::nowide::cout << "File exported to " << outfile << std::endl;
} else if (cli_config.export_svg) { } else if (cli_config.export_svg) {
std::string outfile = cli_config.output.value; std::string outfile = cli_config.output.value;
if (outfile.empty()) outfile = model->objects.front()->input_file + ".svg"; if (outfile.empty()) outfile = model.objects.front()->input_file + ".svg";
SLAPrint print(&*model); SLAPrint print(&model);
print.config.apply(print_config, true); print.config.apply(print_config, true);
print.slice(); print.slice();
print.write_svg(outfile); print.write_svg(outfile);
printf("SVG file exported to %s\n", outfile.c_str()); boost::nowide::cout << "SVG file exported to " << outfile << std::endl;
} else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) { } else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) {
model->repair(); model.repair();
model->translate(0, 0, -model->bounding_box().min.z); model.translate(0, 0, -model.bounding_box().min.z);
if (!model->objects.empty()) { if (!model.objects.empty()) {
// FIXME: cut all objects // FIXME: cut all objects
Model out; Model out;
if (cli_config.cut_x > 0) { if (cli_config.cut_x > 0) {
model->objects.front()->cut(X, cli_config.cut_x, &out); model.objects.front()->cut(X, cli_config.cut_x, &out);
} else if (cli_config.cut_y > 0) { } else if (cli_config.cut_y > 0) {
model->objects.front()->cut(Y, cli_config.cut_y, &out); model.objects.front()->cut(Y, cli_config.cut_y, &out);
} else { } else {
model->objects.front()->cut(Z, cli_config.cut, &out); model.objects.front()->cut(Z, cli_config.cut, &out);
} }
ModelObject &upper = *out.objects[0]; ModelObject &upper = *out.objects[0];
@ -156,18 +162,19 @@ main(const int argc, const char **argv)
} }
} }
} else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) { } else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) {
TriangleMesh mesh = model->mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value); TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value);
for (TriangleMeshPtrs::iterator m = meshes.begin(); m != meshes.end(); ++m) { size_t i = 0;
for (TriangleMesh* m : meshes) {
std::ostringstream ss; std::ostringstream ss;
ss << model->objects.front()->input_file << "_" << (m - meshes.begin()) << ".stl"; ss << model.objects.front()->input_file << "_" << i++ << ".stl";
IO::STL::write(**m, ss.str()); IO::STL::write(*m, ss.str());
delete *m; delete m;
} }
} else { } else {
std::cerr << "error: command not supported" << std::endl; boost::nowide::cerr << "error: command not supported" << std::endl;
return 1; return 1;
} }
} }

View File

@ -3,14 +3,20 @@
#include "IO.hpp" #include "IO.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
using namespace Slic3r; using namespace Slic3r;
void confess_at(const char *file, int line, const char *func, const char *pat, ...){} void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
int int
main(const int argc, const char **argv) main(int argc, char **argv)
{ {
// Convert arguments to UTF-8 (needed on Windows).
// argv then points to memory owned by a.
boost::nowide::args a(argc, argv);
// read config // read config
ConfigDef config_def; ConfigDef config_def;
{ {
@ -40,7 +46,7 @@ main(const int argc, const char **argv)
if (outfile.empty()) outfile = *it + "_extruded.stl"; if (outfile.empty()) outfile = *it + "_extruded.stl";
Slic3r::IO::STL::write(mesh, outfile); Slic3r::IO::STL::write(mesh, outfile);
printf("Extruded mesh written to %s\n", outfile.c_str()); boost::nowide::cout << "Extruded mesh written to " << outfile << std::endl;
} }
return 0; return 0;

View File

@ -1,4 +1,4 @@
use Test::More tests => 18; use Test::More tests => 20;
use strict; use strict;
use warnings; use warnings;
@ -99,6 +99,24 @@ use Slic3r::Test;
'correct bridge angle for rectangle'; 'correct bridge angle for rectangle';
} }
{
# GH #3929: This test case checks that narrow gaps in lower slices don't prevent correct
# direction detection.
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new([10099996,45867519],[3762370,45867519],[3762370,2132479],[10099996,2132479]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new([13534103,210089],[13629884,235753],[14249999,401901],[14269611,421510],[14272931,424830],[14287518,439411],[14484206,636101],[15348099,1500000],[15360812,1547449],[15365467,1564815],[15388623,1651235],[15391897,1663454],[15393088,1667906],[15399044,1690134],[15457593,1908648],[15750000,2999999],[15750000,45000000],[15742825,45026783],[15741540,45031580],[15735900,45052628],[15663980,45321047],[15348099,46500000],[15151410,46696691],[14287518,47560587],[14267907,47580196],[14264587,47583515],[14249999,47598100],[14211041,47608539],[14204785,47610215],[14176024,47617916],[14105602,47636784],[14097768,47638884],[14048000,47652220],[13871472,47699515],[12750000,48000000],[10446106,48000000],[10446124,47990347],[10446124,9652],[10446106,0],[12750000,0]),
Slic3r::Polygon->new([10251886,5013],[10251886,47994988],[10251907,48000000],[10100006,48000000],[10100006,0],[10251907,0]),
Slic3r::Polygon->new([3762360,17017],[3762360,47982984],[3762397,48000000],[1249999,48000000],[536029,47808700],[456599,47787419],[73471,47684764],[0,47665076],[0,23124327],[119299,22907322],[159278,22834601],[196290,22690451],[239412,22522516],[303787,22271780],[639274,20965103],[639274,19034896],[616959,18947983],[607651,18911729],[559146,18722807],[494769,18472073],[159278,17165397],[38931,16946491],[0,16875676],[0,334922],[128529,300484],[1250000,0],[3762397,0]),
),
];
ok check_angle($lower, $bridge, 0, undef, $bridge->area, 500000),
'correct bridge angle when lower slices have narrow gap';
}
sub check_angle { sub check_angle {
my ($lower, $bridge, $expected, $tolerance, $expected_coverage, $extrusion_width) = @_; my ($lower, $bridge, $expected, $tolerance, $expected_coverage, $extrusion_width) = @_;
@ -121,9 +139,9 @@ sub check_angle {
# our epsilon is equal to the steps used by the bridge detection algorithm # our epsilon is equal to the steps used by the bridge detection algorithm
###use XXX; YYY [ rad2deg($result), $expected ]; ###use XXX; YYY [ rad2deg($result), $expected ];
# returned value must be non-negative, check for that too # returned value must be non-negative, check for that too
my $delta=rad2deg($result) - $expected; my $delta = rad2deg($result) - $expected;
$delta-=180 if $delta>=180 - epsilon; $delta -= 180 if $delta >= 180 - epsilon;
return defined $result && $result>=0 && abs($delta) < $tolerance; return defined $result && $result >= 0 && abs($delta) < $tolerance;
} }
{ {

View File

@ -2,7 +2,7 @@ use Test::More;
use strict; use strict;
use warnings; use warnings;
plan tests => 42; plan tests => 44;
BEGIN { BEGIN {
use FindBin; use FindBin;
@ -254,4 +254,14 @@ my $polygons = [
is scalar(@$simplified), 3, 'triangle is never simplified to less than 3 points'; is scalar(@$simplified), 3, 'triangle is never simplified to less than 3 points';
} }
{
# Two concave vertices of this polygon have angle = PI*4/3, so this test fails
# if epsilon is not used.
my $polygon = Slic3r::Polygon->new(
[60246458,14802768],[64477191,12360001],[63727343,11060995],[64086449,10853608],[66393722,14850069],[66034704,15057334],[65284646,13758387],[61053864,16200839],[69200258,30310849],[62172547,42483120],[61137680,41850279],[67799985,30310848],[51399866,1905506],[38092663,1905506],[38092663,692699],[52100125,692699],
);
is scalar(@{$polygon->concave_points(PI*4/3)}), 6, 'expected number of concave points';
is scalar(@{$polygon->convex_points(PI*2/3)}), 10, 'expected number of convex points';
}
__END__ __END__

View File

@ -16,6 +16,8 @@ use Slic3r::Test;
{ {
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new_from_defaults;
$config->set('layer_height', 0.3);
$config->set('first_layer_height', 0.35);
$config->set('raft_layers', 2); $config->set('raft_layers', 2);
$config->set('infill_extruder', 2); $config->set('infill_extruder', 2);
$config->set('solid_infill_extruder', 3); $config->set('solid_infill_extruder', 3);
@ -24,7 +26,9 @@ use Slic3r::Test;
$config->set('extruder_offset', [ [0,0], [20,0], [0,20], [20,20] ]); $config->set('extruder_offset', [ [0,0], [20,0], [0,20], [20,20] ]);
$config->set('temperature', [200, 180, 170, 160]); $config->set('temperature', [200, 180, 170, 160]);
$config->set('first_layer_temperature', [206, 186, 166, 156]); $config->set('first_layer_temperature', [206, 186, 166, 156]);
$config->set('standby_temperature_delta', -5);
$config->set('toolchange_gcode', ';toolchange'); # test that it doesn't crash when this is supplied $config->set('toolchange_gcode', ';toolchange'); # test that it doesn't crash when this is supplied
$config->set('skirts', 2); # test correct temperatures are applied to skirt as well
my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
@ -57,6 +61,18 @@ use Slic3r::Test;
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y}); push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y});
$point->translate(map +scale($_), @{ $config->extruder_offset->[$tool] }); $point->translate(map +scale($_), @{ $config->extruder_offset->[$tool] });
# check temperature (we don't do it at M104/M109 because that may be
# issued before layer change)
if ($self->Z == $config->first_layer_height) {
fail 'unexpected temperature in first layer'
unless $tool_temp[$tool] == ($config->first_layer_temperature->[$tool] + $config->standby_temperature_delta)
|| $tool_temp[$tool] == $config->first_layer_temperature->[$tool];
} else {
fail 'unexpected temperature'
unless $tool_temp[$tool] == ($config->temperature->[$tool] + $config->standby_temperature_delta)
|| $tool_temp[$tool] == $config->temperature->[$tool];
}
} }
}); });
my $convex_hull = convex_hull(\@extrusion_points); my $convex_hull = convex_hull(\@extrusion_points);

View File

@ -1,4 +1,4 @@
use Test::More tests => 59; use Test::More tests => 63;
use strict; use strict;
use warnings; use warnings;
@ -393,10 +393,37 @@ use Slic3r::Test;
}); });
return scalar keys %z_with_bridges; return scalar keys %z_with_bridges;
}; };
ok $test->(Slic3r::Test::init_print('V', config => $config)) == 1, is $test->(Slic3r::Test::init_print('V', config => $config)), 1,
'no overhangs printed with bridge speed'; # except for the first internal solid layers above void 'no overhangs printed with bridge speed'; # except for the first internal solid layers above void
ok $test->(Slic3r::Test::init_print('V', config => $config, scale_xyz => [3,1,1])) > 1, ok $test->(Slic3r::Test::init_print('V', config => $config, scale_xyz => [3,1,1])) > 1,
'overhangs printed with bridge speed'; 'overhangs printed with bridge speed';
$config->set('bottom_solid_layers', 0);
$config->set('top_solid_layers', 0);
my $test2 = sub {
my ($print) = @_;
my $num_bridges = 0;
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($info->{extruding} && $info->{dist_XY} > 0) {
$num_bridges++ if ($args->{F} // $self->F) == $config->bridge_speed*60;
}
});
return $num_bridges;
};
is $test2->(Slic3r::Test::init_print('overhang', config => $config)), $config->perimeters*3,
'expected number of segments use bridge speed (overhang)';
is $test2->(Slic3r::Test::init_print('bridge', config => $config)), $config->perimeters*2,
'expected number of segments use bridge speed (bridge)';
is $test2->(Slic3r::Test::init_print('bridge_with_hole', config => $config)), $config->perimeters*4,
'expected number of segments use bridge speed (bridge with hole)';
# scale the test model so that the hole perimeter length is smaller than small perimeter
# threshold (~40mm). check that we still use overhang settings regardless of it being small
is $test2->(Slic3r::Test::init_print('bridge_with_hole', config => $config, scale_xyz => [0.3,0.5,1])),
$config->perimeters*4,
'expected number of segments use bridge speed (bridge with small hole)';
} }
{ {

View File

@ -1,4 +1,4 @@
use Test::More tests => 26; use Test::More tests => 27;
use strict; use strict;
use warnings; use warnings;
@ -10,6 +10,7 @@ BEGIN {
use List::Util qw(any); use List::Util qw(any);
use Slic3r; use Slic3r;
use Slic3r::Geometry qw(epsilon);
use Slic3r::Test qw(_eq); use Slic3r::Test qw(_eq);
{ {
@ -209,20 +210,55 @@ use Slic3r::Test qw(_eq);
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new_from_defaults;
$config->set('start_gcode', ''); $config->set('start_gcode', '');
$config->set('retract_lift', [3, 4]); $config->set('retract_lift', [3, 4]);
$config->set('only_retract_when_crossing_perimeters', 0);
my @lifted_at = (); my @lifted_at = ();
my $test = sub { my $test = sub {
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
@lifted_at = (); @lifted_at = ();
my $retracted = 0;
my $lifted = 0;
my $tool = 0;
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_; my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && $info->{dist_Z} < 0) { if ($cmd eq 'G1') {
push @lifted_at, $info->{new_Z}; if ($info->{dist_Z} < 0) {
# going downwards; this is a "restore lift" move
$lifted = 0;
push @lifted_at, $info->{new_Z};
} elsif (abs($info->{dist_Z} - $config->get_at('retract_lift', $tool)) < &epsilon) {
# going upwards by a large amount; this is a lift move
fail 'always lift after retraction' if !$retracted;
### It would be useful to prevent the double lift happening at layer change:
###fail 'no double lift' if $lifted;
$lifted = 1;
} elsif ($info->{retracting}) {
$retracted = 1;
} elsif ($info->{extruding} && $info->{dist_XY} == 0) {
# consider the lift as layer change if they are configured with the same distance
$lifted = 0 if $config->get_at('retract_lift', $tool) == $config->layer_height;
fail 'always unretract after unlifting' if $lifted;
$retracted = 0;
} elsif ($info->{travel} && $info->{dist_XY} >= $config->get_at('retract_before_travel', $tool)) {
#printf "dist_XY = %f, rbt = %f\n", $info->{dist_XY}, $config->get_at('retract_before_travel', 0);
my $below = $config->get_at('retract_lift_below', $tool);
fail 'no retraction for long travel move' if !$retracted;
fail 'no lift for long travel move'
if !$lifted
&& $self->Z >= $config->get_at('retract_lift_above', $tool)
&& ($below == 0 || $self->Z <= $below);
}
} elsif ($cmd =~ /^T(\d+)/) {
$tool = $1;
} }
}); });
}; };
$config->set('retract_layer_change', [1,1]);
$test->();
ok !!@lifted_at, 'lift is compatible with retract_layer_change';
$config->set('retract_lift_above', [0, 0]); $config->set('retract_lift_above', [0, 0]);
$config->set('retract_lift_below', [0, 0]); $config->set('retract_lift_below', [0, 0]);
$test->(); $test->();
@ -234,8 +270,12 @@ use Slic3r::Test qw(_eq);
ok !!@lifted_at, 'lift takes place when above/below != 0'; ok !!@lifted_at, 'lift takes place when above/below != 0';
ok !(any { $_ < $config->get_at('retract_lift_above', 0) } @lifted_at), ok !(any { $_ < $config->get_at('retract_lift_above', 0) } @lifted_at),
'Z is not lifted below the configured value'; 'Z is not lifted below the configured value';
ok !(any { $_ > $config->get_at('retract_lift_below', 0) } @lifted_at), {
'Z is not lifted above the configured value'; my $below = $config->get_at('retract_lift_below', 0);
$below += $config->layer_height if $config->get_at('retract_layer_change', 0);
ok !(any { $_ > $below } @lifted_at),
'Z is not lifted above the configured value';
}
# check lifting with different values for 2. extruder # check lifting with different values for 2. extruder
$config->set('perimeter_extruder', 2); $config->set('perimeter_extruder', 2);
@ -251,8 +291,20 @@ use Slic3r::Test qw(_eq);
ok !!@lifted_at, 'lift takes place when above/below != 0 for 2. extruder'; ok !!@lifted_at, 'lift takes place when above/below != 0 for 2. extruder';
ok !(any { $_ < $config->get_at('retract_lift_above', 1) } @lifted_at), ok !(any { $_ < $config->get_at('retract_lift_above', 1) } @lifted_at),
'Z is not lifted below the configured value for 2. extruder'; 'Z is not lifted below the configured value for 2. extruder';
ok !(any { $_ > $config->get_at('retract_lift_below', 1) } @lifted_at), {
'Z is not lifted above the configured value for 2. extruder'; my $below = $config->get_at('retract_lift_below', 1);
$below += $config->layer_height if $config->get_at('retract_layer_change', 1);
ok !(any { $_ > $below } @lifted_at),
'Z is not lifted above the configured value for 2. extruder';
}
$config->set('retract_lift', [$config->layer_height]);
$config->set('perimeter_extruder', 1);
$config->set('infill_extruder', 1);
$config->set('retract_lift_above', [0, 0]);
$config->set('retract_lift_below', [0, 0]);
$test->();
} }
__END__ __END__

View File

@ -1,4 +1,4 @@
use Test::More tests => 27; use Test::More tests => 28;
use strict; use strict;
use warnings; use warnings;
@ -258,5 +258,32 @@ use Slic3r::Test;
@{ $layer_heights_by_tool{$config->support_material_extruder-1} }), @{ $layer_heights_by_tool{$config->support_material_extruder-1} }),
'no support material layer is as thin as object layers'; 'no support material layer is as thin as object layers';
} }
{
my $config = Slic3r::Config->new_from_defaults;
$config->set('support_material_enforce_layers', 100);
$config->set('support_material', 0);
my @contact_z = my @top_z = ();
my $test = sub {
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
my $flow = $print->print->objects->[0]->support_material_flow;
my $support = Slic3r::Print::SupportMaterial->new(
object_config => $print->print->objects->[0]->config,
print_config => $print->print->config,
flow => $flow,
interface_flow => $flow,
first_layer_flow => $flow,
);
my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
'forced support is generated';
};
$config->set('layer_height', 0.2);
$config->set('first_layer_height', 0.3);
@contact_z = (1.9);
@top_z = (1.1);
$test->();
}
__END__ __END__

BIN
var/zoom_in.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

BIN
var/zoom_out.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@ -10,6 +10,10 @@ use Module::Build::WithXSpp;
my $cpp_guess = ExtUtils::CppGuess->new; my $cpp_guess = ExtUtils::CppGuess->new;
my $mswin = $^O eq 'MSWin32'; my $mswin = $^O eq 'MSWin32';
my $linux = $^O eq 'linux';
# prevent an annoying concatenation warning by Devel::CheckLib
$ENV{LD_RUN_PATH} //= "";
# _GLIBCXX_USE_C99 : to get the long long type for g++ # _GLIBCXX_USE_C99 : to get the long long type for g++
# HAS_BOOL : stops Perl/lib/CORE/handy.h from doing "# define bool char" for MSVC # HAS_BOOL : stops Perl/lib/CORE/handy.h from doing "# define bool char" for MSVC
@ -20,14 +24,32 @@ if ($cpp_guess->is_gcc) {
# GCC is pedantic with c++11 std, so undefine strict ansi to get M_PI back # GCC is pedantic with c++11 std, so undefine strict ansi to get M_PI back
push @cflags, qw(-U__STRICT_ANSI__); push @cflags, qw(-U__STRICT_ANSI__);
} }
if (`$Config{cc} -v` =~ /gcc version 4\.6\./) {
# Compatibility with GCC 4.6 is not a requirement, but as long as code compiles on it we try to support it. # std=c++11 Enforce usage of C++11 (required now). Minimum compiler supported: gcc 4.9, clang 3.3, MSVC 14.0
push @cflags, qw(-std=c++0x); push @cflags, qw(-std=c++11);
} else {
# std=c++11 Enforce usage of C++11 (required now). Minimum compiler supported: gcc 4.9, clang 3.3, MSVC 14.0
push @cflags, qw(-std=c++11);
}
my @ldflags = (); my @ldflags = ();
if ($linux && (defined $ENV{SLIC3R_STATIC} && $ENV{SLIC3R_STATIC})) {
push @ldflags, qw(-static-libgcc -static-libstdc++);
if ($ENV{TRAVIS}) {
# On the build server, link to the actual static libraries to make sure we get them in the list.
push @ldflags, qw(/usr/lib/gcc/x86_64-linux-gnu/4.9/libstdc++.a /usr/lib/gcc/x86_64-linux-gnu/4.9/libgcc.a);
}
# ExtUtils::CppGuess has a hard-coded -lstdc++, so we filter it out
{
no strict 'refs';
no warnings 'redefine';
my $func = "ExtUtils::CppGuess::_get_lflags";
my $orig = *$func{CODE};
*{$func} = sub {
my $lflags = $orig->(@_);
$lflags =~ s/\s*-lstdc\+\+//;
return $lflags;
};
}
}
if ($^O eq 'darwin') { if ($^O eq 'darwin') {
push @cflags, qw(-stdlib=libc++); push @cflags, qw(-stdlib=libc++);
push @ldflags, qw(-framework IOKit -framework CoreFoundation -lc++); push @ldflags, qw(-framework IOKit -framework CoreFoundation -lc++);
@ -67,15 +89,13 @@ my @boost_include = ();
if (defined $ENV{BOOST_INCLUDEDIR}) { if (defined $ENV{BOOST_INCLUDEDIR}) {
push @boost_include, $ENV{BOOST_INCLUDEDIR} push @boost_include, $ENV{BOOST_INCLUDEDIR}
} elsif (defined $ENV{BOOST_DIR}) { } elsif (defined $ENV{BOOST_DIR}) {
my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include'); # User might have provided a path relative to the Build.PL in the main directory
if (-d $subdir) { foreach my $subdir ($ENV{BOOST_DIR}, "../$ENV{BOOST_DIR}", "$ENV{BOOST_DIR}/include", "../$ENV{BOOST_DIR}/include") {
push @boost_include, $subdir; if (-d $subdir) {
} elsif (-d "../$subdir") { push @boost_include, $subdir;
# User might have provided a path relative to the Build.PL in the main directory }
push @boost_include, "../$subdir";
} else {
push @boost_include, $ENV{BOOST_DIR};
} }
die "Invalid BOOST_DIR: no such directory\n" if !@boost_include;
} else { } else {
# Boost library was not defined by the environment. # Boost library was not defined by the environment.
# Try to guess at some default paths. # Try to guess at some default paths.
@ -99,15 +119,13 @@ my @boost_libs = ();
if (defined $ENV{BOOST_LIBRARYPATH}) { if (defined $ENV{BOOST_LIBRARYPATH}) {
push @boost_libs, $ENV{BOOST_LIBRARYPATH} push @boost_libs, $ENV{BOOST_LIBRARYPATH}
} elsif (defined $ENV{BOOST_DIR}) { } elsif (defined $ENV{BOOST_DIR}) {
my $subdir = $ENV{BOOST_DIR} . ($mswin ? '\stage\lib' : '/stage/lib'); # User might have provided a path relative to the Build.PL in the main directory
if (-d $subdir) { foreach my $subdir ("$ENV{BOOST_DIR}/stage/lib", "../$ENV{BOOST_DIR}/stage/lib") {
push @boost_libs, $subdir; if (-d $subdir) {
} elsif (-d "../$subdir") { push @boost_libs, $subdir;
# User might have provided a path relative to the Build.PL in the main directory }
push @boost_libs, "../$subdir";
} else {
push @boost_libs, $ENV{BOOST_DIR};
} }
die "Invalid BOOST_DIR: no such directory\n" if !@boost_libs;
} else { } else {
# Boost library was not defined by the environment. # Boost library was not defined by the environment.
# Try to guess at some default paths. # Try to guess at some default paths.
@ -196,13 +214,8 @@ if ($ENV{SLIC3R_DEBUG}) {
push @cflags, '-DNDEBUG'; push @cflags, '-DNDEBUG';
} }
if ($cpp_guess->is_gcc) { if ($cpp_guess->is_gcc) {
# check whether we're dealing with a buggy GCC version # our templated XS bindings cause undefined-var-template warnings
# see https://github.com/alexrj/Slic3r/issues/1965 push @cflags, qw(-Wno-undefined-var-template);
if (`cc --version` =~ / 4\.7\.[012]/) {
# Workaround suggested by Boost devs:
# https://svn.boost.org/trac/boost/ticket/8695
push @cflags, qw(-fno-inline-small-functions);
}
} }
my $build = Module::Build::WithXSpp->new( my $build = Module::Build::WithXSpp->new(

View File

@ -1,5 +1,6 @@
Build.PL Build.PL
lib/Slic3r/XS.pm lib/Slic3r/XS.pm
libslic3r.doxygen
MANIFEST This list of files MANIFEST This list of files
src/admesh/connect.c src/admesh/connect.c
src/admesh/normals.c src/admesh/normals.c
@ -8,6 +9,21 @@ src/admesh/stl.h
src/admesh/stl_io.c src/admesh/stl_io.c
src/admesh/stlinit.c src/admesh/stlinit.c
src/admesh/util.c src/admesh/util.c
src/boost/nowide/args.hpp
src/boost/nowide/cenv.hpp
src/boost/nowide/config.hpp
src/boost/nowide/convert.hpp
src/boost/nowide/cstdio.hpp
src/boost/nowide/cstdlib.hpp
src/boost/nowide/filebuf.hpp
src/boost/nowide/fstream.hpp
src/boost/nowide/integration/filesystem.hpp
src/boost/nowide/iostream.cpp
src/boost/nowide/iostream.hpp
src/boost/nowide/stackstring.hpp
src/boost/nowide/system.hpp
src/boost/nowide/utf8_codecvt.hpp
src/boost/nowide/windows.hpp
src/clipper.cpp src/clipper.cpp
src/clipper.hpp src/clipper.hpp
src/expat/ascii.h src/expat/ascii.h

2478
xs/libslic3r.doxygen Normal file

File diff suppressed because it is too large Load Diff

View File

@ -141,7 +141,7 @@ stl_generate_shared_vertices(stl_file *stl) {
} }
void void
stl_write_off(stl_file *stl, char *file) { stl_write_off(stl_file *stl, ADMESH_CHAR *file) {
int i; int i;
FILE *fp; FILE *fp;
char *error_msg; char *error_msg;
@ -149,14 +149,9 @@ stl_write_off(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_ascii: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -177,7 +172,7 @@ stl_write_off(stl_file *stl, char *file) {
} }
void void
stl_write_vrml(stl_file *stl, char *file) { stl_write_vrml(stl_file *stl, ADMESH_CHAR *file) {
int i; int i;
FILE *fp; FILE *fp;
char *error_msg; char *error_msg;
@ -185,14 +180,9 @@ stl_write_vrml(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_ascii: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -234,19 +224,16 @@ stl_write_vrml(stl_file *stl, char *file) {
fclose(fp); fclose(fp);
} }
void stl_write_obj (stl_file *stl, const char *file) { void stl_write_obj (stl_file *stl, const ADMESH_CHAR *file) {
int i; int i;
FILE* fp; FILE* fp;
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if (fp == NULL) { if (fp == NULL) {
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ perror("stl_write_ascii: Couldn't open file for writing");
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }

View File

@ -32,6 +32,15 @@
#error "admesh works correctly on little endian machines only!" #error "admesh works correctly on little endian machines only!"
#endif #endif
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
#include "windows.h"
#define ADMESH_CHAR wchar_t
#define stl_fopen(file, mode) _wfopen(file, L##mode)
#else
#define ADMESH_CHAR char
#define stl_fopen(file, mode) fopen(file, mode)
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -163,15 +172,15 @@ typedef struct {
} stl_file; } stl_file;
extern void stl_open(stl_file *stl, const char *file); extern void stl_open(stl_file *stl, const ADMESH_CHAR *file);
extern void stl_close(stl_file *stl); extern void stl_close(stl_file *stl);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file); extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
extern void stl_print_edges(stl_file *stl, FILE *file); extern void stl_print_edges(stl_file *stl, FILE *file);
extern void stl_print_neighbors(stl_file *stl, char *file); extern void stl_print_neighbors(stl_file *stl, ADMESH_CHAR *file);
extern void stl_put_little_int(FILE *fp, int value_in); extern void stl_put_little_int(FILE *fp, int value_in);
extern void stl_put_little_float(FILE *fp, float value_in); extern void stl_put_little_float(FILE *fp, float value_in);
extern void stl_write_ascii(stl_file *stl, const char *file, const char *label); extern void stl_write_ascii(stl_file *stl, const ADMESH_CHAR *file, const char *label);
extern void stl_write_binary(stl_file *stl, const char *file, const char *label); extern void stl_write_binary(stl_file *stl, const ADMESH_CHAR *file, const char *label);
extern void stl_write_binary_block(stl_file *stl, FILE *fp); extern void stl_write_binary_block(stl_file *stl, FILE *fp);
extern void stl_check_facets_exact(stl_file *stl); extern void stl_check_facets_exact(stl_file *stl);
extern void stl_check_facets_nearby(stl_file *stl, float tolerance); extern void stl_check_facets_nearby(stl_file *stl, float tolerance);
@ -180,7 +189,7 @@ extern void stl_write_vertex(stl_file *stl, int facet, int vertex);
extern void stl_write_facet(stl_file *stl, char *label, int facet); extern void stl_write_facet(stl_file *stl, char *label, int facet);
extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge); extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge);
extern void stl_write_neighbor(stl_file *stl, int facet); extern void stl_write_neighbor(stl_file *stl, int facet);
extern void stl_write_quad_object(stl_file *stl, char *file); extern void stl_write_quad_object(stl_file *stl, ADMESH_CHAR *file);
extern void stl_verify_neighbors(stl_file *stl); extern void stl_verify_neighbors(stl_file *stl);
extern void stl_fill_holes(stl_file *stl); extern void stl_fill_holes(stl_file *stl);
extern void stl_fix_normal_directions(stl_file *stl); extern void stl_fix_normal_directions(stl_file *stl);
@ -198,13 +207,13 @@ extern void stl_mirror_xy(stl_file *stl);
extern void stl_mirror_yz(stl_file *stl); extern void stl_mirror_yz(stl_file *stl);
extern void stl_mirror_xz(stl_file *stl); extern void stl_mirror_xz(stl_file *stl);
extern void stl_transform(stl_file *stl, float *trafo3x4); extern void stl_transform(stl_file *stl, float *trafo3x4);
extern void stl_open_merge(stl_file *stl, char *file); extern void stl_open_merge(stl_file *stl, ADMESH_CHAR *file);
extern void stl_invalidate_shared_vertices(stl_file *stl); extern void stl_invalidate_shared_vertices(stl_file *stl);
extern void stl_generate_shared_vertices(stl_file *stl); extern void stl_generate_shared_vertices(stl_file *stl);
extern void stl_write_obj(stl_file *stl, const char *file); extern void stl_write_obj(stl_file *stl, const ADMESH_CHAR *file);
extern void stl_write_off(stl_file *stl, char *file); extern void stl_write_off(stl_file *stl, ADMESH_CHAR *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label); extern void stl_write_dxf(stl_file *stl, ADMESH_CHAR *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file); extern void stl_write_vrml(stl_file *stl, ADMESH_CHAR *file);
extern void stl_calculate_normal(float normal[], stl_facet *facet); extern void stl_calculate_normal(float normal[], stl_facet *facet);
extern void stl_normalize_vector(float v[]); extern void stl_normalize_vector(float v[]);
extern void stl_calculate_volume(stl_file *stl); extern void stl_calculate_volume(stl_file *stl);
@ -212,7 +221,7 @@ extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
extern void stl_initialize(stl_file *stl); extern void stl_initialize(stl_file *stl);
extern void stl_count_facets(stl_file *stl, const char *file); extern void stl_count_facets(stl_file *stl, const ADMESH_CHAR *file);
extern void stl_allocate(stl_file *stl); extern void stl_allocate(stl_file *stl);
extern void stl_read(stl_file *stl, int first_facet, int first); extern void stl_read(stl_file *stl, int first_facet, int first);
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);

View File

@ -124,7 +124,7 @@ Normals fixed : %5d\n", stl->stats.normals_fixed);
} }
void void
stl_write_ascii(stl_file *stl, const char *file, const char *label) { stl_write_ascii(stl_file *stl, const ADMESH_CHAR *file, const char *label) {
int i; int i;
FILE *fp; FILE *fp;
char *error_msg; char *error_msg;
@ -132,14 +132,9 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_ascii: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -170,7 +165,7 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) {
} }
void void
stl_print_neighbors(stl_file *stl, char *file) { stl_print_neighbors(stl_file *stl, ADMESH_CHAR *file) {
int i; int i;
FILE *fp; FILE *fp;
char *error_msg; char *error_msg;
@ -178,14 +173,9 @@ stl_print_neighbors(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_print_neighbors: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -204,7 +194,7 @@ stl_print_neighbors(stl_file *stl, char *file) {
} }
void void
stl_write_binary(stl_file *stl, const char *file, const char *label) { stl_write_binary(stl_file *stl, const ADMESH_CHAR *file, const char *label) {
FILE *fp; FILE *fp;
int i; int i;
char *error_msg; char *error_msg;
@ -212,14 +202,9 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "wb"); fp = stl_fopen(file, "wb");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_binary: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -278,7 +263,7 @@ stl_write_neighbor(stl_file *stl, int facet) {
} }
void void
stl_write_quad_object(stl_file *stl, char *file) { stl_write_quad_object(stl_file *stl, ADMESH_CHAR *file) {
FILE *fp; FILE *fp;
int i; int i;
int j; int j;
@ -292,14 +277,9 @@ stl_write_quad_object(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_quad_object: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -352,7 +332,7 @@ stl_write_quad_object(stl_file *stl, char *file) {
} }
void void
stl_write_dxf(stl_file *stl, char *file, char *label) { stl_write_dxf(stl_file *stl, ADMESH_CHAR *file, char *label) {
int i; int i;
FILE *fp; FILE *fp;
char *error_msg; char *error_msg;
@ -360,14 +340,9 @@ stl_write_dxf(stl_file *stl, char *file, char *label) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = stl_fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) perror("stl_write_ascii: Couldn't open file for writing");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }

View File

@ -33,7 +33,7 @@
#endif #endif
void void
stl_open(stl_file *stl, const char *file) { stl_open(stl_file *stl, const ADMESH_CHAR *file) {
stl_initialize(stl); stl_initialize(stl);
stl_count_facets(stl, file); stl_count_facets(stl, file);
stl_allocate(stl); stl_allocate(stl);
@ -66,7 +66,7 @@ stl_initialize(stl_file *stl) {
} }
void void
stl_count_facets(stl_file *stl, const char *file) { stl_count_facets(stl_file *stl, const ADMESH_CHAR *file) {
long file_size; long file_size;
int header_num_facets; int header_num_facets;
int num_facets; int num_facets;
@ -79,14 +79,9 @@ stl_count_facets(stl_file *stl, const char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file in binary mode first */ /* Open the file in binary mode first */
stl->fp = fopen(file, "rb"); stl->fp = stl_fopen(file, "rb");
if(stl->fp == NULL) { if(stl->fp == NULL) {
error_msg = (char*) perror("stl_initialize: Couldn't open file for reading");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -144,16 +139,12 @@ stl_count_facets(stl_file *stl, const char *file) {
/* Reopen the file in text mode (for getting correct newlines on Windows) */ /* Reopen the file in text mode (for getting correct newlines on Windows) */
// fix to silence a warning about unused return value. // fix to silence a warning about unused return value.
// obviously if it fails we have problems.... // obviously if it fails we have problems....
stl->fp = freopen(file, "r", stl->fp); fclose(stl->fp);
stl->fp = stl_fopen(file, "r");
// do another null check to be safe // do another null check to be safe
if(stl->fp == NULL) { if(stl->fp == NULL) {
error_msg = (char*) perror("stl_initialize: Couldn't open file for reading");
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
file);
perror(error_msg);
free(error_msg);
stl->error = 1; stl->error = 1;
return; return;
} }
@ -201,7 +192,7 @@ stl_allocate(stl_file *stl) {
} }
void void
stl_open_merge(stl_file *stl, char *file_to_merge) { stl_open_merge(stl_file *stl, ADMESH_CHAR *file_to_merge) {
int num_facets_so_far; int num_facets_so_far;
stl_type origStlType; stl_type origStlType;
FILE *origFp; FILE *origFp;

167
xs/src/boost/nowide/args.hpp Executable file
View File

@ -0,0 +1,167 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_ARGS_HPP_INCLUDED
#define BOOST_NOWIDE_ARGS_HPP_INCLUDED
#include <boost/config.hpp>
#include <boost/nowide/stackstring.hpp>
#include <vector>
#ifdef BOOST_WINDOWS
#include <boost/nowide/windows.hpp>
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
class args {
public:
args(int &,char **&) {}
args(int &,char **&,char **&){}
~args() {}
};
#else
///
/// \brief args is a class that fixes standard main() function arguments and changes them to UTF-8 under
/// Microsoft Windows.
///
/// The class uses \c GetCommandLineW(), \c CommandLineToArgvW() and \c GetEnvironmentStringsW()
/// in order to obtain the information. It does not relates to actual values of argc,argv and env
/// under Windows.
///
/// It restores the original values in its destructor
///
/// \note the class owns the memory of the newly allocated strings
///
class args {
public:
///
/// Fix command line agruments
///
args(int &argc,char **&argv) :
old_argc_(argc),
old_argv_(argv),
old_env_(0),
old_argc_ptr_(&argc),
old_argv_ptr_(&argv),
old_env_ptr_(0)
{
fix_args(argc,argv);
}
///
/// Fix command line agruments and environment
///
args(int &argc,char **&argv,char **&en) :
old_argc_(argc),
old_argv_(argv),
old_env_(en),
old_argc_ptr_(&argc),
old_argv_ptr_(&argv),
old_env_ptr_(&en)
{
fix_args(argc,argv);
fix_env(en);
}
///
/// Restore original argc,argv,env values, if changed
///
~args()
{
if(old_argc_ptr_)
*old_argc_ptr_ = old_argc_;
if(old_argv_ptr_)
*old_argv_ptr_ = old_argv_;
if(old_env_ptr_)
*old_env_ptr_ = old_env_;
}
private:
void fix_args(int &argc,char **&argv)
{
int wargc;
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(),&wargc);
if(!wargv) {
argc = 0;
static char *dummy = 0;
argv = &dummy;
return;
}
try{
args_.resize(wargc+1,0);
arg_values_.resize(wargc);
for(int i=0;i<wargc;i++) {
if(!arg_values_[i].convert(wargv[i])) {
wargc = i;
break;
}
args_[i] = arg_values_[i].c_str();
}
argc = wargc;
argv = &args_[0];
}
catch(...) {
LocalFree(wargv);
throw;
}
LocalFree(wargv);
}
void fix_env(char **&en)
{
static char *dummy = 0;
en = &dummy;
wchar_t *wstrings = GetEnvironmentStringsW();
if(!wstrings)
return;
try {
wchar_t *wstrings_end = 0;
int count = 0;
for(wstrings_end = wstrings;*wstrings_end;wstrings_end+=wcslen(wstrings_end)+1)
count++;
if(env_.convert(wstrings,wstrings_end)) {
envp_.resize(count+1,0);
char *p=env_.c_str();
int pos = 0;
for(int i=0;i<count;i++) {
if(*p!='=')
envp_[pos++] = p;
p+=strlen(p)+1;
}
en = &envp_[0];
}
}
catch(...) {
FreeEnvironmentStringsW(wstrings);
throw;
}
FreeEnvironmentStringsW(wstrings);
}
std::vector<char *> args_;
std::vector<short_stackstring> arg_values_;
stackstring env_;
std::vector<char *> envp_;
int old_argc_;
char **old_argv_;
char **old_env_;
int *old_argc_ptr_;
char ***old_argv_ptr_;
char ***old_env_ptr_;
};
#endif
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

126
xs/src/boost/nowide/cenv.hpp Executable file
View File

@ -0,0 +1,126 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CENV_H_INCLUDED
#define BOOST_NOWIDE_CENV_H_INCLUDED
#include <string>
#include <stdexcept>
#include <stdlib.h>
#include <boost/config.hpp>
#include <boost/nowide/stackstring.hpp>
#include <vector>
#ifdef BOOST_WINDOWS
#include <boost/nowide/windows.hpp>
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using ::getenv;
using ::setenv;
using ::unsetenv;
using ::putenv;
#else
///
/// \brief UTF-8 aware getenv. Returns 0 if the variable is not set.
///
/// This function is not thread safe or reenterable as defined by the standard library
///
inline char *getenv(char const *key)
{
static stackstring value;
wshort_stackstring name;
if(!name.convert(key))
return 0;
static const size_t buf_size = 64;
wchar_t buf[buf_size];
std::vector<wchar_t> tmp;
wchar_t *ptr = buf;
size_t n = GetEnvironmentVariableW(name.c_str(),buf,buf_size);
if(n == 0 && GetLastError() == 203) // ERROR_ENVVAR_NOT_FOUND
return 0;
if(n >= buf_size) {
tmp.resize(n+1,L'\0');
n = GetEnvironmentVariableW(name.c_str(),&tmp[0],static_cast<unsigned>(tmp.size() - 1));
// The size may have changed
if(n >= tmp.size() - 1)
return 0;
ptr = &tmp[0];
}
if(!value.convert(ptr))
return 0;
return value.c_str();
}
///
/// \brief UTF-8 aware setenv, \a key - the variable name, \a value is a new UTF-8 value,
///
/// if override is not 0, that the old value is always overridded, otherwise,
/// if the variable exists it remains unchanged
///
inline int setenv(char const *key,char const *value,int override)
{
wshort_stackstring name;
if(!name.convert(key))
return -1;
if(!override) {
wchar_t unused[2];
if(!(GetEnvironmentVariableW(name.c_str(),unused,2)==0 && GetLastError() == 203)) // ERROR_ENVVAR_NOT_FOUND
return 0;
}
wstackstring wval;
if(!wval.convert(value))
return -1;
if(SetEnvironmentVariableW(name.c_str(),wval.c_str()))
return 0;
return -1;
}
///
/// \brief Remove enviroment variable \a key
///
inline int unsetenv(char const *key)
{
wshort_stackstring name;
if(!name.convert(key))
return -1;
if(SetEnvironmentVariableW(name.c_str(),0))
return 0;
return -1;
}
///
/// \brief UTF-8 aware putenv implementation, expects string in format KEY=VALUE
///
inline int putenv(char *string)
{
char const *key = string;
char const *key_end = string;
while(*key_end!='=' && key_end!='\0')
key_end++;
if(*key_end == '\0')
return -1;
wshort_stackstring wkey;
if(!wkey.convert(key,key_end))
return -1;
wstackstring wvalue;
if(!wvalue.convert(key_end+1))
return -1;
if(SetEnvironmentVariableW(wkey.c_str(),wvalue.c_str()))
return 0;
return -1;
}
#endif
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

54
xs/src/boost/nowide/config.hpp Executable file
View File

@ -0,0 +1,54 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CONFIG_HPP_INCLUDED
#define BOOST_NOWIDE_CONFIG_HPP_INCLUDED
#include <boost/config.hpp>
#ifndef BOOST_SYMBOL_VISIBLE
# define BOOST_SYMBOL_VISIBLE
#endif
#ifdef BOOST_HAS_DECLSPEC
# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK)
# ifdef BOOST_NOWIDE_SOURCE
# define BOOST_NOWIDE_DECL BOOST_SYMBOL_EXPORT
# else
# define BOOST_NOWIDE_DECL BOOST_SYMBOL_IMPORT
# endif // BOOST_NOWIDE_SOURCE
# endif // DYN_LINK
#endif // BOOST_HAS_DECLSPEC
#ifndef BOOST_NOWIDE_DECL
# define BOOST_NOWIDE_DECL
#endif
//
// Automatically link to the correct build variant where possible.
//
#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_NOWIDE_NO_LIB) && !defined(BOOST_NOWIDE_SOURCE)
//
// Set the name of our library, this will get undef'ed by auto_link.hpp
// once it's done with it:
//
#define BOOST_LIB_NAME boost_nowide
//
// If we're importing code from a dll, then tell auto_link.hpp about it:
//
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK)
# define BOOST_DYN_LINK
#endif
//
// And include the header that does the work:
//
#include <boost/config/auto_link.hpp>
#endif // auto-linking disabled
#endif // boost/nowide/config.hpp
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

154
xs/src/boost/nowide/convert.hpp Executable file
View File

@ -0,0 +1,154 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CONVERT_H_INCLUDED
#define BOOST_NOWIDE_CONVERT_H_INCLUDED
#include <string>
#include <boost/locale/encoding_utf.hpp>
namespace boost {
namespace nowide {
///
/// \brief Template function that converts a buffer of UTF sequences in range [source_begin,source_end)
/// to the output \a buffer of size \a buffer_size.
///
/// In case of success a NULL terminated string is returned (buffer), otherwise 0 is returned.
///
/// If there is not enough room in the buffer or the source sequence contains invalid UTF,
/// 0 is returned, and the contents of the buffer are undefined.
///
template<typename CharOut,typename CharIn>
CharOut *basic_convert(CharOut *buffer,size_t buffer_size,CharIn const *source_begin,CharIn const *source_end)
{
CharOut *rv = buffer;
if(buffer_size == 0)
return 0;
buffer_size --;
while(source_begin!=source_end) {
using namespace boost::locale::utf;
code_point c = utf_traits<CharIn>::template decode<CharIn const *>(source_begin,source_end);
if(c==illegal || c==incomplete) {
rv = 0;
break;
}
size_t width = utf_traits<CharOut>::width(c);
if(buffer_size < width) {
rv=0;
break;
}
buffer = utf_traits<CharOut>::template encode<CharOut *>(c,buffer);
buffer_size -= width;
}
*buffer++ = 0;
return rv;
}
/// \cond INTERNAL
namespace details {
//
// wcslen defined only in C99... So we will not use it
//
template<typename Char>
Char const *basic_strend(Char const *s)
{
while(*s)
s++;
return s;
}
}
/// \endcond
///
/// Convert NULL terminated UTF source string to NULL terminated \a output string of size at
/// most output_size (including NULL)
///
/// In case of success output is returned, if the input sequence is illegal,
/// or there is not enough room NULL is returned
///
inline char *narrow(char *output,size_t output_size,wchar_t const *source)
{
return basic_convert(output,output_size,source,details::basic_strend(source));
}
///
/// Convert UTF text in range [begin,end) to NULL terminated \a output string of size at
/// most output_size (including NULL)
///
/// In case of success output is returned, if the input sequence is illegal,
/// or there is not enough room NULL is returned
///
inline char *narrow(char *output,size_t output_size,wchar_t const *begin,wchar_t const *end)
{
return basic_convert(output,output_size,begin,end);
}
///
/// Convert NULL terminated UTF source string to NULL terminated \a output string of size at
/// most output_size (including NULL)
///
/// In case of success output is returned, if the input sequence is illegal,
/// or there is not enough room NULL is returned
///
inline wchar_t *widen(wchar_t *output,size_t output_size,char const *source)
{
return basic_convert(output,output_size,source,details::basic_strend(source));
}
///
/// Convert UTF text in range [begin,end) to NULL terminated \a output string of size at
/// most output_size (including NULL)
///
/// In case of success output is returned, if the input sequence is illegal,
/// or there is not enough room NULL is returned
///
inline wchar_t *widen(wchar_t *output,size_t output_size,char const *begin,char const *end)
{
return basic_convert(output,output_size,begin,end);
}
///
/// Convert between Wide - UTF-16/32 string and UTF-8 string.
///
/// boost::locale::conv::conversion_error is thrown in a case of a error
///
inline std::string narrow(wchar_t const *s)
{
return boost::locale::conv::utf_to_utf<char>(s);
}
///
/// Convert between UTF-8 and UTF-16 string, implemented only on Windows platform
///
/// boost::locale::conv::conversion_error is thrown in a case of a error
///
inline std::wstring widen(char const *s)
{
return boost::locale::conv::utf_to_utf<wchar_t>(s);
}
///
/// Convert between Wide - UTF-16/32 string and UTF-8 string
///
/// boost::locale::conv::conversion_error is thrown in a case of a error
///
inline std::string narrow(std::wstring const &s)
{
return boost::locale::conv::utf_to_utf<char>(s);
}
///
/// Convert between UTF-8 and UTF-16 string, implemented only on Windows platform
///
/// boost::locale::conv::conversion_error is thrown in a case of a error
///
inline std::wstring widen(std::string const &s)
{
return boost::locale::conv::utf_to_utf<wchar_t>(s);
}
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

101
xs/src/boost/nowide/cstdio.hpp Executable file
View File

@ -0,0 +1,101 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CSTDIO_H_INCLUDED
#define BOOST_NOWIDE_CSTDIO_H_INCLUDED
#include <cstdio>
#include <stdio.h>
#include <boost/config.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/stackstring.hpp>
#include <errno.h>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4996)
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::fopen;
using std::freopen;
using std::remove;
using std::rename;
#else
///
/// \brief Same as freopen but file_name and mode are UTF-8 strings
///
/// If invalid UTF-8 given, NULL is returned and errno is set to EINVAL
///
inline FILE *freopen(char const *file_name,char const *mode,FILE *stream)
{
wstackstring wname;
wshort_stackstring wmode;
if(!wname.convert(file_name) || !wmode.convert(mode)) {
errno = EINVAL;
return 0;
}
return _wfreopen(wname.c_str(),wmode.c_str(),stream);
}
///
/// \brief Same as fopen but file_name and mode are UTF-8 strings
///
/// If invalid UTF-8 given, NULL is returned and errno is set to EINVAL
///
inline FILE *fopen(char const *file_name,char const *mode)
{
wstackstring wname;
wshort_stackstring wmode;
if(!wname.convert(file_name) || !wmode.convert(mode)) {
errno = EINVAL;
return 0;
}
return _wfopen(wname.c_str(),wmode.c_str());
}
///
/// \brief Same as rename but old_name and new_name are UTF-8 strings
///
/// If invalid UTF-8 given, -1 is returned and errno is set to EINVAL
///
inline int rename(char const *old_name,char const *new_name)
{
wstackstring wold,wnew;
if(!wold.convert(old_name) || !wnew.convert(new_name)) {
errno = EINVAL;
return -1;
}
return _wrename(wold.c_str(),wnew.c_str());
}
///
/// \brief Same as rename but name is UTF-8 string
///
/// If invalid UTF-8 given, -1 is returned and errno is set to EINVAL
///
inline int remove(char const *name)
{
wstackstring wname;
if(!wname.convert(name)) {
errno = EINVAL;
return -1;
}
return _wremove(wname.c_str());
}
#endif
} // nowide
} // namespace boost
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

16
xs/src/boost/nowide/cstdlib.hpp Executable file
View File

@ -0,0 +1,16 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED
#define BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/system.hpp>
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

415
xs/src/boost/nowide/filebuf.hpp Executable file
View File

@ -0,0 +1,415 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_FILEBUF_HPP
#define BOOST_NOWIDE_FILEBUF_HPP
#include <iosfwd>
#include <boost/config.hpp>
#include <boost/nowide/stackstring.hpp>
#include <fstream>
#include <streambuf>
#include <stdio.h>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4996 4244 4800)
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_FSTREAM_TESTS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::basic_filebuf;
using std::filebuf;
#else // Windows
///
/// \brief This forward declaration defined the basic_filebuf type.
///
/// it is implemented and specialized for CharType = char, it behaves
/// implements std::filebuf over standard C I/O
///
template<typename CharType,typename Traits = std::char_traits<CharType> >
class basic_filebuf;
///
/// \brief This is implementation of std::filebuf
///
/// it is implemented and specialized for CharType = char, it behaves
/// implements std::filebuf over standard C I/O
///
template<>
class basic_filebuf<char> : public std::basic_streambuf<char> {
public:
///
/// Creates new filebuf
///
basic_filebuf() :
buffer_size_(4),
buffer_(0),
file_(0),
own_(true),
mode_(std::ios::in | std::ios::out)
{
setg(0,0,0);
setp(0,0);
}
virtual ~basic_filebuf()
{
if(file_) {
::fclose(file_);
file_ = 0;
}
if(own_ && buffer_)
delete [] buffer_;
}
///
/// Same as std::filebuf::open but s is UTF-8 string
///
basic_filebuf *open(std::string const &s,std::ios_base::openmode mode)
{
return open(s.c_str(),mode);
}
///
/// Same as std::filebuf::open but s is UTF-8 string
///
basic_filebuf *open(char const *s,std::ios_base::openmode mode)
{
if(file_) {
sync();
::fclose(file_);
file_ = 0;
}
bool ate = bool(mode & std::ios_base::ate);
if(ate)
mode = mode ^ std::ios_base::ate;
wchar_t const *smode = get_mode(mode);
if(!smode)
return 0;
wstackstring name;
if(!name.convert(s))
return 0;
#ifdef BOOST_NOWIDE_FSTREAM_TESTS
FILE *f = ::fopen(s,boost::nowide::convert(smode).c_str());
#else
FILE *f = ::_wfopen(name.c_str(),smode);
#endif
if(!f)
return 0;
if(ate && fseek(f,0,SEEK_END)!=0) {
fclose(f);
return 0;
}
file_ = f;
return this;
}
///
/// Same as std::filebuf::close()
///
basic_filebuf *close()
{
bool res = sync() == 0;
if(file_) {
if(::fclose(file_)!=0)
res = false;
file_ = 0;
}
return res ? this : 0;
}
///
/// Same as std::filebuf::is_open()
///
bool is_open() const
{
return file_ != 0;
}
private:
void make_buffer()
{
if(buffer_)
return;
if(buffer_size_ > 0) {
buffer_ = new char [buffer_size_];
own_ = true;
}
}
protected:
virtual std::streambuf *setbuf(char *s,std::streamsize n)
{
if(!buffer_ && n>=0) {
buffer_ = s;
buffer_size_ = n;
own_ = false;
}
return this;
}
#ifdef BOOST_NOWIDE_DEBUG_FILEBUF
void print_buf(char *b,char *p,char *e)
{
std::cerr << "-- Is Null: " << (b==0) << std::endl;;
if(b==0)
return;
if(e != 0)
std::cerr << "-- Total: " << e - b <<" offset from start " << p - b << std::endl;
else
std::cerr << "-- Total: " << p - b << std::endl;
std::cerr << "-- [";
for(char *ptr = b;ptr<p;ptr++)
std::cerr << *ptr;
if(e!=0) {
std::cerr << "|";
for(char *ptr = p;ptr<e;ptr++)
std::cerr << *ptr;
}
std::cerr << "]" << std::endl;
}
void print_state()
{
std::cerr << "- Output:" << std::endl;
print_buf(pbase(),pptr(),0);
std::cerr << "- Input:" << std::endl;
print_buf(eback(),gptr(),egptr());
std::cerr << "- fpos: " << (file_ ? ftell(file_) : -1L) << std::endl;
}
struct print_guard
{
print_guard(basic_filebuf *p,char const *func)
{
self = p;
f=func;
std::cerr << "In: " << f << std::endl;
self->print_state();
}
~print_guard()
{
std::cerr << "Out: " << f << std::endl;
self->print_state();
}
basic_filebuf *self;
char const *f;
};
#else
#endif
int overflow(int c)
{
#ifdef BOOST_NOWIDE_DEBUG_FILEBUF
print_guard g(this,__FUNCTION__);
#endif
if(!file_)
return EOF;
if(fixg() < 0)
return EOF;
size_t n = pptr() - pbase();
if(n > 0) {
if(::fwrite(pbase(),1,n,file_) < n)
return -1;
fflush(file_);
}
if(buffer_size_ > 0) {
make_buffer();
setp(buffer_,buffer_+buffer_size_);
if(c!=EOF)
sputc(c);
}
else if(c!=EOF) {
if(::fputc(c,file_)==EOF)
return EOF;
fflush(file_);
}
return 0;
}
int sync()
{
return overflow(EOF);
}
int underflow()
{
#ifdef BOOST_NOWIDE_DEBUG_FILEBUF
print_guard g(this,__FUNCTION__);
#endif
if(!file_)
return EOF;
if(fixp() < 0)
return EOF;
if(buffer_size_ == 0) {
int c = ::fgetc(file_);
if(c==EOF) {
return EOF;
}
last_char_ = c;
setg(&last_char_,&last_char_,&last_char_ + 1);
return c;
}
make_buffer();
size_t n = ::fread(buffer_,1,buffer_size_,file_);
setg(buffer_,buffer_,buffer_+n);
if(n == 0)
return EOF;
return std::char_traits<char>::to_int_type(*gptr());
}
int pbackfail(int)
{
return pubseekoff(-1,std::ios::cur);
}
std::streampos seekoff(std::streamoff off,
std::ios_base::seekdir seekdir,
std::ios_base::openmode /*m*/)
{
#ifdef BOOST_NOWIDE_DEBUG_FILEBUF
print_guard g(this,__FUNCTION__);
#endif
if(!file_)
return EOF;
if(fixp() < 0 || fixg() < 0)
return EOF;
if(seekdir == std::ios_base::cur) {
if( ::fseek(file_,off,SEEK_CUR) < 0)
return EOF;
}
else if(seekdir == std::ios_base::beg) {
if( ::fseek(file_,off,SEEK_SET) < 0)
return EOF;
}
else if(seekdir == std::ios_base::end) {
if( ::fseek(file_,off,SEEK_END) < 0)
return EOF;
}
else
return -1;
return ftell(file_);
}
std::streampos seekpos(std::streampos off,std::ios_base::openmode m)
{
return seekoff(std::streamoff(off),std::ios_base::beg,m);
}
private:
int fixg()
{
if(gptr()!=egptr()) {
std::streamsize off = gptr() - egptr();
setg(0,0,0);
if(fseek(file_,off,SEEK_CUR) != 0)
return -1;
}
setg(0,0,0);
return 0;
}
int fixp()
{
if(pptr()!=0) {
int r = sync();
setp(0,0);
return r;
}
return 0;
}
void reset(FILE *f = 0)
{
sync();
if(file_) {
fclose(file_);
file_ = 0;
}
file_ = f;
}
static wchar_t const *get_mode(std::ios_base::openmode mode)
{
//
// done according to n2914 table 106 27.9.1.4
//
// note can't use switch case as overload operator can't be used
// in constant expression
if(mode == (std::ios_base::out))
return L"w";
if(mode == (std::ios_base::out | std::ios_base::app))
return L"a";
if(mode == (std::ios_base::app))
return L"a";
if(mode == (std::ios_base::out | std::ios_base::trunc))
return L"w";
if(mode == (std::ios_base::in))
return L"r";
if(mode == (std::ios_base::in | std::ios_base::out))
return L"r+";
if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
return L"w+";
if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::app))
return L"a+";
if(mode == (std::ios_base::in | std::ios_base::app))
return L"a+";
if(mode == (std::ios_base::binary | std::ios_base::out))
return L"wb";
if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::app))
return L"ab";
if(mode == (std::ios_base::binary | std::ios_base::app))
return L"ab";
if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc))
return L"wb";
if(mode == (std::ios_base::binary | std::ios_base::in))
return L"rb";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out))
return L"r+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
return L"w+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::app))
return L"a+b";
if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app))
return L"a+b";
return 0;
}
size_t buffer_size_;
char *buffer_;
FILE *file_;
bool own_;
char last_char_;
std::ios::openmode mode_;
};
///
/// \brief Convinience typedef
///
typedef basic_filebuf<char> filebuf;
#endif // windows
} // nowide
} // namespace boost
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

283
xs/src/boost/nowide/fstream.hpp Executable file
View File

@ -0,0 +1,283 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_FSTREAM_INCLUDED_HPP
#define BOOST_NOWIDE_FSTREAM_INCLUDED_HPP
//#include <iosfwd>
#include <boost/config.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/scoped_ptr.hpp>
#include <fstream>
#include <memory>
#include <boost/nowide/filebuf.hpp>
namespace boost {
///
/// \brief This namespace includes implementation of the standard library functios
/// such that they accept UTF-8 strings on Windows. On other platforms it is just an alias
/// of std namespace (i.e. not on Windows)
///
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_FSTREAM_TESTS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::basic_ifstream;
using std::basic_ofstream;
using std::basic_fstream;
using std::ifstream;
using std::ofstream;
using std::fstream;
#else
///
/// \brief Same as std::basic_ifstream<char> but accepts UTF-8 strings under Windows
///
template<typename CharType,typename Traits = std::char_traits<CharType> >
class basic_ifstream : public std::basic_istream<CharType,Traits>
{
public:
typedef basic_filebuf<CharType,Traits> internal_buffer_type;
typedef std::basic_istream<CharType,Traits> internal_stream_type;
basic_ifstream() :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
}
explicit basic_ifstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::in) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
explicit basic_ifstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::in) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::in)
{
open(file_name.c_str(),mode);
}
void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::in)
{
if(!buf_->open(file_name,mode | std::ios_base::in)) {
this->setstate(std::ios_base::failbit);
}
else {
this->clear();
}
}
bool is_open()
{
return buf_->is_open();
}
bool is_open() const
{
return buf_->is_open();
}
void close()
{
if(!buf_->close())
this->setstate(std::ios_base::failbit);
else
this->clear();
}
internal_buffer_type *rdbuf() const
{
return buf_.get();
}
~basic_ifstream()
{
buf_->close();
}
private:
boost::scoped_ptr<internal_buffer_type> buf_;
};
///
/// \brief Same as std::basic_ofstream<char> but accepts UTF-8 strings under Windows
///
template<typename CharType,typename Traits = std::char_traits<CharType> >
class basic_ofstream : public std::basic_ostream<CharType,Traits>
{
public:
typedef basic_filebuf<CharType,Traits> internal_buffer_type;
typedef std::basic_ostream<CharType,Traits> internal_stream_type;
basic_ofstream() :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
}
explicit basic_ofstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::out) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
explicit basic_ofstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out)
{
open(file_name.c_str(),mode);
}
void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::out)
{
if(!buf_->open(file_name,mode | std::ios_base::out)) {
this->setstate(std::ios_base::failbit);
}
else {
this->clear();
}
}
bool is_open()
{
return buf_->is_open();
}
bool is_open() const
{
return buf_->is_open();
}
void close()
{
if(!buf_->close())
this->setstate(std::ios_base::failbit);
else
this->clear();
}
internal_buffer_type *rdbuf() const
{
return buf_.get();
}
~basic_ofstream()
{
buf_->close();
}
private:
boost::scoped_ptr<internal_buffer_type> buf_;
};
///
/// \brief Same as std::basic_fstream<char> but accepts UTF-8 strings under Windows
///
template<typename CharType,typename Traits = std::char_traits<CharType> >
class basic_fstream : public std::basic_iostream<CharType,Traits>
{
public:
typedef basic_filebuf<CharType,Traits> internal_buffer_type;
typedef std::basic_iostream<CharType,Traits> internal_stream_type;
basic_fstream() :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
}
explicit basic_fstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::in) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
explicit basic_fstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::in) :
internal_stream_type(0)
{
buf_.reset(new internal_buffer_type());
std::ios::rdbuf(buf_.get());
open(file_name,mode);
}
void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::out)
{
open(file_name.c_str(),mode);
}
void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::out)
{
if(!buf_->open(file_name,mode)) {
this->setstate(std::ios_base::failbit);
}
else {
this->clear();
}
}
bool is_open()
{
return buf_->is_open();
}
bool is_open() const
{
return buf_->is_open();
}
void close()
{
if(!buf_->close())
this->setstate(std::ios_base::failbit);
else
this->clear();
}
internal_buffer_type *rdbuf() const
{
return buf_.get();
}
~basic_fstream()
{
buf_->close();
}
private:
boost::scoped_ptr<internal_buffer_type> buf_;
};
///
/// \brief Same as std::filebuf but accepts UTF-8 strings under Windows
///
typedef basic_filebuf<char> filebuf;
///
/// Same as std::ifstream but accepts UTF-8 strings under Windows
///
typedef basic_ifstream<char> ifstream;
///
/// Same as std::ofstream but accepts UTF-8 strings under Windows
///
typedef basic_ofstream<char> ofstream;
///
/// Same as std::fstream but accepts UTF-8 strings under Windows
///
typedef basic_fstream<char> fstream;
#endif
} // nowide
} // namespace boost
#endif
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -0,0 +1,28 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED
#define BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED
#include <boost/filesystem/path.hpp>
#include <boost/nowide/utf8_codecvt.hpp>
namespace boost {
namespace nowide {
///
/// Instal utf8_codecvt facet into boost::filesystem::path such all char strings are interpreted as utf-8 strings
///
inline void nowide_filesystem()
{
std::locale tmp = std::locale(std::locale(),new boost::nowide::utf8_codecvt<wchar_t>());
boost::filesystem::path::imbue(tmp);
}
} // nowide
} // boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

261
xs/src/boost/nowide/iostream.cpp Executable file
View File

@ -0,0 +1,261 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#define BOOST_NOWIDE_SOURCE
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/convert.hpp>
#include <stdio.h>
#include <vector>
#ifdef BOOST_WINDOWS
#ifndef NOMINMAX
# define NOMINMAX
#endif
#include <windows.h>
namespace boost {
namespace nowide {
namespace details {
class console_output_buffer : public std::streambuf {
public:
console_output_buffer(HANDLE h) :
handle_(h),
isatty_(false)
{
if(handle_) {
DWORD dummy;
isatty_ = GetConsoleMode(handle_,&dummy) == TRUE;
}
}
protected:
int sync()
{
return overflow(EOF);
}
int overflow(int c)
{
if(!handle_)
return -1;
int n = pptr() - pbase();
int r = 0;
if(n > 0 && (r=write(pbase(),n)) < 0)
return -1;
if(r < n) {
memmove(pbase(),pbase() + r,n-r);
}
setp(buffer_, buffer_ + buffer_size);
pbump(n-r);
if(c!=EOF)
sputc(c);
return 0;
}
private:
int write(char const *p,int n)
{
namespace uf = boost::locale::utf;
char const *b = p;
char const *e = p+n;
DWORD size=0;
if(!isatty_) {
if(!WriteFile(handle_,p,n,&size,0) || static_cast<int>(size) != n)
return -1;
return n;
}
if(n > buffer_size)
return -1;
wchar_t *out = wbuffer_;
uf::code_point c;
size_t decoded = 0;
while(p < e && (c = uf::utf_traits<char>::decode(p,e))!=uf::illegal && c!=uf::incomplete) {
out = uf::utf_traits<wchar_t>::encode(c,out);
decoded = p-b;
}
if(c==uf::illegal)
return -1;
if(!WriteConsoleW(handle_,wbuffer_,out - wbuffer_,&size,0))
return -1;
return decoded;
}
static const int buffer_size = 1024;
char buffer_[buffer_size];
wchar_t wbuffer_[buffer_size]; // for null
HANDLE handle_;
bool isatty_;
};
class console_input_buffer: public std::streambuf {
public:
console_input_buffer(HANDLE h) :
handle_(h),
isatty_(false),
wsize_(0)
{
if(handle_) {
DWORD dummy;
isatty_ = GetConsoleMode(handle_,&dummy) == TRUE;
}
}
protected:
int pbackfail(int c)
{
if(c==EOF)
return EOF;
if(gptr()!=eback()) {
gbump(-1);
*gptr() = c;
return 0;
}
if(pback_buffer_.empty()) {
pback_buffer_.resize(4);
char *b = &pback_buffer_[0];
char *e = b + pback_buffer_.size();
setg(b,e-1,e);
*gptr() = c;
}
else {
size_t n = pback_buffer_.size();
std::vector<char> tmp;
tmp.resize(n*2);
memcpy(&tmp[n],&pback_buffer_[0],n);
tmp.swap(pback_buffer_);
char *b = &pback_buffer_[0];
char *e = b + n * 2;
char *p = b+n-1;
*p = c;
setg(b,p,e);
}
return 0;
}
int underflow()
{
if(!handle_)
return -1;
if(!pback_buffer_.empty())
pback_buffer_.clear();
size_t n = read();
setg(buffer_,buffer_,buffer_+n);
if(n == 0)
return EOF;
return std::char_traits<char>::to_int_type(*gptr());
}
private:
size_t read()
{
namespace uf = boost::locale::utf;
if(!isatty_) {
DWORD read_bytes = 0;
if(!ReadFile(handle_,buffer_,buffer_size,&read_bytes,0))
return 0;
return read_bytes;
}
DWORD read_wchars = 0;
size_t n = wbuffer_size - wsize_;
if(!ReadConsoleW(handle_,wbuffer_,n,&read_wchars,0))
return 0;
wsize_ += read_wchars;
char *out = buffer_;
wchar_t *b = wbuffer_;
wchar_t *e = b + wsize_;
wchar_t *p = b;
uf::code_point c;
wsize_ = e-p;
while(p < e && (c = uf::utf_traits<wchar_t>::decode(p,e))!=uf::illegal && c!=uf::incomplete) {
out = uf::utf_traits<char>::encode(c,out);
wsize_ = e-p;
}
if(c==uf::illegal)
return -1;
if(c==uf::incomplete) {
memmove(b,e-wsize_,sizeof(wchar_t)*wsize_);
}
return out - buffer_;
}
static const size_t buffer_size = 1024 * 3;
static const size_t wbuffer_size = 1024;
char buffer_[buffer_size];
wchar_t wbuffer_[buffer_size]; // for null
HANDLE handle_;
bool isatty_;
int wsize_;
std::vector<char> pback_buffer_;
};
winconsole_ostream::winconsole_ostream(int fd) : std::ostream(0)
{
HANDLE h = 0;
switch(fd) {
case 1:
h = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case 2:
h = GetStdHandle(STD_ERROR_HANDLE);
break;
}
d.reset(new console_output_buffer(h));
std::ostream::rdbuf(d.get());
}
winconsole_ostream::~winconsole_ostream()
{
}
winconsole_istream::winconsole_istream() : std::istream(0)
{
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
d.reset(new console_input_buffer(h));
std::istream::rdbuf(d.get());
}
winconsole_istream::~winconsole_istream()
{
}
} // details
BOOST_NOWIDE_DECL details::winconsole_istream cin;
BOOST_NOWIDE_DECL details::winconsole_ostream cout(1);
BOOST_NOWIDE_DECL details::winconsole_ostream cerr(2);
BOOST_NOWIDE_DECL details::winconsole_ostream clog(2);
namespace {
struct initialize {
initialize()
{
boost::nowide::cin.tie(&boost::nowide::cout);
boost::nowide::cerr.tie(&boost::nowide::cout);
boost::nowide::clog.tie(&boost::nowide::cout);
}
} inst;
}
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -0,0 +1,99 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED
#define BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED
#include <boost/nowide/config.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
#include <ostream>
#include <istream>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4251)
#endif
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using std::cout;
using std::cerr;
using std::cin;
using std::clog;
#else
/// \cond INTERNAL
namespace details {
class console_output_buffer;
class console_input_buffer;
class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream {
winconsole_ostream(winconsole_ostream const &);
void operator=(winconsole_ostream const &);
public:
winconsole_ostream(int fd);
~winconsole_ostream();
private:
boost::scoped_ptr<console_output_buffer> d;
};
class BOOST_NOWIDE_DECL winconsole_istream : public std::istream {
winconsole_istream(winconsole_istream const &);
void operator=(winconsole_istream const &);
public:
winconsole_istream();
~winconsole_istream();
private:
struct data;
boost::scoped_ptr<console_input_buffer> d;
};
} // details
/// \endcond
///
/// \brief Same as std::cin, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL details::winconsole_istream cin;
///
/// \brief Same as std::cout, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL details::winconsole_ostream cout;
///
/// \brief Same as std::cerr, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL details::winconsole_ostream cerr;
///
/// \brief Same as std::clog, but uses UTF-8
///
/// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio
///
extern BOOST_NOWIDE_DECL details::winconsole_ostream clog;
#endif
} // nowide
} // namespace boost
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -0,0 +1,154 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_DETAILS_WIDESTR_H_INCLUDED
#define BOOST_NOWIDE_DETAILS_WIDESTR_H_INCLUDED
#include <boost/nowide/convert.hpp>
#include <string.h>
#include <algorithm>
namespace boost {
namespace nowide {
///
/// \brief A class that allows to create a temporary wide or narrow UTF strings from
/// wide or narrow UTF source.
///
/// It uses on stack buffer of the string is short enough
/// and allocated a buffer on the heap if the size of the buffer is too small
///
template<typename CharOut=wchar_t,typename CharIn = char,size_t BufferSize = 256>
class basic_stackstring {
public:
static const size_t buffer_size = BufferSize;
typedef CharOut output_char;
typedef CharIn input_char;
basic_stackstring(basic_stackstring const &other) :
mem_buffer_(0)
{
clear();
if(other.mem_buffer_) {
size_t len = 0;
while(other.mem_buffer_[len])
len ++;
mem_buffer_ = new output_char[len + 1];
memcpy(mem_buffer_,other.mem_buffer_,sizeof(output_char) * (len+1));
}
else {
memcpy(buffer_,other.buffer_,buffer_size * sizeof(output_char));
}
}
void swap(basic_stackstring &other)
{
std::swap(mem_buffer_,other.mem_buffer_);
for(size_t i=0;i<buffer_size;i++)
std::swap(buffer_[i],other.buffer_[i]);
}
basic_stackstring &operator=(basic_stackstring const &other)
{
if(this != &other) {
basic_stackstring tmp(other);
swap(tmp);
}
return *this;
}
basic_stackstring() : mem_buffer_(0)
{
}
bool convert(input_char const *input)
{
return convert(input,details::basic_strend(input));
}
bool convert(input_char const *begin,input_char const *end)
{
clear();
size_t space = get_space(sizeof(input_char),sizeof(output_char),end - begin) + 1;
if(space <= buffer_size) {
if(basic_convert(buffer_,buffer_size,begin,end))
return true;
clear();
return false;
}
else {
mem_buffer_ = new output_char[space];
if(!basic_convert(mem_buffer_,space,begin,end)) {
clear();
return false;
}
return true;
}
}
output_char *c_str()
{
if(mem_buffer_)
return mem_buffer_;
return buffer_;
}
output_char const *c_str() const
{
if(mem_buffer_)
return mem_buffer_;
return buffer_;
}
void clear()
{
if(mem_buffer_) {
delete [] mem_buffer_;
mem_buffer_=0;
}
buffer_[0] = 0;
}
~basic_stackstring()
{
clear();
}
private:
static size_t get_space(size_t insize,size_t outsize,size_t in)
{
if(insize <= outsize)
return in;
else if(insize == 2 && outsize == 1)
return 3 * in;
else if(insize == 4 && outsize == 1)
return 4 * in;
else // if(insize == 4 && outsize == 2)
return 2 * in;
}
output_char buffer_[buffer_size];
output_char *mem_buffer_;
}; //basic_stackstring
///
/// Convinience typedef
///
typedef basic_stackstring<wchar_t,char,256> wstackstring;
///
/// Convinience typedef
///
typedef basic_stackstring<char,wchar_t,256> stackstring;
///
/// Convinience typedef
///
typedef basic_stackstring<wchar_t,char,16> wshort_stackstring;
///
/// Convinience typedef
///
typedef basic_stackstring<char,wchar_t,16> short_stackstring;
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

46
xs/src/boost/nowide/system.hpp Executable file
View File

@ -0,0 +1,46 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_CSTDLIB_HPP
#define BOOST_NOWIDE_CSTDLIB_HPP
#include <stdlib.h>
#include <errno.h>
#include <boost/nowide/stackstring.hpp>
namespace boost {
namespace nowide {
#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN)
using ::system;
#else // Windows
///
/// Same as std::system but cmd is UTF-8.
///
/// If the input is not valid UTF-8, -1 returned and errno set to EINVAL
///
inline int system(char const *cmd)
{
if(!cmd)
return _wsystem(0);
wstackstring wcmd;
if(!wcmd.convert(cmd)) {
errno = EINVAL;
return -1;
}
return _wsystem(wcmd.c_str());
}
#endif
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -0,0 +1,499 @@
//
// Copyright (c) 2015 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_UTF8_CODECVT_HPP
#define BOOST_NOWIDE_UTF8_CODECVT_HPP
#include <boost/locale/utf.hpp>
#include <boost/cstdint.hpp>
#include <boost/static_assert.hpp>
#include <locale>
namespace boost {
namespace nowide {
//
// Make sure that mbstate can keep 16 bit of UTF-16 sequence
//
BOOST_STATIC_ASSERT(sizeof(std::mbstate_t)>=2);
#ifdef _MSC_VER
// MSVC do_length is non-standard it counts wide characters instead of narrow and does not change mbstate
#define BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
#endif
template<typename CharType,int CharSize=sizeof(CharType)>
class utf8_codecvt;
template<typename CharType>
class utf8_codecvt<CharType,2> : public std::codecvt<CharType,char,std::mbstate_t>
{
public:
utf8_codecvt(size_t refs = 0) : std::codecvt<CharType,char,std::mbstate_t>(refs)
{
}
protected:
typedef CharType uchar;
virtual std::codecvt_base::result do_unshift(std::mbstate_t &s,char *from,char * /*to*/,char *&next) const
{
boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&s);
#ifdef DEBUG_CODECVT
std::cout << "Entering unshift " << std::hex << state << std::dec << std::endl;
#endif
if(state != 0)
return std::codecvt_base::error;
next=from;
return std::codecvt_base::ok;
}
virtual int do_encoding() const throw()
{
return 0;
}
virtual int do_max_length() const throw()
{
return 4;
}
virtual bool do_always_noconv() const throw()
{
return false;
}
virtual int
do_length( std::mbstate_t
#ifdef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
const
#endif
&std_state,
char const *from,
char const *from_end,
size_t max) const
{
#ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
char const *save_from = from;
boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state);
#else
size_t save_max = max;
boost::uint16_t state = *reinterpret_cast<boost::uint16_t const *>(&std_state);
#endif
while(max > 0 && from < from_end){
char const *prev_from = from;
boost::uint32_t ch=boost::locale::utf::utf_traits<char>::decode(from,from_end);
if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) {
from = prev_from;
break;
}
max --;
if(ch > 0xFFFF) {
if(state == 0) {
from = prev_from;
state = 1;
}
else {
state = 0;
}
}
}
#ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
return from - save_from;
#else
return save_max - max;
#endif
}
virtual std::codecvt_base::result
do_in( std::mbstate_t &std_state,
char const *from,
char const *from_end,
char const *&from_next,
uchar *to,
uchar *to_end,
uchar *&to_next) const
{
std::codecvt_base::result r=std::codecvt_base::ok;
// mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT())
// according to standard. We use it to keep a flag 0/1 for surrogate pair writing
//
// if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd
// and first pair is written, but no input consumed
boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state);
while(to < to_end && from < from_end)
{
#ifdef DEBUG_CODECVT
std::cout << "Entering IN--------------" << std::endl;
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
char const *from_saved = from;
uint32_t ch=boost::locale::utf::utf_traits<char>::decode(from,from_end);
if(ch==boost::locale::utf::illegal) {
from = from_saved;
r=std::codecvt_base::error;
break;
}
if(ch==boost::locale::utf::incomplete) {
from = from_saved;
r=std::codecvt_base::partial;
break;
}
// Normal codepoints go direcly to stream
if(ch <= 0xFFFF) {
*to++=ch;
}
else {
// for other codepoints we do following
//
// 1. We can't consume our input as we may find ourselfs
// in state where all input consumed but not all output written,i.e. only
// 1st pair is written
// 2. We only write first pair and mark this in the state, we also revert back
// the from pointer in order to make sure this codepoint would be read
// once again and then we would consume our input together with writing
// second surrogate pair
ch-=0x10000;
boost::uint16_t vh = ch >> 10;
boost::uint16_t vl = ch & 0x3FF;
boost::uint16_t w1 = vh + 0xD800;
boost::uint16_t w2 = vl + 0xDC00;
if(state == 0) {
from = from_saved;
*to++ = w1;
state = 1;
}
else {
*to++ = w2;
state = 0;
}
}
}
from_next=from;
to_next=to;
if(r == std::codecvt_base::ok && (from!=from_end || state!=0))
r = std::codecvt_base::partial;
#ifdef DEBUG_CODECVT
std::cout << "Returning ";
switch(r) {
case std::codecvt_base::ok:
std::cout << "ok" << std::endl;
break;
case std::codecvt_base::partial:
std::cout << "partial" << std::endl;
break;
case std::codecvt_base::error:
std::cout << "error" << std::endl;
break;
default:
std::cout << "other" << std::endl;
break;
}
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
return r;
}
virtual std::codecvt_base::result
do_out( std::mbstate_t &std_state,
uchar const *from,
uchar const *from_end,
uchar const *&from_next,
char *to,
char *to_end,
char *&to_next) const
{
std::codecvt_base::result r=std::codecvt_base::ok;
// mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT())
// according to standard. We assume that sizeof(mbstate_t) >=2 in order
// to be able to store first observerd surrogate pair
//
// State: state!=0 - a first surrogate pair was observerd (state = first pair),
// we expect the second one to come and then zero the state
///
boost::uint16_t &state = *reinterpret_cast<boost::uint16_t *>(&std_state);
while(to < to_end && from < from_end)
{
#ifdef DEBUG_CODECVT
std::cout << "Entering OUT --------------" << std::endl;
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
boost::uint32_t ch=0;
if(state != 0) {
// if the state idecates that 1st surrogate pair was written
// we should make sure that the second one that comes is actually
// second surrogate
boost::uint16_t w1 = state;
boost::uint16_t w2 = *from;
// we don't forward from as writing may fail to incomplete or
// partial conversion
if(0xDC00 <= w2 && w2<=0xDFFF) {
boost::uint16_t vh = w1 - 0xD800;
boost::uint16_t vl = w2 - 0xDC00;
ch=((uint32_t(vh) << 10) | vl) + 0x10000;
}
else {
// Invalid surrogate
r=std::codecvt_base::error;
break;
}
}
else {
ch = *from;
if(0xD800 <= ch && ch<=0xDBFF) {
// if this is a first surrogate pair we put
// it into the state and consume it, note we don't
// go forward as it should be illegal so we increase
// the from pointer manually
state = ch;
from++;
continue;
}
else if(0xDC00 <= ch && ch<=0xDFFF) {
// if we observe second surrogate pair and
// first only may be expected we should break from the loop with error
// as it is illegal input
r=std::codecvt_base::error;
break;
}
}
if(!boost::locale::utf::is_valid_codepoint(ch)) {
r=std::codecvt_base::error;
break;
}
int len = boost::locale::utf::utf_traits<char>::width(ch);
if(to_end - to < len) {
r=std::codecvt_base::partial;
break;
}
to = boost::locale::utf::utf_traits<char>::encode(ch,to);
state = 0;
from++;
}
from_next=from;
to_next=to;
if(r==std::codecvt_base::ok && from!=from_end)
r = std::codecvt_base::partial;
#ifdef DEBUG_CODECVT
std::cout << "Returning ";
switch(r) {
case std::codecvt_base::ok:
std::cout << "ok" << std::endl;
break;
case std::codecvt_base::partial:
std::cout << "partial" << std::endl;
break;
case std::codecvt_base::error:
std::cout << "error" << std::endl;
break;
default:
std::cout << "other" << std::endl;
break;
}
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
return r;
}
};
template<typename CharType>
class utf8_codecvt<CharType,4> : public std::codecvt<CharType,char,std::mbstate_t>
{
public:
utf8_codecvt(size_t refs = 0) : std::codecvt<CharType,char,std::mbstate_t>(refs)
{
}
protected:
typedef CharType uchar;
virtual std::codecvt_base::result do_unshift(std::mbstate_t &/*s*/,char *from,char * /*to*/,char *&next) const
{
next=from;
return std::codecvt_base::ok;
}
virtual int do_encoding() const throw()
{
return 0;
}
virtual int do_max_length() const throw()
{
return 4;
}
virtual bool do_always_noconv() const throw()
{
return false;
}
virtual int
do_length( std::mbstate_t
#ifdef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
const
#endif
&/*state*/,
char const *from,
char const *from_end,
size_t max) const
{
#ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
char const *start_from = from;
#else
size_t save_max = max;
#endif
while(max > 0 && from < from_end){
char const *save_from = from;
boost::uint32_t ch=boost::locale::utf::utf_traits<char>::decode(from,from_end);
if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) {
from = save_from;
break;
}
max--;
}
#ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST
return from - start_from;
#else
return save_max - max;
#endif
}
virtual std::codecvt_base::result
do_in( std::mbstate_t &/*state*/,
char const *from,
char const *from_end,
char const *&from_next,
uchar *to,
uchar *to_end,
uchar *&to_next) const
{
std::codecvt_base::result r=std::codecvt_base::ok;
// mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT())
// according to standard. We use it to keep a flag 0/1 for surrogate pair writing
//
// if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd
// and first pair is written, but no input consumed
while(to < to_end && from < from_end)
{
#ifdef DEBUG_CODECVT
std::cout << "Entering IN--------------" << std::endl;
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
char const *from_saved = from;
uint32_t ch=boost::locale::utf::utf_traits<char>::decode(from,from_end);
if(ch==boost::locale::utf::illegal) {
r=std::codecvt_base::error;
from = from_saved;
break;
}
if(ch==boost::locale::utf::incomplete) {
r=std::codecvt_base::partial;
from=from_saved;
break;
}
*to++=ch;
}
from_next=from;
to_next=to;
if(r == std::codecvt_base::ok && from!=from_end)
r = std::codecvt_base::partial;
#ifdef DEBUG_CODECVT
std::cout << "Returning ";
switch(r) {
case std::codecvt_base::ok:
std::cout << "ok" << std::endl;
break;
case std::codecvt_base::partial:
std::cout << "partial" << std::endl;
break;
case std::codecvt_base::error:
std::cout << "error" << std::endl;
break;
default:
std::cout << "other" << std::endl;
break;
}
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
return r;
}
virtual std::codecvt_base::result
do_out( std::mbstate_t &std_state,
uchar const *from,
uchar const *from_end,
uchar const *&from_next,
char *to,
char *to_end,
char *&to_next) const
{
std::codecvt_base::result r=std::codecvt_base::ok;
while(to < to_end && from < from_end)
{
#ifdef DEBUG_CODECVT
std::cout << "Entering OUT --------------" << std::endl;
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
boost::uint32_t ch=0;
ch = *from;
if(!boost::locale::utf::is_valid_codepoint(ch)) {
r=std::codecvt_base::error;
break;
}
int len = boost::locale::utf::utf_traits<char>::width(ch);
if(to_end - to < len) {
r=std::codecvt_base::partial;
break;
}
to = boost::locale::utf::utf_traits<char>::encode(ch,to);
from++;
}
from_next=from;
to_next=to;
if(r==std::codecvt_base::ok && from!=from_end)
r = std::codecvt_base::partial;
#ifdef DEBUG_CODECVT
std::cout << "Returning ";
switch(r) {
case std::codecvt_base::ok:
std::cout << "ok" << std::endl;
break;
case std::codecvt_base::partial:
std::cout << "partial" << std::endl;
break;
case std::codecvt_base::error:
std::cout << "error" << std::endl;
break;
default:
std::cout << "other" << std::endl;
break;
}
std::cout << "State " << std::hex << state <<std::endl;
std::cout << "Left in " << std::dec << from_end - from << " out " << to_end -to << std::endl;
#endif
return r;
}
};
} // nowide
} // namespace boost
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

39
xs/src/boost/nowide/windows.hpp Executable file
View File

@ -0,0 +1,39 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_NOWIDE_WINDOWS_HPP_INCLUDED
#define BOOST_NOWIDE_WINDOWS_HPP_INCLUDED
#include <stddef.h>
#ifdef BOOST_NOWIDE_USE_WINDOWS_H
#include <windows.h>
#else
//
// These are function prototypes... Allow to to include windows.h
//
extern "C" {
__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void);
__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t *);
__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void);
__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(wchar_t const *,int *);
__declspec(dllimport) unsigned long __stdcall GetLastError();
__declspec(dllimport) void* __stdcall LocalFree(void *);
__declspec(dllimport) int __stdcall SetEnvironmentVariableW(wchar_t const *,wchar_t const *);
__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(wchar_t const *,wchar_t *,unsigned long);
}
#endif
#endif
///
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View File

@ -7,13 +7,23 @@ namespace Slic3r {
BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices,
coord_t _extrusion_width) coord_t _extrusion_width)
: expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width), : expolygon(_expolygon), extrusion_width(_extrusion_width),
resolution(PI/36.0), angle(-1) resolution(PI/36.0), angle(-1)
{ {
/* outset our bridge by an arbitrary amout; we'll use this outer margin /* outset our bridge by an arbitrary amout; we'll use this outer margin
for detecting anchors */ for detecting anchors */
Polygons grown = offset(this->expolygon, this->extrusion_width); Polygons grown = offset(this->expolygon, this->extrusion_width);
// remove narrow gaps from lower slices
// (this is only needed as long as we use clipped test lines for angle detection
// and we check their endpoints: when endpoint fall in the gap we'd get false
// negatives)
this->lower_slices.expolygons = offset2_ex(
_lower_slices,
+this->extrusion_width/2,
-this->extrusion_width/2
);
// detect what edges lie on lower slices by turning bridge contour and holes // detect what edges lie on lower slices by turning bridge contour and holes
// into polylines and then clipping them with each lower slice's contour // into polylines and then clipping them with each lower slice's contour
this->_edges = intersection_pl(grown, this->lower_slices.contours()); this->_edges = intersection_pl(grown, this->lower_slices.contours());
@ -34,6 +44,10 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
svg.draw(this->_anchors, "yellow"); svg.draw(this->_anchors, "yellow");
svg.draw(this->_edges, "black", scale_(0.2)); svg.draw(this->_edges, "black", scale_(0.2));
svg.Close(); svg.Close();
std::cout << "expolygon: " << this->expolygon.dump_perl() << std::endl;
for (const ExPolygon &e : this->lower_slices.expolygons)
std::cout << "lower: " << e.dump_perl() << std::endl;
} }
#endif #endif
} }
@ -133,6 +147,13 @@ BridgeDetector::detect_angle()
)); ));
} }
if (candidate.coverage > 0) have_coverage = true; if (candidate.coverage > 0) have_coverage = true;
#if 0
std::cout << "angle = " << Slic3r::Geometry::rad2deg(candidate.angle)
<< "; coverage = " << candidate.coverage
<< "; max_length = " << candidate.max_length
<< std::endl;
#endif
} }
// if no direction produced coverage, then there's no bridge direction // if no direction produced coverage, then there's no bridge direction
@ -159,12 +180,16 @@ BridgeDetector::detect_angle()
return true; return true;
} }
Polygons
BridgeDetector::coverage() const
{
if (this->angle == -1) return Polygons();
return this->coverage(this->angle);
}
Polygons Polygons
BridgeDetector::coverage(double angle) const BridgeDetector::coverage(double angle) const
{ {
if (angle == -1) angle = this->angle;
if (angle == -1) return Polygons();
// Clone our expolygon and rotate it so that we work with vertical lines. // Clone our expolygon and rotate it so that we work with vertical lines.
ExPolygon expolygon = this->expolygon; ExPolygon expolygon = this->expolygon;
expolygon.rotate(PI/2.0 - angle, Point(0,0)); expolygon.rotate(PI/2.0 - angle, Point(0,0));

View File

@ -10,32 +10,33 @@ namespace Slic3r {
class BridgeDetector { class BridgeDetector {
public: public:
// The non-grown hole. /// The non-grown hole.
ExPolygon expolygon; ExPolygon expolygon;
// Lower slices, all regions. /// Lower slices, all regions.
ExPolygonCollection lower_slices; ExPolygonCollection lower_slices;
// Scaled extrusion width of the infill. /// Scaled extrusion width of the infill.
coord_t extrusion_width; coord_t extrusion_width;
// Angle resolution for the brute force search of the best bridging angle. /// Angle resolution for the brute force search of the best bridging angle.
double resolution; double resolution;
// The final optimal angle. /// The final optimal angle.
double angle; double angle;
BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
bool detect_angle(); bool detect_angle();
Polygons coverage(double angle = -1) const; Polygons coverage() const;
Polygons coverage(double angle) const;
Polylines unsupported_edges(double angle = -1) const; Polylines unsupported_edges(double angle = -1) const;
private: private:
// Open lines representing the supporting edges. /// Open lines representing the supporting edges.
Polylines _edges; Polylines _edges;
// Closed polygons representing the supporting areas. /// Closed polygons representing the supporting areas.
ExPolygons _anchors; ExPolygons _anchors;
class BridgeDirection { class BridgeDirection {
public: public:
BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {} BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {}
// the best direction is the one causing most lines to be bridged (thus most coverage) /// the best direction is the one causing most lines to be bridged (thus most coverage)
bool operator<(const BridgeDirection &other) const { bool operator<(const BridgeDirection &other) const {
// Initial sort by coverage only - comparator must obey strict weak ordering // Initial sort by coverage only - comparator must obey strict weak ordering
return this->coverage > other.coverage; return this->coverage > other.coverage;

View File

@ -1,5 +1,4 @@
#include "Config.hpp" #include "Config.hpp"
#include <stdlib.h> // for setenv()
#include <assert.h> #include <assert.h>
#include <ctime> #include <ctime>
#include <fstream> #include <fstream>
@ -11,16 +10,15 @@
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/config.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/lexical_cast.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/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <string.h> #include <string.h>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
#define setenv(k, v, o) _putenv_s(k, v)
#endif
namespace Slic3r { namespace Slic3r {
std::string escape_string_cstyle(const std::string &str) std::string escape_string_cstyle(const std::string &str)
@ -258,7 +256,7 @@ ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &opt_
for (const t_config_option_key &opt_key : opt_keys) { for (const t_config_option_key &opt_key : opt_keys) {
ConfigOption* my_opt = this->option(opt_key, true); ConfigOption* my_opt = this->option(opt_key, true);
if (my_opt == NULL) { if (my_opt == NULL) {
if (ignore_nonexistent == false) throw "Attempt to apply non-existent option"; if (ignore_nonexistent == false) throw UnknownOptionException();
continue; continue;
} }
@ -341,7 +339,7 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key) const {
} else if (const ConfigOptionFloat* optv = dynamic_cast<const ConfigOptionFloat*>(opt)) { } else if (const ConfigOptionFloat* optv = dynamic_cast<const ConfigOptionFloat*>(opt)) {
return optv->value; return optv->value;
} else { } else {
throw "Not a valid option type for get_abs_value()"; throw std::runtime_error("Not a valid option type for get_abs_value()");
} }
} }
@ -360,7 +358,6 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over)
void void
ConfigBase::setenv_() ConfigBase::setenv_()
{ {
#ifdef setenv
t_config_option_keys opt_keys = this->keys(); t_config_option_keys opt_keys = this->keys();
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) { for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
// prepend the SLIC3R_ prefix // prepend the SLIC3R_ prefix
@ -373,9 +370,8 @@ ConfigBase::setenv_()
for (size_t i = 0; i < envname.size(); ++i) for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i]; envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
setenv(envname.c_str(), this->serialize(*it).c_str(), 1); boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
} }
#endif
} }
const ConfigOption* const ConfigOption*
@ -393,7 +389,8 @@ ConfigBase::load(const std::string &file)
{ {
namespace pt = boost::property_tree; namespace pt = boost::property_tree;
pt::ptree tree; pt::ptree tree;
pt::read_ini(file, tree); boost::nowide::ifstream ifs(file);
pt::read_ini(ifs, tree);
BOOST_FOREACH(const pt::ptree::value_type &v, tree) { BOOST_FOREACH(const pt::ptree::value_type &v, tree) {
try { try {
t_config_option_key opt_key = v.first; t_config_option_key opt_key = v.first;
@ -409,8 +406,8 @@ void
ConfigBase::save(const std::string &file) const ConfigBase::save(const std::string &file) const
{ {
using namespace std; using namespace std;
ofstream c; boost::nowide::ofstream c;
c.open(file.c_str(), ios::out | ios::trunc); c.open(file, ios::out | ios::trunc);
{ {
time_t now; time_t now;
@ -490,7 +487,7 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) {
optv->keys_map = &optdef->enum_keys_map; optv->keys_map = &optdef->enum_keys_map;
opt = static_cast<ConfigOption*>(optv); opt = static_cast<ConfigOption*>(optv);
} else { } else {
throw "Unknown option type"; throw std::runtime_error("Unknown option type");
} }
this->options[opt_key] = opt; this->options[opt_key] = opt;
return opt; return opt;
@ -527,19 +524,19 @@ DynamicConfig::empty() const {
void void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra) DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{ {
std::vector<const char*> _argv; std::vector<char*> _argv;
// push a bogus executable name (argv[0]) // push a bogus executable name (argv[0])
_argv.push_back(""); _argv.push_back(const_cast<char*>(""));
for (size_t i = 0; i < tokens.size(); ++i) for (size_t i = 0; i < tokens.size(); ++i)
_argv.push_back(const_cast<const char*>(tokens[i].c_str())); _argv.push_back(const_cast<char *>(tokens[i].c_str()));
this->read_cli(_argv.size(), &_argv[0], extra); this->read_cli(_argv.size(), &_argv[0], extra);
} }
void void
DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* extra) DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
{ {
// cache the CLI option => opt_key mapping // cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts; std::map<std::string,std::string> opts;

View File

@ -15,7 +15,7 @@
namespace Slic3r { namespace Slic3r {
// Name of the configuration option. /// Name of the configuration option.
typedef std::string t_config_option_key; typedef std::string t_config_option_key;
typedef std::vector<std::string> t_config_option_keys; typedef std::vector<std::string> t_config_option_keys;
@ -24,7 +24,11 @@ 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_string_cstyle(const std::string &str, std::string &out);
extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out); extern bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &out);
// A generic value of a configuration option. /// \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 { class ConfigOption {
public: public:
virtual ~ConfigOption() {}; virtual ~ConfigOption() {};
@ -41,7 +45,7 @@ class ConfigOption {
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) /// Value of a single valued option (bool, int, float, string, point, enum)
template <class T> template <class T>
class ConfigOptionSingle : public ConfigOption { class ConfigOptionSingle : public ConfigOption {
public: public:
@ -55,14 +59,14 @@ class ConfigOptionSingle : public ConfigOption {
}; };
}; };
// Value of a vector valued option (bools, ints, floats, strings, points) /// Virtual base class, represents value of a vector valued option (bools, ints, floats, strings, points)
class ConfigOptionVectorBase : public ConfigOption { class ConfigOptionVectorBase : public ConfigOption {
public: public:
virtual ~ConfigOptionVectorBase() {}; virtual ~ConfigOptionVectorBase() {};
virtual std::vector<std::string> vserialize() const = 0; virtual std::vector<std::string> vserialize() const = 0;
}; };
// Value of a vector valued option (bools, ints, floats, strings, points), template /// Value of a vector valued option (bools, ints, floats, strings, points), template
template <class T> template <class T>
class ConfigOptionVector : public ConfigOptionVectorBase class ConfigOptionVector : public ConfigOptionVectorBase
{ {
@ -86,6 +90,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase
}; };
}; };
/// Template specialization for a single ConfigOption
/// Internally resolves to a double.
class ConfigOptionFloat : public ConfigOptionSingle<double> class ConfigOptionFloat : public ConfigOptionSingle<double>
{ {
public: public:
@ -108,6 +114,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
}; };
}; };
/// Vector form of template specialization for floating point numbers.
class ConfigOptionFloats : public ConfigOptionVector<double> class ConfigOptionFloats : public ConfigOptionVector<double>
{ {
public: public:
@ -231,7 +238,7 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
}; };
}; };
// semicolon-separated strings /// semicolon-separated strings
class ConfigOptionStrings : public ConfigOptionVector<std::string> class ConfigOptionStrings : public ConfigOptionVector<std::string>
{ {
public: public:
@ -253,6 +260,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
}; };
}; };
/// \brief Specialized floating point class to represent some percentage value of
/// another numeric configuration option.
class ConfigOptionPercent : public ConfigOptionFloat class ConfigOptionPercent : public ConfigOptionFloat
{ {
public: public:
@ -260,6 +269,8 @@ class ConfigOptionPercent : public ConfigOptionFloat
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {}; ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->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 { double get_abs_value(double ratio_over) const {
return ratio_over * this->value / 100; return ratio_over * this->value / 100;
}; };
@ -280,6 +291,9 @@ class ConfigOptionPercent : public ConfigOptionFloat
}; };
}; };
/// 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 class ConfigOptionFloatOrPercent : public ConfigOptionPercent
{ {
public: public:
@ -321,6 +335,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
}; };
}; };
/// \brief Configuration option to store a 2D (x,y) tuple.
class ConfigOptionPoint : public ConfigOptionSingle<Pointf> class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
{ {
public: public:
@ -339,6 +354,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
bool deserialize(std::string str, bool append = false); bool deserialize(std::string str, bool append = false);
}; };
/// \brief Configuration option to store a 3D (x,y,z) tuple.
class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3> class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
{ {
public: public:
@ -394,6 +410,8 @@ class ConfigOptionPoints : public ConfigOptionVector<Pointf>
bool deserialize(std::string str, bool append = false); bool deserialize(std::string str, bool append = false);
}; };
/// \brief Represents a boolean flag
class ConfigOptionBool : public ConfigOptionSingle<bool> class ConfigOptionBool : public ConfigOptionSingle<bool>
{ {
public: public:
@ -450,9 +468,10 @@ class ConfigOptionBools : public ConfigOptionVector<bool>
}; };
}; };
// Map from an enum name to an enum integer value. /// Map from an enum name to an enum integer value.
typedef std::map<std::string,int> t_config_enum_values; typedef std::map<std::string,int> t_config_enum_values;
/// \brief Templated enumeration representation.
template <class T> template <class T>
class ConfigOptionEnum : public ConfigOptionSingle<T> class ConfigOptionEnum : public ConfigOptionSingle<T>
{ {
@ -477,14 +496,15 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
return true; return true;
}; };
// Map from an enum name to an enum integer value. /// Map from an enum name to an enum integer value.
//FIXME The map is called often, it shall be initialized statically. //FIXME The map is called often, it shall be initialized statically.
static t_config_enum_values get_enum_values(); static t_config_enum_values get_enum_values();
}; };
// Generic enum configuration value. /// \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. /// 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 class ConfigOptionEnumGeneric : public ConfigOptionInt
{ {
public: public:
@ -504,102 +524,120 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
}; };
}; };
// Type of a configuration value. /// Type of a configuration value.
enum ConfigOptionType { enum ConfigOptionType {
coNone, coNone,
// single float /// single float
coFloat, coFloat,
// vector of floats /// vector of floats
coFloats, coFloats,
// single int /// single int
coInt, coInt,
// vector of ints /// vector of ints
coInts, coInts,
// single string /// single string
coString, coString,
// vector of strings /// vector of strings
coStrings, coStrings,
// percent value. Currently only used for infill. /// percent value. Currently only used for infill.
coPercent, coPercent,
// a fraction or an absolute value /// a fraction or an absolute value
coFloatOrPercent, coFloatOrPercent,
// single 2d point. Currently not used. /// single 2d point. Currently not used.
coPoint, coPoint,
// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. /// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
coPoints, coPoints,
coPoint3, coPoint3,
// single boolean value /// single boolean value
coBool, coBool,
// vector of boolean values /// vector of boolean values
coBools, coBools,
// a generic enum /// a generic enum
coEnum, coEnum,
}; };
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling. /// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef class ConfigOptionDef
{ {
public: public:
// What type? bool, int, string etc. /// \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; ConfigOptionType type;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. /// \brief Default value of this option.
///
/// The default value object is owned by ConfigDef, it is released in its destructor.
ConfigOption* default_value; ConfigOption* default_value;
// Usually empty. /// \brief Specialization to indicate to the GUI what kind of control is more appropriate.
// 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). /// 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; std::string gui_type;
// The flags may be combined. /// The flags may be combined.
// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label. /// "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 /// "align_label_right" - align label to right
std::string gui_flags; std::string gui_flags;
// Label of the GUI input field. /// 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, /// 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. /// 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. /// The full label is shown, when adding an override parameter for an object or a modified object.
std::string label; std::string label;
std::string full_label; std::string full_label;
// Category of a configuration field, from the GUI perspective. /// Category of a configuration field, from the GUI perspective.
// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width" /// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
std::string category; std::string category;
// A tooltip text shown in the GUI. /// A tooltip text shown in the GUI.
std::string tooltip; std::string tooltip;
// Text right from the input field, usually a unit of measurement. /// Text right from the input field, usually a unit of measurement.
std::string sidetext; std::string sidetext;
// Format of this parameter on a command line. /// Format of this parameter on a command line.
std::string cli; std::string cli;
// Set for type == coFloatOrPercent. /// Set for type == coFloatOrPercent.
// It provides a link to a configuration value, of which this option provides a ratio. /// It provides a link to a configuration value, of which this option provides a ratio.
// For example, /// For example,
// For example external_perimeter_speed may be defined as a fraction of perimeter_speed. /// For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
t_config_option_key ratio_over; t_config_option_key ratio_over;
// True for multiline strings. /// True for multiline strings.
bool multiline; bool multiline;
// For text input: If true, the GUI text box spans the complete page width. /// For text input: If true, the GUI text box spans the complete page width.
bool full_width; bool full_width;
// Not editable. Currently only used for the display of the number of threads.
/// This configuration item is not editable.
/// Currently only used for the display of the number of threads.
bool readonly; bool readonly;
// Height of a multiline GUI text box. /// Height of a multiline GUI text box.
int height; int height;
// Optional width of an input field. /// Optional width of an input field.
int width; int width;
// <min, max> limit of a numeric input. /// <min, max> limit of a numeric input.
// If not set, the <min, max> is set to <INT_MIN, INT_MAX> /// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
// By setting min=0, only nonnegative input is allowed. /// By setting min=0, only nonnegative input is allowed.
int min; int min;
int max; int max;
// Legacy names for this configuration option.
// Used when parsing legacy configuration file. /// Legacy names for this configuration option.
/// Used when parsing legacy configuration file.
std::vector<t_config_option_key> aliases; std::vector<t_config_option_key> aliases;
// Sometimes a single value may well define multiple values in a "beginner" mode. /// 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". /// Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
std::vector<t_config_option_key> shortcut; std::vector<t_config_option_key> shortcut;
// Definition of values / labels for a combo box. /// 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". /// 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_values;
std::vector<std::string> enum_labels; std::vector<std::string> enum_labels;
// For enums (when type == coEnum). Maps enum_values to enums. /// For enums (when type == coEnum). Maps enum_values to enums.
// Initialized by ConfigOptionEnum<xxx>::get_enum_values() /// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
t_config_enum_values enum_keys_map; t_config_enum_values enum_keys_map;
ConfigOptionDef() : type(coNone), default_value(NULL), ConfigOptionDef() : type(coNone), default_value(NULL),
@ -612,14 +650,14 @@ class ConfigOptionDef
ConfigOptionDef& operator= (ConfigOptionDef other); ConfigOptionDef& operator= (ConfigOptionDef other);
}; };
// Map from a config option name to its definition. /// Map from a config option name to its definition.
// The definition does not carry an actual value of the config option, only its constant default value. //i The definition does not carry an actual value of the config option, only its constant default value.
// t_config_option_key is std::string //i t_config_option_key is std::string
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map; 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. /// 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, /// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values. /// but it carries the defaults of the configuration values.
class ConfigDef class ConfigDef
{ {
public: public:
@ -631,14 +669,14 @@ class ConfigDef
void merge(const ConfigDef &other); void merge(const ConfigDef &other);
}; };
// An abstract configuration store. /// An abstract configuration store.
class ConfigBase class ConfigBase
{ {
public: public:
// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. /// 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, /// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values. /// but it carries the defaults of the configuration values.
// ConfigBase does not own ConfigDef, it only references it. /// ConfigBase does not own ConfigDef, it only references it.
const ConfigDef* def; const ConfigDef* def;
ConfigBase() : def(NULL) {}; ConfigBase() : def(NULL) {};
@ -668,8 +706,8 @@ class ConfigBase
void save(const std::string &file) const; void save(const std::string &file) const;
}; };
// Configuration store with dynamic number of configuration values. /// Configuration store with dynamic number of configuration values.
// In Slic3r, the dynamic config is mostly used at the user interface layer. /// In Slic3r, the dynamic config is mostly used at the user interface layer.
class DynamicConfig : public virtual ConfigBase class DynamicConfig : public virtual ConfigBase
{ {
public: public:
@ -685,29 +723,30 @@ class DynamicConfig : public virtual ConfigBase
void clear(); void clear();
bool empty() const; bool empty() const;
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra); void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
void read_cli(const int argc, const char **argv, t_config_option_keys* extra); void read_cli(int argc, char** argv, t_config_option_keys* extra);
private: private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map; typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options; t_options_map options;
}; };
// Configuration store with a static definition of configuration values. /// 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, /// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
// because the configuration values could be accessed directly. /// because the configuration values could be accessed directly.
class StaticConfig : public virtual ConfigBase class StaticConfig : public virtual ConfigBase
{ {
public: public:
StaticConfig() : ConfigBase() {}; 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 /// 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. /// and which could be resolved by this->optptr(key) call.
t_config_option_keys keys() const; t_config_option_keys keys() const;
// Set all statically defined config options to their defaults defined by this->def. /// Set all statically defined config options to their defaults defined by this->def.
void set_defaults(); void set_defaults();
// The derived class has to implement optptr to resolve a static configuration value. /// 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; /// 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 {}; class UnknownOptionException : public std::exception {};
} }

View File

@ -450,25 +450,37 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
void void
ExPolygon::triangulate_p2t(Polygons* polygons) const ExPolygon::triangulate_p2t(Polygons* polygons) const
{ {
ExPolygons expp = simplify_polygons_ex(*this, true); for (const ExPolygon &ex : simplify_polygons_ex(*this, true)) {
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
// TODO: prevent duplicate points
// contour // contour
std::vector<p2t::Point*> ContourPoints; std::vector<p2t::Point*> ContourPoints;
for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) {
Polygon contour = ex.contour;
contour.remove_duplicate_points();
for (const Point &point : contour.points) {
// We should delete each p2t::Point object // We should delete each p2t::Point object
ContourPoints.push_back(new p2t::Point(point->x, point->y)); ContourPoints.push_back(new p2t::Point(point.x, point.y));
} }
p2t::CDT cdt(ContourPoints); p2t::CDT cdt(ContourPoints);
// holes // holes
for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { for (Polygon hole : ex.holes) {
hole.remove_duplicate_points();
std::vector<p2t::Point*> points; std::vector<p2t::Point*> points;
for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) { Point prev = hole.points.back();
for (Point &point : hole.points) {
// Shrink large polygons by reducing each coordinate by 1 in the
// general direction of the last point as we wind around
// This normally wouldn't work in every case, but our upscaled polygons
// have little chance to create new duplicate points with this method.
// For information on why this was needed, see:
// https://code.google.com/p/poly2tri/issues/detail?id=90
// https://github.com/raptor/clip2tri
(point.x - prev.x) > 0 ? point.x-- : point.x++;
(point.y - prev.y) > 0 ? point.y-- : point.y++;
prev = point;
// will be destructed in SweepContext::~SweepContext // will be destructed in SweepContext::~SweepContext
points.push_back(new p2t::Point(point->x, point->y)); points.push_back(new p2t::Point(point.x, point.y));
} }
cdt.AddHole(points); cdt.AddHole(points);
} }
@ -477,18 +489,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
cdt.Triangulate(); cdt.Triangulate();
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles(); std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) { for (p2t::Triangle* triangle : triangles) {
Polygon p; Polygon p;
for (int i = 0; i <= 2; ++i) { for (int i = 0; i <= 2; ++i) {
p2t::Point* point = (*triangle)->GetPoint(i); p2t::Point* point = triangle->GetPoint(i);
p.points.push_back(Point(point->x, point->y)); p.points.push_back(Point(point->x, point->y));
} }
polygons->push_back(p); polygons->push_back(p);
} }
for(std::vector<p2t::Point*>::iterator it = ContourPoints.begin(); it != ContourPoints.end(); ++it) { for (p2t::Point* it : ContourPoints)
delete *it; delete it;
}
} }
} }

View File

@ -18,6 +18,8 @@ class ExPolygon
public: public:
Polygon contour; Polygon contour;
Polygons holes; Polygons holes;
ExPolygon() {};
explicit ExPolygon(const Polygon &_contour) : contour(_contour) {};
operator Points() const; operator Points() const;
operator Polygons() const; operator Polygons() const;
void scale(double factor); void scale(double factor);

View File

@ -39,13 +39,14 @@ Extruder::extrude(double dE)
return dE; return dE;
} }
/* This method makes sure the extruder is retracted by the specified amount /** This method makes sure the extruder is retracted by the specified amount
of filament and returns the amount of filament retracted. of filament and returns the amount of filament retracted.
If the extruder is already retracted by the same or a greater amount, If the extruder is already retracted by the same or a greater amount,
this method is a no-op. this method is a no-op.
The restart_extra argument sets the extra length to be used for The restart_extra argument sets the extra length to be used for
unretraction. If we're actually performing a retraction, any restart_extra unretraction. If we're actually performing a retraction, any restart_extra
value supplied will overwrite the previous one if any. */ value supplied will overwrite the previous one if any.
*/
double double
Extruder::retract(double length, double restart_extra) Extruder::retract(double length, double restart_extra)
{ {

View File

@ -10,6 +10,7 @@ namespace Slic3r {
class Extruder class Extruder
{ {
public: public:
/// ID of current object.
unsigned int id; unsigned int id;
double E; double E;
double absolute_E; double absolute_E;
@ -21,16 +22,23 @@ class Extruder
Extruder(unsigned int id, GCodeConfig *config); Extruder(unsigned int id, GCodeConfig *config);
virtual ~Extruder() {} virtual ~Extruder() {}
void reset(); void reset();
/// Calculate the amount extruded for relative or absolute moves.
double extrude(double dE); double extrude(double dE);
double retract(double length, double restart_extra); double retract(double length, double restart_extra);
double unretract(); double unretract();
double e_per_mm(double mm3_per_mm) const; double e_per_mm(double mm3_per_mm) const;
double extruded_volume() const; double extruded_volume() const;
/// Calculate amount of filament used for current Extruder object.
double used_filament() const; double used_filament() const;
/// Retrieve the filament diameter for this Extruder from config.
double filament_diameter() const; double filament_diameter() const;
/// Retrieve the filament density for this Extruder from config.
double filament_density() const; double filament_density() const;
/// Retrieve the filament cost for this Extruder from config.
double filament_cost() const; double filament_cost() const;
/// Retrieve the extrustion multiplier for this Extruder from config.
double extrusion_multiplier() const; double extrusion_multiplier() const;
double retract_length() const; double retract_length() const;
double retract_lift() const; double retract_lift() const;

View File

@ -11,7 +11,8 @@ class ExPolygonCollection;
class ExtrusionEntityCollection; class ExtrusionEntityCollection;
class Extruder; class Extruder;
/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */ /** \brief Each ExtrusionRole value identifies a distinct set of { extruder, speed }
*/
enum ExtrusionRole { enum ExtrusionRole {
erNone, erNone,
erPerimeter, erPerimeter,
@ -27,7 +28,7 @@ enum ExtrusionRole {
erSupportMaterialInterface, erSupportMaterialInterface,
}; };
/* Special flags describing loop */ /** \brief Special flags describing loop */
enum ExtrusionLoopRole { enum ExtrusionLoopRole {
elrDefault, elrDefault,
elrContourInternalPerimeter, elrContourInternalPerimeter,
@ -45,9 +46,9 @@ public:
virtual void reverse() = 0; virtual void reverse() = 0;
virtual Point first_point() const = 0; virtual Point first_point() const = 0;
virtual Point last_point() const = 0; virtual Point last_point() const = 0;
// Produce a list of 2D polygons covered by the extruded path. /// Produce a list of 2D polygons covered by the extruded path.
virtual Polygons grow() const = 0; virtual Polygons grow() const = 0;
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. /// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
virtual double min_mm3_per_mm() const = 0; virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0; virtual Polyline as_polyline() const = 0;
virtual double length() const { return 0; }; virtual double length() const { return 0; };
@ -60,11 +61,11 @@ class ExtrusionPath : public ExtrusionEntity
public: public:
Polyline polyline; Polyline polyline;
ExtrusionRole role; ExtrusionRole role;
// Volumetric velocity. mm^3 of plastic per mm of linear head motion /// Volumetric velocity. mm^3 of plastic per mm of linear head motion
double mm3_per_mm; double mm3_per_mm;
// Width of the extrusion. /// Width of the extrusion.
float width; float width;
// Height of the extrusion. /// Height of the extrusion.
float height; float height;
ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {};
@ -74,11 +75,11 @@ public:
void reverse() { this->polyline.reverse(); } void reverse() { this->polyline.reverse(); }
Point first_point() const { return this->polyline.points.front(); } Point first_point() const { return this->polyline.points.front(); }
Point last_point() const { return this->polyline.points.back(); } Point last_point() const { return this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection. /// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used. /// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection. /// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection.
// Currently not used. /// Currently not used.
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance); void clip_end(double distance);
void simplify(double tolerance); void simplify(double tolerance);
@ -103,9 +104,9 @@ public:
return this->role == erBridgeInfill return this->role == erBridgeInfill
|| this->role == erOverhangPerimeter; || this->role == erOverhangPerimeter;
}; };
// Produce a list of 2D polygons covered by the extruded path. /// Produce a list of 2D polygons covered by the extruded path.
Polygons grow() const; Polygons grow() const;
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. /// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; } double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; } Polyline as_polyline() const { return this->polyline; }
@ -141,7 +142,7 @@ class ExtrusionLoop : public ExtrusionEntity
bool split_at_vertex(const Point &point); bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang = false); void split_at(const Point &point, bool prefer_non_overhang = false);
void clip_end(double distance, ExtrusionPaths* paths) const; void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow. /// Test, whether the point is extruded by a bridging flow.
bool has_overhang_point(const Point &point) const; bool has_overhang_point(const Point &point) const;
bool is_perimeter() const { bool is_perimeter() const {
return this->paths.front().role == erPerimeter return this->paths.front().role == erPerimeter
@ -159,14 +160,19 @@ class ExtrusionLoop : public ExtrusionEntity
|| this->paths.front().role == erSolidInfill || this->paths.front().role == erSolidInfill
|| this->paths.front().role == erTopSolidInfill; || this->paths.front().role == erTopSolidInfill;
} }
// Produce a list of 2D polygons covered by the extruded path. /// Produce a list of 2D polygons covered by the extruded path.
Polygons grow() const; Polygons grow() const;
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. /// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const; double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); } Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
void append(const ExtrusionPath &path) { void append(const ExtrusionPath &path) {
this->paths.push_back(path); this->paths.push_back(path);
}; };
bool has(ExtrusionRole role) const {
for (const auto &path : this->paths)
if (path.role == role) return true;
return false;
};
}; };
} }

View File

@ -1,10 +1,9 @@
#define DEBUG
#undef NDEBUG
#include <cassert> #include <cassert>
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Geometry.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
@ -96,10 +95,8 @@ Fill::_infill_direction(const Surface &surface) const
if (surface.bridge_angle >= 0) { if (surface.bridge_angle >= 0) {
// use bridge angle // use bridge angle
//FIXME Vojtech: Add a debugf?
// Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf("Filling bridge with angle %f\n", surface.bridge_angle); printf("Filling bridge with angle %f\n", Slic3r::Geometry::rad2deg(surface.bridge_angle));
#endif #endif
out_angle = surface.bridge_angle; out_angle = surface.bridge_angle;
} else if (this->layer_id != size_t(-1)) { } else if (this->layer_id != size_t(-1)) {

View File

@ -525,7 +525,7 @@ void FillCubic::_fill_surface_single(
direction_t direction2 = direction; direction_t direction2 = direction;
const coord_t range = scale_(this->min_spacing / this->density); const coord_t range = scale_(this->min_spacing / this->density);
const coord_t x_shift = abs(( (coord_t)(scale_(this->z) + range) % (coord_t)(range * 2)) - range); const coord_t x_shift = (coord_t)(scale_(this->z) + range) % (coord_t)(range*3);
fill2._fill_single_direction(expolygon, direction2, -x_shift, out); fill2._fill_single_direction(expolygon, direction2, -x_shift, out);

View File

@ -120,11 +120,11 @@ Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bo
return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0);
} }
// Calculate a new spacing to fill width with possibly integer number of lines, /// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends. /// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases, /// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe, /// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase. /// therefore the adjustment is limited to 20% increase.
template <class T> template <class T>
T T
Flow::solid_spacing(const T total_width, const T spacing) Flow::solid_spacing(const T total_width, const T spacing)

View File

@ -10,6 +10,7 @@ namespace Slic3r {
constexpr auto BRIDGE_EXTRA_SPACING = 0.05; constexpr auto BRIDGE_EXTRA_SPACING = 0.05;
constexpr auto OVERLAP_FACTOR = 1.0; constexpr auto OVERLAP_FACTOR = 1.0;
/// Enumeration for different flow roles
enum FlowRole { enum FlowRole {
frExternalPerimeter, frExternalPerimeter,
frPerimeter, frPerimeter,
@ -20,6 +21,8 @@ enum FlowRole {
frSupportMaterialInterface, frSupportMaterialInterface,
}; };
/// Represents material flow; provides methods to predict material spacing.
class Flow class Flow
{ {
public: public:
@ -28,7 +31,13 @@ class Flow
Flow(float _w, float _h, float _nd, bool _bridge = false) Flow(float _w, float _h, float _nd, bool _bridge = false)
: width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};
/// Return the centerline spacing between two adjacent extrusions that have the same properties (width, etc).
/// Models as a rectangle with semicircles at the ends.
float spacing() const; float spacing() const;
/// Return the centerline spacing between two Flow objects (current and some other flow).
/// \remark this->spacing(other) == other.spacing(this)
float spacing(const Flow &other) const; float spacing(const Flow &other) const;
void set_spacing(float spacing); void set_spacing(float spacing);
void set_solid_spacing(const coord_t total_width) { void set_solid_spacing(const coord_t total_width) {
@ -48,12 +57,18 @@ class Flow
return scale_(this->spacing(other)); return scale_(this->spacing(other));
}; };
/// Static method to build a Flow object from an extrusion width config setting and some other context properties
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
/// Static method to build a Flow object from a specified centerline spacing (center-to-center).
static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
template <class T> static T solid_spacing(const T total_width, const T spacing); template <class T> static T solid_spacing(const T total_width, const T spacing);
private: private:
static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio); static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio);
/// Calculate a relatively sane extrusion width, based on height and nozzle diameter.
/// Algorithm used does not play nice with layer heights < 0.1mm.
static float _auto_width(FlowRole role, float nozzle_diameter, float height); static float _auto_width(FlowRole role, float nozzle_diameter, float height);
static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
}; };

View File

@ -119,7 +119,7 @@ OozePrevention::post_toolchange(GCode &gcodegen)
int int
OozePrevention::_get_temp(GCode &gcodegen) OozePrevention::_get_temp(GCode &gcodegen)
{ {
return (gcodegen.layer != NULL && gcodegen.layer->id() == 0) return gcodegen.first_layer
? gcodegen.config.first_layer_temperature.get_at(gcodegen.writer.extruder()->id) ? gcodegen.config.first_layer_temperature.get_at(gcodegen.writer.extruder()->id)
: gcodegen.config.temperature.get_at(gcodegen.writer.extruder()->id); : gcodegen.config.temperature.get_at(gcodegen.writer.extruder()->id);
} }
@ -262,6 +262,12 @@ GCode::set_origin(const Pointf &pointf)
this->origin = pointf; this->origin = pointf;
} }
std::string
GCode::notes()
{
return this->writer.notes();
}
std::string std::string
GCode::preamble() GCode::preamble()
{ {
@ -334,25 +340,19 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
// restore original winding order so that concave and convex detection always happens // restore original winding order so that concave and convex detection always happens
// on the right/outer side of the polygon // on the right/outer side of the polygon
if (was_clockwise) { if (was_clockwise)
for (Polygons::iterator p = simplified.begin(); p != simplified.end(); ++p) for (Polygon &p : simplified)
p->reverse(); p.reverse();
}
// concave vertices have priority // concave vertices have priority
Points candidates; Points candidates;
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) { for (const Polygon &p : simplified)
Points concave = p->concave_points(PI*4/3); append_to(candidates, p.concave_points(PI*4/3));
candidates.insert(candidates.end(), concave.begin(), concave.end());
}
// if no concave points were found, look for convex vertices // if no concave points were found, look for convex vertices
if (candidates.empty()) { if (candidates.empty())
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) { for (const Polygon &p : simplified)
Points convex = p->convex_points(PI*2/3); append_to(candidates, p.convex_points(PI*2/3));
candidates.insert(candidates.end(), convex.begin(), convex.end());
}
}
// retrieve the last start position for this object // retrieve the last start position for this object
if (this->layer != NULL) { if (this->layer != NULL) {
@ -375,10 +375,9 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
if (!loop.split_at_vertex(point)) loop.split_at(point); if (!loop.split_at_vertex(point)) loop.split_at(point);
} else if (!candidates.empty()) { } else if (!candidates.empty()) {
Points non_overhang; Points non_overhang;
for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) { for (const Point &p : candidates)
if (!loop.has_overhang_point(*p)) if (!loop.has_overhang_point(p))
non_overhang.push_back(*p); non_overhang.push_back(p);
}
if (!non_overhang.empty()) if (!non_overhang.empty())
candidates = non_overhang; candidates = non_overhang;
@ -389,8 +388,15 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
point = last_pos.projection_onto(polygon); point = last_pos.projection_onto(polygon);
loop.split_at(point); loop.split_at(point);
} }
if (this->layer != NULL) if (this->layer != NULL) {
/* Keeping a single starting point for each object works best with objects
having a single island. In the other cases, this works when layers are
similar, so the algorithm picks the same chain of points. But when an
island disappears, the others might suffer from a starting point change
even if their shape didn't change. We should probably keep multiple
starting points for each layer and test all of them. */
this->_seam_position[this->layer->object()] = point; this->_seam_position[this->layer->object()] = point;
}
} else if (seam_position == spRandom) { } else if (seam_position == spRandom) {
if (loop.role == elrContourInternalPerimeter) { if (loop.role == elrContourInternalPerimeter) {
Polygon polygon = loop.polygon(); Polygon polygon = loop.polygon();
@ -415,9 +421,11 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
if (paths.empty()) return ""; if (paths.empty()) return "";
// apply the small perimeter speed // apply the small perimeter speed
if (paths.front().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH) { if (paths.front().is_perimeter()
if (speed == -1) speed = this->config.get_abs_value("small_perimeter_speed"); && !loop.has(erOverhangPerimeter)
} && loop.length() <= SMALL_PERIMETER_LENGTH
&& speed == -1)
speed = this->config.get_abs_value("small_perimeter_speed");
// extrude along the path // extrude along the path
std::string gcode; std::string gcode;

View File

@ -100,6 +100,7 @@ class GCode {
void set_extruders(const std::vector<unsigned int> &extruder_ids); void set_extruders(const std::vector<unsigned int> &extruder_ids);
void set_origin(const Pointf &pointf); void set_origin(const Pointf &pointf);
std::string preamble(); std::string preamble();
std::string notes();
std::string change_layer(const Layer &layer); std::string change_layer(const Layer &layer);
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1); std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1); std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);

View File

@ -135,8 +135,8 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
ios.c_ispeed = ios.c_ospeed = baud_rate; ios.c_ispeed = ios.c_ospeed = baud_rate;
ios.c_cflag &= ~CBAUD; ios.c_cflag &= ~CBAUD;
ios.c_cflag |= BOTHER | CLOCAL | CREAD; ios.c_cflag |= BOTHER | CLOCAL | CREAD;
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
ios.c_cc[VTIME] = 1; ios.c_cc[VTIME] = 1;
if (ioctl(handle, TCSETS2, &ios)) if (ioctl(handle, TCSETS2, &ios))
printf("Error in TCSETS2: %s\n", strerror(errno)); printf("Error in TCSETS2: %s\n", strerror(errno));

View File

@ -1,4 +1,5 @@
#include "GCodeWriter.hpp" #include "GCodeWriter.hpp"
#include "utils.hpp"
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
@ -32,6 +33,46 @@ GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0; this->multiple_extruders = (*std::max_element(extruder_ids.begin(), extruder_ids.end())) > 0;
} }
std::string
GCodeWriter::notes()
{
std::ostringstream gcode;
// Write the contents of the three notes sections
// a semicolon at the beginning of each line.
if (this->config.notes.getString().size() > 0) {
gcode << "; Print Config Notes: \n";
std::vector<std::string> temp_line = split_at_regex(this->config.notes.getString(),"\n");
for (auto j = temp_line.cbegin(); j != temp_line.cend(); j++) {
gcode << "; " << *j << "\n";
}
gcode << "; \n";
}
for (auto i = this->config.filament_notes.values.cbegin(); i != this->config.filament_notes.values.cend(); i++) {
if (i->size() > 0) {
gcode << "; Filament notes: \n";
std::vector<std::string> temp_line = split_at_regex(*i,"\n");
for (auto j = temp_line.cbegin(); j != temp_line.cend(); j++) {
gcode << "; " << *j << "\n";
}
gcode << "; \n";
}
}
if (this->config.printer_notes.getString().size() > 0) {
gcode << "; Printer Config Notes: \n";
std::vector<std::string> temp_line = split_at_regex(this->config.printer_notes.getString(),"\n");
for (auto j = temp_line.cbegin(); j != temp_line.cend(); j++) {
gcode << "; " << *j << "\n";
}
gcode << "; \n";
}
return gcode.str();
}
std::string std::string
GCodeWriter::preamble() GCodeWriter::preamble()
{ {
@ -50,6 +91,7 @@ GCodeWriter::preamble()
gcode << this->reset_e(true); gcode << this->reset_e(true);
} }
return gcode.str(); return gcode.str();
} }
@ -330,7 +372,7 @@ GCodeWriter::travel_to_z(double z, const std::string &comment)
reducing the lift amount that will be used for unlift. */ reducing the lift amount that will be used for unlift. */
if (!this->will_move_z(z)) { if (!this->will_move_z(z)) {
double nominal_z = this->_pos.z - this->_lifted; double nominal_z = this->_pos.z - this->_lifted;
this->_lifted = this->_lifted - (z - nominal_z); this->_lifted -= (z - nominal_z);
return ""; return "";
} }
@ -500,10 +542,15 @@ GCodeWriter::lift()
if (this->_pos.z >= above && (below == 0 || this->_pos.z <= below)) if (this->_pos.z >= above && (below == 0 || this->_pos.z <= below))
target_lift = this->config.retract_lift.get_at(this->_extruder->id); target_lift = this->config.retract_lift.get_at(this->_extruder->id);
} }
if (this->_lifted == 0 && target_lift > 0) {
// compare against epsilon because travel_to_z() does math on it
// and subtracting layer_height from retract_lift might not give
// exactly zero
if (std::abs(this->_lifted) < EPSILON && target_lift > 0) {
this->_lifted = target_lift; this->_lifted = target_lift;
return this->_travel_to_z(this->_pos.z + target_lift, "lift Z"); return this->_travel_to_z(this->_pos.z + target_lift, "lift Z");
} }
return ""; return "";
} }

View File

@ -23,6 +23,10 @@ public:
std::string extrusion_axis() const { return this->_extrusion_axis; } std::string extrusion_axis() const { return this->_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
void set_extruders(const std::vector<unsigned int> &extruder_ids); void set_extruders(const std::vector<unsigned int> &extruder_ids);
/// Write any notes provided by the user as comments in the gcode header.
std::string notes();
/// Actually write the preamble information.
std::string preamble(); std::string preamble();
std::string postamble() const; std::string postamble() const;
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;

View File

@ -507,9 +507,6 @@ MedialAxis::build(ThickPolylines* polylines)
} }
*/ */
typedef const VD::vertex_type vert_t;
typedef const VD::edge_type edge_t;
// collect valid edges (i.e. prune those not belonging to MAT) // collect valid edges (i.e. prune those not belonging to MAT)
// note: this keeps twins, so it inserts twice the number of the valid edges // note: this keeps twins, so it inserts twice the number of the valid edges
this->valid_edges.clear(); this->valid_edges.clear();
@ -534,7 +531,7 @@ MedialAxis::build(ThickPolylines* polylines)
// iterate through the valid edges to build polylines // iterate through the valid edges to build polylines
while (!this->edges.empty()) { while (!this->edges.empty()) {
const edge_t* edge = *this->edges.begin(); const VD::edge_type* edge = *this->edges.begin();
// start a polyline // start a polyline
ThickPolyline polyline; ThickPolyline polyline;

View File

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/nowide/fstream.hpp>
#define TINYOBJLOADER_IMPLEMENTATION #define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h" #include "tiny_obj_loader.h"
@ -12,7 +13,6 @@ namespace Slic3r { namespace IO {
bool bool
STL::read(std::string input_file, TriangleMesh* mesh) STL::read(std::string input_file, TriangleMesh* mesh)
{ {
// TODO: encode file name
// TODO: check that file exists // TODO: check that file exists
try { try {
@ -81,7 +81,8 @@ OBJ::read(std::string input_file, Model* model)
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, input_file.c_str()); boost::nowide::ifstream ifs(input_file);
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &ifs);
if (!err.empty()) { // `err` may contain warning message. if (!err.empty()) { // `err` may contain warning message.
std::cerr << err << std::endl; std::cerr << err << std::endl;
@ -153,7 +154,7 @@ POV::write(TriangleMesh& mesh, std::string output_file)
mesh2.center_around_origin(); mesh2.center_around_origin();
using namespace std; using namespace std;
ofstream pov; boost::nowide::ofstream pov;
pov.open(output_file.c_str(), ios::out | ios::trunc); pov.open(output_file.c_str(), ios::out | ios::trunc);
for (int i = 0; i < mesh2.stl.stats.number_of_facets; ++i) { for (int i = 0; i < mesh2.stl.stats.number_of_facets; ++i) {
const stl_facet &f = mesh2.stl.facet_start[i]; const stl_facet &f = mesh2.stl.facet_start[i];

View File

@ -5,6 +5,8 @@
#include <map> #include <map>
#include <string> #include <string>
#include <boost/move/move.hpp> #include <boost/move/move.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/iostream.hpp>
#include <expat/expat.h> #include <expat/expat.h>
namespace Slic3r { namespace IO { namespace Slic3r { namespace IO {
@ -454,9 +456,9 @@ AMF::read(std::string input_file, Model* model)
return false; return false;
} }
FILE *pFile = ::fopen(input_file.c_str(), "rt"); boost::nowide::ifstream fin(input_file, std::ios::in);
if (pFile == NULL) { if (!fin.is_open()) {
printf("Cannot open file %s\n", input_file.c_str()); boost::nowide::cerr << "Cannot open file: " << input_file << std::endl;
return false; return false;
} }
@ -467,27 +469,26 @@ AMF::read(std::string input_file, Model* model)
char buff[8192]; char buff[8192];
bool result = false; bool result = false;
for (;;) { while (!fin.eof()) {
int len = (int)fread(buff, 1, 8192, pFile); fin.read(buff, sizeof(buff));
if (ferror(pFile)) { if (fin.bad()) {
printf("AMF parser: Read error\n"); printf("AMF parser: Read error\n");
break; break;
} }
int done = feof(pFile); if (XML_Parse(parser, buff, fin.gcount(), fin.eof()) == XML_STATUS_ERROR) {
if (XML_Parse(parser, buff, len, done) == XML_STATUS_ERROR) {
printf("AMF parser: Parse error at line %lu:\n%s\n", printf("AMF parser: Parse error at line %lu:\n%s\n",
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser))); XML_ErrorString(XML_GetErrorCode(parser)));
break; break;
} }
if (done) { if (fin.eof()) {
result = true; result = true;
break; break;
} }
} }
XML_ParserFree(parser); XML_ParserFree(parser);
::fclose(pFile); fin.close();
if (result) if (result)
ctx.endDocument(); ctx.endDocument();
@ -499,12 +500,12 @@ AMF::write(Model& model, std::string output_file)
{ {
using namespace std; using namespace std;
ofstream file; boost::nowide::ofstream file;
file.open(output_file, ios::out | ios::trunc); file.open(output_file, ios::out | ios::trunc);
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl
<< "<amf unit=\"millimeter\">" << endl << "<amf unit=\"millimeter\">" << endl
<< "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>" << endl; << " <metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>" << endl;
for (const auto &material : model.materials) { for (const auto &material : model.materials) {
if (material.first.empty()) if (material.first.empty())
@ -598,7 +599,7 @@ AMF::write(Model& model, std::string output_file)
<< " <instance objectid=\"" << object_id << "\">" << endl << " <instance objectid=\"" << object_id << "\">" << endl
<< " <deltax>" << instance->offset.x + object->origin_translation.x << "</deltax>" << endl << " <deltax>" << instance->offset.x + object->origin_translation.x << "</deltax>" << endl
<< " <deltay>" << instance->offset.y + object->origin_translation.y << "</deltay>" << endl << " <deltay>" << instance->offset.y + object->origin_translation.y << "</deltay>" << endl
<< " <rz>%" << instance->rotation << "</rz>" << endl << " <rz>" << instance->rotation << "</rz>" << endl
<< " <scale>" << instance->scaling_factor << "</scale>" << endl << " <scale>" << instance->scaling_factor << "</scale>" << endl
<< " </instance>" << endl; << " </instance>" << endl;
} }

View File

@ -5,6 +5,8 @@
namespace Slic3r { namespace Slic3r {
/// Initialises upper_layer, lower_layer to NULL
/// Initialises slicing_errors to false
Layer::Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, Layer::Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z) coordf_t slice_z)
: upper_layer(NULL), : upper_layer(NULL),
@ -20,6 +22,7 @@ Layer::Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
{ {
} }
/// Removes references to self and clears regions
Layer::~Layer() Layer::~Layer()
{ {
// remove references to self // remove references to self
@ -34,25 +37,28 @@ Layer::~Layer()
this->clear_regions(); this->clear_regions();
} }
/// Getter for this->_id
size_t size_t
Layer::id() const Layer::id() const
{ {
return this->_id; return this->_id;
} }
/// Setter for this->_id
void void
Layer::set_id(size_t id) Layer::set_id(size_t id)
{ {
this->_id = id; this->_id = id;
} }
/// Getter for this->regions.size()
size_t size_t
Layer::region_count() const Layer::region_count() const
{ {
return this->regions.size(); return this->regions.size();
} }
/// Deletes all regions using this->delete_region()
void void
Layer::clear_regions() Layer::clear_regions()
{ {
@ -60,6 +66,7 @@ Layer::clear_regions()
this->delete_region(i); this->delete_region(i);
} }
/// Creates a LayerRegion from a PrintRegion and adds it to this->regions
LayerRegion* LayerRegion*
Layer::add_region(PrintRegion* print_region) Layer::add_region(PrintRegion* print_region)
{ {
@ -68,6 +75,7 @@ Layer::add_region(PrintRegion* print_region)
return region; return region;
} }
/// Deletes an individual region
void void
Layer::delete_region(int idx) Layer::delete_region(int idx)
{ {
@ -77,7 +85,8 @@ Layer::delete_region(int idx)
delete item; delete item;
} }
// merge all regions' slices to get islands /// Merge all regions' slices to get islands
//TODO: is this right?
void void
Layer::make_slices() Layer::make_slices()
{ {
@ -98,21 +107,24 @@ Layer::make_slices()
this->slices.expolygons.reserve(slices.size()); this->slices.expolygons.reserve(slices.size());
// prepare ordering points // prepare ordering points
// While it's more computationally expensive, we use centroid()
// instead of first_point() because it's [much more] deterministic
// and preserves ordering across similar layers.
Points ordering_points; Points ordering_points;
ordering_points.reserve(slices.size()); ordering_points.reserve(slices.size());
for (ExPolygons::const_iterator ex = slices.begin(); ex != slices.end(); ++ex) for (const ExPolygon &ex : slices)
ordering_points.push_back(ex->contour.first_point()); ordering_points.push_back(ex.contour.centroid());
// sort slices // sort slices
std::vector<Points::size_type> order; std::vector<Points::size_type> order;
Slic3r::Geometry::chained_path(ordering_points, order); Slic3r::Geometry::chained_path(ordering_points, order);
// populate slices vector // populate slices vector
for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it) { for (const Points::size_type &o : order)
this->slices.expolygons.push_back(slices[*it]); this->slices.expolygons.push_back(slices[o]);
}
} }
/// Iterates over all of the LayerRegion and invokes LayerRegion->merge_slices()
void void
Layer::merge_slices() Layer::merge_slices()
{ {
@ -121,6 +133,7 @@ Layer::merge_slices()
} }
} }
/// Uses LayerRegion->slices.any_internal_contains(item)
template <class T> template <class T>
bool bool
Layer::any_internal_region_slice_contains(const T &item) const Layer::any_internal_region_slice_contains(const T &item) const
@ -132,6 +145,7 @@ Layer::any_internal_region_slice_contains(const T &item) const
} }
template bool Layer::any_internal_region_slice_contains<Polyline>(const Polyline &item) const; template bool Layer::any_internal_region_slice_contains<Polyline>(const Polyline &item) const;
/// Uses LayerRegion->slices.any_bottom_contains(item)
template <class T> template <class T>
bool bool
Layer::any_bottom_region_slice_contains(const T &item) const Layer::any_bottom_region_slice_contains(const T &item) const
@ -143,10 +157,8 @@ Layer::any_bottom_region_slice_contains(const T &item) const
} }
template bool Layer::any_bottom_region_slice_contains<Polyline>(const Polyline &item) const; template bool Layer::any_bottom_region_slice_contains<Polyline>(const Polyline &item) const;
/// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters. /// The resulting fill surface is split back among the originating regions.
// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
// The resulting fill surface is split back among the originating regions.
void void
Layer::make_perimeters() Layer::make_perimeters()
{ {
@ -230,6 +242,8 @@ Layer::make_perimeters()
} }
} }
/// Iterates over all of the LayerRegion and invokes LayerRegion->make_fill()
/// Asserts that the fills created are not NULL
void void
Layer::make_fills() Layer::make_fills()
{ {
@ -247,15 +261,15 @@ Layer::make_fills()
} }
} }
// This function analyzes slices of a region (SurfaceCollection slices). /// Analyzes slices of a region (SurfaceCollection slices).
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. /// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type S_TYPE_INTERNAL. /// Initially all slices are of type S_TYPE_INTERNAL.
// Slices are compared against the top / bottom slices and regions and classified to the following groups: /// Slices are compared against the top / bottom slices and regions and classified to the following groups:
// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill. /// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill.
// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft. /// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft.
// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer. /// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer.
// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type. /// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type.
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins. /// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
void void
Layer::detect_surfaces_type() Layer::detect_surfaces_type()
{ {
@ -421,6 +435,7 @@ Layer::detect_surfaces_type()
} }
} }
///Iterates over all LayerRegions and invokes LayerRegion->process_external_surfaces
void void
Layer::process_external_surfaces() Layer::process_external_surfaces()
{ {

View File

@ -31,97 +31,126 @@ class LayerRegion
PrintRegion* region() { return this->_region; }; PrintRegion* region() { return this->_region; };
const PrintRegion* region() const { return this->_region; }; const PrintRegion* region() const { return this->_region; };
// collection of surfaces generated by slicing the original geometry /// Collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal /// Divided by type top/bottom/internal
SurfaceCollection slices; SurfaceCollection slices;
// collection of extrusion paths/loops filling gaps /// Collection of extrusion paths/loops filling gaps
ExtrusionEntityCollection thin_fills; ExtrusionEntityCollection thin_fills;
// collection of surfaces for infill generation /// Collection of surfaces for infill generation
SurfaceCollection fill_surfaces; SurfaceCollection fill_surfaces;
// collection of expolygons representing the bridged areas (thus not /// Collection of expolygons representing the bridged areas (thus not
// needing support material) /// needing support material)
Polygons bridged; Polygons bridged;
// collection of polylines representing the unsupported bridge edges /// Collection of polylines representing the unsupported bridge edges
PolylineCollection unsupported_bridge_edges; PolylineCollection unsupported_bridge_edges;
// ordered collection of extrusion paths/loops to build all perimeters /// Ordered collection of extrusion paths/loops to build all perimeters
// (this collection contains only ExtrusionEntityCollection objects) /// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection perimeters; ExtrusionEntityCollection perimeters;
// ordered collection of extrusion paths to fill surfaces /// Ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects) /// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection fills; ExtrusionEntityCollection fills;
/// Flow object which provides methods to predict material spacing.
Flow flow(FlowRole role, bool bridge = false, double width = -1) const; Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
/// Merges this->slices
void merge_slices(); void merge_slices();
/// Preprocesses fill surfaces
void prepare_fill_surfaces(); void prepare_fill_surfaces();
/// Generates and stores the perimeters and thin fills
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
/// Generate infills for a LayerRegion.
void make_fill(); void make_fill();
/// Processes external surfaces for bridges and top/bottom surfaces
void process_external_surfaces(); void process_external_surfaces();
/// Gets the smallest fillable area
double infill_area_threshold() const; double infill_area_threshold() const;
private: private:
/// Pointer to associated Layer
Layer *_layer; Layer *_layer;
/// Pointer to associated PrintRegion
PrintRegion *_region; PrintRegion *_region;
/// Mutex object for slices.
mutable boost::mutex _slices_mutex; mutable boost::mutex _slices_mutex;
///Constructor
LayerRegion(Layer *layer, PrintRegion *region) LayerRegion(Layer *layer, PrintRegion *region)
: _layer(layer), _region(region) {}; : _layer(layer), _region(region) {};
///Destructor
~LayerRegion() {}; ~LayerRegion() {};
}; };
/// A std::vector of LayerRegion Pointers
typedef std::vector<LayerRegion*> LayerRegionPtrs; typedef std::vector<LayerRegion*> LayerRegionPtrs;
class Layer { class Layer {
friend class PrintObject; friend class PrintObject;
public: public:
/// ID number
size_t id() const; size_t id() const;
/// Setter for this->_id
void set_id(size_t id); void set_id(size_t id);
/// Getter for _object
PrintObject* object() { return this->_object; }; PrintObject* object() { return this->_object; };
const PrintObject* object() const { return this->_object; }; const PrintObject* object() const { return this->_object; };
Layer *upper_layer; Layer *upper_layer; ///< Pointer to layer above
Layer *lower_layer; Layer *lower_layer; ///< Pointer to layer below
LayerRegionPtrs regions; LayerRegionPtrs regions; ///< Vector of pointers to the LayerRegions of this layer
bool slicing_errors; bool slicing_errors; ///< Presence of slicing errors
coordf_t slice_z; // Z used for slicing in unscaled coordinates coordf_t slice_z; ///< Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates coordf_t print_z; ///< Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates coordf_t height; ///< layer height in unscaled coordinates
// collection of expolygons generated by slicing the original geometry;
// also known as 'islands' (all regions and surface types are merged here)
ExPolygonCollection slices;
ExPolygonCollection slices; ///< collection of expolygons generated by slicing the original geometry;
///< also known as 'islands' (all regions and surface types are merged here)
/// Returns the number of regions
size_t region_count() const; size_t region_count() const;
/// Gets a region at a specific id
LayerRegion* get_region(size_t idx) { return this->regions.at(idx); }; LayerRegion* get_region(size_t idx) { return this->regions.at(idx); };
/// Gets a region at a specific id as const
const LayerRegion* get_region(size_t idx) const { return this->regions.at(idx); }; const LayerRegion* get_region(size_t idx) const { return this->regions.at(idx); };
LayerRegion* add_region(PrintRegion* print_region);
/// Adds a PrintRegion
LayerRegion* add_region(PrintRegion* print_region);
/// Merge all regions' slices to get islands
void make_slices(); void make_slices();
/// Merges all of the LayerRegions' slices
void merge_slices(); void merge_slices();
/// Template which iterates over all of the LayerRegion for internally containing the argument
template <class T> bool any_internal_region_slice_contains(const T &item) const; template <class T> bool any_internal_region_slice_contains(const T &item) const;
/// Template which iterates over all of the LayerRegion for containing on the bottom the argument
template <class T> bool any_bottom_region_slice_contains(const T &item) const; template <class T> bool any_bottom_region_slice_contains(const T &item) const;
/// Creates the perimeters cummulatively for all layer regions sharing the same parameters influencing the perimeters.
void make_perimeters(); void make_perimeters();
/// Makes fills for all the LayerRegion
void make_fills(); void make_fills();
/// Determines the type of surface (top/bottombridge/bottom/internal) each region is
void detect_surfaces_type(); void detect_surfaces_type();
/// Processes the external surfaces
void process_external_surfaces(); void process_external_surfaces();
protected: protected:
size_t _id; // sequential number of layer, 0-based size_t _id; ///< sequential number of layer, 0-based
PrintObject* _object; PrintObject* _object; ///< Associated PrintObject
/// Constructor
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z); coordf_t slice_z);
/// Destructor
virtual ~Layer(); virtual ~Layer();
/// Deletes all regions
void clear_regions(); void clear_regions();
/// Deletes a specific region
void delete_region(int idx); void delete_region(int idx);
}; };
@ -130,14 +159,22 @@ class SupportLayer : public Layer {
friend class PrintObject; friend class PrintObject;
public: public:
/// Collection of support islands.
/// Populated in SupportMaterial.pm in sub generate_toolpaths
ExPolygonCollection support_islands; ExPolygonCollection support_islands;
/// Collection of support fills.
/// Populated in SupportMaterial.pm in sub generate_toolpaths
ExtrusionEntityCollection support_fills; ExtrusionEntityCollection support_fills;
/// Collection of support interface fills.
/// Populated in SupportMaterial.pm in sub generate_toolpaths
ExtrusionEntityCollection support_interface_fills; ExtrusionEntityCollection support_interface_fills;
protected: protected:
/// Constructor
SupportLayer(size_t id, PrintObject *object, coordf_t height, SupportLayer(size_t id, PrintObject *object, coordf_t height,
coordf_t print_z, coordf_t slice_z) coordf_t print_z, coordf_t slice_z)
: Layer(id, object, height, print_z, slice_z) {}; : Layer(id, object, height, print_z, slice_z) {};
/// Destructor
virtual ~SupportLayer() {}; virtual ~SupportLayer() {};
}; };

View File

@ -8,6 +8,7 @@
namespace Slic3r { namespace Slic3r {
/// Creates a new Flow object with the arguments and the variables of this LayerRegion
Flow Flow
LayerRegion::flow(FlowRole role, bool bridge, double width) const LayerRegion::flow(FlowRole role, bool bridge, double width) const
{ {
@ -21,6 +22,7 @@ LayerRegion::flow(FlowRole role, bool bridge, double width) const
); );
} }
/// Merges this->slices with union_ex, and then repopulates this->slices.surfaces
void void
LayerRegion::merge_slices() LayerRegion::merge_slices()
{ {
@ -33,6 +35,8 @@ LayerRegion::merge_slices()
this->slices.surfaces.push_back(Surface(stInternal, *expoly)); this->slices.surfaces.push_back(Surface(stInternal, *expoly));
} }
/// Creates a new PerimeterGenerator object
/// Which will return the perimeters by its construction
void void
LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{ {
@ -66,25 +70,28 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
g.process(); g.process();
} }
// This function reads layer->slices and lower_layer->slices /// Processes bridges with holes which are internal features.
// and writes this->bridged and this->fill_surfaces, so it's thread-safe. /// Detects same-orientation bridges and merges them.
/// Processes and groups top and bottom surfaces
/// This function reads layer->slices and lower_layer->slices
/// and writes this->bridged and this->fill_surfaces, so it's thread-safe.
void void
LayerRegion::process_external_surfaces() LayerRegion::process_external_surfaces()
{ {
Surfaces &surfaces = this->fill_surfaces.surfaces; Surfaces &surfaces = this->fill_surfaces.surfaces;
for (size_t j = 0; j < surfaces.size(); ++j) { for (size_t j = 0; j < surfaces.size(); ++j) {
Surface &surface = surfaces[j]; // we don't get any reference to surface because it would be invalidated
// by the erase() call below
if (this->layer()->lower_layer != NULL && surface.is_bridge()) { if (this->layer()->lower_layer != NULL && surfaces[j].is_bridge()) {
// If this bridge has one or more holes that are internal surfaces // If this bridge has one or more holes that are internal surfaces
// (thus not visible from the outside), like a slab sustained by // (thus not visible from the outside), like a slab sustained by
// pillars, include them in the bridge in order to have better and // pillars, include them in the bridge in order to have better and
// more continuous bridging. // more continuous bridging.
Polygons &holes = surface.expolygon.holes; for (int i = 0; i < surfaces[j].expolygon.holes.size(); ++i) {
for (int i = 0; i < holes.size(); ++i) {
// reverse the hole and consider it a polygon // reverse the hole and consider it a polygon
Polygon h = holes[i]; Polygon h = surfaces[j].expolygon.holes[i];
h.reverse(); h.reverse();
// Is this hole fully contained in the layer slices? // Is this hole fully contained in the layer slices?
@ -94,10 +101,12 @@ LayerRegion::process_external_surfaces()
if (k == j) continue; if (k == j) continue;
if (h.contains(surfaces[k].expolygon.contour.first_point())) { if (h.contains(surfaces[k].expolygon.contour.first_point())) {
surfaces.erase(surfaces.begin() + k); surfaces.erase(surfaces.begin() + k);
if (j > k) --j;
--k; --k;
} }
} }
Polygons &holes = surfaces[j].expolygon.holes;
holes.erase(holes.begin() + i); holes.erase(holes.begin() + i);
--i; --i;
} }
@ -218,6 +227,8 @@ LayerRegion::process_external_surfaces()
this->fill_surfaces = std::move(new_surfaces); this->fill_surfaces = std::move(new_surfaces);
} }
/// If no solid layers are requested, turns top/bottom surfaces to internal
/// Turns too small internal regions into solid regions according to the user setting
void void
LayerRegion::prepare_fill_surfaces() LayerRegion::prepare_fill_surfaces()
{ {
@ -257,6 +268,7 @@ LayerRegion::prepare_fill_surfaces()
} }
} }
/// Gets smallest area by squaring the Flow's scaled spacing
double double
LayerRegion::infill_area_threshold() const LayerRegion::infill_area_threshold() const
{ {

View File

@ -8,23 +8,24 @@
namespace Slic3r { namespace Slic3r {
/// Struct for the main attributes of a Surface
/// Used for comparing properties
struct SurfaceGroupAttrib struct SurfaceGroupAttrib
{ {
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {}
/// True iff all all three attributes are the same
bool operator==(const SurfaceGroupAttrib &other) const bool operator==(const SurfaceGroupAttrib &other) const
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; }
bool is_solid; bool is_solid; ///< if a solid infill should be used
float fw; float fw; ///< flow Width
// pattern is of type InfillPattern, -1 for an unset pattern. int pattern; ///< pattern is of type InfillPattern, -1 for an unset pattern.
int pattern;
}; };
// Generate infills for a LayerRegion. /// The LayerRegion at this point of time may contain
// The LayerRegion at this point of time may contain /// surfaces of various types (internal/bridge/top/bottom/solid).
// surfaces of various types (internal/bridge/top/bottom/solid). /// The infills are generated on the groups of surfaces with a compatible type.
// The infills are generated on the groups of surfaces with a compatible type. /// Fills an array of ExtrusionPathCollection objects containing the infills generated now
// Fills an array of ExtrusionPathCollection objects containing the infills generated now /// and the thin fills generated by generate_perimeters().
// and the thin fills generated by generate_perimeters().
void void
LayerRegion::make_fill() LayerRegion::make_fill()
{ {

View File

@ -211,16 +211,6 @@ Model::center_instances_around_point(const Pointf &point)
} }
} }
void
Model::align_instances_to_origin()
{
BoundingBoxf3 bb = this->bounding_box();
Pointf new_center = (Pointf)bb.size();
new_center.translate(-new_center.x/2, -new_center.y/2);
this->center_instances_around_point(new_center);
}
void void
Model::translate(coordf_t x, coordf_t y, coordf_t z) Model::translate(coordf_t x, coordf_t y, coordf_t z)
{ {
@ -349,8 +339,8 @@ Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* b
void void
Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
{ {
if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; if (this->objects.size() > 1) throw std::runtime_error("Grid duplication is not supported with multiple objects");
if (this->objects.empty()) throw "No objects!"; if (this->objects.empty()) throw std::runtime_error("No objects!");
ModelObject* object = this->objects.front(); ModelObject* object = this->objects.front();
object->clear_instances(); object->clear_instances();
@ -635,6 +625,16 @@ ModelObject::instance_bounding_box(size_t instance_idx) const
return bb; return bb;
} }
void
Model::align_instances_to_origin()
{
BoundingBoxf3 bb = this->bounding_box();
Pointf new_center = (Pointf)bb.size();
new_center.translate(-new_center.x/2, -new_center.y/2);
this->center_instances_around_point(new_center);
}
void void
ModelObject::align_to_ground() ModelObject::align_to_ground()
{ {

View File

@ -29,223 +29,540 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs; typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs; typedef std::vector<ModelInstance*> ModelInstancePtrs;
// The print bed content.
// Description of a triangular model with multiple materials, multiple instances with various affine transformations /// Model Class representing the print bed content
// and with multiple modifier meshes. /// Description of a triangular model with multiple materials, multiple instances with various affine transformations
// A model groups multiple objects, each object having possibly multiple instances, /// and with multiple modifier meshes.
// all objects may share mutliple materials. /// A model groups multiple objects, each object having possibly multiple instances,
/// all objects may share multiple materials.
class Model class Model
{ {
public: public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
ModelMaterialMap materials; ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). ///< Materials are owned by a model and referenced by objects through t_model_material_id.
ModelObjectPtrs objects; ///< Single material may be shared by multiple models.
ModelObjectPtrs objects;
///< Objects are owned by a model. Each object may have multiple instances
///< , each instance having its own transformation (shift, scale, rotation).
/// Model constructor.
Model(); Model();
/// Model constructor.
/// \param other Model the model to be copied
Model(const Model &other); Model(const Model &other);
/// = Operator overloading.
/// \param other Model the model to be copied
/// \return Model& the current Model to enable operator cascading
Model& operator= (Model other); Model& operator= (Model other);
/// Swap objects and materials with another model.
/// \param other Model the model to be swapped with
void swap(Model &other); void swap(Model &other);
/// Model destructor
~Model(); ~Model();
/// Read a model from file.
/// This function supports the following formats (STL, OBJ, AMF), It auto-detects the file format from the file name suffix.
/// \param input_file std::string the file path expressed in UTF-8
/// \return Model the read Model
static Model read_from_file(std::string input_file); static Model read_from_file(std::string input_file);
/// Create a new object and add it to the current Model.
/// \return ModelObject* a pointer to the new Model
ModelObject* add_object(); ModelObject* add_object();
/// Create a new object and add it to the current Model.
/// This function copies another model object
/// \param other ModelObject the ModelObject to be copied
/// \param copy_volumes if you also want to copy volumes of the other object. By default = true
/// \return ModelObject* a pointer to the new ModelObject
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true); ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
/// Delete a ModelObject from the current Model.
/// \param idx size_t the index of the desired ModelObject
void delete_object(size_t idx); void delete_object(size_t idx);
/// Delete all ModelObjects found in the current Model.
void clear_objects(); void clear_objects();
/// Add a new ModelMaterial to the model.
/// \param material_id t_model_material_id the id of the new ModelMaterial to be added
/// \return ModelMaterial* a pointer to the new ModelMaterial
ModelMaterial* add_material(t_model_material_id material_id); ModelMaterial* add_material(t_model_material_id material_id);
/// Add a new ModelMaterial to the current Model.
/// This function copies another ModelMaterial, It also delete the current ModelMaterial carrying the same
/// material id in the map.
/// \param material_id t_model_material_id the id of the new ModelMaterial to be added
/// \param other ModelMaterial the model material to be copied
/// \return ModelMaterial* a pointer to the new ModelMaterial
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other); ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
/// Get the ModelMaterial object instance having a certain material id.
/// Returns null if the ModelMaterial object instance is not found.
/// \param material_id t_model_material_id the id of the needed ModelMaterial object instance
/// \return ModelMaterial* a pointer to the ModelMaterial object instance or null if not found
ModelMaterial* get_material(t_model_material_id material_id); ModelMaterial* get_material(t_model_material_id material_id);
/// Delete a ModelMaterial carrying a certain material id if found.
/// \param material_id t_model_material_id the id of the ModelMaterial to be deleted
void delete_material(t_model_material_id material_id); void delete_material(t_model_material_id material_id);
/// Delete all the ModelMaterial objects found in the current Model.
void clear_materials(); void clear_materials();
/// Check if any ModelObject has no ModelInstances.
/// \return bool true means there exists at least one ModelObject with no ModelInstance objects
bool has_objects_with_no_instances() const; bool has_objects_with_no_instances() const;
/// Add a new ModelInstance to each ModelObject having no ModelInstance objects
/// \return bool
bool add_default_instances(); bool add_default_instances();
/// Get the bounding box of the transformed instances.
/// \return BoundingBoxf3 a bounding box object.
BoundingBoxf3 bounding_box() const; BoundingBoxf3 bounding_box() const;
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair(); void repair();
/// Center the total bounding box of the instances around a point.
/// This transformation works in the XY plane only and no transformation in Z is performed.
/// \param point pointf object to center the model instances of model objects around
void center_instances_around_point(const Pointf &point); void center_instances_around_point(const Pointf &point);
void align_instances_to_origin(); void align_instances_to_origin();
/// Translate each ModelObject with x, y, z units.
/// \param x coordf_t units in the x direction
/// \param y coordf_t units in the y direction
/// \param z coordf_t units in the z direction
void translate(coordf_t x, coordf_t y, coordf_t z); void translate(coordf_t x, coordf_t y, coordf_t z);
/// Flatten all ModelInstances to a single mesh
/// after performing instance transformations (if the object was rotated or translated).
/// \return TriangleMesh a single TriangleMesh object
TriangleMesh mesh() const; TriangleMesh mesh() const;
/// Flatten all ModelVolumes to a single mesh without any extra processing (i.e. without applying any instance duplication and/or transformation).
/// \return TriangleMesh a single TriangleMesh object
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
/// Arrange ModelInstances. ModelInstances of the same ModelObject do not preserve their relative positions.
/// It uses the given BoundingBoxf as a hint, but falls back to free arrangement if it's not possible to fit all the parts in it.
/// \param sizes Pointfs& number of parts
/// \param dist coordf_t distance between cells
/// \param bb BoundingBoxf* (optional) pointer to the bounding box of the area to fill
/// \param out Pointfs& vector of the output positions
/// \return bool whether the function finished arranging objects or it is impossible to arrange
bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const; bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const;
/// Arrange ModelObjects preserving their ModelInstance count but altering their ModelInstance positions.
/// \param dist coordf_t distance between cells
/// \param bb BoundingBoxf* (optional) pointer to the bounding box of the area to fill
/// \return bool whether the function finished arranging objects or it is impossible to arrange
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
/// Duplicate the ModelInstances of each ModelObject as a whole preserving their relative positions.
/// This function croaks if the duplicated objects do not fit the print bed.
/// \param copies_num size_t number of copies
/// \param dist coordf_t distance between cells
/// \param bb BoundingBoxf* (optional) pointer to the bounding box of the area to fill
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
/// Duplicate each entire ModelInstances of the each ModelObject as a whole.
/// This function will append more instances to each object
/// and then calls arrange_objects() function to automatically rearrange everything.
/// \param copies_num size_t number of copies
/// \param dist coordf_t distance between cells
/// \param bb BoundingBoxf* (optional) pointer to the bounding box of the area to fill
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
/// Duplicate a single ModelObject and arranges them on a grid.
/// Grid duplication is not supported with multiple objects. It throws an exception if there is more than one ModelObject.
/// It also throws an exception if there are no ModelObjects in the current Model.
/// \param x size_t number of duplicates in x direction
/// \param y size_t offset number of duplicates in y direction
/// \param dist coordf_t distance supposed to be between the duplicated ModelObjects
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
/// This function calls the print_info() function of each ModelObject.
void print_info() const; void print_info() const;
/// Check to see if the current Model has characteristics of having multiple parts (usually multiple volumes, etc).
/// \return bool
bool looks_like_multipart_object() const; bool looks_like_multipart_object() const;
/// Take all of the ModelObjects in the current Model and combines them into a single ModelObject
void convert_multipart_object(); void convert_multipart_object();
}; };
// Material, which may be shared across multiple ModelObjects of a single Model. /// Model Material class
/// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial class ModelMaterial
{ {
friend class Model; friend class Model;
public: public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes; t_model_material_attributes attributes;
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration. ///< Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
DynamicPrintConfig config;
DynamicPrintConfig config;
///< Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
/// Get the parent model owing this material
/// \return Model* the onwer Model
Model* get_model() const { return this->model; }; Model* get_model() const { return this->model; };
/// Apply attributes defined by the AMF file format
/// \param attributes t_model_material_attributes the attributes map
void apply(const t_model_material_attributes &attributes); void apply(const t_model_material_attributes &attributes);
private: private:
// Parent, owning this material. Model* model; ///<Parent, owning this material.
Model* model;
/// Constructor
/// \param model the parent model owning this material.
ModelMaterial(Model *model); ModelMaterial(Model *model);
/// Constructor
/// \param model Model* the parent model owning this material.
/// \param other ModelMaterial& the other model material to be copied
ModelMaterial(Model *model, const ModelMaterial &other); ModelMaterial(Model *model, const ModelMaterial &other);
}; };
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), /// Model Object class
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. /// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, /// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// different rotation and different uniform scaling. /// Each ModelObject may be instantiated multiple times, each instance having different placement on the print bed,
/// different rotation and different uniform scaling.
class ModelObject class ModelObject
{ {
friend class Model; friend class Model;
public: public:
std::string name; std::string name;
///< This ModelObject name.
std::string input_file; std::string input_file;
// Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. ///< Input file path.
// Instances are owned by this ModelObject.
ModelInstancePtrs instances; ModelInstancePtrs instances;
// Printable and modifier volumes, each with its material ID and a set of override parameters. ///< Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling.
// ModelVolumes are owned by this ModelObject. ///< Instances are owned by this ModelObject.
ModelVolumePtrs volumes; ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. ///< Printable and modifier volumes, each with its material ID and a set of override parameters.
DynamicPrintConfig config; ///< ModelVolumes are owned by this ModelObject.
// Variation of a layer thickness for spans of Z coordinates.
t_layer_height_ranges layer_height_ranges; DynamicPrintConfig config; ///< Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
t_layer_height_ranges layer_height_ranges; ///< Variation of a layer thickness for spans of Z coordinates.
// Spline based variations of layer thickness for interactive user manipulation // Spline based variations of layer thickness for interactive user manipulation
LayerHeightSpline layer_height_spline; LayerHeightSpline layer_height_spline;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment
when user expects that. */
Pointf3 origin_translation; Pointf3 origin_translation;
///< This vector accumulates the total translation applied to the object by the
///< center_around_origin() method. Callers might want to apply the same translation
///< to new volumes before adding them to this object in order to preserve alignment
///< when user expects that.
// these should be private but we need to expose them via XS until all methods are ported // these should be private but we need to expose them via XS until all methods are ported
BoundingBoxf3 _bounding_box; BoundingBoxf3 _bounding_box;
bool _bounding_box_valid; bool _bounding_box_valid;
/// Get the owning parent Model.
/// \return parent Model* pointer to the owner Model
Model* get_model() const { return this->model; }; Model* get_model() const { return this->model; };
/// Add a new ModelVolume to the current ModelObject. The mesh is copied into the newly created ModelVolume.
/// \param mesh TriangularMesh
/// \return ModelVolume* pointer to the new volume
ModelVolume* add_volume(const TriangleMesh &mesh); ModelVolume* add_volume(const TriangleMesh &mesh);
/// Add a new ModelVolume to the current ModelObject.
/// \param volume the ModelVolume object to be copied
/// \return ModelVolume* pointer to the new volume
ModelVolume* add_volume(const ModelVolume &volume); ModelVolume* add_volume(const ModelVolume &volume);
/// Delete a ModelVolume object.
/// \param idx size_t the index of the ModelVolume to be deleted
void delete_volume(size_t idx); void delete_volume(size_t idx);
/// Delete all ModelVolumes in the
void clear_volumes(); void clear_volumes();
/// Add a new ModelInstance to the current ModelObject.
/// \return ModelInstance* a pointer to the new instance
ModelInstance* add_instance(); ModelInstance* add_instance();
/// Add a new ModelInstance to the current ModelObject.
/// \param instance the ModelInstance to be copied
/// \return ModelInstance* a pointer to the new instance
ModelInstance* add_instance(const ModelInstance &instance); ModelInstance* add_instance(const ModelInstance &instance);
/// Delete a ModelInstance.
/// \param idx size_t the index of the ModelInstance to be deleted
void delete_instance(size_t idx); void delete_instance(size_t idx);
/// Delete the last created ModelInstance object.
void delete_last_instance(); void delete_last_instance();
/// Delete all ModelInstance objects found in the current ModelObject.
void clear_instances(); void clear_instances();
/// Get the bounding box of the *transformed* instances.
BoundingBoxf3 bounding_box(); BoundingBoxf3 bounding_box();
/// Invalidate the bounding box in the current ModelObject.
void invalidate_bounding_box(); void invalidate_bounding_box();
/// Repair all TriangleMesh objects found in each ModelVolume.
void repair(); void repair();
/// Flatten all volumes and instances into a single mesh and applying all the ModelInstances transformations.
TriangleMesh mesh() const; TriangleMesh mesh() const;
/// Flatten all volumes into a single mesh.
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
/// Get the raw bounding box.
/// This function croaks when there are no ModelInstances for this ModelObject
/// \return BoundingBoxf3
BoundingBoxf3 raw_bounding_box() const; BoundingBoxf3 raw_bounding_box() const;
/// Get the bounding box of the *transformed* given instance.
/// \param instance_idx size_t the index of the ModelInstance in the ModelInstance vector
/// \return BoundingBoxf3 the bounding box at the given index
BoundingBoxf3 instance_bounding_box(size_t instance_idx) const; BoundingBoxf3 instance_bounding_box(size_t instance_idx) const;
/// Align the current ModelObject to ground by translating the ModelVolumes in the z axis the needed units.
void align_to_ground(); void align_to_ground();
/// Center the current ModelObject to origin by translating the ModelVolumes
void center_around_origin(); void center_around_origin();
/// Translate the current ModelObject by translating ModelVolumes with (x,y,z) units.
/// This function calls translate(coordf_t x, coordf_t y, coordf_t z) to translate every TriangleMesh in each ModelVolume.
/// \param vector Vectorf3 the translation vector
void translate(const Vectorf3 &vector); void translate(const Vectorf3 &vector);
/// Translate the current ModelObject by translating ModelVolumes with (x,y,z) units.
/// \param x coordf_t the x units
/// \param y coordf_t the y units
/// \param z coordf_t the z units
void translate(coordf_t x, coordf_t y, coordf_t z); void translate(coordf_t x, coordf_t y, coordf_t z);
/// Scale the current ModelObject by scaling its ModelVolumes.
/// This function calls scale(const Pointf3 &versor) to scale every TriangleMesh in each ModelVolume.
/// \param factor float the scaling factor
void scale(float factor); void scale(float factor);
/// Scale the current ModelObject by scaling its ModelVolumes.
/// \param versor Pointf3 the scaling factor in a 3d vector.
void scale(const Pointf3 &versor); void scale(const Pointf3 &versor);
/// Scale the current ModelObject to fit by altering the scaling factor of ModelInstances.
/// It operates on the total size by duplicating the object according to all the instances.
/// \param size Sizef3 the size vector
void scale_to_fit(const Sizef3 &size); void scale_to_fit(const Sizef3 &size);
/// Rotate the current ModelObject by rotating ModelVolumes.
/// \param angle float the angle in radians
/// \param axis Axis the axis to be rotated around
void rotate(float angle, const Axis &axis); void rotate(float angle, const Axis &axis);
/// Mirror the current Model around a certain axis.
/// \param axis Axis enum member
void mirror(const Axis &axis); void mirror(const Axis &axis);
/// Transform the current ModelObject by a certain ModelInstance attributes.
/// Inverse transformation is applied to all the ModelInstances, so that the final size/position/rotation of the transformed objects doesn't change.
/// \param instance ModelInstance the instance used to transform the current ModelObject
/// \param dont_translate bool whether to translate the current ModelObject or not
void transform_by_instance(ModelInstance instance, bool dont_translate = false); void transform_by_instance(ModelInstance instance, bool dont_translate = false);
/// Get the number of the unique ModelMaterial objects in this ModelObject.
/// \return size_t the materials count
size_t materials_count() const; size_t materials_count() const;
/// Get the number of the facets found in all ModelVolume objects in this ModelObject which are not modifier volumes.
/// \return size_t the facets count
size_t facets_count() const; size_t facets_count() const;
/// Know whether there exists a TriangleMesh object that needed repair or not.
/// \return bool
bool needed_repair() const; bool needed_repair() const;
/// Cut (Slice) the current ModelObject along a certain axis at a certain coordinate.
/// \param axis Axis the axis to slice at (X = 0 or Y or Z)
/// \param z coordf_t the point at the certain axis to cut(slice) the Model at
/// \param model Model* pointer to the Model which will get the resulting objects added
void cut(Axis axis, coordf_t z, Model* model) const; void cut(Axis axis, coordf_t z, Model* model) const;
/// Split the meshes of the ModelVolume in this ModelObject if there exists only one ModelVolume in this ModelObject.
/// \param new_objects ModelObjectPtrs the generated ModelObjects after the single ModelVolume split
void split(ModelObjectPtrs* new_objects); void split(ModelObjectPtrs* new_objects);
/// Update the bounding box in this ModelObject
void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS
/// Print the current info of this ModelObject
void print_info() const; void print_info() const;
private: private:
// Parent object, owning this ModelObject. Model* model; ///< Parent object, owning this ModelObject.
Model* model;
/// Constructor
/// \param model Model the owner Model.
ModelObject(Model *model); ModelObject(Model *model);
/// Constructor
/// \param model Model the owner Model.
/// \param other ModelObject the other ModelObject to be copied
/// \param copy_volumes bool whether to also copy its volumes or not, by default = true
ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true); ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true);
/// = Operator overloading
/// \param other ModelObject the other ModelObject to be copied
/// \return ModelObject& the current ModelObject to enable operator cascading
ModelObject& operator= (ModelObject other); ModelObject& operator= (ModelObject other);
/// Swap the attributes between another ModelObject
/// \param other ModelObject the other ModelObject to be swapped with.
void swap(ModelObject &other); void swap(ModelObject &other);
/// Destructor
~ModelObject(); ~ModelObject();
}; };
// An object STL, or a modifier volume, over which a different set of parameters shall be applied. /// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject. /// ModelVolume instances are owned by a ModelObject.
class ModelVolume class ModelVolume
{ {
friend class ModelObject; friend class ModelObject;
public: public:
std::string name;
// The triangular model.
TriangleMesh mesh;
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
// Is it an object to be printed, or a modifier volume?
bool modifier;
// A parent object owning this modifier volume. std::string name; ///< Name of this ModelVolume object
TriangleMesh mesh; ///< The triangular model.
DynamicPrintConfig config;
///< Configuration parameters specific to an object model geometry or a modifier volume,
///< overriding the global Slic3r settings and the ModelObject settings.
bool modifier; ///< Is it an object to be printed, or a modifier volume?
/// Get the parent object owning this modifier volume.
/// \return ModelObject* pointer to the owner ModelObject
ModelObject* get_object() const { return this->object; }; ModelObject* get_object() const { return this->object; };
/// Get the material id of this ModelVolume object
/// \return t_model_material_id the material id string
t_model_material_id material_id() const; t_model_material_id material_id() const;
/// Set the material id to this ModelVolume object
/// \param material_id t_model_material_id the id of the material
void material_id(t_model_material_id material_id); void material_id(t_model_material_id material_id);
/// Get the current ModelMaterial in this ModelVolume object
/// \return ModelMaterial* a pointer to the ModelMaterial
ModelMaterial* material() const; ModelMaterial* material() const;
/// Add a new ModelMaterial to this ModelVolume
/// \param material_id t_model_material_id the id of the material to be added
/// \param material ModelMaterial the material to be coppied
void set_material(t_model_material_id material_id, const ModelMaterial &material); void set_material(t_model_material_id material_id, const ModelMaterial &material);
/// Add a unique ModelMaterial to the current ModelVolume
/// \return ModelMaterial* pointer to the new ModelMaterial
ModelMaterial* assign_unique_material(); ModelMaterial* assign_unique_material();
private: private:
// Parent object owning this ModelVolume. ///< Parent object owning this ModelVolume.
ModelObject* object; ModelObject* object;
///< The id of the this ModelVolume
t_model_material_id _material_id; t_model_material_id _material_id;
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
/// \param mesh TriangleMesh the mesh of the new ModelVolume object
ModelVolume(ModelObject *object, const TriangleMesh &mesh); ModelVolume(ModelObject *object, const TriangleMesh &mesh);
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
/// \param other ModelVolume the ModelVolume object to be copied
ModelVolume(ModelObject *object, const ModelVolume &other); ModelVolume(ModelObject *object, const ModelVolume &other);
/// = Operator overloading
/// \param other ModelVolume a volume to be copied in the current ModelVolume object
/// \return ModelVolume& the current ModelVolume to enable operator cascading
ModelVolume& operator= (ModelVolume other); ModelVolume& operator= (ModelVolume other);
/// Swap attributes between another ModelVolume object
/// \param other ModelVolume the other volume object
void swap(ModelVolume &other); void swap(ModelVolume &other);
}; };
// A single instance of a ModelObject. /// A single instance of a ModelObject.
// Knows the affine transformation of an object. /// Knows the affine transformation of an object.
class ModelInstance class ModelInstance
{ {
friend class ModelObject; friend class ModelObject;
public: public:
double rotation; // Rotation around the Z axis, in radians around mesh center point double rotation; ///< Rotation around the Z axis, in radians around mesh center point
double scaling_factor; double scaling_factor; ///< scaling factor
Pointf offset; // in unscaled coordinates Pointf offset; ///< offset in unscaled coordinates
/// Get the owning ModelObject
/// \return ModelObject* pointer to the owner ModelObject
ModelObject* get_object() const { return this->object; }; ModelObject* get_object() const { return this->object; };
// To be called on an external mesh /// Transform an external TriangleMesh object
/// \param mesh TriangleMesh* pointer to the the mesh
/// \param dont_translate bool whether to translate the mesh or not
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
/// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
/// \param mesh TriangleMesh* pointer to the the mesh
/// \param dont_translate bool whether to translate the bounding box or not
/// \return BoundingBoxf3 the bounding box after transformation
BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const; BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const;
// Transform an external bounding box.
/// Transform an external bounding box.
/// \param bbox BoundingBoxf3 the bounding box to be transformed
/// \param dont_translate bool whether to translate the bounding box or not
/// \return BoundingBoxf3 the bounding box after transformation
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
// To be called on an external polygon. It does not translate the polygon, only rotates and scales.
/// Rotate or scale an external polygon. It does not translate the polygon.
/// \param polygon Polygon* a pointer to the Polygon
void transform_polygon(Polygon* polygon) const; void transform_polygon(Polygon* polygon) const;
private: private:
// Parent object, owning this instance. ModelObject* object; ///< Parent object, owning this instance.
ModelObject* object;
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
ModelInstance(ModelObject *object); ModelInstance(ModelObject *object);
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
/// \param other ModelInstance an instance to be copied in the new ModelInstance object
ModelInstance(ModelObject *object, const ModelInstance &other); ModelInstance(ModelObject *object, const ModelInstance &other);
/// = Operator overloading
/// \param other ModelInstance an instance to be copied in the current ModelInstance object
/// \return ModelInstance& the current ModelInstance to enable operator cascading
ModelInstance& operator= (ModelInstance other); ModelInstance& operator= (ModelInstance other);
/// Swap attributes between another ModelInstance object
/// \param other ModelInstance& the other instance object
void swap(ModelInstance &other); void swap(ModelInstance &other);
}; };

View File

@ -13,17 +13,17 @@ MotionPlanner::MotionPlanner(const ExPolygons &islands)
: initialized(false) : initialized(false)
{ {
ExPolygons expp; ExPolygons expp;
for (ExPolygons::const_iterator island = islands.begin(); island != islands.end(); ++island) for (const ExPolygon &island : islands)
island->simplify(SCALED_EPSILON, &expp); island.simplify(MP_INNER_MARGIN/10, &expp);
for (ExPolygons::const_iterator island = expp.begin(); island != expp.end(); ++island) for (const ExPolygon &island : expp)
this->islands.push_back(MotionPlannerEnv(*island)); this->islands.push_back(MotionPlannerEnv(island));
} }
MotionPlanner::~MotionPlanner() MotionPlanner::~MotionPlanner()
{ {
for (std::vector<MotionPlannerGraph*>::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph) for (MotionPlannerGraph* graph : this->graphs)
delete *graph; delete graph;
} }
size_t size_t
@ -40,20 +40,20 @@ MotionPlanner::initialize()
// loop through islands in order to create inner expolygons and collect their contours // loop through islands in order to create inner expolygons and collect their contours
Polygons outer_holes; Polygons outer_holes;
for (std::vector<MotionPlannerEnv>::iterator island = this->islands.begin(); island != this->islands.end(); ++island) { for (MotionPlannerEnv &island : this->islands) {
// generate the internal env boundaries by shrinking the island // generate the internal env boundaries by shrinking the island
// we'll use these inner rings for motion planning (endpoints of the Voronoi-based // we'll use these inner rings for motion planning (endpoints of the Voronoi-based
// graph, visibility check) in order to avoid moving too close to the boundaries // graph, visibility check) in order to avoid moving too close to the boundaries
island->env = offset_ex(island->island, -MP_INNER_MARGIN); island.env = offset_ex(island.island, -MP_INNER_MARGIN);
// island contours are holes of our external environment // island contours are holes of our external environment
outer_holes.push_back(island->island.contour); outer_holes.push_back(island.island.contour);
} }
// generate outer contour as bounding box of everything // generate outer contour as bounding box of everything
BoundingBox bb; BoundingBox bb;
for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour) for (const Polygon contour : outer_holes)
bb.merge(contour->bounding_box()); bb.merge(contour.bounding_box());
// grow outer contour // grow outer contour
Polygons contour = offset(bb.polygon(), +MP_OUTER_MARGIN*2); Polygons contour = offset(bb.polygon(), +MP_OUTER_MARGIN*2);
@ -166,6 +166,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
} }
} }
// Perform some quick simplification (simplify_by_visibility() would make this
// unnecessary, but this is much faster)
polyline.simplify(MP_INNER_MARGIN/10);
// remove unnecessary vertices // remove unnecessary vertices
// Note: this is computationally intensive and does not look very necessary // Note: this is computationally intensive and does not look very necessary
// now that we prune the endpoints with the logic above, // now that we prune the endpoints with the logic above,

View File

@ -17,8 +17,6 @@ class MultiPoint
Points points; Points points;
operator Points() const; operator Points() const;
MultiPoint() {};
explicit MultiPoint(const Points &_points): points(_points) {};
void scale(double factor); void scale(double factor);
void translate(double x, double y); void translate(double x, double y);
void translate(const Point &vector); void translate(const Point &vector);
@ -48,6 +46,11 @@ class MultiPoint
std::string dump_perl() const; std::string dump_perl() const;
static Points _douglas_peucker(const Points &points, const double tolerance); static Points _douglas_peucker(const Points &points, const double tolerance);
protected:
MultiPoint() {};
explicit MultiPoint(const Points &_points): points(_points) {};
~MultiPoint() = default;
}; };
} // namespace Slic3r } // namespace Slic3r

View File

@ -349,11 +349,10 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
// get non-overhang paths by intersecting this loop with the grown lower slices // get non-overhang paths by intersecting this loop with the grown lower slices
{ {
Polylines polylines = intersection_pl(loop->polygon, this->_lower_slices_p); const Polylines polylines = intersection_pl(loop->polygon, this->_lower_slices_p);
for (const Polyline &polyline : polylines) {
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
ExtrusionPath path(role); ExtrusionPath path(role);
path.polyline = *polyline; path.polyline = polyline;
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
path.height = this->layer_height; path.height = this->layer_height;
@ -365,11 +364,10 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
// outside the grown lower slices (thus where the distance between // outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter // the loop centerline and original lower slices is >= half nozzle diameter
{ {
Polylines polylines = diff_pl(loop->polygon, this->_lower_slices_p); const Polylines polylines = diff_pl(loop->polygon, this->_lower_slices_p);
for (const Polyline &polyline : polylines) {
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
ExtrusionPath path(erOverhangPerimeter); ExtrusionPath path(erOverhangPerimeter);
path.polyline = *polyline; path.polyline = polyline;
path.mm3_per_mm = this->_mm3_per_mm_overhang; path.mm3_per_mm = this->_mm3_per_mm_overhang;
path.width = this->overhang_flow.width; path.width = this->overhang_flow.width;
path.height = this->overhang_flow.height; path.height = this->overhang_flow.height;

Some files were not shown because too many files have changed in this diff Show More