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 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`_

View File

@ -2,7 +2,6 @@ language: perl
before_install:
- sh package/linux/travis-decrypt-key
install:
- export LDLOADLIBS=-lstdc++
- export BOOST_DIR=$HOME/boost_1_63_0
- export SLIC3R_STATIC=1
- 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
* [Low Effort tasks](https://github.com/alexrj/Slic3r/labels/Low%20Effort): pick one of them!
* [More available tasks](https://github.com/alexrj/Slic3r/milestone/31): let's discuss together before you start working on them
* Please comment in the related github issue that you are working on it so that other people know.
* Please comment in the related GitHub issue that you are working on it so that other people know.
* Please comment in the related GitHub issue that you are working on it so that other people know.
* Contribute to the [Manual](http://manual.slic3r.org/)! (see its [GitHub repository](https://github.com/alexrj/Slic3r-Manual))
* You can also find us in #slic3r on [FreeNode](https://webchat.freenode.net): talk to _Sound_, _LoH_ or the other members of the Slic3r community.
* Add an [issue](https://github.com/alexrj/Slic3r/issues) to the GitHub tracker if it isn't already present.

View File

@ -7,6 +7,7 @@ environment:
SLIC3R_STATIC: 1
SLIC3R_VERSION: 1.3.0
BOOST_DIR: C:\dev\boost_1_63_0
WXDIR: C:\dev\wxwidgets
WXSHARED: SHARED=0
FORCE_WX_BUILD: 0
FORCE_BOOST_REINSTALL: 0
@ -14,32 +15,37 @@ environment:
secure: QfeTOSKXz1uFCEACqFKLNw==
UPLOAD_USER:
secure: fYPwnI3p6HNR+eMRJR3JfmyNolFn+Uc0MUn2bBXp9uU=
matrix:
- ARCH: 64bit
- ARCH: 32bit
install:
- 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%
- ps: "& package/win/appveyor_preinstall.ps1"
cache:
- C:\Users\appveyor\boost.1.63.0.7z
- C:\Users\appveyor\local-lib.7z
- C:\Strawberry\perl\site
- C:\Users\appveyor\freeglut.7z
- C:\users\appveyor\strawberry.msi
- C:\Users\appveyor\local-lib-64bit.7z
- C:\Users\appveyor\local-lib-32bit.7z
- C:\Users\appveyor\freeglut.64bit.7z
- C:\Users\appveyor\freeglut.32bit.7z
- C:\users\appveyor\strawberry.64bit.msi
- C:\users\appveyor\strawberry.32bit.msi
- C:\Users\appveyor\winscp.zip
- 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:
- 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:
- path: .\slic3r*zip
name: slic3r-dev
deploy_script:
- ps: "& package/win/appveyor_deploy.ps1"
- ps: "cd C:/projects/slic3r; & package/win/appveyor_deploy.ps1"
on_success:
- ps:
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:
- ps:

View File

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

View File

@ -95,7 +95,8 @@ sub load {
# legacy syntax of load()
my $config = $class->new;
$config->_load(Slic3r::encode_path($file));
$config->_load($file);
return $config;
}
@ -103,7 +104,7 @@ sub save {
my $self = shift;
my ($file) = @_;
return $self->_save(Slic3r::encode_path($file));
return $self->_save($file);
}
# 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);
}
# 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
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
:filedialog :font);
use Wx::Event qw(EVT_MENU);
BEGIN {
# 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;
# 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 $threads;
our @cb;
@ -84,23 +84,26 @@ our $Settings = {
autocenter => 1,
invert_zoom => 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},
color_toolpaths_by => 'role',
tabbed_preset_editors => 1,
},
};
our $have_button_icons = &Wx::wxVERSION_STRING =~ / (?:2\.9\.[1-9]|3\.)/;
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);
$small_bold_font->SetPointSize(11) if !&Wx::wxMSW;
$small_bold_font->SetPointSize(11) if &Wx::wxMAC;
$small_bold_font->SetWeight(wxFONTWEIGHT_BOLD);
our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$medium_font->SetPointSize(12);
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 $DLP_projection_screen;
@ -437,6 +440,30 @@ sub scan_serial_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 {
my ($self, $menuItem, $icon) = @_;

View File

@ -330,6 +330,19 @@ sub set_viewport_from_scene {
$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,
# zoom to volumes.
sub select_view {
@ -1441,7 +1454,7 @@ sub load_print_object_toolpaths {
}
}
$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);
} else {
$add->($layerm->fills, $top_z, $copy, $color);

View File

@ -40,20 +40,20 @@ sub new {
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
my %presets = map { $_->name => $_ } wxTheApp->presets('printer');
my %presets = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
# remove printers that already exist
my @panels = $self->print_panels;
delete $presets{$_} for map $_->printer_name, @panels;
foreach my $preset_name (sort keys %presets) {
my $config = $presets{$preset_name}->dirty_config;
next if !$config->serial_port;
my $preset = $presets{$preset_name};
next if !$preset->dirty_config->serial_port;
my $id = &Wx::NewId();
$menu->Append($id, $preset_name);
EVT_MENU($menu, $id, sub {
$self->add_printer($preset_name, $config);
$self->add_printer($preset);
});
}
$self->PopupMenu($menu, $btn->GetPosition);
@ -100,10 +100,10 @@ sub OnActivate {
# get all available presets
my %presets = ();
{
my %all = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
my %configs = map { my $name = $_; $name => $all{$name}->load_config } keys %all;
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
foreach my $preset (@{wxTheApp->presets->{printer}}) {
$preset->load_config;
next if !$preset->dirty_config->serial_port;
$presets{$preset->name} = $preset;
}
# decide which ones we want to keep
@ -124,7 +124,7 @@ sub OnActivate {
# enable printers whose port is available
my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports;
$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) {
# enable currently selected printer if it is configured
@ -140,7 +140,7 @@ sub OnActivate {
$self->{sizer}->DetachWindow($panel);
$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
$self->{text_no_printers}->Show(!%presets);
@ -156,16 +156,16 @@ sub OnActivate {
}
sub add_printer {
my ($self, $printer_name, $config) = @_;
my ($self, $preset) = @_;
# check that printer doesn't exist already
foreach my $panel ($self->print_panels) {
if ($panel->printer_name eq $printer_name) {
if ($panel->printer_name eq $preset->name) {
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->Layout;

View File

@ -3,6 +3,7 @@ use strict;
use warnings;
use utf8;
use List::Util qw(first);
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer
:textctrl :font :systemsettings);
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
sub new {
my ($class, $parent, $printer_name, $config) = @_;
my ($class, $parent, $preset) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]);
$self->printer_name($printer_name || 'Printer');
$self->config($config);
$self->printer_name($preset->name);
$self->config($preset->dirty_config);
$self->manual_control_config({
xy_travel_speed => 130,
z_travel_speed => 10,
@ -103,7 +104,7 @@ sub new {
}
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->update_serial_ports;
$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);
{
$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"]);
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
$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
$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 {
$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);
__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 {
my ($class, $parent, $job) = @_;
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->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);
EVT_ERASE_BACKGROUND($self, sub {
my ($self, $event) = @_;
@ -574,13 +595,18 @@ sub new {
if ($job->printed) {
$text->SetForegroundColour($Slic3r::GUI::grey);
}
$text->SetToolTipString("Queued on " . $self->queued)
if $text->can('SetToolTipString');
$left_sizer->Add($text, 0, wxEXPAND, 0);
}
{
my $filament_stats = join "\n",
my $stats = join "\n",
map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)",
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);
if ($job->printed && !$job->printing) {
$text->SetForegroundColour($Slic3r::GUI::grey);

View File

@ -8,9 +8,10 @@ use utf8;
use File::Basename qw(basename dirname);
use List::Util qw(min);
use Slic3r::Geometry qw(X Y Z);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
:font :icon wxTheApp);
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
use Wx qw(:frame :bitmap :id :misc :panel :sizer :menu :dialog :filedialog
:font :icon :aui wxTheApp);
use Wx::AUI;
use Wx::Event qw(EVT_CLOSE EVT_AUINOTEBOOK_PAGE_CHANGED EVT_AUINOTEBOOK_PAGE_CLOSE);
use base 'Wx::Frame';
our $qs_last_input_file;
@ -28,6 +29,7 @@ sub new {
}
$self->{loaded} = 0;
$self->{preset_editor_tabs} = {}; # group => panel
# initialize tabpanel and menubar
$self->_init_tabpanel;
@ -92,15 +94,28 @@ sub new {
sub _init_tabpanel {
my ($self) = @_;
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage;
$self->{tabpanel} = my $panel = Wx::AuiNotebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxAUI_NB_TOP);
EVT_AUINOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetPage($self->{tabpanel}->GetSelection);
$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->{controller} = Slic3r::GUI::Controller->new($panel), "Controller")
unless ($Slic3r::GUI::Settings->{_}{no_controller});
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
}
sub _init_menubar {
@ -109,60 +124,60 @@ sub _init_menubar {
# File menu
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};
}, 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};
}, undef, 'map_add.png');
$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;
}, 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;
}, 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;
}, 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;
}, undef, 'lorry_go.png');
$fileMenu->AppendSeparator();
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 {
$self->quick_slice;
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
});
}, 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 {
$self->quick_slice(save_as => 1);
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
});
}, 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 {
$self->quick_slice(reslice => 1);
});
}, undef, 'cog_go.png');
$repeat->Enable(0);
$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);
}, undef, 'shape_handles.png');
$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;
}, undef, 'wrench.png');
$fileMenu->AppendSeparator();
# 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;
}, wxID_PREFERENCES);
$fileMenu->AppendSeparator();
$self->_append_menu_item($fileMenu, "&Quit", 'Quit Slic3r', sub {
wxTheApp->append_menu_item($fileMenu, "&Quit", 'Quit Slic3r', sub {
$self->Close(0);
}, wxID_EXIT);
}
@ -174,40 +189,43 @@ sub _init_menubar {
$self->{plater_menu} = Wx::Menu->new;
{
my $selectMenu = $self->{plater_select_menu} = Wx::Menu->new;
my $selectMenuItem = $self->{plater_menu}->AppendSubMenu($selectMenu, "Select", 'Select an object in the plater');
wxTheApp->set_menu_item_icon($selectMenuItem, 'brick.png');
wxTheApp->append_submenu($self->{plater_menu}, "Select", 'Select an object in the plater', $selectMenu, undef, '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;
}, 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;
}, 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->_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;
}, 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;
}, 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;
}, undef, 'brick_go.png');
$self->{object_menu} = $self->{plater}->object_menu;
$self->on_plater_object_list_changed(0);
$self->on_plater_selection_changed(0);
}
# Settings menu
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');
}, 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');
}, 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');
}, undef, 'printer_empty.png');
}
@ -215,15 +233,15 @@ sub _init_menubar {
# View menu
{
$self->{viewMenu} = Wx::Menu->new;
$self->_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' ); });
$self->_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' ); });
$self->_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' ); });
$self->_append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); });
wxTheApp->append_menu_item($self->{viewMenu}, "Top\tCtrl+4" , 'Top View' , sub { $self->select_view('top' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Bottom\tCtrl+5" , 'Bottom View' , sub { $self->select_view('bottom' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Left\tCtrl+6" , 'Left View' , sub { $self->select_view('left' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Right\tCtrl+7" , 'Right View' , sub { $self->select_view('right' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Front\tCtrl+8" , 'Front View' , sub { $self->select_view('front' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Back\tCtrl+9" , 'Back View' , sub { $self->select_view('back' ); });
wxTheApp->append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); });
$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 according to perimeter/infill/support material',
sub {
@ -233,7 +251,7 @@ sub _init_menubar {
},
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 using the configured extruder/filament color',
sub {
@ -253,13 +271,13 @@ sub _init_menubar {
# Window menu
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);
}, 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);
}, undef, 'printer_empty.png') unless ($Slic3r::GUI::Settings->{_}{no_controller});
$self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
}, undef, 'printer_empty.png');
wxTheApp->append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
$self->{plater}->pause_background_process;
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
$self->{plater}->resume_background_process;
@ -269,22 +287,22 @@ sub _init_menubar {
# Help menu
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;
});
$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/');
});
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);
});
$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/');
});
$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;
});
}
@ -310,6 +328,14 @@ sub is_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 {
my ($self, $have_selection) = @_;
@ -458,9 +484,9 @@ sub repair_stl {
}
my $tmesh = Slic3r::TriangleMesh->new;
$tmesh->ReadSTLFile(Slic3r::encode_path($input_file));
$tmesh->ReadSTLFile($input_file);
$tmesh->repair;
$tmesh->WriteOBJFile(Slic3r::encode_path($output_file));
$tmesh->WriteOBJFile($output_file);
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
}
@ -627,19 +653,8 @@ sub select_tab {
# Set a camera direction, zoom to all objects.
sub select_view {
my ($self, $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;

View File

@ -50,7 +50,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config->new_from_defaults(qw(
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->{print} = Slic3r::Print->new;
@ -92,7 +92,7 @@ sub new {
my $on_instances_moved = sub {
$self->on_model_change;
};
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
@ -329,7 +329,6 @@ sub new {
if ($self->{preview3D}) {
$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);
@ -373,9 +372,37 @@ sub new {
{
my $o = $self->{settings_override_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self,
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;
# 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 ]);
$self->{settings_override_config} = Slic3r::Config->new;
$o->set_default_config($self->{settings_override_config});
@ -541,6 +568,14 @@ sub _on_change_combobox {
return 0 if !$self->prompt_unsaved_changes;
}
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);
# 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_panel = $self->{settings_override_panel};
if ($changed) {
# Preserve current options if re-selecting the same preset
$o_config->clear;
}
my $shortcuts = $config->get('shortcuts');
my $overridable = $config->get('overridable');
# Add/remove options (we do it this way for preserving current options)
foreach my $opt_key (@$overridable) {
# Populate option with the default value taken from configuration
# (re-set the override always, because if we here it means user
# switched to this preset or opened/closed the editor, so he expects
# the new values set in the editor to be used).
# Re-populate the override panel with the configured shortcuts
# and the dirty options.
$o_config->clear;
foreach my $opt_key (@$shortcuts, $presets[0]->dirty_options) {
# Don't add shortcut for shortcuts!
next if $opt_key eq 'shortcuts';
$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_fixed_options(\@$overridable);
$o_panel->set_fixed_options(\@$shortcuts);
$o_panel->update_optgroup;
} elsif ($group eq 'printer') {
# 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 {
my ($self, $group, $i) = @_;
my $class = "Slic3r::GUI::PresetEditorDialog::" . ucfirst($group);
my $dlg = $class->new($self);
wxTheApp->CallAfter(sub {
my @presets = $self->selected_presets($group);
my @presets = $self->selected_presets($group);
$dlg->preset_editor->select_preset_by_name($presets[$i // 0]->name);
$dlg->ShowModal;
my $preset_editor;
my $dlg;
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.
$self->load_presets;
$preset_editor->select_preset_by_name($presets[$i // 0]->name);
$preset_editor->on_value_change(sub {
# Re-load the presets in order to toggle the (modified) suffix
$self->load_presets;
# Update shortcuts
$self->_on_select_preset($group);
# 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);
# Select the preset that was last selected in the editor.
$self->select_preset_by_name
($dlg->preset_editor->current_preset->name, $group, $i, 1);
if ($dlg) {
$dlg->Show;
}
});
}
# 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;
# 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}));
my %classes = map { $_ => "Slic3r::GUI::PresetEditor::".ucfirst($_) }
@ -893,6 +951,11 @@ sub load_file {
sub load_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_shape = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape});
my $bed_size = $bed_shape->bounding_box->size;
@ -1353,8 +1416,8 @@ sub config_changed {
$self->{btn_print}->Hide;
}
$self->Layout;
} elsif ($opt_key eq 'octoprint_host') {
if ($config->get('octoprint_host')) {
} elsif ($opt_key eq 'print_host') {
if ($config->get('print_host')) {
$self->{btn_send_gcode}->Show;
} else {
$self->{btn_send_gcode}->Hide;
@ -1506,7 +1569,7 @@ sub pause_background_process {
return 1;
} elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) {
$self->{apply_config_timer}->Stop;
return 1;
return 0; # we didn't actually pause any running thread; need to reschedule
}
return 0;
@ -1677,7 +1740,7 @@ sub on_export_completed {
$message = "File added to print queue";
$do_print = 1;
} 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;
} else {
$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 $printer_name = $current_presets{printer}->[0]->name;
my $printer_panel = $controller->add_printer($printer_name, $self->config);
my $printer_panel = $controller->add_printer($current_presets{printer}->[0], $self->config);
my $filament_stats = $self->{print}->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;
$ua->timeout(5);
my $res = $ua->get(
"http://" . $self->{config}->octoprint_host . "/api/files/local",
'X-Api-Key' => $self->{config}->octoprint_apikey,
);
my $res;
if ($self->{config}->print_host) {
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;
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,
"It looks like a file with the same name already exists in the server. "
. "Shall I overwrite it?",
'OctoPrint', wxICON_WARNING | wxYES | wxNO);
$self->{config}->host_type, wxICON_WARNING | wxYES | wxNO);
if ($dialog->ShowModal() == wxID_NO) {
return;
}
}
} 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);
return;
}
@ -1786,24 +1858,52 @@ sub send_gcode {
$ua->timeout(180);
my $path = Slic3r::encode_path($self->{send_gcode_file});
my $res = $ua->post(
"http://" . $self->{config}->octoprint_host . "/api/files/local",
Content_Type => 'form-data',
'X-Api-Key' => $self->{config}->octoprint_apikey,
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,
],
);
my $filename = basename($self->{print}->output_filepath($main::opt{output} // ''));
my $res;
if($self->{config}->print_host){
if($self->{config}->host_type eq 'octoprint'){
$res = $ua->post(
"http://" . $self->{config}->print_host . "/api/files/local",
Content_Type => 'form-data',
'X-Api-Key' => $self->{config}->octoprint_apikey,
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;
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 {
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);
$self->statusbar->SetStatusText($message);
}
@ -1971,7 +2071,7 @@ sub on_model_change {
if ($count > 1) {
$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->refresh_canvases;
}, undef, undef, wxITEM_CHECK);
@ -2187,6 +2287,9 @@ sub object_list_changed {
$self->{htoolbar}->EnableTool($_, $have_objects)
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 {
@ -2335,108 +2438,112 @@ sub object_menu {
my $frame = $self->GetFrame;
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;
}, 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;
}, 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;
}, 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;
}, undef, 'textfield.png');
$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;
}, 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);
}, 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);
}, undef, 'arrow_rotate_anticlockwise.png');
my $rotateMenu = Wx::Menu->new;
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png');
$frame->_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');
$frame->_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');
$frame->_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');
{
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, 'textfield.png');
}
my $mirrorMenu = Wx::Menu->new;
my $mirrorMenuItem = $menu->AppendSubMenu($mirrorMenu, "Mirror", 'Mirror the selected object');
wxTheApp->set_menu_item_icon($mirrorMenuItem, 'shape_flip_horizontal.png');
$frame->_append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
$self->mirror(X);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub {
$self->mirror(Y);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub {
$self->mirror(Z);
}, undef, 'bullet_blue.png');
{
my $mirrorMenu = Wx::Menu->new;
wxTheApp->append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
$self->mirror(X);
}, undef, 'bullet_red.png');
wxTheApp->append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub {
$self->mirror(Y);
}, undef, 'bullet_green.png');
wxTheApp->append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub {
$self->mirror(Z);
}, 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');
wxTheApp->set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef);
});
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$self->changescale(Z);
}, undef, 'bullet_blue.png');
{
my $scaleMenu = Wx::Menu->new;
wxTheApp->append_menu_item($scaleMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef);
});
wxTheApp->append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X);
}, undef, 'bullet_red.png');
wxTheApp->append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y);
}, undef, 'bullet_green.png');
wxTheApp->append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$self->changescale(Z);
}, 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');
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef, 1);
});
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X, 1);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y, 1);
}, undef, 'bullet_green.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;
wxTheApp->append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef, 1);
});
wxTheApp->append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X, 1);
}, undef, 'bullet_red.png');
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y, 1);
}, undef, 'bullet_green.png');
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$self->changescale(Z, 1);
}, 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;
}, 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;
}, undef, 'package.png');
$frame->_append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub {
$self->object_layers_dialog;
}, undef, 'cog.png');
$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;
}, undef, 'cog.png');
$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;
}, 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;
}, 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;
}, 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;
use Wx::DND;
use base 'Wx::FileDropTarget';
@ -2546,7 +2668,7 @@ use base 'Wx::Dialog';
sub new {
my $class = shift;
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]);
$self->{filename} = $filename;
@ -2555,7 +2677,7 @@ sub new {
my $optgroup;
$optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $self,
title => 'Send to OctoPrint',
title => 'Send to Server',
on_change => sub {
my ($opt_id) = @_;

View File

@ -60,6 +60,8 @@ sub new {
} elsif ($key == 68 || $key == 317) {
$slider->SetValue($slider->GetValue - 1);
$self->set_z($self->{layers_z}[$slider->GetValue]);
} else {
$event->Skip;
}
});
@ -187,21 +189,21 @@ sub new {
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
$self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
{
# In order to zoom around the mouse point we need to translate
# the camera target. This math is almost there but not perfect yet...
my $camera_bb_size = $self->_camera_bb->size;
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
# calculate the zooming center in pixel coordinates relative to the viewport center
my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #-
# calculate where this point will end up after applying the new zoom
my $vec2 = $vec->clone;
$vec2->scale($old_zoom / $self->_zoom);
# move the camera target by the difference of the two positions
$self->_camera_target->translate(
-($vec->x - $vec2->x) * $camera_bb_size->x / $size->x,
@ -217,6 +219,20 @@ sub new {
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 {
my ($self, $e) = @_;

View File

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

View File

@ -163,35 +163,44 @@ sub new {
my $item = $event->GetItem;
my $frame = $self->GetFrame;
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');
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef, 1);
});
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X, 1);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y, 1);
}, undef, 'bullet_green.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 $rotateMenu = Wx::Menu->new;
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png');
$frame->_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');
$frame->_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');
$frame->_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');
{
my $scaleMenu = Wx::Menu->new;
wxTheApp->append_menu_item($scaleMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes',
sub { $self->changescale(undef, 0) });
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');
wxTheApp->append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis',
sub { $self->changescale(Y, 0) }, undef, 'bullet_green.png');
wxTheApp->append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis',
sub { $self->changescale(Z, 0) }, 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;
wxTheApp->append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes',
sub { $self->changescale(undef, 1) });
wxTheApp->append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis',
sub { $self->changescale(X, 1) }, undef, 'bullet_red.png');
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis',
sub { $self->changescale(Y, 1) }, undef, 'bullet_green.png');
wxTheApp->append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis',
sub { $self->changescale(Z, 1) }, 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');
}
{
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);
});
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
$new_volume->config->set_ifndef('extruder', 0);
$self->{parts_changed} = 1;
$self->_parts_changed($self->{model_object}->volumes_count-1);
}
@ -436,6 +444,7 @@ sub on_btn_delete {
sub _parts_changed {
my ($self, $selected_volume_idx) = @_;
$self->{parts_changed} = 1;
$self->reload_tree($selected_volume_idx);
if ($self->{canvas}) {
$self->{canvas}->reset_objects;

View File

@ -35,6 +35,9 @@ sub new {
# notify tabs
$self->{layers}->Closing;
# save window size
wxTheApp->save_window_pos($self, "object_settings");
$self->EndModal(wxID_OK);
$self->Destroy;
});
@ -46,6 +49,8 @@ sub new {
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
wxTheApp->restore_window_pos($self, "object_settings");
return $self;
}
@ -58,16 +63,6 @@ sub PartSettingsChanged {
my ($self) = @_;
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;

View File

@ -34,7 +34,8 @@ sub new {
$self->{default_config} = Slic3r::Config->new;
$self->{config} = Slic3r::Config->new;
$self->{on_change} = $params{on_change};
$self->{editable} = 1;
$self->{can_add} = 1;
$self->{can_delete} = 1;
$self->{fixed_options} = {};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
@ -52,27 +53,32 @@ sub new {
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
my $last_cat = '';
# create category submenus
my %categories = (); # category => submenu
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 ($last_cat && $cat ne $last_cat) {
$menu->AppendSeparator;
}
$last_cat = $cat;
$icon = $icons{$cat};
$categories{$cat} //= Wx::Menu->new;
}
}
# append submenus to main menu
foreach my $cat (sort keys %categories) {
wxTheApp->append_submenu($menu, $cat, "", $categories{$cat}, undef, $icons{$cat});
}
# append options to submenus
foreach my $opt_key (@{$self->{options}}) {
my $cat = $Slic3r::Config::Options->{$opt_key}{category} or next;
my $menuItem = $menu->Append($id, $self->{option_labels}{$opt_key});
wxTheApp->set_menu_item_icon($menuItem, $icon) if $icon;
EVT_MENU($menu, $id, sub {
my $cb = sub {
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
$self->update_optgroup;
$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);
$menu->Destroy;
@ -84,7 +90,9 @@ sub new {
}
$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->update_optgroup;
@ -109,17 +117,11 @@ sub set_config {
sub set_opt_keys {
my ($self, $opt_keys) = @_;
# sort options by category+label
# sort options by label
$self->{option_labels} = {};
foreach my $opt_key (@$opt_keys) {
my $def = $Slic3r::Config::Options->{$opt_key} or next;
if (!$def->{category}) {
#printf "Skipping %s\n", $opt_key;
next;
}
$self->{option_labels}{$opt_key} = sprintf '%s > %s',
$def->{category},
$def->{full_label} // $def->{label};
$self->{option_labels}{$opt_key} = $def->{full_label} // $def->{label};
};
$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);
return if !defined $self->{config};
$self->{btn_add}->Show($self->{editable});
$self->{btn_add}->Show($self->{can_add});
my %categories = ();
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} };
# 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),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
@ -209,11 +211,27 @@ sub disable {
$self->Disable;
}
# Shows or hides the Add button.
# Shows or hides the Add/Delete buttons.
sub set_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;

View File

@ -65,11 +65,11 @@ sub new {
default => $Slic3r::GUI::Settings->{_}{threads},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_controller',
opt_id => 'tabbed_preset_editors',
type => 'bool',
label => 'Disable USB/serial connection',
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.',
default => $Slic3r::GUI::Settings->{_}{no_controller},
label => 'Display profile editors as tabs',
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->{_}{tabbed_preset_editors},
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@ -88,7 +88,7 @@ sub new {
sub _accept {
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.");
}

View File

@ -94,7 +94,7 @@ sub prompt_unsaved_changes {
if ($res == wxID_CANCEL) {
return 0;
} 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) {
$self->dismiss_changes;
return 1;
@ -104,20 +104,29 @@ sub prompt_unsaved_changes {
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 {
my ($self, $name, $parent) = @_;
my ($self, $opt_keys) = @_;
if (!$name) {
my $default_name = $self->default ? 'Untitled' : $self->name;
$default_name =~ s/\.ini$//i;
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
default => $default_name,
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
);
return 0 unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name;
}
return $self->save_as($self->name, $opt_keys);
}
sub save_as {
my ($self, $name, $opt_keys) = @_;
$self->rename($name);
@ -125,8 +134,15 @@ sub save {
die "Calling save() without setting filename";
}
$self->_config->clear;
$self->_config->apply($self->_dirty_config);
if ($opt_keys) {
$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);
wxTheApp->load_presets;
@ -167,6 +183,8 @@ sub dirty_config {
sub load_config {
my ($self) = @_;
return $self->_config if $self->_loaded;
my @keys = $self->_group_class->options;
my @extra_keys = $self->_group_class->overriding_options;

View File

@ -87,7 +87,7 @@ sub new {
});
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 });
@ -123,13 +123,20 @@ sub save_preset {
$self->{treectrl}->SetFocus;
my $preset = $self->current_preset;
$preset->save(undef, $self);
$preset->save_prompt($self);
$self->load_presets;
$self->select_preset_by_name($preset->name);
$self->{on_save_preset}->($self->name, $preset) if $self->{on_save_preset};
return 1;
}
sub on_save_preset {
my ($self, $cb) = @_;
$self->{on_save_preset} = $cb;
}
sub on_value_change {
my ($self, $cb) = @_;
$self->{on_value_change} = $cb;
@ -141,10 +148,12 @@ sub on_value_change {
sub _on_value_change {
my ($self, $opt_key) = @_;
$self->current_preset->_dirty_config->apply($self->config);
$self->{on_value_change}->($opt_key) if $self->{on_value_change};
$self->load_presets;
$self->_update;
wxTheApp->CallAfter(sub {
$self->current_preset->_dirty_config->apply($self->config);
$self->{on_value_change}->($opt_key) if $self->{on_value_change};
$self->load_presets;
$self->_update;
});
}
sub _update {}
@ -155,7 +164,7 @@ sub select_preset {
my ($self, $i, $force) = @_;
$self->{presets_choice}->SetSelection($i);
$self->on_select_preset($force);
$self->_on_select_preset($force);
}
sub select_preset_by_name {
@ -163,8 +172,12 @@ sub select_preset_by_name {
my $presets = wxTheApp->presets->{$self->name};
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->on_select_preset($force);
$self->_on_select_preset($force);
}
sub prompt_unsaved_changes {
@ -175,6 +188,11 @@ sub prompt_unsaved_changes {
}
sub on_select_preset {
my ($self, $cb) = @_;
$self->{on_select_preset} = $cb;
}
sub _on_select_preset {
my ($self, $force) = @_;
# This method is called:
@ -186,7 +204,12 @@ sub on_select_preset {
my $preset = wxTheApp->presets->{$self->name}->[$self->{presets_choice}->GetSelection];
# 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 (!$force && !$self->prompt_unsaved_changes) {
@ -203,9 +226,7 @@ sub on_select_preset {
# prompted and chose to discard changes.
$self->load_presets;
$preset->load_config if !$preset->_loaded;
$self->config->clear;
$self->config->apply($preset->dirty_config);
$self->reload_preset;
eval {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
@ -215,12 +236,13 @@ sub on_select_preset {
$self->_update;
$self->on_preset_loaded;
$self->reload_config;
};
if ($@) {
$@ = "I was unable to load the selected config file: $@";
Slic3r::GUI::catch_error($self);
}
$self->{on_select_preset}->($self->name, $preset) if $self->{on_select_preset};
}
sub add_options_page {
@ -240,6 +262,15 @@ sub add_options_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 {
my $self = shift;
@ -437,12 +468,12 @@ sub options {
perimeter_extruder infill_extruder solid_infill_extruder
support_material_extruder support_material_interface_extruder
ooze_prevention standby_temperature_delta
interface_shells
interface_shells regions_overlap
extrusion_width first_layer_extrusion_width perimeter_extrusion_width
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
top_infill_extrusion_width support_material_extrusion_width
infill_overlap bridge_flow_ratio
xy_size_compensation resolution overridable compatible_printers
xy_size_compensation resolution shortcuts compatible_printers
print_settings_id
)
}
@ -450,7 +481,7 @@ sub options {
sub build {
my $self = shift;
my $overridable_widget = sub {
my $shortcuts_widget = sub {
my ($parent) = @_;
my $Options = $Slic3r::Config::Options;
@ -459,15 +490,15 @@ sub build {
grep { exists $Options->{$_} && $Options->{$_}{category} } $self->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 ]);
EVT_CHECKLISTBOX($self, $listbox, sub {
my $value = [ map $opt_keys[$_], grep $listbox->IsChecked($_), 0..$#opt_keys ];
$self->config->set('overridable', $value);
$self->_on_value_change('overridable');
$self->config->set('shortcuts', $value);
$self->_on_value_change('shortcuts');
});
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@ -639,6 +670,7 @@ sub build {
}
{
my $optgroup = $page->new_optgroup('Advanced');
$optgroup->append_single_option_line('regions_overlap');
$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');
{
@ -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(
widget => $overridable_widget,
widget => $shortcuts_widget,
full_width => 1,
);
$optgroup->append_line($line);
@ -750,9 +782,9 @@ sub reload_config {
$self->_reload_compatible_printers_widget;
{
my %overridable = map { $_ => 1 } @{ $self->config->get('overridable') };
for my $i (0..$#{$self->{overridable_opt_keys}}) {
$self->{overridable_list}->Check($i, $overridable{ $self->{overridable_opt_keys}[$i] });
my %shortcuts = map { $_ => 1 } @{ $self->config->get('shortcuts') };
for my $i (0..$#{$self->{shortcuts_opt_keys}}) {
$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
gcode_flavor use_relative_e_distances
serial_port serial_speed
octoprint_host octoprint_apikey
host_type print_host octoprint_apikey
use_firmware_retraction pressure_advance vibration_limit
use_volumetric_e
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode
notes
nozzle_diameter extruder_offset
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
@ -1243,7 +1274,7 @@ sub build {
}
});
}
unless ($Slic3r::GUI::Settings->{_}{no_controller}) {
{
my $optgroup = $page->new_optgroup('USB/Serial connection');
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Serial port',
@ -1277,9 +1308,11 @@ sub build {
$optgroup->append_line($line);
}
{
my $optgroup = $page->new_optgroup('OctoPrint upload');
my $optgroup = $page->new_optgroup('Print server upload');
$optgroup->append_single_option_line('host_type');
my $host_line = $optgroup->create_single_option_line('octoprint_host');
my $host_line = $optgroup->create_single_option_line('print_host');
$host_line->append_button("Browse…", "zoom.png", sub {
# look for devices
my $entries;
@ -1292,19 +1325,19 @@ sub build {
my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
if ($dlg->ShowModal == wxID_OK) {
my $value = $dlg->GetValue . ":" . $dlg->GetPort;
$self->config->set('octoprint_host', $value);
$self->_on_value_change('octoprint_host');
$self->config->set('print_host', $value);
$self->_on_value_change('print_host');
}
} else {
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 {
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
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,
);
if ($res->is_success) {
@ -1314,7 +1347,7 @@ sub build {
"I wasn't able to connect to OctoPrint (" . $res->status_line . "). "
. "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_single_option_line('octoprint_apikey');
}
@ -1398,13 +1431,13 @@ sub build {
my $optgroup = $page->new_optgroup('Notes',
label_width => 0,
);
my $option = $optgroup->get_option('notes');
my $option = $optgroup->get_option('printer_notes');
$option->full_width(1);
$option->height(250);
$optgroup->append_single_option_line($option);
}
}
$self->_update_serial_ports unless $Slic3r::GUI::Settings->{_}{no_controller};
$self->_update_serial_ports;
}
sub _update_serial_ports {
@ -1520,12 +1553,17 @@ sub _update {
$self->{serial_test_btn}->Disable;
}
}
if ($config->get('octoprint_host') && eval "use LWP::UserAgent; 1") {
$self->{octoprint_host_test_btn}->Enable;
} else {
$self->{octoprint_host_test_btn}->Disable;
if (($config->get('host_type') eq 'octoprint')) {
$self->{print_host_browse_btn}->Enable;
}else{
$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;
$self->get_field('toolchange_gcode')->toggle($have_multiple_extruders);
@ -1618,11 +1656,12 @@ sub new {
$self->{title} = $title;
$self->{iconID} = $iconID;
$self->SetScrollbars(1, 1, 1, 1);
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
$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;
}

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 utf8;
__PACKAGE__->mk_accessors(qw(config config2 screen controller _optgroups));
__PACKAGE__->mk_accessors(qw(config config2 manual_control_config screen controller _optgroups));
sub new {
my ($class, $parent) = @_;
@ -27,6 +27,12 @@ sub new {
z_lift_speed => 8,
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") };
if ($ini) {
@ -286,7 +292,7 @@ sub new {
return;
}
my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new
($self, $self->config, $sender);
($self, $self->config, $sender, $self->manual_control_config);
$dlg->ShowModal;
$sender->disconnect;
});
@ -605,8 +611,12 @@ sub start_print {
}
Slic3r::debugf "connected to " . $self->config->serial_port . "\n";
# TODO: this wait should be handled by GCodeSender
sleep 4;
# send custom start G-code
$self->sender->send($_, 1) for grep !/^;/, split /\n/, $self->config->start_gcode;
$self->sender->("G90", 1); # set absolute positioning
}
$self->is_printing(1);

View File

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

View File

@ -72,9 +72,8 @@ sub export {
my @lt = localtime;
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];
# Write notes (content of the Print Settings tab -> Notes)
print $fh "; $_\n" foreach split /\R/, $self->config->notes;
print $fh "\n" if $self->config->notes;
# Write notes (content of all Settings tabs -> Notes)
print $fh $gcodegen->notes;
# Write some terse information on the slicing parameters.
my $first_object = $self->objects->[0];
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
$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
print $fh $gcodegen->writer->set_fan(0, 1)
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;
}
}
# filter out 0-width segments
@mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm;
# ignore too thin segments
@mm3_per_mm = grep $_ > 0.01, @mm3_per_mm;
if (@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

View File

@ -213,7 +213,11 @@ sub generate_support_material {
$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);
return;
}

View File

@ -93,18 +93,21 @@ sub generate {
sub contact_area {
# $object is Slic3r::Print::Object
my ($self, $object) = @_;
my $conf = $self->object_config;
# if user specified a custom angle threshold, convert it to radians
my $threshold_rad;
if (!($self->object_config->support_material_threshold =~ /%$/)) {
$threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive
if (!($conf->support_material_threshold =~ /%$/)) {
$threshold_rad = deg2rad($conf->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
}
# Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces
# and subtract $buildplate_only_top_surfaces from the contact surfaces, so
# there is no contact surface supported by a top surface.
my $buildplate_only = $self->object_config->support_material && $self->object_config->support_material_buildplate_only;
my $buildplate_only =
( $conf->support_material || $conf->support_material_enforce_layers)
&& $conf->support_material_buildplate_only;
my $buildplate_only_top_surfaces = [];
# determine contact areas
@ -115,12 +118,19 @@ sub contact_area {
# so $layer_id == 0 means first object layer
# and $layer->id == 0 means first print layer (including raft)
if ($self->object_config->raft_layers == 0) {
next if $layer_id == 0;
} elsif (!$self->object_config->support_material) {
# if no raft, and we're at layer 0, skip to layer 1
if ( $conf->raft_layers == 0 && $layer_id == 0 ) {
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
# the 'overhangs' of the first object layer
last if $layer_id > 0;
last;
}
my $layer = $object->get_layer($layer_id);
@ -154,12 +164,19 @@ sub contact_area {
my $diff;
# If a threshold angle was specified, use a different logic for detecting overhangs.
if (defined $threshold_rad
|| $layer_id < $self->object_config->support_material_enforce_layers
|| ($self->object_config->raft_layers > 0 && $layer_id == 0)) {
my $d = defined $threshold_rad
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
: 0;
if (($conf->support_material && defined $threshold_rad)
|| $layer_id <= $conf->support_material_enforce_layers
|| ($conf->raft_layers > 0 && $layer_id == 0)) {
my $d = 0;
my $layer_threshold_rad = $threshold_rad;
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(
offset([ map $_->p, @{$layerm->slices} ], -$d),
@ -177,7 +194,7 @@ sub contact_area {
} else {
$diff = diff(
[ 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
@ -189,22 +206,31 @@ sub contact_area {
# 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
# Note: this is duplicate code from GCode.pm, we need to refactor
my $bridged_perimeters; # Polygons
{
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);
my $lower_grown_slices = offset([ map @$_, @{$lower_layer->slices} ], +scale($nozzle_diameter/2));
# Get the lower layer's slices and grow them by half the nozzle diameter
# 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
my @overhang_perimeters =
map { $_->isa('Slic3r::ExtrusionLoop') ? $_->polygon->split_at_first_point : $_->polyline->clone }
map @$_, @{$layerm->perimeters};
# Get all perimeters as polylines.
# TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops)
# could split a bridge mid-way
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()
$_->[0]->translate(1,0) for @overhang_perimeters;
@overhang_perimeters = @{diff_pl(
@ -226,11 +252,16 @@ sub contact_area {
# 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,
# so we take the largest value and also apply safety offset to be ensure no gaps
# are left in between
my $w = max($bridge_flow->scaled_width, $bridge_flow->scaled_spacing);
# For bridges we can't assume width is larger than spacing because they
# are positioned according to non-bridging perimeters spacing.
my $w = max(
$bridge_flow->scaled_width,
$bridge_flow->scaled_spacing,
$fw, # width of external perimeters
$layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width,
);
$bridged_perimeters = union([
# Also apply safety offset to ensure no gaps are left in between.
map @{$_->grow($w/2 + 10)}, @overhang_perimeters
]);
}
@ -241,7 +272,7 @@ sub contact_area {
my @bridges = map $_->expolygon,
grep $_->bridge_angle != -1,
@{$layerm->fill_surfaces->filter_by_type(S_TYPE_BOTTOMBRIDGE)};
$diff = diff(
$diff,
[
@ -264,7 +295,7 @@ sub contact_area {
1,
);
}
} # if ($self->object_config->dont_support_bridges)
} # if ($conf->dont_support_bridges)
if ($buildplate_only) {
# 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);
# 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 ];
$overhang{$contact_z} = [ @overhang ];

View File

@ -133,6 +133,13 @@ sub mesh {
$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]
];
} 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') {
$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]

View File

@ -19,8 +19,16 @@ fi
if [ -s $KEY ]; then
for i in $FILES; do
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
else
echo "$KEY is not available, not deploying."
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_gtk2u_adv-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_core-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_gl-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_html-3.0.so.0
/lib/x86_64-linux-gnu/liblzma.so.5
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libpcre.so.3
/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/libpango-1.0.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/libjbig.so.0
/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 \
-M attributes -M base -M bytes -M B -M POSIX \
-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 warnings -M local::lib \
-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*
find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \
-or -name AUI -or -name Calendar -or -name DataView \
-or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \;
rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include

View File

@ -1,5 +1,5 @@
#!/bin/bash
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" $@

View File

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

View File

@ -1,21 +1,27 @@
if (!(Test-Path "C:\users\appveyor\local-lib.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
if (!(Test-Path "C:\users\appveyor\local-lib-$env:ARCH.7z")) {
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") {
cmd /c "7z x C:\Users\appveyor\local-lib.7z -oC:\projects\slic3r" -y | Write-Output
if (Test-Path "C:\users\appveyor\local-lib-$env:ARCH.7z") {
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*'
}
$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;" + $env:Path
cd C:\projects\slic3r
rm -r 'C:\Program Files (x86)\Microsoft Vis*\bin' -Force
Add-AppveyorCompilationMessage -Message "Building Slic3r XS"
perl Build.pl
if (-NOT ($LASTEXITCODE -eq 0)) {
perl ./Build.pl
if ($LastExitCode -ne 0) {
Add-AppveyorCompilationMessage -Message "XS Failed to Build" -Category Error
$host.SetShouldExit($LastExitCode)
exit
}
Add-AppveyorCompilationMessage -Message "Making ZIP package"
cd package/win
./compile_wrapper.ps1 524| Write-Output
./compile_wrapper.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."
& ./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
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")) {
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 (!(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
if (!(Test-Path "C:\users\appveyor\strawberry.$env:ARCH.msi")) {
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
} else {
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
cmd /c "7z x -aoa C:\Users\appveyor\extra_perl.7z -oC:\"
if (!($env:ARCH -eq "32bit")) {
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")) {
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
cmd /c mklink /D C:\Perl C:\Strawberry\perl
mkdir C:\dev
if (!(Test-Path "C:\users\appveyor\boost.1.63.0.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 (!(Test-Path "C:\users\appveyor\boost.1.63.0.$env:ARCH.7z") -Or $env:FORCE_BOOST_REINSTALL -eq 1) {
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."
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
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))"
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"
git clone https://github.com/wxWidgets/wxWidgets -b "v3.1.0" -q C:\dev\wxWidgets
cd C:\dev\wxwidgets
cp .\include\wx\msw\setup0.h include/wx/msw/setup.h
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
if (!(Test-Path "C:\Users\appveyor\wxwidgets-$env:ARCH.7z")) {
Add-AppveyorCompilationMessage -Message "Extracting wxWidgets for $env:ARCH"
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=wxwidgets-$env:ARCH.7z" -o C:\users\appveyor\wxwidgets-$env:ARCH.7z
7z x C:\users\appveyor\wxwidgets-$env:ARCH.7z -oC:\dev
} else {
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
if ($args[0])
{
$perlver = $args[0]
} else
{
$perlver = 524
}
Param
(
[string]$perlVersion = "524",
[string]$STRAWBERRY_PATH = "C:\Strawberry",
# Path to C++ compiler, or just name if it is in path
[string]$cxx = "g++"
)
$perllib = "-lperl$perlver"
$shell_loc = "..\common\shell.cpp"
function Get-ScriptDirectory
{
$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)
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.
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
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
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
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
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.
# ------------------------
# You need to have Strawberry Perl 5.24.0.1 (or slic3r-perl) installed for this to work,
param (
[switch]$exe = $false
Param
(
# 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 "Requires PAR."
New-Variable -Name "current_branch" -Value ""
New-Variable -Name "current_date" -Value "$(Get-Date -UFormat '%Y.%m.%d')"
New-Variable -Name "output_file" -Value ""
$perldll = "perl$perlVersion"
if ($args[0]) {
$perlversion = $args[0]
if ($branch -eq "") {
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 {
$perlversion = "524"
$output_zip = "${scriptDir}\..\..\Slic3r-${branch}.${currentDate}.$(git rev-parse --short HEAD).${arch}.zip"
}
$perldll = "perl$perlversion"
git branch | foreach {
if ($env:APPVEYOR) {
if ($_ -match "` (.*)") {
$current_branch += $matches[1]
}
} else {
if ($_ -match "\*` (.*)"){
$current_branch += $matches[1]
}
}
if ($outputFile -eq "") {
$outputFile = $output_zip
}
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 {
$output_file = "slic3r.par"
$perlarch = "seh"
$glut = "libglut-0__.dll"
$pthread= "pthreadGC2-w64.dll"
}
# Change this to where you have Strawberry Perl installed.
New-Variable -Name "STRAWBERRY_PATH" -Value "C:\Strawberry"
if (!( (Test-Path -Path "${scriptDir}\slic3r.exe") -And (Test-Path -Path "${scriptDir}\slic3r-console.exe") -And (Test-Path -Path "${scriptDir}\slic3r-debug-console.exe") ) ) {
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 `
-a "slic3r.exe;slic3r.exe" `
-a "slic3r-console.exe;slic3r-console.exe" `
-a "slic3r-debug-console.exe;slic3r-debug-console.exe" `
-a "../../lib;lib" `
-a "../../local-lib;local-lib" `
-a "../../slic3r.pl;slic3r.pl" `
-a "../../utils;utils" `
-a "../../var;var" `
-a "../../FreeGLUT/freeglut.dll;freeglut.dll" `
-a "${STRAWBERRY_PATH}\perl\bin\perl${perlversion}.dll;perl${perlversion}.dll" `
-a "${scriptDir}/slic3r.exe;Slic3r.exe" `
-a "${scriptDir}/slic3r-console.exe;Slic3r-console.exe" `
-a "${scriptDir}/slic3r-debug-console.exe;Slic3r-debug-console.exe" `
-a "${scriptDir}/../../lib;lib" `
-a "${scriptDir}/../../local-lib;local-lib" `
-a "${scriptDir}/../../slic3r.pl;slic3r.pl" `
-a "${scriptDir}/../../var;var" `
-a "${scriptDir}/../../FreeGLUT/freeglut.dll;freeglut.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\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}\c\bin\pthreadGC2-w64.dll;pthreadGC2-w64.dll" `
-a "${STRAWBERRY_PATH}\c\bin\libglut-0__.dll;libglut-0__.dll" `
-a "${STRAWBERRY_PATH}\c\bin\${pthread};${pthread}" `
-a "${STRAWBERRY_PATH}\c\bin\${glut};${glut}" `
-M AutoLoader `
-M B `
-M Carp `
@ -93,6 +150,7 @@ pp `
-M IO `
-M IO::Handle `
-M IO::Select `
-M IO::Socket `
-M LWP `
-M LWP::MediaTypes `
-M LWP::MemberMixin `
@ -137,23 +195,11 @@ pp `
-M XSLoader `
-B `
-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
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
if ($env:APPVEYOR) {
copy ..\..\slic3r.par "..\..\slic3r-${current_branch}.${current_date}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).zip"
} else {
copy ..\..\slic3r.par "..\..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).zip"
del ..\..\slic3r.par
}
}
copy ${scriptDir}\..\..\slic3r.par ${outputFile}
echo "Package saved as ${outputFile}"
del ${scriptDir}\..\..\slic3r.par

