diff --git a/Build.PL b/Build.PL index 196544846..0f340a585 100644 --- a/Build.PL +++ b/Build.PL @@ -8,8 +8,6 @@ use File::Spec; my %prereqs = qw( Devel::CheckLib 0 - Encode 0 - Encode::Locale 1.05 ExtUtils::MakeMaker 6.80 ExtUtils::ParseXS 3.22 File::Basename 0 @@ -24,7 +22,6 @@ my %prereqs = qw( IO::Scalar 0 threads 1.96 Time::HiRes 0 - Unicode::Normalize 0 ); my %recommends = qw( Class::XSAccessor 0 diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index bdd326140..17b783269 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -75,20 +75,10 @@ use Slic3r::Print::Simple; use Slic3r::Surface; our $build = eval "use Slic3r::Build; 1"; use Thread::Semaphore; -use Encode::Locale 1.05; -use Encode; -use Unicode::Normalize; # Scaling between the float and integer coordinates. # Floats are in mm. use constant SCALING_FACTOR => 0.000001; -use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15; - -# Following constants are used by the infill algorithms and integration tests. -# Resolution to simplify perimeters to. These constants are now used in C++ code only. Better to publish them to Perl from the C++ code. -# use constant RESOLUTION => 0.0125; -# use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; -use constant INFILL_OVERLAP_OVER_SPACING => 0.3; # Keep track of threads we created. Perl worker threads shall not create further threads. my @threads = (); @@ -211,39 +201,6 @@ sub resume_all_threads { $pause_sema->up; } -# Convert a Unicode path to a file system locale. -# The encoding is (from Encode::Locale POD): -# Alias | Windows | Mac OS X | POSIX -# locale_fs | ANSI | UTF-8 | nl_langinfo -# where nl_langinfo is en-US.UTF-8 on a modern Linux as well. -# So this conversion seems to make the most sense on Windows. -sub encode_path { - my ($path) = @_; - - # UTF8 encoding is not unique. Normalize the UTF8 string to make the file names unique. - # Unicode::Normalize::NFC() returns the Normalization Form C (formed by canonical decomposition followed by canonical composition). - $path = Unicode::Normalize::NFC($path); - $path = Encode::encode(locale_fs => $path); - - return $path; -} - -# Convert a path coded by a file system locale to Unicode. -sub decode_path { - my ($path) = @_; - - $path = Encode::decode(locale_fs => $path) - unless Encode::is_utf8($path); - - # The filesystem might force a normalization form (like HFS+ does) so - # if we rely on the filename being comparable after the open() + readdir() - # roundtrip (like when creating and then selecting a preset), we need to - # restore our normalization form. - $path = Unicode::Normalize::NFC($path); - - return $path; -} - # Open a file by converting $filename to local file system locales. sub open { my ($fh, $mode, $filename) = @_; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index ccf0e9c9b..cf2729c92 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -101,7 +101,7 @@ sub OnInit { # Unix: ~/.Slic3r # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" # Mac: "~/Library/Application Support/Slic3r" - $datadir ||= Slic3r::decode_path(Wx::StandardPaths::Get->GetUserDataDir); + $datadir ||= Wx::StandardPaths::Get->GetUserDataDir; my $enc_datadir = Slic3r::encode_path($datadir); Slic3r::debugf "Data directory: %s\n", $datadir; @@ -370,9 +370,8 @@ sub open_model { $dialog->Destroy; return; } - my @input_files = map Slic3r::decode_path($_), $dialog->GetPaths; + my @input_files = $dialog->GetPaths; $dialog->Destroy; - return @input_files; } diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm index d71ceddba..70c8e0256 100644 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ b/lib/Slic3r/GUI/BedShapeDialog.pm @@ -286,7 +286,7 @@ sub _load_stl { $dialog->Destroy; return; } - my $input_file = Slic3r::decode_path($dialog->GetPaths); + my $input_file = $dialog->GetPaths; $dialog->Destroy; my $model = Slic3r::Model->read_from_file($input_file); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index f075b4b28..e15868fb6 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -368,7 +368,7 @@ sub quick_slice { $dialog->Destroy; return; } - $input_file = Slic3r::decode_path($dialog->GetPaths); + $input_file = $dialog->GetPaths; $dialog->Destroy; $qs_last_input_file = $input_file unless $params{export_svg}; } else { @@ -428,7 +428,7 @@ sub quick_slice { $dlg->Destroy; return; } - $output_file = Slic3r::decode_path($dlg->GetPath); + $output_file = $dlg->GetPath; $qs_last_output_file = $output_file unless $params{export_svg}; $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file); wxTheApp->save_settings; @@ -482,7 +482,7 @@ sub repair_stl { $dialog->Destroy; return; } - $input_file = Slic3r::decode_path($dialog->GetPaths); + $input_file = $dialog->GetPaths; $dialog->Destroy; } @@ -495,14 +495,14 @@ sub repair_stl { $dlg->Destroy; return undef; } - $output_file = Slic3r::decode_path($dlg->GetPath); + $output_file = $dlg->GetPath; $dlg->Destroy; } 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"); } @@ -529,7 +529,7 @@ sub export_config { my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal == wxID_OK) { - my $file = Slic3r::decode_path($dlg->GetPath); + my $file = $dlg->GetPath; $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; $last_config = $file; @@ -548,7 +548,7 @@ sub load_config_file { my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; - $file = Slic3r::decode_path($dlg->GetPaths); + $file = $dlg->GetPaths; $dlg->Destroy; } for my $tab (values %{$self->{options_tabs}}) { @@ -574,7 +574,7 @@ sub export_configbundle { my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal == wxID_OK) { - my $file = Slic3r::decode_path($dlg->GetPath); + my $file = $dlg->GetPath; $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); wxTheApp->save_settings; @@ -604,7 +604,7 @@ sub load_configbundle { my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); return unless $dlg->ShowModal == wxID_OK; - $file = Slic3r::decode_path($dlg->GetPaths); + $file = $dlg->GetPaths; $dlg->Destroy; } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c473688c2..91dca96b9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1408,7 +1408,7 @@ sub export_gcode { $dlg->Destroy; return; } - my $path = Slic3r::decode_path($dlg->GetPath); + my $path = $dlg->GetPath; $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path); wxTheApp->save_settings; $self->{export_gcode_output_file} = $path; @@ -1574,7 +1574,7 @@ sub send_gcode { my $ua = LWP::UserAgent->new; $ua->timeout(180); - my $path = Slic3r::encode_path($self->{send_gcode_file}); + my $enc_path = Slic3r::encode_path($self->{send_gcode_file}); my $res = $ua->post( "http://" . $self->{config}->octoprint_host . "/api/files/local", Content_Type => 'form-data', @@ -1582,7 +1582,7 @@ sub send_gcode { 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) ], + file => [ $enc_path, basename($enc_path) ], ], ); @@ -1603,7 +1603,7 @@ sub export_stl { return if !@{$self->{objects}}; my $output_file = $self->_get_export_file('STL') or return; - $self->{model}->store_stl(Slic3r::encode_path($output_file), 1); + $self->{model}->store_stl($output_file, 1); $self->statusbar->SetStatusText("STL file exported to $output_file"); } @@ -1644,7 +1644,7 @@ sub export_object_stl { my $model_object = $self->{model}->objects->[$obj_idx]; my $output_file = $self->_get_export_file('STL') or return; - $model_object->mesh->write_binary(Slic3r::encode_path($output_file)); + $model_object->mesh->write_binary($output_file); $self->statusbar->SetStatusText("STL file exported to $output_file"); } @@ -1654,7 +1654,7 @@ sub export_amf { return if !@{$self->{objects}}; my $output_file = $self->_get_export_file('AMF') or return; - $self->{model}->store_amf(Slic3r::encode_path($output_file)); + $self->{model}->store_amf($output_file); $self->statusbar->SetStatusText("AMF file exported to $output_file"); } @@ -1674,7 +1674,7 @@ sub _get_export_file { $dlg->Destroy; return undef; } - $output_file = Slic3r::decode_path($dlg->GetPath); + $output_file = $dlg->GetPath; $dlg->Destroy; } return $output_file; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 08b44ef1e..33b7a7752 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -112,8 +112,9 @@ sub new { my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; return unless $res == wxID_YES; # Delete the file. - my $path = Slic3r::encode_path($self->{presets}[$i]->file); - if (-e $path && ! unlink $path) { + my $path = $self->{presets}[$i]->file; + my $enc_path = Slic3r::encode_path($path); + if (-e $enc_path && ! unlink $enc_path) { # Cannot delete the file, therefore the item will not be removed from the selection. Slic3r::GUI::show_error($self, "Cannot delete file $path : $!"); return; @@ -255,7 +256,7 @@ sub select_preset { sub select_preset_by_name { my ($self, $name) = @_; - $name = Unicode::Normalize::NFC($name); + $name = Slic3r::normalize_utf8_nfc($name); $self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}}); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0c0bc4b0e..3db1b0b43 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -75,21 +75,9 @@ sub export_gcode { # output everything to a G-code file my $output_file = $self->output_filepath($params{output_file} // ''); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); - - { - # open output gcode file if we weren't supplied a file-handle - my $tempfile = "$output_file.tmp"; - my $gcode = Slic3r::GCode->new(); - my $result = $gcode->do_export($self, Slic3r::encode_path($tempfile)); - die $result . "\n" if ($result ne ''); - my $i; - for ($i = 0; $i < 5; $i += 1) { - last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file)); - # Wait for 1/4 seconds and try to rename once again. - select(undef, undef, undef, 0.25); - } - Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5); - } + + die "G-code export to " . $output_file . " failed\n" + if ! Slic3r::GCode->new->do_export($self, $output_file); # run post-processing scripts if (@{$self->config->post_process}) { diff --git a/slic3r.pl b/slic3r.pl index 26a7f51d3..1d3807a6c 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -18,6 +18,9 @@ use Time::HiRes qw(gettimeofday tv_interval); $|++; binmode STDOUT, ':utf8'; +# Convert all parameters from the local code page to utf8 on Windows. +@ARGV = map Slic3r::decode_path($_), @ARGV if $^O eq 'MSWin32'; + our %opt = (); my %cli_options = (); { @@ -65,7 +68,6 @@ my %cli_options = (); my @external_configs = (); if ($opt{load}) { foreach my $configfile (@{$opt{load}}) { - $configfile = Slic3r::decode_path($configfile); if (-e $configfile) { push @external_configs, Slic3r::Config->load($configfile); } elsif (-e "$FindBin::Bin/$configfile") { @@ -102,7 +104,7 @@ my $gui; if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { { no warnings 'once'; - $Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); + $Slic3r::GUI::datadir = $opt{datadir} // ''; $Slic3r::GUI::no_controller = $opt{no_controller}; $Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::autosave = $opt{autosave}; @@ -111,7 +113,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { setlocale(LC_NUMERIC, 'C'); $gui->{mainframe}->load_config_file($_) for @{$opt{load}}; $gui->{mainframe}->load_config($cli_config); - my @input_files = map Slic3r::decode_path($_), @ARGV; + my @input_files = @ARGV; $gui->{mainframe}{plater}->load_files(\@input_files) unless $opt{no_plater}; $gui->MainLoop; exit; @@ -123,7 +125,6 @@ if (@ARGV) { # slicing from command line if ($opt{repair}) { foreach my $file (@ARGV) { - $file = Slic3r::decode_path($file); die "Repair is currently supported only on STL files\n" if $file !~ /\.[sS][tT][lL]$/; @@ -139,7 +140,6 @@ if (@ARGV) { # slicing from command line if ($opt{cut}) { foreach my $file (@ARGV) { - $file = Slic3r::decode_path($file); my $model = Slic3r::Model->read_from_file($file); my $mesh = $model->mesh; $mesh->translate(0, 0, -$mesh->bounding_box->z_min); @@ -158,7 +158,6 @@ if (@ARGV) { # slicing from command line if ($opt{split}) { foreach my $file (@ARGV) { - $file = Slic3r::decode_path($file); my $model = Slic3r::Model->read_from_file($file); my $mesh = $model->mesh; $mesh->repair; @@ -167,14 +166,13 @@ if (@ARGV) { # slicing from command line foreach my $new_mesh (@{$mesh->split}) { my $output_file = sprintf '%s_%02d.stl', $file, ++$part_count; printf "Writing to %s\n", basename($output_file); - $new_mesh->write_binary(Slic3r::encode_path($output_file)); + $new_mesh->write_binary($output_file); } } exit; } while (my $input_file = shift @ARGV) { - $input_file = Slic3r::decode_path($input_file); my $model; if ($opt{merge}) { my @models = map Slic3r::Model->read_from_file($_), $input_file, (splice @ARGV, 0); diff --git a/utils/amf-to-stl.pl b/utils/amf-to-stl.pl index f49af94eb..2648ba25d 100755 --- a/utils/amf-to-stl.pl +++ b/utils/amf-to-stl.pl @@ -14,6 +14,9 @@ use Getopt::Long qw(:config no_auto_abbrev); use Slic3r; $|++; +# Convert all parameters from the local code page to utf8 on Windows. +@ARGV = map Slic3r::decode_path($_), @ARGV if $^O eq 'MSWin32'; + my %opt = (); { my %options = ( @@ -25,12 +28,12 @@ my %opt = (); } { - my $model = Slic3r::Model->load_amf(Slic3r::encode_path($ARGV[0])); + my $model = Slic3r::Model->load_amf($ARGV[0]); my $output_file = $ARGV[0]; $output_file =~ s/\.[aA][mM][fF](?:\.[xX][mM][lL])?$/\.stl/; printf "Writing to %s\n", basename($output_file); - $model->store_stl(Slic3r::encode_path($output_file), binary => !$opt{ascii}); + $model->store_stl($output_file, binary => !$opt{ascii}); } diff --git a/utils/dump-stl.pl b/utils/dump-stl.pl index 12746feab..0f459ffb1 100644 --- a/utils/dump-stl.pl +++ b/utils/dump-stl.pl @@ -18,7 +18,7 @@ $|++; $ARGV[0] or usage(1); if (-e $ARGV[0]) { - my $model = Slic3r::Model->load_stl(Slic3r::encode_path($ARGV[0]), basename($ARGV[0])); + my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0])); $model->objects->[0]->add_instance(offset => Slic3r::Pointf->new(0,0)); my $mesh = $model->mesh; $mesh->repair; @@ -27,7 +27,7 @@ if (-e $ARGV[0]) { exit 0; } elsif ((my $model = Slic3r::Test::model($ARGV[0]))) { $ARGV[1] or die "Missing writeable destination as second argument\n"; - $model->store_stl(Slic3r::encode_path($ARGV[1]), 1); + $model->store_stl($ARGV[1], 1); printf "Model $ARGV[0] written to $ARGV[1]\n"; exit 0; } else { diff --git a/utils/split_stl.pl b/utils/split_stl.pl index ac890fc3e..fffc57665 100755 --- a/utils/split_stl.pl +++ b/utils/split_stl.pl @@ -25,7 +25,7 @@ my %opt = (); } { - my $model = Slic3r::Model->load_stl(Slic3r::encode_path($ARGV[0]), basename($ARGV[0])); + my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0])); my $basename = $ARGV[0]; $basename =~ s/\.[sS][tT][lL]$//; @@ -43,7 +43,7 @@ my %opt = (); my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count; printf "Writing to %s\n", basename($output_file); - $new_model->store_stl(Slic3r::encode_path($output_file), !$opt{ascii}); + $new_model->store_stl($output_file, !$opt{ascii}); } } diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index d32e799aa..f0d537b04 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -25,7 +25,7 @@ my %opt = (); } { - my @models = map Slic3r::Model->load_stl(Slic3r::encode_path($_), basename($_)), @ARGV; + my @models = map Slic3r::Model->load_stl($_, basename($_)), @ARGV; my $output_file = $ARGV[0]; $output_file =~ s/\.[sS][tT][lL]$/.amf.xml/; @@ -53,7 +53,7 @@ my %opt = (); } printf "Writing to %s\n", basename($output_file); - $new_model->store_amf(Slic3r::encode_path($output_file)); + $new_model->store_amf($output_file); } diff --git a/xs/Build.PL b/xs/Build.PL index ff91114f0..7f2a03926 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -129,11 +129,11 @@ if (defined $ENV{BOOST_LIBRARYDIR}) { # In order to generate the -l switches we need to know how Boost libraries are named my $have_boost = 0; -my @boost_libraries = qw(system thread filesystem log); # we need these +my @boost_libraries = qw(system thread filesystem locale log); # we need these if (!$ENV{SLIC3R_STATIC}) { # Dynamic linking of boost libraries. - push @cflags, qw(-DBOOST_LOG_DYN_LINK); + push @cflags, qw(-DBOOST_LOG_DYN_LINK -DBOOST_LOCALE_DYN_LINK); if (! $mswin) { # Check without explicit lib path (works on Linux and OSX). $have_boost = 1 @@ -193,10 +193,10 @@ path through the BOOST_DIR environment variable: Or you may specify BOOST_INCLUDEDIR and BOOST_LIBRARYDIR separatly, which is handy, if you have built Boost libraries with mutliple settings. -Following boost libraries are needed by Slic3r Prusa Edition: system, filesystem, thread, log. +Following boost libraries are needed by Slic3r Prusa Edition: system, filesystem, thread, locale, log. On Debian, you need to run -sudo apt-get install libboost-thread-dev libboost-system-dev libboost-filesystem-dev libboost-log-dev +sudo apt-get install libboost-thread-dev libboost-system-dev libboost-filesystem-dev libboost-locale-dev libboost-log-dev EOF diff --git a/xs/MANIFEST b/xs/MANIFEST index 028556906..9148b3e08 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1,13 +1,13 @@ Build.PL lib/Slic3r/XS.pm MANIFEST This list of files -src/admesh/connect.c -src/admesh/normals.c -src/admesh/shared.c +src/admesh/connect.cpp +src/admesh/normals.cpp +src/admesh/shared.cpp src/admesh/stl.h -src/admesh/stl_io.c -src/admesh/stlinit.c -src/admesh/util.c +src/admesh/stl_io.cpp +src/admesh/stlinit.cpp +src/admesh/util.cpp src/boost/nowide/args.hpp src/boost/nowide/cenv.hpp src/boost/nowide/config.hpp diff --git a/xs/src/admesh/connect.c b/xs/src/admesh/connect.cpp similarity index 100% rename from xs/src/admesh/connect.c rename to xs/src/admesh/connect.cpp diff --git a/xs/src/admesh/normals.c b/xs/src/admesh/normals.cpp similarity index 92% rename from xs/src/admesh/normals.c rename to xs/src/admesh/normals.cpp index 2832899fa..28b1c47b0 100644 --- a/xs/src/admesh/normals.c +++ b/xs/src/admesh/normals.cpp @@ -27,9 +27,13 @@ #include "stl.h" -static void stl_reverse_facet(stl_file *stl, int facet_num); -static void stl_reverse_vector(float v[]); -int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); +static void stl_reverse_vector(float v[]) { + v[0] *= -1; + v[1] *= -1; + v[2] *= -1; +} + +static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); static void stl_reverse_facet(stl_file *stl, int facet_num) { @@ -136,10 +140,8 @@ stl_fix_normal_directions(stl_file *stl) { /* Reverse the neighboring facets if necessary. */ if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) { /* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */ - if(stl->neighbors_start[facet_num].neighbor[j] != -1) { - stl_reverse_facet - (stl, stl->neighbors_start[facet_num].neighbor[j]); - } + if(stl->neighbors_start[facet_num].neighbor[j] != -1) + stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]); } /* If this edge of the facet is connected: */ if(stl->neighbors_start[facet_num].neighbor[j] != -1) { @@ -193,8 +195,7 @@ stl_fix_normal_directions(stl_file *stl) { free(norm_sw); } -int -stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { +static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { /* Returns 0 if the normal is within tolerance */ /* Returns 1 if the normal is not within tolerance, but direction is OK */ /* Returns 2 if the normal is not within tolerance and backwards */ @@ -259,26 +260,17 @@ stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { return 4; } -static void -stl_reverse_vector(float v[]) { - v[0] *= -1; - v[1] *= -1; - v[2] *= -1; -} - - -void -stl_calculate_normal(float normal[], stl_facet *facet) { - float v1[3]; - float v2[3]; - - v1[0] = facet->vertex[1].x - facet->vertex[0].x; - v1[1] = facet->vertex[1].y - facet->vertex[0].y; - v1[2] = facet->vertex[1].z - facet->vertex[0].z; - v2[0] = facet->vertex[2].x - facet->vertex[0].x; - v2[1] = facet->vertex[2].y - facet->vertex[0].y; - v2[2] = facet->vertex[2].z - facet->vertex[0].z; - +void stl_calculate_normal(float normal[], stl_facet *facet) { + float v1[3] = { + facet->vertex[1].x - facet->vertex[0].x, + facet->vertex[1].y - facet->vertex[0].y, + facet->vertex[1].z - facet->vertex[0].z + }; + float v2[3] = { + facet->vertex[2].x - facet->vertex[0].x, + facet->vertex[2].y - facet->vertex[0].y, + facet->vertex[2].z - facet->vertex[0].z + }; normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]); normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]); normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]); diff --git a/xs/src/admesh/shared.c b/xs/src/admesh/shared.cpp similarity index 98% rename from xs/src/admesh/shared.c rename to xs/src/admesh/shared.cpp index 56164e25f..8080f3574 100644 --- a/xs/src/admesh/shared.c +++ b/xs/src/admesh/shared.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "stl.h" void @@ -149,7 +151,7 @@ stl_write_off(stl_file *stl, char *file) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -185,7 +187,7 @@ stl_write_vrml(stl_file *stl, char *file) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -241,7 +243,7 @@ void stl_write_obj (stl_file *stl, char *file) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::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); diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index 139983632..82c860636 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -32,10 +32,6 @@ #error "admesh works correctly on little endian machines only!" #endif -#ifdef __cplusplus -extern "C" { -#endif - #define STL_MAX(A,B) ((A)>(B)? (A):(B)) #define STL_MIN(A,B) ((A)<(B)? (A):(B)) #define ABS(X) ((X) < 0 ? -(X) : (X)) @@ -223,8 +219,4 @@ extern void stl_clear_error(stl_file *stl); extern int stl_get_error(stl_file *stl); extern void stl_exit_on_error(stl_file *stl); -#ifdef __cplusplus -} -#endif - #endif diff --git a/xs/src/admesh/stl_io.c b/xs/src/admesh/stl_io.cpp similarity index 98% rename from xs/src/admesh/stl_io.c rename to xs/src/admesh/stl_io.cpp index 7d8e4eab8..f58d6b4f6 100644 --- a/xs/src/admesh/stl_io.c +++ b/xs/src/admesh/stl_io.cpp @@ -25,6 +25,8 @@ #include "stl.h" #include "config.h" +#include + #if !defined(SEEK_SET) #define SEEK_SET 0 #define SEEK_CUR 1 @@ -126,13 +128,12 @@ Normals fixed : %5d\n", stl->stats.normals_fixed); void stl_write_ascii(stl_file *stl, const char *file, const char *label) { int i; - FILE *fp; char *error_msg; if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + FILE *fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -178,7 +179,7 @@ stl_print_neighbors(stl_file *stl, char *file) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -212,7 +213,7 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "wb"); + fp = boost::nowide::fopen(file, "wb"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -292,7 +293,7 @@ stl_write_quad_object(stl_file *stl, char *file) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -360,7 +361,7 @@ stl_write_dxf(stl_file *stl, char *file, char *label) { if (stl->error) return; /* Open the file */ - fp = fopen(file, "w"); + fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.cpp similarity index 99% rename from xs/src/admesh/stlinit.c rename to xs/src/admesh/stlinit.cpp index 13f1f43af..f5110d394 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include "stl.h" #ifndef SEEK_SET @@ -62,7 +64,7 @@ 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 = boost::nowide::fopen(file, "rb"); if(stl->fp == NULL) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ @@ -121,7 +123,7 @@ 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); + stl->fp = boost::nowide::freopen(file, "r", stl->fp); // do another null check to be safe if(stl->fp == NULL) { diff --git a/xs/src/admesh/util.c b/xs/src/admesh/util.cpp similarity index 99% rename from xs/src/admesh/util.c rename to xs/src/admesh/util.cpp index cabb8a778..b0c31469d 100644 --- a/xs/src/admesh/util.c +++ b/xs/src/admesh/util.cpp @@ -186,7 +186,7 @@ static void calculate_normals(stl_file *stl) { } void stl_transform(stl_file *stl, float *trafo3x4) { - int i_face, i_vertex, i, j; + int i_face, i_vertex; if (stl->error) return; for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { @@ -404,7 +404,7 @@ static float get_volume(stl_file *stl) { n = stl->facet_start[i].normal; height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z); area = get_area(&stl->facet_start[i]); - volume += (area * height) / 3.0; + volume += (area * height) / 3.0f; } return volume; } diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 9a9047eac..b8abf038b 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "../libslic3r.h" #include "../Model.hpp" #include "AMF.hpp" @@ -480,7 +482,7 @@ bool load_amf(const char *path, Model *model) return false; } - FILE *pFile = ::fopen(path, "rt"); + FILE *pFile = boost::nowide::fopen(path, "rt"); if (pFile == nullptr) { printf("Cannot open file %s\n", path); return false; @@ -522,7 +524,7 @@ bool load_amf(const char *path, Model *model) bool store_amf(const char *path, Model *model) { - FILE *file = ::fopen(path, "wb"); + FILE *file = boost::nowide::fopen(path, "wb"); if (file == nullptr) return false; diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp index 6b686d6ea..b7ef33774 100644 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ b/xs/src/libslic3r/Format/PRUS.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include #include @@ -119,7 +121,14 @@ bool load_prus(const char *path, Model *model) { // To receive the content of the zipped 'scene.xml' file. std::vector scene_xml_data; - wxFFileInputStream in(path); + wxFFileInputStream in( +#ifdef WIN32 + // On Windows, convert to a 16bit unicode string. + boost::nowide::widen(path).c_str() +#else + path +#endif + ); wxZipInputStream zip(in); std::unique_ptr entry; size_t num_models = 0; diff --git a/xs/src/libslic3r/Format/objparser.cpp b/xs/src/libslic3r/Format/objparser.cpp index d8b86b703..88dfae695 100644 --- a/xs/src/libslic3r/Format/objparser.cpp +++ b/xs/src/libslic3r/Format/objparser.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include "objparser.hpp" namespace ObjParser { @@ -318,7 +320,7 @@ static bool obj_parseline(const char *line, ObjData &data) bool objparse(const char *path, ObjData &data) { - FILE *pFile = ::fopen(path, "rt"); + FILE *pFile = boost::nowide::fopen(path, "rt"); if (pFile == 0) return false; @@ -446,7 +448,7 @@ bool loadvectornameidx(FILE *pFile, std::vector &v) bool objbinsave(const char *path, const ObjData &data) { - FILE *pFile = ::fopen(path, "wb"); + FILE *pFile = boost::nowide::fopen(path, "wb"); if (pFile == 0) return false; @@ -471,7 +473,7 @@ bool objbinsave(const char *path, const ObjData &data) bool objbinload(const char *path, ObjData &data) { - FILE *pFile = ::fopen(path, "rb"); + FILE *pFile = boost::nowide::fopen(path, "rb"); if (pFile == 0) return false; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 5acda3a98..699621974 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -13,6 +13,10 @@ #include #include +#include +#include +#include + #include "SVG.hpp" #if 0 @@ -306,7 +310,33 @@ std::vector>> GCode::collec return layers_to_print; } -bool GCode::do_export(FILE *file, Print &print) +bool GCode::do_export(Print *print, const char *path) +{ + // Remove the old g-code if it exists. + boost::nowide::remove(path); + + std::string path_tmp(path); + path_tmp += ".tmp"; + + FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); + if (file == nullptr) + return false; + + bool result = this->_do_export(*print, file); + fclose(file); + + if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) { + boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path + << ". Is " << path_tmp << " locked?" << std::endl; + result = false; + } + + if (! result) + boost::nowide::remove(path_tmp.c_str()); + return result; +} + +bool GCode::_do_export(Print &print, FILE *file) { // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 51ab5eb84..ac0af3f2c 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -126,7 +126,7 @@ public: {} ~GCode() {} - bool do_export(FILE *file, Print &print); + bool do_export(Print *print, const char *path); // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Pointf& origin() const { return m_origin; } @@ -146,6 +146,8 @@ public: void apply_print_config(const PrintConfig &print_config); protected: + bool _do_export(Print &print, FILE *file); + // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint { diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 5d0de1576..455469f8a 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace Slic3r { @@ -739,7 +740,7 @@ void ModelObject::print_info() const { using namespace std; cout << fixed; - cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; + boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; TriangleMesh mesh = this->raw_mesh(); mesh.check_topology(); diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index b8580fd5e..c94db8e74 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -1,6 +1,8 @@ #include "SVG.hpp" #include +#include + #define COORD(x) ((float)unscale((x))*10) namespace Slic3r { @@ -8,7 +10,7 @@ namespace Slic3r { bool SVG::open(const char* afilename) { this->filename = afilename; - this->f = fopen(afilename, "w"); + this->f = boost::nowide::fopen(afilename, "w"); if (this->f == NULL) return false; fprintf(this->f, @@ -27,7 +29,7 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo this->filename = afilename; this->origin = bbox.min - Point(bbox_offset, bbox_offset); this->flipY = aflipY; - this->f = ::fopen(afilename, "w"); + this->f = boost::nowide::fopen(afilename, "w"); if (f == NULL) return false; float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset); diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 0b6b87d28..45d1f2c51 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -6,6 +6,10 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +extern std::string encode_path(const char *src); +extern std::string decode_path(const char *src); +extern std::string normalize_utf8_nfc(const char *src); + // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html template diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 50db6f300..ea34fb18c 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -1,7 +1,21 @@ +#include + #include #include #include +#include + +#include +#include + +#ifdef WIN32 +extern "C" { +__declspec(dllimport) int WideCharToMultiByte(unsigned int, unsigned long, wchar_t const *, int, char *, int, char const *, int *); +__declspec(dllimport) int MultiByteToWideChar(unsigned int, unsigned long, char const *, int, wchar_t *, int); +} +#endif /* WIN32 */ + namespace Slic3r { static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error; @@ -30,9 +44,13 @@ void set_logging_level(unsigned int level) } // Force set_logging_level(<=error) after loading of the DLL. -static struct SetLoggingLevelOnInit { - SetLoggingLevelOnInit() { set_logging_level(1); } -} g_SetLoggingLevelOnInit; +// Switch boost::filesystem to utf8. +static struct RunOnInit { + RunOnInit() { + boost::nowide::nowide_filesystem(); + set_logging_level(1); + } +} g_RunOnInit; void trace(unsigned int level, const char *message) { @@ -56,6 +74,46 @@ void trace(unsigned int level, const char *message) (::boost::log::keywords::severity = severity)) << message; } +std::string encode_path(const char *src) +{ +#ifdef WIN32 + // Convert the source utf8 encoded string to a wide string. + std::wstring wstr_src = boost::nowide::widen(src); + if (wstr_src.length() == 0) + return std::string(); + // Convert a wide string to a local code page. + int size_needed = ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), nullptr, 0, nullptr, nullptr); + std::string str_dst(size_needed, 0); + ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), const_cast(str_dst.data()), size_needed, nullptr, nullptr); + return str_dst; +#else /* WIN32 */ + return src; +#endif /* WIN32 */ +} + +std::string decode_path(const char *src) +{ +#ifdef WIN32 + int len = strlen(src); + if (len == 0) + return std::string(); + // Convert the string encoded using the local code page to a wide string. + int size_needed = ::MultiByteToWideChar(0, 0, src, len, nullptr, 0); + std::wstring wstr_dst(size_needed, 0); + ::MultiByteToWideChar(0, 0, src, len, const_cast(wstr_dst.data()), size_needed); + // Convert a wide string to utf8. + return boost::nowide::narrow(wstr_dst.c_str()); +#else /* WIN32 */ + return src; +#endif /* WIN32 */ +} + +std::string normalize_utf8_nfc(const char *src) +{ + static std::locale locale_utf8("en_US.UTF-8"); + return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8); +} + } // namespace Slic3r #ifdef SLIC3R_HAS_BROKEN_CROAK diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 9104e481a..ea54fd3a7 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -17,17 +17,7 @@ %name{Slic3r::GCode} class GCode { GCode(); ~GCode(); - std::string do_export(Print *print, const char *path) - %code{% - FILE *file = fopen(path, "wb"); - if (file == nullptr) { - RETVAL = std::string("Failed to open ") + path + " for writing."; - } else { - THIS->do_export(file, *print); - fclose(file); - RETVAL = std::string(); - } - %}; + std::string do_export(Print *print, const char *path); Ref origin() %code{% RETVAL = &(THIS->origin()); %}; diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index 8389e2d99..6ba6817c1 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -48,6 +48,24 @@ trace(level, message) CODE: Slic3r::trace(level, message); +std::string +encode_path(src) + const char *src; + CODE: + Slic3r::encode_path(src); + +std::string +decode_path(src) + const char *src; + CODE: + Slic3r::decode_path(src); + +std::string +normalize_utf8_nfc(src) + const char *src; + CODE: + Slic3r::normalize_utf8_nfc(src); + void xspp_test_croak_hangs_on_strawberry() CODE: