mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-09-24 06:53:16 +08:00
Merge branch 'master' into adaptive-slicing
This commit is contained in:
commit
c0dfffe0ff
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@ -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`_
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
26
appveyor.yml
26
appveyor.yml
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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}) {
|
||||
|
@ -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) = @_;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) = @_;
|
||||
|
||||
|
@ -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) = @_;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ];
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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" $@
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
14
slic3r.pl
14
slic3r.pl
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
26
t/bridges.t
26
t/bridges.t
@ -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;
|
||||
}
|
||||
|
||||
{
|
||||
|
12
t/geometry.t
12
t/geometry.t
@ -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__
|
||||
|
16
t/multi.t
16
t/multi.t
@ -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);
|
||||
|
@ -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)';
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -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__
|
||||
|
31
t/support.t
31
t/support.t
@ -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
BIN
var/zoom_in.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 725 B |
BIN
var/zoom_out.png
Executable file
BIN
var/zoom_out.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 708 B |
73
xs/Build.PL
73
xs/Build.PL
@ -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(
|
||||
|
16
xs/MANIFEST
16
xs/MANIFEST
@ -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
2478
xs/libslic3r.doxygen
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
167
xs/src/boost/nowide/args.hpp
Executable 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
126
xs/src/boost/nowide/cenv.hpp
Executable 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
54
xs/src/boost/nowide/config.hpp
Executable 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
154
xs/src/boost/nowide/convert.hpp
Executable 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
101
xs/src/boost/nowide/cstdio.hpp
Executable 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
16
xs/src/boost/nowide/cstdlib.hpp
Executable 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
415
xs/src/boost/nowide/filebuf.hpp
Executable 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
283
xs/src/boost/nowide/fstream.hpp
Executable 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
|
28
xs/src/boost/nowide/integration/filesystem.hpp
Executable file
28
xs/src/boost/nowide/integration/filesystem.hpp
Executable 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
261
xs/src/boost/nowide/iostream.cpp
Executable 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
|
99
xs/src/boost/nowide/iostream.hpp
Executable file
99
xs/src/boost/nowide/iostream.hpp
Executable 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
|
154
xs/src/boost/nowide/stackstring.hpp
Executable file
154
xs/src/boost/nowide/stackstring.hpp
Executable 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
46
xs/src/boost/nowide/system.hpp
Executable 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
|
499
xs/src/boost/nowide/utf8_codecvt.hpp
Executable file
499
xs/src/boost/nowide/utf8_codecvt.hpp
Executable 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
39
xs/src/boost/nowide/windows.hpp
Executable 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
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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 "";
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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() {};
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user