View File

@ -18,6 +18,7 @@ use Slic3r::Geometry qw(epsilon X Y Z deg2rad);
use Time::HiRes qw(gettimeofday tv_interval);
$|++;
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
our %opt = ();
my %cli_options = ();
@ -35,7 +36,6 @@ my %cli_options = ();
'load=s@' => \$opt{load},
'autosave=s' => \$opt{autosave},
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
'no-controller' => \$opt{no_controller},
'datadir=s' => \$opt{datadir},
'export-svg' => \$opt{export_svg},
'merge|m' => \$opt{merge},
@ -74,9 +74,9 @@ my @external_configs = ();
if ($opt{load}) {
foreach my $configfile (@{$opt{load}}) {
$configfile = Slic3r::decode_path($configfile);
if (-e $configfile) {
if (-e Slic3r::encode_path($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";
push @external_configs, Slic3r::Config->load("$FindBin::Bin/$configfile");
} else {
@ -99,7 +99,7 @@ if ($opt{save}) {
if (@{$config->get_keys} > 0) {
$config->save($opt{save});
} 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';
$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::threads = $opt{threads};
}
@ -266,7 +265,7 @@ if (@ARGV) { # slicing from command line
my ($percent, $message) = @_;
printf "=> %s\n", $message;
},
output_file => $opt{output},
output_file => Slic3r::decode_path($opt{output}),
);
$sprint->apply_config($config);
@ -316,6 +315,9 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
--save <file> Save configuration to the specified file
--load <file> Load configuration from the specified file. It can be used
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
into the same directory as the input file using the
--output-filename-format to generate the filename.) If a

View File

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

View File

@ -10,15 +10,22 @@
#include <cstring>
#include <iostream>
#include <math.h>
#include "boost/filesystem.hpp"
#include <boost/filesystem.hpp>
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
using namespace Slic3r;
void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
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
ConfigDef config_def;
config_def.merge(cli_config_def);
@ -34,18 +41,17 @@ main(const int argc, const char **argv)
DynamicPrintConfig print_config;
// load config files supplied via --load
for (std::vector<std::string>::const_iterator file = cli_config.load.values.begin();
file != cli_config.load.values.end(); ++file) {
if (!boost::filesystem::exists(*file)) {
std::cout << "No such file: " << *file << std::endl;
for (const std::string &file : cli_config.load.values) {
if (!boost::filesystem::exists(file)) {
boost::nowide::cout << "No such file: " << file << std::endl;
exit(1);
}
DynamicPrintConfig c;
try {
c.load(*file);
c.load(file);
} 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);
}
c.normalize();
@ -62,85 +68,85 @@ main(const int argc, const char **argv)
// read input file(s) if any
std::vector<Model> models;
for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) {
if (!boost::filesystem::exists(*it)) {
std::cout << "No such file: " << *it << std::endl;
for (const t_config_option_key &file : input_files) {
if (!boost::filesystem::exists(file)) {
boost::nowide::cerr << "No such file: " << file << std::endl;
exit(1);
}
Model model;
try {
model = Model::read_from_file(*it);
model = Model::read_from_file(file);
} catch (std::exception &e) {
std::cout << *it << ": " << e.what() << std::endl;
boost::nowide::cerr << file << ": " << e.what() << std::endl;
exit(1);
}
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;
}
model.add_default_instances();
// 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())
(*o)->scale_to_fit(cli_config.scale_to_fit.value);
o->scale_to_fit(cli_config.scale_to_fit.value);
// TODO: honor option order?
(*o)->scale(cli_config.scale.value);
(*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.value), Z);
o->scale(cli_config.scale.value);
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.value), Z);
}
// TODO: handle --merge
models.push_back(model);
}
for (std::vector<Model>::iterator model = models.begin(); model != models.end(); ++model) {
for (Model &model : models) {
if (cli_config.info) {
// --info works on unrepaired model
model->print_info();
model.print_info();
} else if (cli_config.export_obj) {
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();
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) {
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();
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) {
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.slice();
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) {
model->repair();
model->translate(0, 0, -model->bounding_box().min.z);
model.repair();
model.translate(0, 0, -model.bounding_box().min.z);
if (!model->objects.empty()) {
if (!model.objects.empty()) {
// FIXME: cut all objects
Model out;
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) {
model->objects.front()->cut(Y, cli_config.cut_y, &out);
model.objects.front()->cut(Y, cli_config.cut_y, &out);
} else {
model->objects.front()->cut(Z, cli_config.cut, &out);
model.objects.front()->cut(Z, cli_config.cut, &out);
}
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) {
TriangleMesh mesh = model->mesh();
TriangleMesh mesh = model.mesh();
mesh.repair();
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;
ss << model->objects.front()->input_file << "_" << (m - meshes.begin()) << ".stl";
IO::STL::write(**m, ss.str());
delete *m;
ss << model.objects.front()->input_file << "_" << i++ << ".stl";
IO::STL::write(*m, ss.str());
delete m;
}
} else {
std::cerr << "error: command not supported" << std::endl;
boost::nowide::cerr << "error: command not supported" << std::endl;
return 1;
}
}

View File

@ -3,14 +3,20 @@
#include "IO.hpp"
#include "TriangleMesh.hpp"
#include "libslic3r.h"
#include <boost/nowide/args.hpp>
#include <boost/nowide/iostream.hpp>
using namespace Slic3r;
void confess_at(const char *file, int line, const char *func, const char *pat, ...){}
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
ConfigDef config_def;
{
@ -40,7 +46,7 @@ main(const int argc, const char **argv)
if (outfile.empty()) outfile = *it + "_extruded.stl";
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;

View File

@ -1,4 +1,4 @@
use Test::More tests => 18;
use Test::More tests => 20;
use strict;
use warnings;
@ -99,6 +99,24 @@ use Slic3r::Test;
'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 {
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
###use XXX; YYY [ rad2deg($result), $expected ];
# returned value must be non-negative, check for that too
my $delta=rad2deg($result) - $expected;
$delta-=180 if $delta>=180 - epsilon;
return defined $result && $result>=0 && abs($delta) < $tolerance;
my $delta = rad2deg($result) - $expected;
$delta -= 180 if $delta >= 180 - epsilon;
return defined $result && $result >= 0 && abs($delta) < $tolerance;
}
{

View File

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 42;
plan tests => 44;
BEGIN {
use FindBin;
@ -254,4 +254,14 @@ my $polygons = [
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__

View File

@ -16,6 +16,8 @@ use Slic3r::Test;
{
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('infill_extruder', 2);
$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('temperature', [200, 180, 170, 160]);
$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('skirts', 2); # test correct temperatures are applied to skirt as well
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) {
push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y});
$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);

View File

@ -1,4 +1,4 @@
use Test::More tests => 59;
use Test::More tests => 63;
use strict;
use warnings;
@ -393,10 +393,37 @@ use Slic3r::Test;
});
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
ok $test->(Slic3r::Test::init_print('V', config => $config, scale_xyz => [3,1,1])) > 1,
'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 warnings;
@ -10,6 +10,7 @@ BEGIN {
use List::Util qw(any);
use Slic3r;
use Slic3r::Geometry qw(epsilon);
use Slic3r::Test qw(_eq);
{
@ -209,20 +210,55 @@ use Slic3r::Test qw(_eq);
my $config = Slic3r::Config->new_from_defaults;
$config->set('start_gcode', '');
$config->set('retract_lift', [3, 4]);
$config->set('only_retract_when_crossing_perimeters', 0);
my @lifted_at = ();
my $test = sub {
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
@lifted_at = ();
my $retracted = 0;
my $lifted = 0;
my $tool = 0;
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && $info->{dist_Z} < 0) {
push @lifted_at, $info->{new_Z};
if ($cmd eq 'G1') {
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_below', [0, 0]);
$test->();
@ -234,9 +270,13 @@ use Slic3r::Test qw(_eq);
ok !!@lifted_at, 'lift takes place when above/below != 0';
ok !(any { $_ < $config->get_at('retract_lift_above', 0) } @lifted_at),
'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
$config->set('perimeter_extruder', 2);
$config->set('infill_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 !(any { $_ < $config->get_at('retract_lift_above', 1) } @lifted_at),
'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__

View File

@ -1,4 +1,4 @@
use Test::More tests => 27;
use Test::More tests => 28;
use strict;
use warnings;
@ -258,5 +258,32 @@ use Slic3r::Test;
@{ $layer_heights_by_tool{$config->support_material_extruder-1} }),
'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__

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 $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++
# 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
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.
push @cflags, qw(-std=c++0x);
} 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);
}
# 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 = ();
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') {
push @cflags, qw(-stdlib=libc++);
push @ldflags, qw(-framework IOKit -framework CoreFoundation -lc++);
@ -67,15 +89,13 @@ my @boost_include = ();
if (defined $ENV{BOOST_INCLUDEDIR}) {
push @boost_include, $ENV{BOOST_INCLUDEDIR}
} elsif (defined $ENV{BOOST_DIR}) {
my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include');
if (-d $subdir) {
push @boost_include, $subdir;
} elsif (-d "../$subdir") {
# User might have provided a path relative to the Build.PL in the main directory
push @boost_include, "../$subdir";
} else {
push @boost_include, $ENV{BOOST_DIR};
# User might have provided a path relative to the Build.PL in the main directory
foreach my $subdir ($ENV{BOOST_DIR}, "../$ENV{BOOST_DIR}", "$ENV{BOOST_DIR}/include", "../$ENV{BOOST_DIR}/include") {
if (-d $subdir) {
push @boost_include, $subdir;
}
}
die "Invalid BOOST_DIR: no such directory\n" if !@boost_include;
} else {
# Boost library was not defined by the environment.
# Try to guess at some default paths.
@ -99,15 +119,13 @@ my @boost_libs = ();
if (defined $ENV{BOOST_LIBRARYPATH}) {
push @boost_libs, $ENV{BOOST_LIBRARYPATH}
} elsif (defined $ENV{BOOST_DIR}) {
my $subdir = $ENV{BOOST_DIR} . ($mswin ? '\stage\lib' : '/stage/lib');
if (-d $subdir) {
push @boost_libs, $subdir;
} elsif (-d "../$subdir") {
# User might have provided a path relative to the Build.PL in the main directory
push @boost_libs, "../$subdir";
} else {
push @boost_libs, $ENV{BOOST_DIR};
# User might have provided a path relative to the Build.PL in the main directory
foreach my $subdir ("$ENV{BOOST_DIR}/stage/lib", "../$ENV{BOOST_DIR}/stage/lib") {
if (-d $subdir) {
push @boost_libs, $subdir;
}
}
die "Invalid BOOST_DIR: no such directory\n" if !@boost_libs;
} else {
# Boost library was not defined by the environment.
# Try to guess at some default paths.
@ -196,13 +214,8 @@ if ($ENV{SLIC3R_DEBUG}) {
push @cflags, '-DNDEBUG';
}
if ($cpp_guess->is_gcc) {
# check whether we're dealing with a buggy GCC version
# see https://github.com/alexrj/Slic3r/issues/1965
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);
}
# our templated XS bindings cause undefined-var-template warnings
push @cflags, qw(-Wno-undefined-var-template);
}
my $build = Module::Build::WithXSpp->new(

View File

@ -1,5 +1,6 @@
Build.PL
lib/Slic3r/XS.pm
libslic3r.doxygen
MANIFEST This list of files
src/admesh/connect.c
src/admesh/normals.c
@ -8,6 +9,21 @@ src/admesh/stl.h
src/admesh/stl_io.c
src/admesh/stlinit.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.hpp
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
stl_write_off(stl_file *stl, char *file) {
stl_write_off(stl_file *stl, ADMESH_CHAR *file) {
int i;
FILE *fp;
char *error_msg;
@ -149,14 +149,9 @@ stl_write_off(stl_file *stl, char *file) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_ascii: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -177,7 +172,7 @@ stl_write_off(stl_file *stl, char *file) {
}
void
stl_write_vrml(stl_file *stl, char *file) {
stl_write_vrml(stl_file *stl, ADMESH_CHAR *file) {
int i;
FILE *fp;
char *error_msg;
@ -185,14 +180,9 @@ stl_write_vrml(stl_file *stl, char *file) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_ascii: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -234,19 +224,16 @@ stl_write_vrml(stl_file *stl, char *file) {
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;
FILE* fp;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if (fp == NULL) {
char* error_msg = (char*)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);
perror("stl_write_ascii: Couldn't open file for writing");
stl->error = 1;
return;
}

View File

@ -32,6 +32,15 @@
#error "admesh works correctly on little endian machines only!"
#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
extern "C" {
#endif
@ -163,15 +172,15 @@ typedef struct {
} 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_stats_out(stl_file *stl, FILE *file, char *input_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_float(FILE *fp, float value_in);
extern void stl_write_ascii(stl_file *stl, const char *file, const char *label);
extern void stl_write_binary(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 ADMESH_CHAR *file, const char *label);
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_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_edge(stl_file *stl, char *label, stl_hash_edge edge);
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_fill_holes(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_xz(stl_file *stl);
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_generate_shared_vertices(stl_file *stl);
extern void stl_write_obj(stl_file *stl, const char *file);
extern void stl_write_off(stl_file *stl, char *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file);
extern void stl_write_obj(stl_file *stl, const ADMESH_CHAR *file);
extern void stl_write_off(stl_file *stl, ADMESH_CHAR *file);
extern void stl_write_dxf(stl_file *stl, ADMESH_CHAR *file, char *label);
extern void stl_write_vrml(stl_file *stl, ADMESH_CHAR *file);
extern void stl_calculate_normal(float normal[], stl_facet *facet);
extern void stl_normalize_vector(float v[]);
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_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_read(stl_file *stl, int first_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
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;
FILE *fp;
char *error_msg;
@ -132,14 +132,9 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_ascii: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -170,7 +165,7 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) {
}
void
stl_print_neighbors(stl_file *stl, char *file) {
stl_print_neighbors(stl_file *stl, ADMESH_CHAR *file) {
int i;
FILE *fp;
char *error_msg;
@ -178,14 +173,9 @@ stl_print_neighbors(stl_file *stl, char *file) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_print_neighbors: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -204,7 +194,7 @@ stl_print_neighbors(stl_file *stl, char *file) {
}
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;
int i;
char *error_msg;
@ -212,14 +202,9 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "wb");
fp = stl_fopen(file, "wb");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_binary: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -278,7 +263,7 @@ stl_write_neighbor(stl_file *stl, int facet) {
}
void
stl_write_quad_object(stl_file *stl, char *file) {
stl_write_quad_object(stl_file *stl, ADMESH_CHAR *file) {
FILE *fp;
int i;
int j;
@ -292,14 +277,9 @@ stl_write_quad_object(stl_file *stl, char *file) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_quad_object: Couldn't open file for writing");
stl->error = 1;
return;
}
@ -352,7 +332,7 @@ stl_write_quad_object(stl_file *stl, char *file) {
}
void
stl_write_dxf(stl_file *stl, char *file, char *label) {
stl_write_dxf(stl_file *stl, ADMESH_CHAR *file, char *label) {
int i;
FILE *fp;
char *error_msg;
@ -360,14 +340,9 @@ stl_write_dxf(stl_file *stl, char *file, char *label) {
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
fp = stl_fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
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);
perror("stl_write_ascii: Couldn't open file for writing");
stl->error = 1;
return;
}

View File

@ -33,7 +33,7 @@
#endif
void
stl_open(stl_file *stl, const char *file) {
stl_open(stl_file *stl, const ADMESH_CHAR *file) {
stl_initialize(stl);
stl_count_facets(stl, file);
stl_allocate(stl);
@ -66,7 +66,7 @@ stl_initialize(stl_file *stl) {
}
void
stl_count_facets(stl_file *stl, const char *file) {
stl_count_facets(stl_file *stl, const ADMESH_CHAR *file) {
long file_size;
int header_num_facets;
int num_facets;
@ -79,14 +79,9 @@ stl_count_facets(stl_file *stl, const char *file) {
if (stl->error) return;
/* Open the file in binary mode first */
stl->fp = fopen(file, "rb");
stl->fp = stl_fopen(file, "rb");
if(stl->fp == NULL) {
error_msg = (char*)
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);
perror("stl_initialize: Couldn't open file for reading");
stl->error = 1;
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) */
// fix to silence a warning about unused return value.
// 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
if(stl->fp == NULL) {
error_msg = (char*)
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);
perror("stl_initialize: Couldn't open file for reading");
stl->error = 1;
return;
}
@ -201,7 +192,7 @@ stl_allocate(stl_file *stl) {
}
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;
stl_type origStlType;
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,
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)
{
/* outset our bridge by an arbitrary amout; we'll use this outer margin
for detecting anchors */
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
// into polylines and then clipping them with each lower slice's contour
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->_edges, "black", scale_(0.2));
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
}
@ -133,6 +147,13 @@ BridgeDetector::detect_angle()
));
}
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
@ -159,12 +180,16 @@ BridgeDetector::detect_angle()
return true;
}
Polygons
BridgeDetector::coverage() const
{
if (this->angle == -1) return Polygons();
return this->coverage(this->angle);
}
Polygons
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.
ExPolygon expolygon = this->expolygon;
expolygon.rotate(PI/2.0 - angle, Point(0,0));

View File

@ -10,32 +10,33 @@ namespace Slic3r {
class BridgeDetector {
public:
// The non-grown hole.
/// The non-grown hole.
ExPolygon expolygon;
// Lower slices, all regions.
/// Lower slices, all regions.
ExPolygonCollection lower_slices;
// Scaled extrusion width of the infill.
/// Scaled extrusion width of the infill.
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;
// The final optimal angle.
/// The final optimal angle.
double angle;
BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
bool detect_angle();
Polygons coverage(double angle = -1) const;
Polygons coverage() const;
Polygons coverage(double angle) const;
Polylines unsupported_edges(double angle = -1) const;
private:
// Open lines representing the supporting edges.
/// Open lines representing the supporting edges.
Polylines _edges;
// Closed polygons representing the supporting areas.
/// Closed polygons representing the supporting areas.
ExPolygons _anchors;
class BridgeDirection {
public:
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 {
// Initial sort by coverage only - comparator must obey strict weak ordering
return this->coverage > other.coverage;

View File

@ -1,5 +1,4 @@
#include "Config.hpp"
#include <stdlib.h> // for setenv()
#include <assert.h>
#include <ctime>
#include <fstream>
@ -11,16 +10,15 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/config.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <string.h>
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
#define setenv(k, v, o) _putenv_s(k, v)
#endif
namespace Slic3r {
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) {
ConfigOption* my_opt = this->option(opt_key, true);
if (my_opt == NULL) {
if (ignore_nonexistent == false) throw "Attempt to apply non-existent option";
if (ignore_nonexistent == false) throw UnknownOptionException();
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)) {
return optv->value;
} 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
ConfigBase::setenv_()
{
#ifdef setenv
t_config_option_keys opt_keys = this->keys();
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
// prepend the SLIC3R_ prefix
@ -373,9 +370,8 @@ ConfigBase::setenv_()
for (size_t i = 0; i < envname.size(); ++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*
@ -393,7 +389,8 @@ ConfigBase::load(const std::string &file)
{
namespace pt = boost::property_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) {
try {
t_config_option_key opt_key = v.first;
@ -409,8 +406,8 @@ void
ConfigBase::save(const std::string &file) const
{
using namespace std;
ofstream c;
c.open(file.c_str(), ios::out | ios::trunc);
boost::nowide::ofstream c;
c.open(file, ios::out | ios::trunc);
{
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;
opt = static_cast<ConfigOption*>(optv);
} else {
throw "Unknown option type";
throw std::runtime_error("Unknown option type");
}
this->options[opt_key] = opt;
return opt;
@ -527,19 +524,19 @@ DynamicConfig::empty() const {
void
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])
_argv.push_back("");
_argv.push_back(const_cast<char*>(""));
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);
}
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
std::map<std::string,std::string> opts;

View File

@ -15,7 +15,7 @@
namespace Slic3r {
// Name of the configuration option.
/// Name of the configuration option.
typedef std::string t_config_option_key;
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_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 {
public:
virtual ~ConfigOption() {};
@ -41,7 +45,7 @@ class ConfigOption {
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>
class ConfigOptionSingle : public ConfigOption {
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 {
public:
virtual ~ConfigOptionVectorBase() {};
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>
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>
{
public:
@ -108,6 +114,7 @@ class ConfigOptionFloat : public ConfigOptionSingle<double>
};
};
/// Vector form of template specialization for floating point numbers.
class ConfigOptionFloats : public ConfigOptionVector<double>
{
public:
@ -231,7 +238,7 @@ class ConfigOptionString : public ConfigOptionSingle<std::string>
};
};
// semicolon-separated strings
/// semicolon-separated strings
class ConfigOptionStrings : public ConfigOptionVector<std::string>
{
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
{
public:
@ -260,6 +269,8 @@ class ConfigOptionPercent : public ConfigOptionFloat
ConfigOptionPercent(double _value) : ConfigOptionFloat(_value) {};
ConfigOptionPercent* clone() const { return new ConfigOptionPercent(this->value); };
/// Calculate the value of this option as it relates to some
/// other numerical value.
double get_abs_value(double ratio_over) const {
return ratio_over * this->value / 100;
};
@ -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
{
public:
@ -321,6 +335,7 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent
};
};
/// \brief Configuration option to store a 2D (x,y) tuple.
class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
{
public:
@ -339,6 +354,7 @@ class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
bool deserialize(std::string str, bool append = false);
};
/// \brief Configuration option to store a 3D (x,y,z) tuple.
class ConfigOptionPoint3 : public ConfigOptionSingle<Pointf3>
{
public:
@ -394,6 +410,8 @@ class ConfigOptionPoints : public ConfigOptionVector<Pointf>
bool deserialize(std::string str, bool append = false);
};
/// \brief Represents a boolean flag
class ConfigOptionBool : public ConfigOptionSingle<bool>
{
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;
/// \brief Templated enumeration representation.
template <class T>
class ConfigOptionEnum : public ConfigOptionSingle<T>
{
@ -477,14 +496,15 @@ class ConfigOptionEnum : public ConfigOptionSingle<T>
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.
static t_config_enum_values get_enum_values();
};
// 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.
/// \brief Generic enum configuration value.
///
/// We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum.
/// In the StaticConfig, it is better to use the specialized ConfigOptionEnum<T> containers.
class ConfigOptionEnumGeneric : public ConfigOptionInt
{
public:
@ -504,102 +524,120 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt
};
};
// Type of a configuration value.
/// Type of a configuration value.
enum ConfigOptionType {
coNone,
// single float
/// single float
coFloat,
// vector of floats
/// vector of floats
coFloats,
// single int
/// single int
coInt,
// vector of ints
/// vector of ints
coInts,
// single string
/// single string
coString,
// vector of strings
/// vector of strings
coStrings,
// percent value. Currently only used for infill.
/// percent value. Currently only used for infill.
coPercent,
// a fraction or an absolute value
/// a fraction or an absolute value
coFloatOrPercent,
// single 2d point. Currently not used.
/// single 2d point. Currently not used.
coPoint,
// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
/// vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
coPoints,
coPoint3,
// single boolean value
/// single boolean value
coBool,
// vector of boolean values
/// vector of boolean values
coBools,
// a generic enum
/// a generic enum
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
{
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;
// 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;
// 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).
/// \brief Specialization to indicate to the GUI what kind of control is more appropriate.
///
/// Usually empty.
/// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
/// "select_open" - to open a selection dialog (currently only a serial port selection).
std::string gui_type;
// The flags may be combined.
// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
// "align_label_right" - align label to right
/// The flags may be combined.
/// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
/// "align_label_right" - align label to right
std::string gui_flags;
// Label of the GUI input field.
// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
// while full_label contains a label of a stand-alone field.
// The full label is shown, when adding an override parameter for an object or a modified object.
/// Label of the GUI input field.
/// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
/// while full_label contains a label of a stand-alone field.
/// The full label is shown, when adding an override parameter for an object or a modified object.
std::string label;
std::string full_label;
// Category of a configuration field, from the GUI perspective.
// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
/// Category of a configuration field, from the GUI perspective.
/// One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width"
std::string category;
// A tooltip text shown in the GUI.
/// A tooltip text shown in the GUI.
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;
// Format of this parameter on a command line.
/// Format of this parameter on a command line.
std::string cli;
// Set for type == coFloatOrPercent.
// It provides a link to a configuration value, of which this option provides a ratio.
// For example,
// For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
/// Set for type == coFloatOrPercent.
/// It provides a link to a configuration value, of which this option provides a ratio.
/// For example,
/// For example external_perimeter_speed may be defined as a fraction of perimeter_speed.
t_config_option_key ratio_over;
// True for multiline strings.
/// True for multiline strings.
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;
// 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;
// Height of a multiline GUI text box.
/// Height of a multiline GUI text box.
int height;
// Optional width of an input field.
/// Optional width of an input field.
int width;
// <min, max> limit of a numeric input.
// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
// By setting min=0, only nonnegative input is allowed.
/// <min, max> limit of a numeric input.
/// If not set, the <min, max> is set to <INT_MIN, INT_MAX>
/// By setting min=0, only nonnegative input is allowed.
int min;
int max;
// Legacy names for this configuration option.
// Used when parsing legacy configuration file.
/// Legacy names for this configuration option.
/// Used when parsing legacy configuration file.
std::vector<t_config_option_key> aliases;
// Sometimes a single value may well define multiple values in a "beginner" mode.
// Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
/// Sometimes a single value may well define multiple values in a "beginner" mode.
/// Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers".
std::vector<t_config_option_key> shortcut;
// Definition of values / labels for a combo box.
// Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open".
/// Definition of values / labels for a combo box.
/// Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open".
std::vector<std::string> enum_values;
std::vector<std::string> enum_labels;
// For enums (when type == coEnum). Maps enum_values to enums.
// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
/// For enums (when type == coEnum). Maps enum_values to enums.
/// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
t_config_enum_values enum_keys_map;
ConfigOptionDef() : type(coNone), default_value(NULL),
@ -612,14 +650,14 @@ class ConfigOptionDef
ConfigOptionDef& operator= (ConfigOptionDef other);
};
// 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.
// t_config_option_key is std::string
/// Map from a config option name to its definition.
//i The definition does not carry an actual value of the config option, only its constant default value.
//i t_config_option_key is std::string
typedef std::map<t_config_option_key,ConfigOptionDef> t_optiondef_map;
// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values.
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
class ConfigDef
{
public:
@ -631,14 +669,14 @@ class ConfigDef
void merge(const ConfigDef &other);
};
// An abstract configuration store.
/// An abstract configuration store.
class ConfigBase
{
public:
// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values.
// ConfigBase does not own ConfigDef, it only references it.
/// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling.
/// The configuration definition is static: It does not carry the actual configuration values,
/// but it carries the defaults of the configuration values.
/// ConfigBase does not own ConfigDef, it only references it.
const ConfigDef* def;
ConfigBase() : def(NULL) {};
@ -668,8 +706,8 @@ class ConfigBase
void save(const std::string &file) const;
};
// Configuration store with dynamic number of configuration values.
// In Slic3r, the dynamic config is mostly used at the user interface layer.
/// Configuration store with dynamic number of configuration values.
/// In Slic3r, the dynamic config is mostly used at the user interface layer.
class DynamicConfig : public virtual ConfigBase
{
public:
@ -685,29 +723,30 @@ class DynamicConfig : public virtual ConfigBase
void clear();
bool empty() const;
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:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;
};
// Configuration store with a static definition of configuration values.
// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
// because the configuration values could be accessed directly.
/// Configuration store with a static definition of configuration values.
/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
/// because the configuration values could be accessed directly.
class StaticConfig : public virtual ConfigBase
{
public:
StaticConfig() : ConfigBase() {};
// Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object
// and which could be resolved by this->optptr(key) call.
/// Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object
/// and which could be resolved by this->optptr(key) call.
t_config_option_keys keys() const;
// Set all statically defined config options to their defaults defined by this->def.
/// Set all statically defined config options to their defaults defined by this->def.
void set_defaults();
// The derived class has to implement optptr to resolve a static configuration value.
// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
/// The derived class has to implement optptr to resolve a static configuration value.
/// virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
};
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
class UnknownOptionException : public std::exception {};
}

View File

@ -450,25 +450,37 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
void
ExPolygon::triangulate_p2t(Polygons* polygons) const
{
ExPolygons expp = simplify_polygons_ex(*this, true);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
// TODO: prevent duplicate points
for (const ExPolygon &ex : simplify_polygons_ex(*this, true)) {
// contour
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
ContourPoints.push_back(new p2t::Point(point->x, point->y));
ContourPoints.push_back(new p2t::Point(point.x, point.y));
}
p2t::CDT cdt(ContourPoints);
// 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;
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
points.push_back(new p2t::Point(point->x, point->y));
points.push_back(new p2t::Point(point.x, point.y));
}
cdt.AddHole(points);
}
@ -477,18 +489,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
cdt.Triangulate();
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;
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));
}
polygons->push_back(p);
}
for(std::vector<p2t::Point*>::iterator it = ContourPoints.begin(); it != ContourPoints.end(); ++it) {
delete *it;
}
for (p2t::Point* it : ContourPoints)
delete it;
}
}

View File

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

View File

@ -39,13 +39,14 @@ Extruder::extrude(double 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.
If the extruder is already retracted by the same or a greater amount,
this method is a no-op.
The restart_extra argument sets the extra length to be used for
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
Extruder::retract(double length, double restart_extra)
{

View File

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

View File

@ -11,7 +11,8 @@ class ExPolygonCollection;
class ExtrusionEntityCollection;
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 {
erNone,
erPerimeter,
@ -27,7 +28,7 @@ enum ExtrusionRole {
erSupportMaterialInterface,
};
/* Special flags describing loop */
/** \brief Special flags describing loop */
enum ExtrusionLoopRole {
elrDefault,
elrContourInternalPerimeter,
@ -45,9 +46,9 @@ public:
virtual void reverse() = 0;
virtual Point first_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;
// 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 Polyline as_polyline() const = 0;
virtual double length() const { return 0; };
@ -60,11 +61,11 @@ class ExtrusionPath : public ExtrusionEntity
public:
Polyline polyline;
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;
// Width of the extrusion.
/// Width of the extrusion.
float width;
// Height of the extrusion.
/// Height of the extrusion.
float height;
ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {};
@ -74,11 +75,11 @@ public:
void reverse() { this->polyline.reverse(); }
Point first_point() const { return this->polyline.points.front(); }
Point last_point() const { return this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used.
/// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
/// Currently not used.
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.
// Currently not used.
/// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection.
/// Currently not used.
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
@ -103,9 +104,9 @@ public:
return this->role == erBridgeInfill
|| 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;
// 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; }
Polyline as_polyline() const { return this->polyline; }
@ -141,7 +142,7 @@ class ExtrusionLoop : public ExtrusionEntity
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang = false);
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 is_perimeter() const {
return this->paths.front().role == erPerimeter
@ -159,14 +160,19 @@ class ExtrusionLoop : public ExtrusionEntity
|| this->paths.front().role == erSolidInfill
|| 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;
// 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;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
void append(const ExtrusionPath &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 <math.h>
#include <stdio.h>
#include "../ClipperUtils.hpp"
#include "../Geometry.hpp"
#include "../Surface.hpp"
#include "../PrintConfig.hpp"
@ -96,10 +95,8 @@ Fill::_infill_direction(const Surface &surface) const
if (surface.bridge_angle >= 0) {
// use bridge angle
//FIXME Vojtech: Add a debugf?
// Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
#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
out_angle = surface.bridge_angle;
} else if (this->layer_id != size_t(-1)) {

View File

@ -525,7 +525,7 @@ void FillCubic::_fill_surface_single(
direction_t direction2 = direction;
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);

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);
}
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
/// Calculate a new spacing to fill width with possibly integer number of lines,
/// the first and last line being centered at the interval ends.
/// This function possibly increases the spacing, never decreases,
/// and for a narrow width the increase in spacing may become severe,
/// therefore the adjustment is limited to 20% increase.
template <class T>
T
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 OVERLAP_FACTOR = 1.0;
/// Enumeration for different flow roles
enum FlowRole {
frExternalPerimeter,
frPerimeter,
@ -20,6 +21,8 @@ enum FlowRole {
frSupportMaterialInterface,
};
/// Represents material flow; provides methods to predict material spacing.
class Flow
{
public:
@ -28,7 +31,13 @@ class Flow
Flow(float _w, float _h, float _nd, bool _bridge = false)
: 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;
/// 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;
void set_spacing(float spacing);
void set_solid_spacing(const coord_t total_width) {
@ -48,12 +57,18 @@ class Flow
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 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);
template <class T> static T solid_spacing(const T total_width, const T spacing);
private:
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 _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
};

View File

@ -119,7 +119,7 @@ OozePrevention::post_toolchange(GCode &gcodegen)
int
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.temperature.get_at(gcodegen.writer.extruder()->id);
}
@ -262,6 +262,12 @@ GCode::set_origin(const Pointf &pointf)
this->origin = pointf;
}
std::string
GCode::notes()
{
return this->writer.notes();
}
std::string
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
// on the right/outer side of the polygon
if (was_clockwise) {
for (Polygons::iterator p = simplified.begin(); p != simplified.end(); ++p)
p->reverse();
}
if (was_clockwise)
for (Polygon &p : simplified)
p.reverse();
// concave vertices have priority
Points candidates;
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
Points concave = p->concave_points(PI*4/3);
candidates.insert(candidates.end(), concave.begin(), concave.end());
}
for (const Polygon &p : simplified)
append_to(candidates, p.concave_points(PI*4/3));
// if no concave points were found, look for convex vertices
if (candidates.empty()) {
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
Points convex = p->convex_points(PI*2/3);
candidates.insert(candidates.end(), convex.begin(), convex.end());
}
}
if (candidates.empty())
for (const Polygon &p : simplified)
append_to(candidates, p.convex_points(PI*2/3));
// retrieve the last start position for this object
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);
} else if (!candidates.empty()) {
Points non_overhang;
for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) {
if (!loop.has_overhang_point(*p))
non_overhang.push_back(*p);
}
for (const Point &p : candidates)
if (!loop.has_overhang_point(p))
non_overhang.push_back(p);
if (!non_overhang.empty())
candidates = non_overhang;
@ -389,8 +388,15 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
point = last_pos.projection_onto(polygon);
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;
}
} else if (seam_position == spRandom) {
if (loop.role == elrContourInternalPerimeter) {
Polygon polygon = loop.polygon();
@ -415,9 +421,11 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
if (paths.empty()) return "";
// apply the small perimeter speed
if (paths.front().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH) {
if (speed == -1) speed = this->config.get_abs_value("small_perimeter_speed");
}
if (paths.front().is_perimeter()
&& !loop.has(erOverhangPerimeter)
&& loop.length() <= SMALL_PERIMETER_LENGTH
&& speed == -1)
speed = this->config.get_abs_value("small_perimeter_speed");
// extrude along the path
std::string gcode;

View File

@ -100,6 +100,7 @@ class GCode {
void set_extruders(const std::vector<unsigned int> &extruder_ids);
void set_origin(const Pointf &pointf);
std::string preamble();
std::string notes();
std::string change_layer(const Layer &layer);
std::string extrude(const ExtrusionEntity &entity, 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_cflag &= ~CBAUD;
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
ios.c_cc[VTIME] = 1;
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
ios.c_cc[VTIME] = 1;
if (ioctl(handle, TCSETS2, &ios))
printf("Error in TCSETS2: %s\n", strerror(errno));

View File

@ -1,4 +1,5 @@
#include "GCodeWriter.hpp"
#include "utils.hpp"
#include <algorithm>
#include <iomanip>
#include <iostream>
@ -32,11 +33,51 @@ GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
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
GCodeWriter::preamble()
{
std::ostringstream gcode;
if (FLAVOR_IS_NOT(gcfMakerWare)) {
gcode << "G21 ; set units to millimeters\n";
gcode << "G90 ; use absolute coordinates\n";
@ -49,7 +90,8 @@ GCodeWriter::preamble()
}
gcode << this->reset_e(true);
}
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. */
if (!this->will_move_z(z)) {
double nominal_z = this->_pos.z - this->_lifted;
this->_lifted = this->_lifted - (z - nominal_z);
this->_lifted -= (z - nominal_z);
return "";
}
@ -500,10 +542,15 @@ GCodeWriter::lift()
if (this->_pos.z >= above && (below == 0 || this->_pos.z <= below))
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;
return this->_travel_to_z(this->_pos.z + target_lift, "lift Z");
}
return "";
}

View File

@ -23,6 +23,10 @@ public:
std::string extrusion_axis() const { return this->_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config);
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 postamble() 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)
// note: this keeps twins, so it inserts twice the number of the valid edges
this->valid_edges.clear();
@ -534,7 +531,7 @@ MedialAxis::build(ThickPolylines* polylines)
// iterate through the valid edges to build polylines
while (!this->edges.empty()) {
const edge_t* edge = *this->edges.begin();
const VD::edge_type* edge = *this->edges.begin();
// start a polyline
ThickPolyline polyline;

View File

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

View File

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

View File

@ -5,6 +5,8 @@
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,
coordf_t slice_z)
: 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()
{
// remove references to self
@ -34,25 +37,28 @@ Layer::~Layer()
this->clear_regions();
}
/// Getter for this->_id
size_t
Layer::id() const
{
return this->_id;
}
/// Setter for this->_id
void
Layer::set_id(size_t id)
{
this->_id = id;
}
/// Getter for this->regions.size()
size_t
Layer::region_count() const
{
return this->regions.size();
}
/// Deletes all regions using this->delete_region()
void
Layer::clear_regions()
{
@ -60,6 +66,7 @@ Layer::clear_regions()
this->delete_region(i);
}
/// Creates a LayerRegion from a PrintRegion and adds it to this->regions
LayerRegion*
Layer::add_region(PrintRegion* print_region)
{
@ -68,6 +75,7 @@ Layer::add_region(PrintRegion* print_region)
return region;
}
/// Deletes an individual region
void
Layer::delete_region(int idx)
{
@ -77,7 +85,8 @@ Layer::delete_region(int idx)
delete item;
}
// merge all regions' slices to get islands
/// Merge all regions' slices to get islands
//TODO: is this right?
void
Layer::make_slices()
{
@ -98,21 +107,24 @@ Layer::make_slices()
this->slices.expolygons.reserve(slices.size());
// 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;
ordering_points.reserve(slices.size());
for (ExPolygons::const_iterator ex = slices.begin(); ex != slices.end(); ++ex)
ordering_points.push_back(ex->contour.first_point());
for (const ExPolygon &ex : slices)
ordering_points.push_back(ex.contour.centroid());
// sort slices
std::vector<Points::size_type> order;
Slic3r::Geometry::chained_path(ordering_points, order);
// populate slices vector
for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it) {
this->slices.expolygons.push_back(slices[*it]);
}
for (const Points::size_type &o : order)
this->slices.expolygons.push_back(slices[o]);
}
/// Iterates over all of the LayerRegion and invokes LayerRegion->merge_slices()
void
Layer::merge_slices()
{
@ -121,6 +133,7 @@ Layer::merge_slices()
}
}
/// Uses LayerRegion->slices.any_internal_contains(item)
template <class T>
bool
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;
/// Uses LayerRegion->slices.any_bottom_contains(item)
template <class T>
bool
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;
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters.
// 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.
/// 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
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
Layer::make_fills()
{
@ -247,15 +261,15 @@ Layer::make_fills()
}
}
// This function 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.
// 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:
// 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_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.
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
/// 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.
/// 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:
/// 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_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.
/// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
void
Layer::detect_surfaces_type()
{
@ -268,7 +282,7 @@ Layer::detect_surfaces_type()
// unless internal shells are requested
// We call layer->slices or layerm->slices on these neighbor layers
// and we convert them into Polygons so we only care about their total
// and we convert them into Polygons so we only care about their total
// coverage. We only write to layerm->slices so we can read layer->slices safely.
Layer* const &upper_layer = this->upper_layer;
Layer* const &lower_layer = this->lower_layer;
@ -313,7 +327,7 @@ Layer::detect_surfaces_type()
if (lower_layer != NULL) {
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print.
const SurfaceType surface_type_bottom =
const SurfaceType surface_type_bottom =
(object.config.support_material.value && object.config.support_material_contact_distance.value == 0)
? stBottom
: stBottomBridge;
@ -321,7 +335,7 @@ Layer::detect_surfaces_type()
// Any surface lying on the void is a true bottom bridge (an overhang)
bottom.append(
offset2_ex(
diff(layerm_slices_surfaces, lower_layer->slices, true),
diff(layerm_slices_surfaces, lower_layer->slices, true),
-offs, offs
),
surface_type_bottom
@ -330,7 +344,7 @@ Layer::detect_surfaces_type()
// if user requested internal shells, we need to identify surfaces
// lying on other slices not belonging to this region
if (object.config.interface_shells) {
// non-bridging bottom surfaces: any part of this layer lying
// non-bridging bottom surfaces: any part of this layer lying
// on something else, excluding those lying on our own region
const LayerRegion* lower_layerm = lower_layer->get_region(region_id);
boost::lock_guard<boost::mutex> l(lower_layerm->_slices_mutex);
@ -338,9 +352,9 @@ Layer::detect_surfaces_type()
offset2_ex(
diff(
intersection(layerm_slices_surfaces, lower_layer->slices), // supported
lower_layerm->slices,
lower_layerm->slices,
true
),
),
-offs, offs
),
stBottom
@ -353,7 +367,7 @@ Layer::detect_surfaces_type()
// if we have raft layers, consider bottom layer as a bridge
// just like any other bottom surface lying on the void
const SurfaceType surface_type_bottom =
const SurfaceType surface_type_bottom =
(object.config.raft_layers.value > 0 && object.config.support_material_contact_distance.value > 0)
? stBottomBridge
: stBottom;
@ -403,7 +417,7 @@ Layer::detect_surfaces_type()
{
/* Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
Note: this method should be idempotent, but fill_surfaces gets modified
Note: this method should be idempotent, but fill_surfaces gets modified
in place. However we're now only using its boundaries (which are invariant)
so we're safe. This guarantees idempotence of prepare_infill() also in case
that combine_infill() turns some fill_surface into VOID surfaces. */
@ -421,6 +435,7 @@ Layer::detect_surfaces_type()
}
}
///Iterates over all LayerRegions and invokes LayerRegion->process_external_surfaces
void
Layer::process_external_surfaces()
{

View File

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

View File

@ -8,6 +8,7 @@
namespace Slic3r {
/// Creates a new Flow object with the arguments and the variables of this LayerRegion
Flow
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
LayerRegion::merge_slices()
{
@ -33,6 +35,8 @@ LayerRegion::merge_slices()
this->slices.surfaces.push_back(Surface(stInternal, *expoly));
}
/// Creates a new PerimeterGenerator object
/// Which will return the perimeters by its construction
void
LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{
@ -66,25 +70,28 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
g.process();
}
// This function reads layer->slices and lower_layer->slices
// and writes this->bridged and this->fill_surfaces, so it's thread-safe.
/// Processes bridges with holes which are internal features.
/// 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
LayerRegion::process_external_surfaces()
{
Surfaces &surfaces = this->fill_surfaces.surfaces;
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
// (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
// more continuous bridging.
Polygons &holes = surface.expolygon.holes;
for (int i = 0; i < holes.size(); ++i) {
for (int i = 0; i < surfaces[j].expolygon.holes.size(); ++i) {
// reverse the hole and consider it a polygon
Polygon h = holes[i];
Polygon h = surfaces[j].expolygon.holes[i];
h.reverse();
// Is this hole fully contained in the layer slices?
@ -94,10 +101,12 @@ LayerRegion::process_external_surfaces()
if (k == j) continue;
if (h.contains(surfaces[k].expolygon.contour.first_point())) {
surfaces.erase(surfaces.begin() + k);
if (j > k) --j;
--k;
}
}
Polygons &holes = surfaces[j].expolygon.holes;
holes.erase(holes.begin() + i);
--i;
}
@ -130,7 +139,7 @@ LayerRegion::process_external_surfaces()
if (this->layer()->object()->config.support_material) {
append_to(this->bridged, bd.coverage());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
}
}
@ -218,6 +227,8 @@ LayerRegion::process_external_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
LayerRegion::prepare_fill_surfaces()
{
@ -257,6 +268,7 @@ LayerRegion::prepare_fill_surfaces()
}
}
/// Gets smallest area by squaring the Flow's scaled spacing
double
LayerRegion::infill_area_threshold() const
{

View File

@ -8,23 +8,24 @@
namespace Slic3r {
/// Struct for the main attributes of a Surface
/// Used for comparing properties
struct SurfaceGroupAttrib
{
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {}
/// True iff all all three attributes are the same
bool operator==(const SurfaceGroupAttrib &other) const
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; }
bool is_solid;
float fw;
// pattern is of type InfillPattern, -1 for an unset pattern.
int pattern;
bool is_solid; ///< if a solid infill should be used
float fw; ///< flow Width
int pattern; ///< pattern is of type InfillPattern, -1 for an unset pattern.
};
// Generate infills for a LayerRegion.
// The LayerRegion at this point of time may contain
// surfaces of various types (internal/bridge/top/bottom/solid).
// The infills are generated on the groups of surfaces with a compatible type.
// Fills an array of ExtrusionPathCollection objects containing the infills generated now
// and the thin fills generated by generate_perimeters().
/// The LayerRegion at this point of time may contain
/// surfaces of various types (internal/bridge/top/bottom/solid).
/// The infills are generated on the groups of surfaces with a compatible type.
/// Fills an array of ExtrusionPathCollection objects containing the infills generated now
/// and the thin fills generated by generate_perimeters().
void
LayerRegion::make_fill()
{
@ -210,7 +211,7 @@ LayerRegion::make_fill()
// calculate flow spacing for infill pattern generation
bool using_internal_flow = false;
if (!surface.is_solid() && !is_bridge) {
// it's internal infill, so we can calculate a generic flow spacing
// it's internal infill, so we can calculate a generic flow spacing
// for all layers, for avoiding the ugly effect of
// misaligned infill on first layer because of different extrusion width and
// layer height

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
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
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.empty()) throw "No objects!";
if (this->objects.size() > 1) throw std::runtime_error("Grid duplication is not supported with multiple objects");
if (this->objects.empty()) throw std::runtime_error("No objects!");
ModelObject* object = this->objects.front();
object->clear_instances();
@ -634,6 +624,16 @@ ModelObject::instance_bounding_box(size_t instance_idx) const
}
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
ModelObject::align_to_ground()

View File

@ -29,223 +29,540 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// The print bed content.
// Description of a triangular model with multiple materials, multiple instances with various affine transformations
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
/// Model Class representing the print bed content
/// Description of a triangular model with multiple materials, multiple instances with various affine transformations
/// and with multiple modifier meshes.
/// A model groups multiple objects, each object having possibly multiple instances,
/// all objects may share multiple materials.
class Model
{
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;
// 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.
///< 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 constructor.
/// \param other Model the model to be copied
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);
/// Swap objects and materials with another model.
/// \param other Model the model to be swapped with
void swap(Model &other);
/// Model destructor
~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);
/// Create a new object and add it to the current Model.
/// \return ModelObject* a pointer to the new Model
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);
/// Delete a ModelObject from the current Model.
/// \param idx size_t the index of the desired ModelObject
void delete_object(size_t idx);
/// Delete all ModelObjects found in the current Model.
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);
/// 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);
/// 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);
/// 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);
/// Delete all the ModelMaterial objects found in the current Model.
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;
/// Add a new ModelInstance to each ModelObject having no ModelInstance objects
/// \return bool
bool add_default_instances();
/// Get the bounding box of the transformed instances.
/// \return BoundingBoxf3 a bounding box object.
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();
/// 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 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);
/// 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;
/// 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;
/// 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;
/// 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);
// 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);
/// 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);
/// 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);
/// This function calls the print_info() function of each ModelObject.
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;
/// Take all of the ModelObjects in the current Model and combines them into a single ModelObject
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
{
friend class Model;
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;
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
DynamicPrintConfig config;
///< Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
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; };
/// 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);
private:
// Parent, owning this material.
Model* model;
Model* model; ///<Parent, owning this material.
/// Constructor
/// \param model the parent model owning this material.
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);
};
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
// different rotation and different uniform scaling.
/// Model Object class
/// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
/// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
/// Each ModelObject may be instantiated multiple times, each instance having different placement on the print bed,
/// different rotation and different uniform scaling.
class ModelObject
{
friend class Model;
public:
std::string name;
///< This ModelObject name.
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.
// Instances are owned by this ModelObject.
///< Input file path.
ModelInstancePtrs instances;
// Printable and modifier volumes, each with its material ID and a set of override parameters.
// ModelVolumes are owned by this ModelObject.
///< Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling.
///< Instances are owned by this ModelObject.
ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
DynamicPrintConfig config;
// Variation of a layer thickness for spans of Z coordinates.
t_layer_height_ranges layer_height_ranges;
///< Printable and modifier volumes, each with its material ID and a set of override parameters.
///< ModelVolumes are owned by this ModelObject.
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
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;
///< 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
BoundingBoxf3 _bounding_box;
bool _bounding_box_valid;
/// Get the owning parent Model.
/// \return parent Model* pointer to the owner 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);
/// 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);
/// Delete a ModelVolume object.
/// \param idx size_t the index of the ModelVolume to be deleted
void delete_volume(size_t idx);
/// Delete all ModelVolumes in the
void clear_volumes();
/// Add a new ModelInstance to the current ModelObject.
/// \return ModelInstance* a pointer to the new 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);
/// Delete a ModelInstance.
/// \param idx size_t the index of the ModelInstance to be deleted
void delete_instance(size_t idx);
/// Delete the last created ModelInstance object.
void delete_last_instance();
/// Delete all ModelInstance objects found in the current ModelObject.
void clear_instances();
/// Get the bounding box of the *transformed* instances.
BoundingBoxf3 bounding_box();
/// Invalidate the bounding box in the current ModelObject.
void invalidate_bounding_box();
/// Repair all TriangleMesh objects found in each ModelVolume.
void repair();
/// Flatten all volumes and instances into a single mesh and applying all the ModelInstances transformations.
TriangleMesh mesh() const;
/// Flatten all volumes into a single mesh.
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;
/// 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;
/// Align the current ModelObject to ground by translating the ModelVolumes in the z axis the needed units.
void align_to_ground();
/// Center the current ModelObject to origin by translating the ModelVolumes
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);
/// 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);
/// 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);
/// Scale the current ModelObject by scaling its ModelVolumes.
/// \param versor Pointf3 the scaling factor in a 3d vector.
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);
/// 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);
/// Mirror the current Model around a certain axis.
/// \param axis Axis enum member
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);
/// Get the number of the unique ModelMaterial objects in this ModelObject.
/// \return size_t the materials count
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;
/// Know whether there exists a TriangleMesh object that needed repair or not.
/// \return bool
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;
/// 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);
/// 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
/// Print the current info of this ModelObject
void print_info() const;
private:
// Parent object, owning this ModelObject.
Model* model;
Model* model; ///< Parent object, owning this ModelObject.
/// Constructor
/// \param model Model the owner 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);
/// = Operator overloading
/// \param other ModelObject the other ModelObject to be copied
/// \return ModelObject& the current ModelObject to enable operator cascading
ModelObject& operator= (ModelObject other);
/// Swap the attributes between another ModelObject
/// \param other ModelObject the other ModelObject to be swapped with.
void swap(ModelObject &other);
/// Destructor
~ModelObject();
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
/// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
/// ModelVolume instances are owned by a ModelObject.
class ModelVolume
{
friend class ModelObject;
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.
std::string name; ///< Name of this ModelVolume object
TriangleMesh mesh; ///< The triangular model.
DynamicPrintConfig config;
// Is it an object to be printed, or a modifier volume?
bool modifier;
// A parent object owning this modifier volume.
///< 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; };
/// Get the material id of this ModelVolume object
/// \return t_model_material_id the material id string
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);
/// Get the current ModelMaterial in this ModelVolume object
/// \return ModelMaterial* a pointer to the ModelMaterial
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);
/// Add a unique ModelMaterial to the current ModelVolume
/// \return ModelMaterial* pointer to the new ModelMaterial
ModelMaterial* assign_unique_material();
private:
// Parent object owning this ModelVolume.
///< Parent object owning this ModelVolume.
ModelObject* object;
///< The id of the this ModelVolume
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);
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
/// \param other ModelVolume the ModelVolume object to be copied
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);
/// Swap attributes between another ModelVolume object
/// \param other ModelVolume the other volume object
void swap(ModelVolume &other);
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
/// A single instance of a ModelObject.
/// Knows the affine transformation of an object.
class ModelInstance
{
friend class ModelObject;
public:
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
Pointf offset; // in unscaled coordinates
double rotation; ///< Rotation around the Z axis, in radians around mesh center point
double scaling_factor; ///< scaling factor
Pointf offset; ///< offset in unscaled coordinates
/// Get the owning ModelObject
/// \return ModelObject* pointer to the owner ModelObject
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;
// 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;
// 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;
// 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;
private:
// Parent object, owning this instance.
ModelObject* object;
ModelObject* object; ///< Parent object, owning this instance.
/// Constructor
/// \param object ModelObject* pointer to the owner ModelObject
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);
/// = 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);
/// Swap attributes between another ModelInstance object
/// \param other ModelInstance& the other instance object
void swap(ModelInstance &other);
};

View File

@ -13,17 +13,17 @@ MotionPlanner::MotionPlanner(const ExPolygons &islands)
: initialized(false)
{
ExPolygons expp;
for (ExPolygons::const_iterator island = islands.begin(); island != islands.end(); ++island)
island->simplify(SCALED_EPSILON, &expp);
for (const ExPolygon &island : islands)
island.simplify(MP_INNER_MARGIN/10, &expp);
for (ExPolygons::const_iterator island = expp.begin(); island != expp.end(); ++island)
this->islands.push_back(MotionPlannerEnv(*island));
for (const ExPolygon &island : expp)
this->islands.push_back(MotionPlannerEnv(island));
}
MotionPlanner::~MotionPlanner()
{
for (std::vector<MotionPlannerGraph*>::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph)
delete *graph;
for (MotionPlannerGraph* graph : this->graphs)
delete graph;
}
size_t
@ -40,20 +40,20 @@ MotionPlanner::initialize()
// loop through islands in order to create inner expolygons and collect their contours
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
// 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
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
outer_holes.push_back(island->island.contour);
outer_holes.push_back(island.island.contour);
}
// generate outer contour as bounding box of everything
BoundingBox bb;
for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour)
bb.merge(contour->bounding_box());
for (const Polygon contour : outer_holes)
bb.merge(contour.bounding_box());
// grow outer contour
Polygons contour = offset(bb.polygon(), +MP_OUTER_MARGIN*2);
@ -166,12 +166,16 @@ 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
// Note: this is computationally intensive and does not look very necessary
// now that we prune the endpoints with the logic above,
// so we comment it for now until a good test case arises
//polyline.simplify_by_visibility(grown_env);
/*
SVG svg("shortest_path.svg");
svg.draw(grown_env.expolygons);

View File

@ -17,8 +17,6 @@ class MultiPoint
Points points;
operator Points() const;
MultiPoint() {};
explicit MultiPoint(const Points &_points): points(_points) {};
void scale(double factor);
void translate(double x, double y);
void translate(const Point &vector);
@ -48,6 +46,11 @@ class MultiPoint
std::string dump_perl() const;
static Points _douglas_peucker(const Points &points, const double tolerance);
protected:
MultiPoint() {};
explicit MultiPoint(const Points &_points): points(_points) {};
~MultiPoint() = default;
};
} // 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)) {
// get non-overhang paths by intersecting this loop with the grown lower slices
{
Polylines polylines = intersection_pl(loop->polygon, this->_lower_slices_p);
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
const Polylines polylines = intersection_pl(loop->polygon, this->_lower_slices_p);
for (const Polyline &polyline : polylines) {
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.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
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
// the loop centerline and original lower slices is >= half nozzle diameter
{
Polylines polylines = diff_pl(loop->polygon, this->_lower_slices_p);
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
const Polylines polylines = diff_pl(loop->polygon, this->_lower_slices_p);
for (const Polyline &polyline : polylines) {
ExtrusionPath path(erOverhangPerimeter);
path.polyline = *polyline;
path.polyline = polyline;
path.mm3_per_mm = this->_mm3_per_mm_overhang;
path.width = this->overhang_flow.width;
path.height = this->overhang_flow.height;

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