From 138501872426ae1fc7023478dd1deba96d936ecf Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 3 Aug 2017 17:31:31 +0200 Subject: Unicode handling: Removed the Perl dependencies on Encode, Encode::Locale and Unicode::Normalize. Added dependency on boost::locale. Added encode_path, decode_path, normalize_utf8 functions to Slic3r.xs Slic3r.xs has been made mostly utf8 safe by using the boost::nowide library, thanks to @alexrj for the idea. Simplified the encode_path / decode_path stuff: wxWidgets are unicode already, so there is no need to decode_path() from it. Perl / win32 interfacing is non-unicode, so decode_path() is executed on ARGV just at the beginning of the perl scripts. --- Build.PL | 3 - lib/Slic3r.pm | 43 -- lib/Slic3r/GUI.pm | 5 +- lib/Slic3r/GUI/BedShapeDialog.pm | 2 +- lib/Slic3r/GUI/MainFrame.pm | 20 +- lib/Slic3r/GUI/Plater.pm | 14 +- lib/Slic3r/GUI/Tab.pm | 7 +- lib/Slic3r/Print.pm | 18 +- slic3r.pl | 14 +- utils/amf-to-stl.pl | 7 +- utils/dump-stl.pl | 4 +- utils/split_stl.pl | 4 +- utils/stl-to-amf.pl | 4 +- xs/Build.PL | 8 +- xs/MANIFEST | 12 +- xs/src/admesh/connect.c | 988 ---------------------------------- xs/src/admesh/connect.cpp | 988 ++++++++++++++++++++++++++++++++++ xs/src/admesh/normals.c | 333 ------------ xs/src/admesh/normals.cpp | 325 +++++++++++ xs/src/admesh/shared.c | 262 --------- xs/src/admesh/shared.cpp | 264 +++++++++ xs/src/admesh/stl.h | 8 - xs/src/admesh/stl_io.c | 420 --------------- xs/src/admesh/stl_io.cpp | 421 +++++++++++++++ xs/src/admesh/stlinit.c | 426 --------------- xs/src/admesh/stlinit.cpp | 428 +++++++++++++++ xs/src/admesh/util.c | 573 -------------------- xs/src/admesh/util.cpp | 573 ++++++++++++++++++++ xs/src/libslic3r/Format/AMF.cpp | 6 +- xs/src/libslic3r/Format/PRUS.cpp | 11 +- xs/src/libslic3r/Format/objparser.cpp | 8 +- xs/src/libslic3r/GCode.cpp | 32 +- xs/src/libslic3r/GCode.hpp | 4 +- xs/src/libslic3r/Model.cpp | 3 +- xs/src/libslic3r/SVG.cpp | 6 +- xs/src/libslic3r/Utils.hpp | 4 + xs/src/libslic3r/utils.cpp | 64 ++- xs/xsp/GCode.xsp | 12 +- xs/xsp/XS.xsp | 18 + 39 files changed, 3196 insertions(+), 3146 deletions(-) delete mode 100644 xs/src/admesh/connect.c create mode 100644 xs/src/admesh/connect.cpp delete mode 100644 xs/src/admesh/normals.c create mode 100644 xs/src/admesh/normals.cpp delete mode 100644 xs/src/admesh/shared.c create mode 100644 xs/src/admesh/shared.cpp delete mode 100644 xs/src/admesh/stl_io.c create mode 100644 xs/src/admesh/stl_io.cpp delete mode 100644 xs/src/admesh/stlinit.c create mode 100644 xs/src/admesh/stlinit.cpp delete mode 100644 xs/src/admesh/util.c create mode 100644 xs/src/admesh/util.cpp 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.c deleted file mode 100644 index e9129d007..000000000 --- a/xs/src/admesh/connect.c +++ /dev/null @@ -1,988 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include -#include -#include - -#include "stl.h" - - -static void stl_match_neighbors_exact(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_match_neighbors_nearby(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_record_neighbors(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_initialize_facet_check_exact(stl_file *stl); -static void stl_initialize_facet_check_nearby(stl_file *stl); -static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b); -static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b, float tolerance); -static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, - void (*match_neighbors)(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b)); -static int stl_get_hash_for_edge(int M, stl_hash_edge *edge); -static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_free_edges(stl_file *stl); -static void stl_remove_facet(stl_file *stl, int facet_number); -static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, - stl_vertex new_vertex); -static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, - stl_hash_edge *edge_b, int *facet1, int *vertex1, - int *facet2, int *vertex2, - stl_vertex *new_vertex1, stl_vertex *new_vertex2); -static void stl_remove_degenerate(stl_file *stl, int facet); -extern int stl_check_normal_vector(stl_file *stl, - int facet_num, int normal_fix_flag); -static void stl_update_connects_remove_1(stl_file *stl, int facet_num); - - -void -stl_check_facets_exact(stl_file *stl) { - /* This function builds the neighbors list. No modifications are made - * to any of the facets. The edges are said to match only if all six - * floats of the first edge matches all six floats of the second edge. - */ - - stl_hash_edge edge; - stl_facet facet; - int i; - int j; - - if (stl->error) return; - - stl->stats.connected_edges = 0; - stl->stats.connected_facets_1_edge = 0; - stl->stats.connected_facets_2_edge = 0; - stl->stats.connected_facets_3_edge = 0; - - stl_initialize_facet_check_exact(stl); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - { - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } - - /* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */ - if( !memcmp(&facet.vertex[0], &facet.vertex[1], - sizeof(stl_vertex)) - || !memcmp(&facet.vertex[1], &facet.vertex[2], - sizeof(stl_vertex)) - || !memcmp(&facet.vertex[0], &facet.vertex[2], - sizeof(stl_vertex))) { - stl->stats.degenerate_facets += 1; - stl_remove_facet(stl, i); - i--; - continue; - - } - for(j = 0; j < 3; j++) { - edge.facet_number = i; - edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], - &facet.vertex[(j + 1) % 3]); - - insert_hash_edge(stl, edge, stl_match_neighbors_exact); - } - } - stl_free_edges(stl); - -#if 0 - printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", - stl->stats.number_of_facets, stl->stats.number_of_facets * 3, - stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); -#endif -} - -static void -stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b) { - - if (stl->error) return; - - { - float diff_x = ABS(a->x - b->x); - float diff_y = ABS(a->y - b->y); - float diff_z = ABS(a->z - b->z); - float max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); - } - - // Ensure identical vertex ordering of equal edges. - // This method is numerically robust. - if ((a->x != b->x) ? - (a->x < b->x) : - ((a->y != b->y) ? - (a->y < b->y) : - (a->z < b->z))) { - memcpy(&edge->key[0], a, sizeof(stl_vertex)); - memcpy(&edge->key[3], b, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], b, sizeof(stl_vertex)); - memcpy(&edge->key[3], a, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } -} - -static void -stl_initialize_facet_check_exact(stl_file *stl) { - int i; - - if (stl->error) return; - - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; - - - stl->M = 81397; - - for(i = 0; i < stl->stats.number_of_facets ; i++) { - /* initialize neighbors list to -1 to mark unconnected edges */ - stl->neighbors_start[i].neighbor[0] = -1; - stl->neighbors_start[i].neighbor[1] = -1; - stl->neighbors_start[i].neighbor[2] = -1; - } - - stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); - if(stl->heads == NULL) perror("stl_initialize_facet_check_exact"); - - stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(stl->tail == NULL) perror("stl_initialize_facet_check_exact"); - - stl->tail->next = stl->tail; - - for(i = 0; i < stl->M; i++) { - stl->heads[i] = stl->tail; - } -} - -static void -insert_hash_edge(stl_file *stl, stl_hash_edge edge, - void (*match_neighbors)(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b)) { - stl_hash_edge *link; - stl_hash_edge *new_edge; - stl_hash_edge *temp; - int chain_number; - - if (stl->error) return; - - chain_number = stl_get_hash_for_edge(stl->M, &edge); - - link = stl->heads[chain_number]; - - if(link == stl->tail) { - /* This list doesn't have any edges currently in it. Add this one. */ - new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(new_edge == NULL) perror("insert_hash_edge"); - stl->stats.malloced++; - *new_edge = edge; - new_edge->next = stl->tail; - stl->heads[chain_number] = new_edge; - return; - } else if(!stl_compare_function(&edge, link)) { - /* This is a match. Record result in neighbors list. */ - match_neighbors(stl, &edge, link); - /* Delete the matched edge from the list. */ - stl->heads[chain_number] = link->next; - free(link); - stl->stats.freed++; - return; - } else { - /* Continue through the rest of the list */ - for(;;) { - if(link->next == stl->tail) { - /* This is the last item in the list. Insert a new edge. */ - new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(new_edge == NULL) perror("insert_hash_edge"); - stl->stats.malloced++; - *new_edge = edge; - new_edge->next = stl->tail; - link->next = new_edge; - stl->stats.collisions++; - return; - } else if(!stl_compare_function(&edge, link->next)) { - /* This is a match. Record result in neighbors list. */ - match_neighbors(stl, &edge, link->next); - - /* Delete the matched edge from the list. */ - temp = link->next; - link->next = link->next->next; - free(temp); - stl->stats.freed++; - return; - } else { - /* This is not a match. Go to the next link */ - link = link->next; - stl->stats.collisions++; - } - } - } -} - - -static int -stl_get_hash_for_edge(int M, stl_hash_edge *edge) { - return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17 - + edge->key[3] /13 + edge->key[4] / 11 + edge->key[5] / 7 ) % M); -} - -static int -stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - if(edge_a->facet_number == edge_b->facet_number) { - return 1; /* Don't match edges of the same facet */ - } else { - return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT); - } -} - -void -stl_check_facets_nearby(stl_file *stl, float tolerance) { - stl_hash_edge edge[3]; - stl_facet facet; - int i; - int j; - - if (stl->error) return; - - if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) - && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) - && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { - /* No need to check any further. All facets are connected */ - return; - } - - stl_initialize_facet_check_nearby(stl); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - { - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } - for(j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] == -1) { - edge[j].facet_number = i; - edge[j].which_edge = j; - if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j], - &facet.vertex[(j + 1) % 3], - tolerance)) { - /* only insert edges that have different keys */ - insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby); - } - } - } - } - - stl_free_edges(stl); -} - -static int -stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b, float tolerance) { - // Index of a grid cell spaced by tolerance. - uint32_t vertex1[3] = { - (uint32_t)((a->x - stl->stats.min.x) / tolerance), - (uint32_t)((a->y - stl->stats.min.y) / tolerance), - (uint32_t)((a->z - stl->stats.min.z) / tolerance) - }; - uint32_t vertex2[3] = { - (uint32_t)((b->x - stl->stats.min.x) / tolerance), - (uint32_t)((b->y - stl->stats.min.y) / tolerance), - (uint32_t)((b->z - stl->stats.min.z) / tolerance) - }; - - if( (vertex1[0] == vertex2[0]) - && (vertex1[1] == vertex2[1]) - && (vertex1[2] == vertex2[2])) { - /* Both vertices hash to the same value */ - return 0; - } - - // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. - // This method is numerically robust. - if ((vertex1[0] != vertex2[0]) ? - (vertex1[0] < vertex2[0]) : - ((vertex1[1] != vertex2[1]) ? - (vertex1[1] < vertex2[1]) : - (vertex1[2] < vertex2[2]))) { - memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } - return 1; -} - -static void -stl_free_edges(stl_file *stl) { - int i; - stl_hash_edge *temp; - - if (stl->error) return; - - if(stl->stats.malloced != stl->stats.freed) { - for(i = 0; i < stl->M; i++) { - for(temp = stl->heads[i]; stl->heads[i] != stl->tail; - temp = stl->heads[i]) { - stl->heads[i] = stl->heads[i]->next; - free(temp); - stl->stats.freed++; - } - } - } - free(stl->heads); - free(stl->tail); -} - -static void -stl_initialize_facet_check_nearby(stl_file *stl) { - int i; - - if (stl->error) return; - - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; - - /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/ - /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ - /* tolerance *= 0.5;*/ - - stl->M = 81397; - - stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); - if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); - - stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby"); - - stl->tail->next = stl->tail; - - for(i = 0; i < stl->M; i++) { - stl->heads[i] = stl->tail; - } -} - - - -static void -stl_record_neighbors(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - int i; - int j; - - if (stl->error) return; - - /* Facet a's neighbor is facet b */ - stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] = - edge_b->facet_number; /* sets the .neighbor part */ - - stl->neighbors_start[edge_a->facet_number]. - which_vertex_not[edge_a->which_edge % 3] = - (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - /* Facet b's neighbor is facet a */ - stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] = - edge_a->facet_number; /* sets the .neighbor part */ - - stl->neighbors_start[edge_b->facet_number]. - which_vertex_not[edge_b->which_edge % 3] = - (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3)) - || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) { - /* these facets are oriented in opposite directions. */ - /* their normals are probably messed up. */ - stl->neighbors_start[edge_a->facet_number]. - which_vertex_not[edge_a->which_edge % 3] += 3; - stl->neighbors_start[edge_b->facet_number]. - which_vertex_not[edge_b->which_edge % 3] += 3; - } - - - /* Count successful connects */ - /* Total connects */ - stl->stats.connected_edges += 2; - /* Count individual connects */ - i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) + - (stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) + - (stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1)); - j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) + - (stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) + - (stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1)); - if(i == 2) { - stl->stats.connected_facets_1_edge +=1; - } else if(i == 1) { - stl->stats.connected_facets_2_edge +=1; - } else { - stl->stats.connected_facets_3_edge +=1; - } - if(j == 2) { - stl->stats.connected_facets_1_edge +=1; - } else if(j == 1) { - stl->stats.connected_facets_2_edge +=1; - } else { - stl->stats.connected_facets_3_edge +=1; - } -} - -static void -stl_match_neighbors_exact(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - if (stl->error) return; - stl_record_neighbors(stl, edge_a, edge_b); -} - -static void -stl_match_neighbors_nearby(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - int facet1; - int facet2; - int vertex1; - int vertex2; - int vnot1; - int vnot2; - stl_vertex new_vertex1; - stl_vertex new_vertex2; - - if (stl->error) return; - - stl_record_neighbors(stl, edge_a, edge_b); - stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1, - &facet2, &vertex2, &new_vertex1, &new_vertex2); - if(facet1 != -1) { - if(facet1 == edge_a->facet_number) { - vnot1 = (edge_a->which_edge + 2) % 3; - } else { - vnot1 = (edge_b->which_edge + 2) % 3; - } - if(((vnot1 + 2) % 3) == vertex1) { - vnot1 += 3; - } - stl_change_vertices(stl, facet1, vnot1, new_vertex1); - } - if(facet2 != -1) { - if(facet2 == edge_a->facet_number) { - vnot2 = (edge_a->which_edge + 2) % 3; - } else { - vnot2 = (edge_b->which_edge + 2) % 3; - } - if(((vnot2 + 2) % 3) == vertex2) { - vnot2 += 3; - } - stl_change_vertices(stl, facet2, vnot2, new_vertex2); - } - stl->stats.edges_fixed += 2; -} - - -static void -stl_change_vertices(stl_file *stl, int facet_num, int vnot, - stl_vertex new_vertex) { - int first_facet; - int direction; - int next_edge; - int pivot_vertex; - - if (stl->error) return; - - first_facet = facet_num; - direction = 0; - - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } -#if 0 - if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && - stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && - stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) - printf("Changing vertex %f,%f,%f: Same !!!\r\n", - new_vertex.x, new_vertex.y, new_vertex.z); - else { - if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].x, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].x), - new_vertex.x, - *reinterpret_cast(&new_vertex.x)); - if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].y, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].y), - new_vertex.y, - *reinterpret_cast(&new_vertex.y)); - if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex].z, - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].z), - new_vertex.z, - *reinterpret_cast(&new_vertex.z)); - } -#endif - stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; - vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; - facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; - - if(facet_num == -1) { - break; - } - - if(facet_num == first_facet) { - /* back to the beginning */ - printf("\ -Back to the first facet changing vertices: probably a mobius part.\n\ -Try using a smaller tolerance or don't do a nearby check\n"); - return; - } - } -} - - -static void -stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, - stl_hash_edge *edge_b, int *facet1, int *vertex1, - int *facet2, int *vertex2, - stl_vertex *new_vertex1, stl_vertex *new_vertex2) { - int v1a; /* pair 1, facet a */ - int v1b; /* pair 1, facet b */ - int v2a; /* pair 2, facet a */ - int v2b; /* pair 2, facet b */ - - /* Find first pair */ - if(edge_a->which_edge < 3) { - v1a = edge_a->which_edge; - v2a = (edge_a->which_edge + 1) % 3; - } else { - v2a = edge_a->which_edge % 3; - v1a = (edge_a->which_edge + 1) % 3; - } - if(edge_b->which_edge < 3) { - v1b = edge_b->which_edge; - v2b = (edge_b->which_edge + 1) % 3; - } else { - v2b = edge_b->which_edge % 3; - v1b = (edge_b->which_edge + 1) % 3; - } - - /* Of the first pair, which vertex, if any, should be changed */ - if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a], - &stl->facet_start[edge_b->facet_number].vertex[v1b], - sizeof(stl_vertex))) { - /* These facets are already equal. No need to change. */ - *facet1 = -1; - } else { - if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) - && (stl->neighbors_start[edge_a->facet_number]. - neighbor[(v1a + 2) % 3] == -1)) { - /* This vertex has no neighbors. This is a good one to change */ - *facet1 = edge_a->facet_number; - *vertex1 = v1a; - *new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b]; - } else { - *facet1 = edge_b->facet_number; - *vertex1 = v1b; - *new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a]; - } - } - - /* Of the second pair, which vertex, if any, should be changed */ - if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a], - &stl->facet_start[edge_b->facet_number].vertex[v2b], - sizeof(stl_vertex))) { - /* These facets are already equal. No need to change. */ - *facet2 = -1; - } else { - if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) - && (stl->neighbors_start[edge_a->facet_number]. - neighbor[(v2a + 2) % 3] == -1)) { - /* This vertex has no neighbors. This is a good one to change */ - *facet2 = edge_a->facet_number; - *vertex2 = v2a; - *new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b]; - } else { - *facet2 = edge_b->facet_number; - *vertex2 = v2b; - *new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a]; - } - } -} - -static void -stl_remove_facet(stl_file *stl, int facet_number) { - int neighbor[3]; - int vnot[3]; - int i; - int j; - - if (stl->error) return; - - stl->stats.facets_removed += 1; - /* Update list of connected edges */ - j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) + - (stl->neighbors_start[facet_number].neighbor[1] == -1) + - (stl->neighbors_start[facet_number].neighbor[2] == -1)); - if(j == 2) { - stl->stats.connected_facets_1_edge -= 1; - } else if(j == 1) { - stl->stats.connected_facets_2_edge -= 1; - stl->stats.connected_facets_1_edge -= 1; - } else if(j == 0) { - stl->stats.connected_facets_3_edge -= 1; - stl->stats.connected_facets_2_edge -= 1; - stl->stats.connected_facets_1_edge -= 1; - } - - stl->facet_start[facet_number] = - stl->facet_start[stl->stats.number_of_facets - 1]; - /* I could reallocate at this point, but it is not really necessary. */ - stl->neighbors_start[facet_number] = - stl->neighbors_start[stl->stats.number_of_facets - 1]; - stl->stats.number_of_facets -= 1; - - for(i = 0; i < 3; i++) { - neighbor[i] = stl->neighbors_start[facet_number].neighbor[i]; - vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i]; - } - - for(i = 0; i < 3; i++) { - if(neighbor[i] != -1) { - if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] != - stl->stats.number_of_facets) { - printf("\ -in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n", - stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3], - stl->stats.number_of_facets); - return; - } - stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] - = facet_number; - } - } -} - -void -stl_remove_unconnected_facets(stl_file *stl) { - /* A couple of things need to be done here. One is to remove any */ - /* completely unconnected facets (0 edges connected) since these are */ - /* useless and could be completely wrong. The second thing that needs to */ - /* be done is to remove any degenerate facets that were created during */ - /* stl_check_facets_nearby(). */ - - int i; - - if (stl->error) return; - - /* remove degenerate facets */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if( !memcmp(&stl->facet_start[i].vertex[0], - &stl->facet_start[i].vertex[1], sizeof(stl_vertex)) - || !memcmp(&stl->facet_start[i].vertex[1], - &stl->facet_start[i].vertex[2], sizeof(stl_vertex)) - || !memcmp(&stl->facet_start[i].vertex[0], - &stl->facet_start[i].vertex[2], sizeof(stl_vertex))) { - stl_remove_degenerate(stl, i); - i--; - } - } - - if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { - /* remove completely unconnected facets */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if( (stl->neighbors_start[i].neighbor[0] == -1) - && (stl->neighbors_start[i].neighbor[1] == -1) - && (stl->neighbors_start[i].neighbor[2] == -1)) { - /* This facet is completely unconnected. Remove it. */ - stl_remove_facet(stl, i); - i--; - } - } - } -} - -static void -stl_remove_degenerate(stl_file *stl, int facet) { - int edge1; - int edge2; - int edge3; - int neighbor1; - int neighbor2; - int neighbor3; - int vnot1; - int vnot2; - int vnot3; - - if (stl->error) return; - - if( !memcmp(&stl->facet_start[facet].vertex[0], - &stl->facet_start[facet].vertex[1], sizeof(stl_vertex)) - && !memcmp(&stl->facet_start[facet].vertex[1], - &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { - /* all 3 vertices are equal. Just remove the facet. I don't think*/ - /* this is really possible, but just in case... */ - printf("removing a facet in stl_remove_degenerate\n"); - - stl_remove_facet(stl, facet); - return; - } - - if(!memcmp(&stl->facet_start[facet].vertex[0], - &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) { - edge1 = 1; - edge2 = 2; - edge3 = 0; - } else if(!memcmp(&stl->facet_start[facet].vertex[1], - &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { - edge1 = 0; - edge2 = 2; - edge3 = 1; - } else if(!memcmp(&stl->facet_start[facet].vertex[2], - &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) { - edge1 = 0; - edge2 = 1; - edge3 = 2; - } else { - /* No degenerate. Function shouldn't have been called. */ - return; - } - neighbor1 = stl->neighbors_start[facet].neighbor[edge1]; - neighbor2 = stl->neighbors_start[facet].neighbor[edge2]; - - if(neighbor1 == -1) { - stl_update_connects_remove_1(stl, neighbor2); - } - if(neighbor2 == -1) { - stl_update_connects_remove_1(stl, neighbor1); - } - - - neighbor3 = stl->neighbors_start[facet].neighbor[edge3]; - vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1]; - vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2]; - vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3]; - - if(neighbor1 >= 0){ - stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2; - stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2; - } - if(neighbor2 >= 0){ - stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1; - stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1; - } - - stl_remove_facet(stl, facet); - - if(neighbor3 >= 0) { - stl_update_connects_remove_1(stl, neighbor3); - stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1; - } -} - -void -stl_update_connects_remove_1(stl_file *stl, int facet_num) { - int j; - - if (stl->error) return; - /* Update list of connected edges */ - j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) + - (stl->neighbors_start[facet_num].neighbor[1] == -1) + - (stl->neighbors_start[facet_num].neighbor[2] == -1)); - if(j == 0) { /* Facet has 3 neighbors */ - stl->stats.connected_facets_3_edge -= 1; - } else if(j == 1) { /* Facet has 2 neighbors */ - stl->stats.connected_facets_2_edge -= 1; - } else if(j == 2) { /* Facet has 1 neighbor */ - stl->stats.connected_facets_1_edge -= 1; - } -} - -void -stl_fill_holes(stl_file *stl) { - stl_facet facet; - stl_facet new_facet; - int neighbors_initial[3]; - stl_hash_edge edge; - int first_facet; - int direction; - int facet_num; - int vnot; - int next_edge; - int pivot_vertex; - int next_facet; - int i; - int j; - int k; - - if (stl->error) return; - - /* Insert all unconnected edges into hash list */ - stl_initialize_facet_check_nearby(stl); - for(i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - for(j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] != -1) continue; - edge.facet_number = i; - edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], - &facet.vertex[(j + 1) % 3]); - - insert_hash_edge(stl, edge, stl_match_neighbors_exact); - } - } - - for(i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - neighbors_initial[0] = stl->neighbors_start[i].neighbor[0]; - neighbors_initial[1] = stl->neighbors_start[i].neighbor[1]; - neighbors_initial[2] = stl->neighbors_start[i].neighbor[2]; - first_facet = i; - for(j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] != -1) continue; - - new_facet.vertex[0] = facet.vertex[j]; - new_facet.vertex[1] = facet.vertex[(j + 1) % 3]; - if(neighbors_initial[(j + 2) % 3] == -1) { - direction = 1; - } else { - direction = 0; - } - - facet_num = i; - vnot = (j + 2) % 3; - - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } - next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; - - if(next_facet == -1) { - new_facet.vertex[2] = stl->facet_start[facet_num]. - vertex[vnot % 3]; - stl_add_facet(stl, &new_facet); - for(k = 0; k < 3; k++) { - edge.facet_number = stl->stats.number_of_facets - 1; - edge.which_edge = k; - stl_load_edge_exact(stl, &edge, &new_facet.vertex[k], - &new_facet.vertex[(k + 1) % 3]); - - insert_hash_edge(stl, edge, stl_match_neighbors_exact); - } - break; - } else { - vnot = stl->neighbors_start[facet_num]. - which_vertex_not[next_edge]; - facet_num = next_facet; - } - - if(facet_num == first_facet) { - /* back to the beginning */ - printf("\ -Back to the first facet filling holes: probably a mobius part.\n\ -Try using a smaller tolerance or don't do a nearby check\n"); - return; - } - } - } - } -} - -void -stl_add_facet(stl_file *stl, stl_facet *new_facet) { - if (stl->error) return; - - stl->stats.facets_added += 1; - if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) { - stl->facet_start = (stl_facet*)realloc(stl->facet_start, - (sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); - if(stl->facet_start == NULL) perror("stl_add_facet"); - stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start, - (sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256))); - if(stl->neighbors_start == NULL) perror("stl_add_facet"); - stl->stats.facets_malloced += 256; - } - stl->facet_start[stl->stats.number_of_facets] = *new_facet; - - /* note that the normal vector is not set here, just initialized to 0 */ - stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0; - stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0; - stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0; - - stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1; - stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1; - stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1; - stl->stats.number_of_facets += 1; -} diff --git a/xs/src/admesh/connect.cpp b/xs/src/admesh/connect.cpp new file mode 100644 index 000000000..e9129d007 --- /dev/null +++ b/xs/src/admesh/connect.cpp @@ -0,0 +1,988 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include +#include +#include + +#include "stl.h" + + +static void stl_match_neighbors_exact(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b); +static void stl_match_neighbors_nearby(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b); +static void stl_record_neighbors(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b); +static void stl_initialize_facet_check_exact(stl_file *stl); +static void stl_initialize_facet_check_nearby(stl_file *stl); +static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, + stl_vertex *a, stl_vertex *b); +static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, + stl_vertex *a, stl_vertex *b, float tolerance); +static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, + void (*match_neighbors)(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b)); +static int stl_get_hash_for_edge(int M, stl_hash_edge *edge); +static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); +static void stl_free_edges(stl_file *stl); +static void stl_remove_facet(stl_file *stl, int facet_number); +static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, + stl_vertex new_vertex); +static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, + stl_hash_edge *edge_b, int *facet1, int *vertex1, + int *facet2, int *vertex2, + stl_vertex *new_vertex1, stl_vertex *new_vertex2); +static void stl_remove_degenerate(stl_file *stl, int facet); +extern int stl_check_normal_vector(stl_file *stl, + int facet_num, int normal_fix_flag); +static void stl_update_connects_remove_1(stl_file *stl, int facet_num); + + +void +stl_check_facets_exact(stl_file *stl) { + /* This function builds the neighbors list. No modifications are made + * to any of the facets. The edges are said to match only if all six + * floats of the first edge matches all six floats of the second edge. + */ + + stl_hash_edge edge; + stl_facet facet; + int i; + int j; + + if (stl->error) return; + + stl->stats.connected_edges = 0; + stl->stats.connected_facets_1_edge = 0; + stl->stats.connected_facets_2_edge = 0; + stl->stats.connected_facets_3_edge = 0; + + stl_initialize_facet_check_exact(stl); + + for(i = 0; i < stl->stats.number_of_facets; i++) { + facet = stl->facet_start[i]; + // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. + // When using a memcmp on raw floats, those numbers report to be different. + // Unify all +0 and -0 to +0 to make the floats equal under memcmp. + { + uint32_t *f = (uint32_t*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f == 0x80000000) + // Negative zero, switch to positive zero. + *f = 0; + } + + /* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */ + if( !memcmp(&facet.vertex[0], &facet.vertex[1], + sizeof(stl_vertex)) + || !memcmp(&facet.vertex[1], &facet.vertex[2], + sizeof(stl_vertex)) + || !memcmp(&facet.vertex[0], &facet.vertex[2], + sizeof(stl_vertex))) { + stl->stats.degenerate_facets += 1; + stl_remove_facet(stl, i); + i--; + continue; + + } + for(j = 0; j < 3; j++) { + edge.facet_number = i; + edge.which_edge = j; + stl_load_edge_exact(stl, &edge, &facet.vertex[j], + &facet.vertex[(j + 1) % 3]); + + insert_hash_edge(stl, edge, stl_match_neighbors_exact); + } + } + stl_free_edges(stl); + +#if 0 + printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", + stl->stats.number_of_facets, stl->stats.number_of_facets * 3, + stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); +#endif +} + +static void +stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, + stl_vertex *a, stl_vertex *b) { + + if (stl->error) return; + + { + float diff_x = ABS(a->x - b->x); + float diff_y = ABS(a->y - b->y); + float diff_z = ABS(a->z - b->z); + float max_diff = STL_MAX(diff_x, diff_y); + max_diff = STL_MAX(diff_z, max_diff); + stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); + } + + // Ensure identical vertex ordering of equal edges. + // This method is numerically robust. + if ((a->x != b->x) ? + (a->x < b->x) : + ((a->y != b->y) ? + (a->y < b->y) : + (a->z < b->z))) { + memcpy(&edge->key[0], a, sizeof(stl_vertex)); + memcpy(&edge->key[3], b, sizeof(stl_vertex)); + } else { + memcpy(&edge->key[0], b, sizeof(stl_vertex)); + memcpy(&edge->key[3], a, sizeof(stl_vertex)); + edge->which_edge += 3; /* this edge is loaded backwards */ + } +} + +static void +stl_initialize_facet_check_exact(stl_file *stl) { + int i; + + if (stl->error) return; + + stl->stats.malloced = 0; + stl->stats.freed = 0; + stl->stats.collisions = 0; + + + stl->M = 81397; + + for(i = 0; i < stl->stats.number_of_facets ; i++) { + /* initialize neighbors list to -1 to mark unconnected edges */ + stl->neighbors_start[i].neighbor[0] = -1; + stl->neighbors_start[i].neighbor[1] = -1; + stl->neighbors_start[i].neighbor[2] = -1; + } + + stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); + if(stl->heads == NULL) perror("stl_initialize_facet_check_exact"); + + stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + if(stl->tail == NULL) perror("stl_initialize_facet_check_exact"); + + stl->tail->next = stl->tail; + + for(i = 0; i < stl->M; i++) { + stl->heads[i] = stl->tail; + } +} + +static void +insert_hash_edge(stl_file *stl, stl_hash_edge edge, + void (*match_neighbors)(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b)) { + stl_hash_edge *link; + stl_hash_edge *new_edge; + stl_hash_edge *temp; + int chain_number; + + if (stl->error) return; + + chain_number = stl_get_hash_for_edge(stl->M, &edge); + + link = stl->heads[chain_number]; + + if(link == stl->tail) { + /* This list doesn't have any edges currently in it. Add this one. */ + new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + if(new_edge == NULL) perror("insert_hash_edge"); + stl->stats.malloced++; + *new_edge = edge; + new_edge->next = stl->tail; + stl->heads[chain_number] = new_edge; + return; + } else if(!stl_compare_function(&edge, link)) { + /* This is a match. Record result in neighbors list. */ + match_neighbors(stl, &edge, link); + /* Delete the matched edge from the list. */ + stl->heads[chain_number] = link->next; + free(link); + stl->stats.freed++; + return; + } else { + /* Continue through the rest of the list */ + for(;;) { + if(link->next == stl->tail) { + /* This is the last item in the list. Insert a new edge. */ + new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + if(new_edge == NULL) perror("insert_hash_edge"); + stl->stats.malloced++; + *new_edge = edge; + new_edge->next = stl->tail; + link->next = new_edge; + stl->stats.collisions++; + return; + } else if(!stl_compare_function(&edge, link->next)) { + /* This is a match. Record result in neighbors list. */ + match_neighbors(stl, &edge, link->next); + + /* Delete the matched edge from the list. */ + temp = link->next; + link->next = link->next->next; + free(temp); + stl->stats.freed++; + return; + } else { + /* This is not a match. Go to the next link */ + link = link->next; + stl->stats.collisions++; + } + } + } +} + + +static int +stl_get_hash_for_edge(int M, stl_hash_edge *edge) { + return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17 + + edge->key[3] /13 + edge->key[4] / 11 + edge->key[5] / 7 ) % M); +} + +static int +stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) { + if(edge_a->facet_number == edge_b->facet_number) { + return 1; /* Don't match edges of the same facet */ + } else { + return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT); + } +} + +void +stl_check_facets_nearby(stl_file *stl, float tolerance) { + stl_hash_edge edge[3]; + stl_facet facet; + int i; + int j; + + if (stl->error) return; + + if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) + && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) + && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { + /* No need to check any further. All facets are connected */ + return; + } + + stl_initialize_facet_check_nearby(stl); + + for(i = 0; i < stl->stats.number_of_facets; i++) { + facet = stl->facet_start[i]; + // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. + // When using a memcmp on raw floats, those numbers report to be different. + // Unify all +0 and -0 to +0 to make the floats equal under memcmp. + { + uint32_t *f = (uint32_t*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f == 0x80000000) + // Negative zero, switch to positive zero. + *f = 0; + } + for(j = 0; j < 3; j++) { + if(stl->neighbors_start[i].neighbor[j] == -1) { + edge[j].facet_number = i; + edge[j].which_edge = j; + if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j], + &facet.vertex[(j + 1) % 3], + tolerance)) { + /* only insert edges that have different keys */ + insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby); + } + } + } + } + + stl_free_edges(stl); +} + +static int +stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, + stl_vertex *a, stl_vertex *b, float tolerance) { + // Index of a grid cell spaced by tolerance. + uint32_t vertex1[3] = { + (uint32_t)((a->x - stl->stats.min.x) / tolerance), + (uint32_t)((a->y - stl->stats.min.y) / tolerance), + (uint32_t)((a->z - stl->stats.min.z) / tolerance) + }; + uint32_t vertex2[3] = { + (uint32_t)((b->x - stl->stats.min.x) / tolerance), + (uint32_t)((b->y - stl->stats.min.y) / tolerance), + (uint32_t)((b->z - stl->stats.min.z) / tolerance) + }; + + if( (vertex1[0] == vertex2[0]) + && (vertex1[1] == vertex2[1]) + && (vertex1[2] == vertex2[2])) { + /* Both vertices hash to the same value */ + return 0; + } + + // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. + // This method is numerically robust. + if ((vertex1[0] != vertex2[0]) ? + (vertex1[0] < vertex2[0]) : + ((vertex1[1] != vertex2[1]) ? + (vertex1[1] < vertex2[1]) : + (vertex1[2] < vertex2[2]))) { + memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); + } else { + memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); + edge->which_edge += 3; /* this edge is loaded backwards */ + } + return 1; +} + +static void +stl_free_edges(stl_file *stl) { + int i; + stl_hash_edge *temp; + + if (stl->error) return; + + if(stl->stats.malloced != stl->stats.freed) { + for(i = 0; i < stl->M; i++) { + for(temp = stl->heads[i]; stl->heads[i] != stl->tail; + temp = stl->heads[i]) { + stl->heads[i] = stl->heads[i]->next; + free(temp); + stl->stats.freed++; + } + } + } + free(stl->heads); + free(stl->tail); +} + +static void +stl_initialize_facet_check_nearby(stl_file *stl) { + int i; + + if (stl->error) return; + + stl->stats.malloced = 0; + stl->stats.freed = 0; + stl->stats.collisions = 0; + + /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/ + /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ + /* tolerance *= 0.5;*/ + + stl->M = 81397; + + stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); + if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); + + stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby"); + + stl->tail->next = stl->tail; + + for(i = 0; i < stl->M; i++) { + stl->heads[i] = stl->tail; + } +} + + + +static void +stl_record_neighbors(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b) { + int i; + int j; + + if (stl->error) return; + + /* Facet a's neighbor is facet b */ + stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] = + edge_b->facet_number; /* sets the .neighbor part */ + + stl->neighbors_start[edge_a->facet_number]. + which_vertex_not[edge_a->which_edge % 3] = + (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + /* Facet b's neighbor is facet a */ + stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] = + edge_a->facet_number; /* sets the .neighbor part */ + + stl->neighbors_start[edge_b->facet_number]. + which_vertex_not[edge_b->which_edge % 3] = + (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3)) + || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) { + /* these facets are oriented in opposite directions. */ + /* their normals are probably messed up. */ + stl->neighbors_start[edge_a->facet_number]. + which_vertex_not[edge_a->which_edge % 3] += 3; + stl->neighbors_start[edge_b->facet_number]. + which_vertex_not[edge_b->which_edge % 3] += 3; + } + + + /* Count successful connects */ + /* Total connects */ + stl->stats.connected_edges += 2; + /* Count individual connects */ + i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) + + (stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) + + (stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1)); + j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) + + (stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) + + (stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1)); + if(i == 2) { + stl->stats.connected_facets_1_edge +=1; + } else if(i == 1) { + stl->stats.connected_facets_2_edge +=1; + } else { + stl->stats.connected_facets_3_edge +=1; + } + if(j == 2) { + stl->stats.connected_facets_1_edge +=1; + } else if(j == 1) { + stl->stats.connected_facets_2_edge +=1; + } else { + stl->stats.connected_facets_3_edge +=1; + } +} + +static void +stl_match_neighbors_exact(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b) { + if (stl->error) return; + stl_record_neighbors(stl, edge_a, edge_b); +} + +static void +stl_match_neighbors_nearby(stl_file *stl, + stl_hash_edge *edge_a, stl_hash_edge *edge_b) { + int facet1; + int facet2; + int vertex1; + int vertex2; + int vnot1; + int vnot2; + stl_vertex new_vertex1; + stl_vertex new_vertex2; + + if (stl->error) return; + + stl_record_neighbors(stl, edge_a, edge_b); + stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1, + &facet2, &vertex2, &new_vertex1, &new_vertex2); + if(facet1 != -1) { + if(facet1 == edge_a->facet_number) { + vnot1 = (edge_a->which_edge + 2) % 3; + } else { + vnot1 = (edge_b->which_edge + 2) % 3; + } + if(((vnot1 + 2) % 3) == vertex1) { + vnot1 += 3; + } + stl_change_vertices(stl, facet1, vnot1, new_vertex1); + } + if(facet2 != -1) { + if(facet2 == edge_a->facet_number) { + vnot2 = (edge_a->which_edge + 2) % 3; + } else { + vnot2 = (edge_b->which_edge + 2) % 3; + } + if(((vnot2 + 2) % 3) == vertex2) { + vnot2 += 3; + } + stl_change_vertices(stl, facet2, vnot2, new_vertex2); + } + stl->stats.edges_fixed += 2; +} + + +static void +stl_change_vertices(stl_file *stl, int facet_num, int vnot, + stl_vertex new_vertex) { + int first_facet; + int direction; + int next_edge; + int pivot_vertex; + + if (stl->error) return; + + first_facet = facet_num; + direction = 0; + + for(;;) { + if(vnot > 2) { + if(direction == 0) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + direction = 1; + } else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + direction = 0; + } + } else { + if(direction == 0) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + } +#if 0 + if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && + stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && + stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) + printf("Changing vertex %f,%f,%f: Same !!!\r\n", + new_vertex.x, new_vertex.y, new_vertex.z); + else { + if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].x, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].x), + new_vertex.x, + *reinterpret_cast(&new_vertex.x)); + if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].y, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].y), + new_vertex.y, + *reinterpret_cast(&new_vertex.y)); + if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].z, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].z), + new_vertex.z, + *reinterpret_cast(&new_vertex.z)); + } +#endif + stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; + vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; + facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; + + if(facet_num == -1) { + break; + } + + if(facet_num == first_facet) { + /* back to the beginning */ + printf("\ +Back to the first facet changing vertices: probably a mobius part.\n\ +Try using a smaller tolerance or don't do a nearby check\n"); + return; + } + } +} + + +static void +stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, + stl_hash_edge *edge_b, int *facet1, int *vertex1, + int *facet2, int *vertex2, + stl_vertex *new_vertex1, stl_vertex *new_vertex2) { + int v1a; /* pair 1, facet a */ + int v1b; /* pair 1, facet b */ + int v2a; /* pair 2, facet a */ + int v2b; /* pair 2, facet b */ + + /* Find first pair */ + if(edge_a->which_edge < 3) { + v1a = edge_a->which_edge; + v2a = (edge_a->which_edge + 1) % 3; + } else { + v2a = edge_a->which_edge % 3; + v1a = (edge_a->which_edge + 1) % 3; + } + if(edge_b->which_edge < 3) { + v1b = edge_b->which_edge; + v2b = (edge_b->which_edge + 1) % 3; + } else { + v2b = edge_b->which_edge % 3; + v1b = (edge_b->which_edge + 1) % 3; + } + + /* Of the first pair, which vertex, if any, should be changed */ + if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a], + &stl->facet_start[edge_b->facet_number].vertex[v1b], + sizeof(stl_vertex))) { + /* These facets are already equal. No need to change. */ + *facet1 = -1; + } else { + if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) + && (stl->neighbors_start[edge_a->facet_number]. + neighbor[(v1a + 2) % 3] == -1)) { + /* This vertex has no neighbors. This is a good one to change */ + *facet1 = edge_a->facet_number; + *vertex1 = v1a; + *new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b]; + } else { + *facet1 = edge_b->facet_number; + *vertex1 = v1b; + *new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a]; + } + } + + /* Of the second pair, which vertex, if any, should be changed */ + if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a], + &stl->facet_start[edge_b->facet_number].vertex[v2b], + sizeof(stl_vertex))) { + /* These facets are already equal. No need to change. */ + *facet2 = -1; + } else { + if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) + && (stl->neighbors_start[edge_a->facet_number]. + neighbor[(v2a + 2) % 3] == -1)) { + /* This vertex has no neighbors. This is a good one to change */ + *facet2 = edge_a->facet_number; + *vertex2 = v2a; + *new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b]; + } else { + *facet2 = edge_b->facet_number; + *vertex2 = v2b; + *new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a]; + } + } +} + +static void +stl_remove_facet(stl_file *stl, int facet_number) { + int neighbor[3]; + int vnot[3]; + int i; + int j; + + if (stl->error) return; + + stl->stats.facets_removed += 1; + /* Update list of connected edges */ + j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) + + (stl->neighbors_start[facet_number].neighbor[1] == -1) + + (stl->neighbors_start[facet_number].neighbor[2] == -1)); + if(j == 2) { + stl->stats.connected_facets_1_edge -= 1; + } else if(j == 1) { + stl->stats.connected_facets_2_edge -= 1; + stl->stats.connected_facets_1_edge -= 1; + } else if(j == 0) { + stl->stats.connected_facets_3_edge -= 1; + stl->stats.connected_facets_2_edge -= 1; + stl->stats.connected_facets_1_edge -= 1; + } + + stl->facet_start[facet_number] = + stl->facet_start[stl->stats.number_of_facets - 1]; + /* I could reallocate at this point, but it is not really necessary. */ + stl->neighbors_start[facet_number] = + stl->neighbors_start[stl->stats.number_of_facets - 1]; + stl->stats.number_of_facets -= 1; + + for(i = 0; i < 3; i++) { + neighbor[i] = stl->neighbors_start[facet_number].neighbor[i]; + vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i]; + } + + for(i = 0; i < 3; i++) { + if(neighbor[i] != -1) { + if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] != + stl->stats.number_of_facets) { + printf("\ +in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n", + stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3], + stl->stats.number_of_facets); + return; + } + stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] + = facet_number; + } + } +} + +void +stl_remove_unconnected_facets(stl_file *stl) { + /* A couple of things need to be done here. One is to remove any */ + /* completely unconnected facets (0 edges connected) since these are */ + /* useless and could be completely wrong. The second thing that needs to */ + /* be done is to remove any degenerate facets that were created during */ + /* stl_check_facets_nearby(). */ + + int i; + + if (stl->error) return; + + /* remove degenerate facets */ + for(i = 0; i < stl->stats.number_of_facets; i++) { + if( !memcmp(&stl->facet_start[i].vertex[0], + &stl->facet_start[i].vertex[1], sizeof(stl_vertex)) + || !memcmp(&stl->facet_start[i].vertex[1], + &stl->facet_start[i].vertex[2], sizeof(stl_vertex)) + || !memcmp(&stl->facet_start[i].vertex[0], + &stl->facet_start[i].vertex[2], sizeof(stl_vertex))) { + stl_remove_degenerate(stl, i); + i--; + } + } + + if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { + /* remove completely unconnected facets */ + for(i = 0; i < stl->stats.number_of_facets; i++) { + if( (stl->neighbors_start[i].neighbor[0] == -1) + && (stl->neighbors_start[i].neighbor[1] == -1) + && (stl->neighbors_start[i].neighbor[2] == -1)) { + /* This facet is completely unconnected. Remove it. */ + stl_remove_facet(stl, i); + i--; + } + } + } +} + +static void +stl_remove_degenerate(stl_file *stl, int facet) { + int edge1; + int edge2; + int edge3; + int neighbor1; + int neighbor2; + int neighbor3; + int vnot1; + int vnot2; + int vnot3; + + if (stl->error) return; + + if( !memcmp(&stl->facet_start[facet].vertex[0], + &stl->facet_start[facet].vertex[1], sizeof(stl_vertex)) + && !memcmp(&stl->facet_start[facet].vertex[1], + &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { + /* all 3 vertices are equal. Just remove the facet. I don't think*/ + /* this is really possible, but just in case... */ + printf("removing a facet in stl_remove_degenerate\n"); + + stl_remove_facet(stl, facet); + return; + } + + if(!memcmp(&stl->facet_start[facet].vertex[0], + &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) { + edge1 = 1; + edge2 = 2; + edge3 = 0; + } else if(!memcmp(&stl->facet_start[facet].vertex[1], + &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) { + edge1 = 0; + edge2 = 2; + edge3 = 1; + } else if(!memcmp(&stl->facet_start[facet].vertex[2], + &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) { + edge1 = 0; + edge2 = 1; + edge3 = 2; + } else { + /* No degenerate. Function shouldn't have been called. */ + return; + } + neighbor1 = stl->neighbors_start[facet].neighbor[edge1]; + neighbor2 = stl->neighbors_start[facet].neighbor[edge2]; + + if(neighbor1 == -1) { + stl_update_connects_remove_1(stl, neighbor2); + } + if(neighbor2 == -1) { + stl_update_connects_remove_1(stl, neighbor1); + } + + + neighbor3 = stl->neighbors_start[facet].neighbor[edge3]; + vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1]; + vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2]; + vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3]; + + if(neighbor1 >= 0){ + stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2; + stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2; + } + if(neighbor2 >= 0){ + stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1; + stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1; + } + + stl_remove_facet(stl, facet); + + if(neighbor3 >= 0) { + stl_update_connects_remove_1(stl, neighbor3); + stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1; + } +} + +void +stl_update_connects_remove_1(stl_file *stl, int facet_num) { + int j; + + if (stl->error) return; + /* Update list of connected edges */ + j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) + + (stl->neighbors_start[facet_num].neighbor[1] == -1) + + (stl->neighbors_start[facet_num].neighbor[2] == -1)); + if(j == 0) { /* Facet has 3 neighbors */ + stl->stats.connected_facets_3_edge -= 1; + } else if(j == 1) { /* Facet has 2 neighbors */ + stl->stats.connected_facets_2_edge -= 1; + } else if(j == 2) { /* Facet has 1 neighbor */ + stl->stats.connected_facets_1_edge -= 1; + } +} + +void +stl_fill_holes(stl_file *stl) { + stl_facet facet; + stl_facet new_facet; + int neighbors_initial[3]; + stl_hash_edge edge; + int first_facet; + int direction; + int facet_num; + int vnot; + int next_edge; + int pivot_vertex; + int next_facet; + int i; + int j; + int k; + + if (stl->error) return; + + /* Insert all unconnected edges into hash list */ + stl_initialize_facet_check_nearby(stl); + for(i = 0; i < stl->stats.number_of_facets; i++) { + facet = stl->facet_start[i]; + for(j = 0; j < 3; j++) { + if(stl->neighbors_start[i].neighbor[j] != -1) continue; + edge.facet_number = i; + edge.which_edge = j; + stl_load_edge_exact(stl, &edge, &facet.vertex[j], + &facet.vertex[(j + 1) % 3]); + + insert_hash_edge(stl, edge, stl_match_neighbors_exact); + } + } + + for(i = 0; i < stl->stats.number_of_facets; i++) { + facet = stl->facet_start[i]; + neighbors_initial[0] = stl->neighbors_start[i].neighbor[0]; + neighbors_initial[1] = stl->neighbors_start[i].neighbor[1]; + neighbors_initial[2] = stl->neighbors_start[i].neighbor[2]; + first_facet = i; + for(j = 0; j < 3; j++) { + if(stl->neighbors_start[i].neighbor[j] != -1) continue; + + new_facet.vertex[0] = facet.vertex[j]; + new_facet.vertex[1] = facet.vertex[(j + 1) % 3]; + if(neighbors_initial[(j + 2) % 3] == -1) { + direction = 1; + } else { + direction = 0; + } + + facet_num = i; + vnot = (j + 2) % 3; + + for(;;) { + if(vnot > 2) { + if(direction == 0) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + direction = 1; + } else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + direction = 0; + } + } else { + if(direction == 0) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + } + next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; + + if(next_facet == -1) { + new_facet.vertex[2] = stl->facet_start[facet_num]. + vertex[vnot % 3]; + stl_add_facet(stl, &new_facet); + for(k = 0; k < 3; k++) { + edge.facet_number = stl->stats.number_of_facets - 1; + edge.which_edge = k; + stl_load_edge_exact(stl, &edge, &new_facet.vertex[k], + &new_facet.vertex[(k + 1) % 3]); + + insert_hash_edge(stl, edge, stl_match_neighbors_exact); + } + break; + } else { + vnot = stl->neighbors_start[facet_num]. + which_vertex_not[next_edge]; + facet_num = next_facet; + } + + if(facet_num == first_facet) { + /* back to the beginning */ + printf("\ +Back to the first facet filling holes: probably a mobius part.\n\ +Try using a smaller tolerance or don't do a nearby check\n"); + return; + } + } + } + } +} + +void +stl_add_facet(stl_file *stl, stl_facet *new_facet) { + if (stl->error) return; + + stl->stats.facets_added += 1; + if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) { + stl->facet_start = (stl_facet*)realloc(stl->facet_start, + (sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); + if(stl->facet_start == NULL) perror("stl_add_facet"); + stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start, + (sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256))); + if(stl->neighbors_start == NULL) perror("stl_add_facet"); + stl->stats.facets_malloced += 256; + } + stl->facet_start[stl->stats.number_of_facets] = *new_facet; + + /* note that the normal vector is not set here, just initialized to 0 */ + stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0; + stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0; + stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0; + + stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1; + stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1; + stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1; + stl->stats.number_of_facets += 1; +} diff --git a/xs/src/admesh/normals.c b/xs/src/admesh/normals.c deleted file mode 100644 index 2832899fa..000000000 --- a/xs/src/admesh/normals.c +++ /dev/null @@ -1,333 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include -#include -#include - -#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_facet(stl_file *stl, int facet_num) { - stl_vertex tmp_vertex; - /* int tmp_neighbor;*/ - int neighbor[3]; - int vnot[3]; - - stl->stats.facets_reversed += 1; - - neighbor[0] = stl->neighbors_start[facet_num].neighbor[0]; - neighbor[1] = stl->neighbors_start[facet_num].neighbor[1]; - neighbor[2] = stl->neighbors_start[facet_num].neighbor[2]; - vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0]; - vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1]; - vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2]; - - /* reverse the facet */ - tmp_vertex = stl->facet_start[facet_num].vertex[0]; - stl->facet_start[facet_num].vertex[0] = - stl->facet_start[facet_num].vertex[1]; - stl->facet_start[facet_num].vertex[1] = tmp_vertex; - - /* fix the vnots of the neighboring facets */ - if(neighbor[0] != -1) - stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = - (stl->neighbors_start[neighbor[0]]. - which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6; - if(neighbor[1] != -1) - stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = - (stl->neighbors_start[neighbor[1]]. - which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6; - if(neighbor[2] != -1) - stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = - (stl->neighbors_start[neighbor[2]]. - which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6; - - /* swap the neighbors of the facet that is being reversed */ - stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; - stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; - - /* swap the vnots of the facet that is being reversed */ - stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2]; - stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1]; - - /* reverse the values of the vnots of the facet that is being reversed */ - stl->neighbors_start[facet_num].which_vertex_not[0] = - (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6; - stl->neighbors_start[facet_num].which_vertex_not[1] = - (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6; - stl->neighbors_start[facet_num].which_vertex_not[2] = - (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6; -} - -void -stl_fix_normal_directions(stl_file *stl) { - char *norm_sw; - /* int edge_num;*/ - /* int vnot;*/ - int checked = 0; - int facet_num; - /* int next_facet;*/ - int i; - int j; - struct stl_normal { - int facet_num; - struct stl_normal *next; - }; - struct stl_normal *head; - struct stl_normal *tail; - struct stl_normal *newn; - struct stl_normal *temp; - - if (stl->error) return; - - /* Initialize linked list. */ - head = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(head == NULL) perror("stl_fix_normal_directions"); - tail = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(tail == NULL) perror("stl_fix_normal_directions"); - head->next = tail; - tail->next = tail; - - /* Initialize list that keeps track of already fixed facets. */ - norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char)); - if(norm_sw == NULL) perror("stl_fix_normal_directions"); - - - facet_num = 0; - /* If normal vector is not within tolerance and backwards: - Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances - of it being wrong randomly are low if most of the triangles are right: */ - if(stl_check_normal_vector(stl, 0, 0) == 2) - stl_reverse_facet(stl, 0); - - /* Say that we've fixed this facet: */ - norm_sw[facet_num] = 1; - checked++; - - for(;;) { - /* Add neighbors_to_list. - Add unconnected neighbors to the list:a */ - for(j = 0; j < 3; j++) { - /* 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 this edge of the facet is connected: */ - if(stl->neighbors_start[facet_num].neighbor[j] != -1) { - /* If we haven't fixed this facet yet, add it to the list: */ - if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { - /* Add node to beginning of list. */ - newn = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(newn == NULL) perror("stl_fix_normal_directions"); - newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; - newn->next = head->next; - head->next = newn; - } - } - } - /* Get next facet to fix from top of list. */ - if(head->next != tail) { - facet_num = head->next->facet_num; - if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */ - norm_sw[facet_num] = 1; /* Record this one as being fixed. */ - checked++; - } - temp = head->next; /* Delete this facet from the list. */ - head->next = head->next->next; - free(temp); - } else { /* if we ran out of facets to fix: */ - /* All of the facets in this part have been fixed. */ - stl->stats.number_of_parts += 1; - if(checked >= stl->stats.number_of_facets) { - /* All of the facets have been checked. Bail out. */ - break; - } else { - /* There is another part here. Find it and continue. */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if(norm_sw[i] == 0) { - /* This is the first facet of the next part. */ - facet_num = i; - if(stl_check_normal_vector(stl, i, 0) == 2) { - stl_reverse_facet(stl, i); - } - - norm_sw[facet_num] = 1; - checked++; - break; - } - } - } - } - } - free(head); - free(tail); - free(norm_sw); -} - -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 */ - /* Returns 4 if the status is unknown. */ - - float normal[3]; - float test_norm[3]; - stl_facet *facet; - - facet = &stl->facet_start[facet_num]; - - stl_calculate_normal(normal, facet); - stl_normalize_vector(normal); - - if( (ABS(normal[0] - facet->normal.x) < 0.001) - && (ABS(normal[1] - facet->normal.y) < 0.001) - && (ABS(normal[2] - facet->normal.z) < 0.001)) { - /* It is not really necessary to change the values here */ - /* but just for consistency, I will. */ - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; - return 0; - } - - test_norm[0] = facet->normal.x; - test_norm[1] = facet->normal.y; - test_norm[2] = facet->normal.z; - - stl_normalize_vector(test_norm); - if( (ABS(normal[0] - test_norm[0]) < 0.001) - && (ABS(normal[1] - test_norm[1]) < 0.001) - && (ABS(normal[2] - test_norm[2]) < 0.001)) { - if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; - stl->stats.normals_fixed += 1; - } - return 1; - } - - stl_reverse_vector(test_norm); - if( (ABS(normal[0] - test_norm[0]) < 0.001) - && (ABS(normal[1] - test_norm[1]) < 0.001) - && (ABS(normal[2] - test_norm[2]) < 0.001)) { - /* Facet is backwards. */ - if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; - stl->stats.normals_fixed += 1; - } - return 2; - } - if(normal_fix_flag) { - facet->normal.x = normal[0]; - facet->normal.y = normal[1]; - facet->normal.z = normal[2]; - stl->stats.normals_fixed += 1; - } - 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; - - 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]); -} - -void stl_normalize_vector(float v[]) { - double length; - double factor; - float min_normal_length; - - length = sqrt((double)v[0] * (double)v[0] + (double)v[1] * (double)v[1] + (double)v[2] * (double)v[2]); - min_normal_length = 0.000000000001; - if(length < min_normal_length) { - v[0] = 0.0; - v[1] = 0.0; - v[2] = 0.0; - return; - } - factor = 1.0 / length; - v[0] *= factor; - v[1] *= factor; - v[2] *= factor; -} - -void -stl_fix_normal_values(stl_file *stl) { - int i; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl_check_normal_vector(stl, i, 1); - } -} - -void -stl_reverse_all_facets(stl_file *stl) { - int i; - float normal[3]; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl_reverse_facet(stl, i); - stl_calculate_normal(normal, &stl->facet_start[i]); - stl_normalize_vector(normal); - stl->facet_start[i].normal.x = normal[0]; - stl->facet_start[i].normal.y = normal[1]; - stl->facet_start[i].normal.z = normal[2]; - } -} - diff --git a/xs/src/admesh/normals.cpp b/xs/src/admesh/normals.cpp new file mode 100644 index 000000000..28b1c47b0 --- /dev/null +++ b/xs/src/admesh/normals.cpp @@ -0,0 +1,325 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include +#include +#include + +#include "stl.h" + +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) { + stl_vertex tmp_vertex; + /* int tmp_neighbor;*/ + int neighbor[3]; + int vnot[3]; + + stl->stats.facets_reversed += 1; + + neighbor[0] = stl->neighbors_start[facet_num].neighbor[0]; + neighbor[1] = stl->neighbors_start[facet_num].neighbor[1]; + neighbor[2] = stl->neighbors_start[facet_num].neighbor[2]; + vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0]; + vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1]; + vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2]; + + /* reverse the facet */ + tmp_vertex = stl->facet_start[facet_num].vertex[0]; + stl->facet_start[facet_num].vertex[0] = + stl->facet_start[facet_num].vertex[1]; + stl->facet_start[facet_num].vertex[1] = tmp_vertex; + + /* fix the vnots of the neighboring facets */ + if(neighbor[0] != -1) + stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = + (stl->neighbors_start[neighbor[0]]. + which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6; + if(neighbor[1] != -1) + stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = + (stl->neighbors_start[neighbor[1]]. + which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6; + if(neighbor[2] != -1) + stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = + (stl->neighbors_start[neighbor[2]]. + which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6; + + /* swap the neighbors of the facet that is being reversed */ + stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; + stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; + + /* swap the vnots of the facet that is being reversed */ + stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2]; + stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1]; + + /* reverse the values of the vnots of the facet that is being reversed */ + stl->neighbors_start[facet_num].which_vertex_not[0] = + (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6; + stl->neighbors_start[facet_num].which_vertex_not[1] = + (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6; + stl->neighbors_start[facet_num].which_vertex_not[2] = + (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6; +} + +void +stl_fix_normal_directions(stl_file *stl) { + char *norm_sw; + /* int edge_num;*/ + /* int vnot;*/ + int checked = 0; + int facet_num; + /* int next_facet;*/ + int i; + int j; + struct stl_normal { + int facet_num; + struct stl_normal *next; + }; + struct stl_normal *head; + struct stl_normal *tail; + struct stl_normal *newn; + struct stl_normal *temp; + + if (stl->error) return; + + /* Initialize linked list. */ + head = (struct stl_normal*)malloc(sizeof(struct stl_normal)); + if(head == NULL) perror("stl_fix_normal_directions"); + tail = (struct stl_normal*)malloc(sizeof(struct stl_normal)); + if(tail == NULL) perror("stl_fix_normal_directions"); + head->next = tail; + tail->next = tail; + + /* Initialize list that keeps track of already fixed facets. */ + norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char)); + if(norm_sw == NULL) perror("stl_fix_normal_directions"); + + + facet_num = 0; + /* If normal vector is not within tolerance and backwards: + Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances + of it being wrong randomly are low if most of the triangles are right: */ + if(stl_check_normal_vector(stl, 0, 0) == 2) + stl_reverse_facet(stl, 0); + + /* Say that we've fixed this facet: */ + norm_sw[facet_num] = 1; + checked++; + + for(;;) { + /* Add neighbors_to_list. + Add unconnected neighbors to the list:a */ + for(j = 0; j < 3; j++) { + /* 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 this edge of the facet is connected: */ + if(stl->neighbors_start[facet_num].neighbor[j] != -1) { + /* If we haven't fixed this facet yet, add it to the list: */ + if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { + /* Add node to beginning of list. */ + newn = (struct stl_normal*)malloc(sizeof(struct stl_normal)); + if(newn == NULL) perror("stl_fix_normal_directions"); + newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; + newn->next = head->next; + head->next = newn; + } + } + } + /* Get next facet to fix from top of list. */ + if(head->next != tail) { + facet_num = head->next->facet_num; + if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */ + norm_sw[facet_num] = 1; /* Record this one as being fixed. */ + checked++; + } + temp = head->next; /* Delete this facet from the list. */ + head->next = head->next->next; + free(temp); + } else { /* if we ran out of facets to fix: */ + /* All of the facets in this part have been fixed. */ + stl->stats.number_of_parts += 1; + if(checked >= stl->stats.number_of_facets) { + /* All of the facets have been checked. Bail out. */ + break; + } else { + /* There is another part here. Find it and continue. */ + for(i = 0; i < stl->stats.number_of_facets; i++) { + if(norm_sw[i] == 0) { + /* This is the first facet of the next part. */ + facet_num = i; + if(stl_check_normal_vector(stl, i, 0) == 2) { + stl_reverse_facet(stl, i); + } + + norm_sw[facet_num] = 1; + checked++; + break; + } + } + } + } + } + free(head); + free(tail); + free(norm_sw); +} + +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 */ + /* Returns 4 if the status is unknown. */ + + float normal[3]; + float test_norm[3]; + stl_facet *facet; + + facet = &stl->facet_start[facet_num]; + + stl_calculate_normal(normal, facet); + stl_normalize_vector(normal); + + if( (ABS(normal[0] - facet->normal.x) < 0.001) + && (ABS(normal[1] - facet->normal.y) < 0.001) + && (ABS(normal[2] - facet->normal.z) < 0.001)) { + /* It is not really necessary to change the values here */ + /* but just for consistency, I will. */ + facet->normal.x = normal[0]; + facet->normal.y = normal[1]; + facet->normal.z = normal[2]; + return 0; + } + + test_norm[0] = facet->normal.x; + test_norm[1] = facet->normal.y; + test_norm[2] = facet->normal.z; + + stl_normalize_vector(test_norm); + if( (ABS(normal[0] - test_norm[0]) < 0.001) + && (ABS(normal[1] - test_norm[1]) < 0.001) + && (ABS(normal[2] - test_norm[2]) < 0.001)) { + if(normal_fix_flag) { + facet->normal.x = normal[0]; + facet->normal.y = normal[1]; + facet->normal.z = normal[2]; + stl->stats.normals_fixed += 1; + } + return 1; + } + + stl_reverse_vector(test_norm); + if( (ABS(normal[0] - test_norm[0]) < 0.001) + && (ABS(normal[1] - test_norm[1]) < 0.001) + && (ABS(normal[2] - test_norm[2]) < 0.001)) { + /* Facet is backwards. */ + if(normal_fix_flag) { + facet->normal.x = normal[0]; + facet->normal.y = normal[1]; + facet->normal.z = normal[2]; + stl->stats.normals_fixed += 1; + } + return 2; + } + if(normal_fix_flag) { + facet->normal.x = normal[0]; + facet->normal.y = normal[1]; + facet->normal.z = normal[2]; + stl->stats.normals_fixed += 1; + } + return 4; +} + +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]); +} + +void stl_normalize_vector(float v[]) { + double length; + double factor; + float min_normal_length; + + length = sqrt((double)v[0] * (double)v[0] + (double)v[1] * (double)v[1] + (double)v[2] * (double)v[2]); + min_normal_length = 0.000000000001; + if(length < min_normal_length) { + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; + return; + } + factor = 1.0 / length; + v[0] *= factor; + v[1] *= factor; + v[2] *= factor; +} + +void +stl_fix_normal_values(stl_file *stl) { + int i; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + stl_check_normal_vector(stl, i, 1); + } +} + +void +stl_reverse_all_facets(stl_file *stl) { + int i; + float normal[3]; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + stl_reverse_facet(stl, i); + stl_calculate_normal(normal, &stl->facet_start[i]); + stl_normalize_vector(normal); + stl->facet_start[i].normal.x = normal[0]; + stl->facet_start[i].normal.y = normal[1]; + stl->facet_start[i].normal.z = normal[2]; + } +} + diff --git a/xs/src/admesh/shared.c b/xs/src/admesh/shared.c deleted file mode 100644 index 56164e25f..000000000 --- a/xs/src/admesh/shared.c +++ /dev/null @@ -1,262 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include - -#include "stl.h" - -void -stl_invalidate_shared_vertices(stl_file *stl) { - if (stl->error) return; - - if (stl->v_indices != NULL) { - free(stl->v_indices); - stl->v_indices = NULL; - } - if (stl->v_shared != NULL) { - free(stl->v_shared); - stl->v_shared = NULL; - } -} - -void -stl_generate_shared_vertices(stl_file *stl) { - int i; - int j; - int first_facet; - int direction; - int facet_num; - int vnot; - int next_edge; - int pivot_vertex; - int next_facet; - int reversed; - - if (stl->error) return; - - /* make sure this function is idempotent and does not leak memory */ - stl_invalidate_shared_vertices(stl); - - stl->v_indices = (v_indices_struct*) - calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); - if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); - stl->v_shared = (stl_vertex*) - calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); - if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); - stl->stats.shared_malloced = stl->stats.number_of_facets / 2; - stl->stats.shared_vertices = 0; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl->v_indices[i].vertex[0] = -1; - stl->v_indices[i].vertex[1] = -1; - stl->v_indices[i].vertex[2] = -1; - } - - - for(i = 0; i < stl->stats.number_of_facets; i++) { - first_facet = i; - for(j = 0; j < 3; j++) { - if(stl->v_indices[i].vertex[j] != -1) { - continue; - } - if(stl->stats.shared_vertices == stl->stats.shared_malloced) { - stl->stats.shared_malloced += 1024; - stl->v_shared = (stl_vertex*)realloc(stl->v_shared, - stl->stats.shared_malloced * sizeof(stl_vertex)); - if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); - } - - stl->v_shared[stl->stats.shared_vertices] = - stl->facet_start[i].vertex[j]; - - direction = 0; - reversed = 0; - facet_num = i; - vnot = (j + 2) % 3; - - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } - stl->v_indices[facet_num].vertex[pivot_vertex] = - stl->stats.shared_vertices; - - next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; - if(next_facet == -1) { - if(reversed) { - break; - } else { - direction = 1; - vnot = (j + 1) % 3; - reversed = 1; - facet_num = first_facet; - } - } else if(next_facet != first_facet) { - vnot = stl->neighbors_start[facet_num]. - which_vertex_not[next_edge]; - facet_num = next_facet; - } else { - break; - } - } - stl->stats.shared_vertices += 1; - } - } -} - -void -stl_write_off(stl_file *stl, char *file) { - int i; - FILE *fp; - char *error_msg; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - fprintf(fp, "OFF\n"); - fprintf(fp, "%d %d 0\n", - stl->stats.shared_vertices, stl->stats.number_of_facets); - - for(i = 0; i < stl->stats.shared_vertices; i++) { - fprintf(fp, "\t%f %f %f\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); - } - for(i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - } - fclose(fp); -} - -void -stl_write_vrml(stl_file *stl, char *file) { - int i; - FILE *fp; - char *error_msg; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - fprintf(fp, "#VRML V1.0 ascii\n\n"); - fprintf(fp, "Separator {\n"); - fprintf(fp, "\tDEF STLShape ShapeHints {\n"); - fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n"); - fprintf(fp, "\t\tfaceType CONVEX\n"); - fprintf(fp, "\t\tshapeType SOLID\n"); - fprintf(fp, "\t\tcreaseAngle 0.0\n"); - fprintf(fp, "\t}\n"); - fprintf(fp, "\tDEF STLModel Separator {\n"); - fprintf(fp, "\t\tDEF STLColor Material {\n"); - fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n"); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n"); - fprintf(fp, "\t\t\tpoint [\n"); - - for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { - fprintf(fp, "\t\t\t\t%f %f %f,\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); - } - fprintf(fp, "\t\t\t\t%f %f %f]\n", - stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); - fprintf(fp, "\t\t\tcoordIndex [\n"); - - for(i = 0; i < (stl->stats.number_of_facets - 1); i++) { - fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - } - fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t}\n"); - fprintf(fp, "}\n"); - fclose(fp); -} - -void stl_write_obj (stl_file *stl, char *file) { - int i; - FILE* fp; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - for (i = 0; i < stl->stats.shared_vertices; i++) { - fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); - } - for (i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); - } - - fclose(fp); -} diff --git a/xs/src/admesh/shared.cpp b/xs/src/admesh/shared.cpp new file mode 100644 index 000000000..8080f3574 --- /dev/null +++ b/xs/src/admesh/shared.cpp @@ -0,0 +1,264 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include + +#include + +#include "stl.h" + +void +stl_invalidate_shared_vertices(stl_file *stl) { + if (stl->error) return; + + if (stl->v_indices != NULL) { + free(stl->v_indices); + stl->v_indices = NULL; + } + if (stl->v_shared != NULL) { + free(stl->v_shared); + stl->v_shared = NULL; + } +} + +void +stl_generate_shared_vertices(stl_file *stl) { + int i; + int j; + int first_facet; + int direction; + int facet_num; + int vnot; + int next_edge; + int pivot_vertex; + int next_facet; + int reversed; + + if (stl->error) return; + + /* make sure this function is idempotent and does not leak memory */ + stl_invalidate_shared_vertices(stl); + + stl->v_indices = (v_indices_struct*) + calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); + if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); + stl->v_shared = (stl_vertex*) + calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); + if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); + stl->stats.shared_malloced = stl->stats.number_of_facets / 2; + stl->stats.shared_vertices = 0; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + stl->v_indices[i].vertex[0] = -1; + stl->v_indices[i].vertex[1] = -1; + stl->v_indices[i].vertex[2] = -1; + } + + + for(i = 0; i < stl->stats.number_of_facets; i++) { + first_facet = i; + for(j = 0; j < 3; j++) { + if(stl->v_indices[i].vertex[j] != -1) { + continue; + } + if(stl->stats.shared_vertices == stl->stats.shared_malloced) { + stl->stats.shared_malloced += 1024; + stl->v_shared = (stl_vertex*)realloc(stl->v_shared, + stl->stats.shared_malloced * sizeof(stl_vertex)); + if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); + } + + stl->v_shared[stl->stats.shared_vertices] = + stl->facet_start[i].vertex[j]; + + direction = 0; + reversed = 0; + facet_num = i; + vnot = (j + 2) % 3; + + for(;;) { + if(vnot > 2) { + if(direction == 0) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + direction = 1; + } else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + direction = 0; + } + } else { + if(direction == 0) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + } + stl->v_indices[facet_num].vertex[pivot_vertex] = + stl->stats.shared_vertices; + + next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; + if(next_facet == -1) { + if(reversed) { + break; + } else { + direction = 1; + vnot = (j + 1) % 3; + reversed = 1; + facet_num = first_facet; + } + } else if(next_facet != first_facet) { + vnot = stl->neighbors_start[facet_num]. + which_vertex_not[next_edge]; + facet_num = next_facet; + } else { + break; + } + } + stl->stats.shared_vertices += 1; + } + } +} + +void +stl_write_off(stl_file *stl, char *file) { + int i; + FILE *fp; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + fprintf(fp, "OFF\n"); + fprintf(fp, "%d %d 0\n", + stl->stats.shared_vertices, stl->stats.number_of_facets); + + for(i = 0; i < stl->stats.shared_vertices; i++) { + fprintf(fp, "\t%f %f %f\n", + stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + } + for(i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], + stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + } + fclose(fp); +} + +void +stl_write_vrml(stl_file *stl, char *file) { + int i; + FILE *fp; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + fprintf(fp, "#VRML V1.0 ascii\n\n"); + fprintf(fp, "Separator {\n"); + fprintf(fp, "\tDEF STLShape ShapeHints {\n"); + fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n"); + fprintf(fp, "\t\tfaceType CONVEX\n"); + fprintf(fp, "\t\tshapeType SOLID\n"); + fprintf(fp, "\t\tcreaseAngle 0.0\n"); + fprintf(fp, "\t}\n"); + fprintf(fp, "\tDEF STLModel Separator {\n"); + fprintf(fp, "\t\tDEF STLColor Material {\n"); + fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n"); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n"); + fprintf(fp, "\t\t\tpoint [\n"); + + for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { + fprintf(fp, "\t\t\t\t%f %f %f,\n", + stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + } + fprintf(fp, "\t\t\t\t%f %f %f]\n", + stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); + fprintf(fp, "\t\t\tcoordIndex [\n"); + + for(i = 0; i < (stl->stats.number_of_facets - 1); i++) { + fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], + stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + } + fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], + stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t}\n"); + fprintf(fp, "}\n"); + fclose(fp); +} + +void stl_write_obj (stl_file *stl, char *file) { + int i; + FILE* fp; + + if (stl->error) return; + + /* Open the file */ + 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); + perror(error_msg); + free(error_msg); + stl->error = 1; + return; + } + + for (i = 0; i < stl->stats.shared_vertices; i++) { + fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z); + } + for (i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); + } + + fclose(fp); +} 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.c deleted file mode 100644 index 7d8e4eab8..000000000 --- a/xs/src/admesh/stl_io.c +++ /dev/null @@ -1,420 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include -#include "stl.h" -#include "config.h" - -#if !defined(SEEK_SET) -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif - -void -stl_print_edges(stl_file *stl, FILE *file) { - int i; - int edges_allocated; - - if (stl->error) return; - - edges_allocated = stl->stats.number_of_facets * 3; - for(i = 0; i < edges_allocated; i++) { - fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n", - stl->edge_start[i].facet_number, - stl->edge_start[i].p1.x, stl->edge_start[i].p1.y, - stl->edge_start[i].p1.z, stl->edge_start[i].p2.x, - stl->edge_start[i].p2.y, stl->edge_start[i].p2.z); - } -} - - -void -stl_stats_out(stl_file *stl, FILE *file, char *input_file) { - if (stl->error) return; - - /* this is here for Slic3r, without our config.h - it won't use this part of the code anyway */ -#ifndef VERSION -#define VERSION "unknown" -#endif - fprintf(file, "\n\ -================= Results produced by ADMesh version " VERSION " ================\n"); - fprintf(file, "\ -Input file : %s\n", input_file); - if(stl->stats.type == binary) { - fprintf(file, "\ -File type : Binary STL file\n"); - } else { - fprintf(file, "\ -File type : ASCII STL file\n"); - } - fprintf(file, "\ -Header : %s\n", stl->stats.header); - fprintf(file, "============== Size ==============\n"); - fprintf(file, "Min X = % f, Max X = % f\n", - stl->stats.min.x, stl->stats.max.x); - fprintf(file, "Min Y = % f, Max Y = % f\n", - stl->stats.min.y, stl->stats.max.y); - fprintf(file, "Min Z = % f, Max Z = % f\n", - stl->stats.min.z, stl->stats.max.z); - - fprintf(file, "\ -========= Facet Status ========== Original ============ Final ====\n"); - fprintf(file, "\ -Number of facets : %5d %5d\n", - stl->stats.original_num_facets, stl->stats.number_of_facets); - fprintf(file, "\ -Facets with 1 disconnected edge : %5d %5d\n", - stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - - stl->stats.connected_facets_3_edge); - fprintf(file, "\ -Facets with 2 disconnected edges : %5d %5d\n", - stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - - stl->stats.connected_facets_2_edge); - fprintf(file, "\ -Facets with 3 disconnected edges : %5d %5d\n", - stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - - stl->stats.connected_facets_1_edge); - fprintf(file, "\ -Total disconnected facets : %5d %5d\n", - stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + - stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - - stl->stats.connected_facets_3_edge); - - fprintf(file, - "=== Processing Statistics === ===== Other Statistics =====\n"); - fprintf(file, "\ -Number of parts : %5d Volume : % f\n", - stl->stats.number_of_parts, stl->stats.volume); - fprintf(file, "\ -Degenerate facets : %5d\n", stl->stats.degenerate_facets); - fprintf(file, "\ -Edges fixed : %5d\n", stl->stats.edges_fixed); - fprintf(file, "\ -Facets removed : %5d\n", stl->stats.facets_removed); - fprintf(file, "\ -Facets added : %5d\n", stl->stats.facets_added); - fprintf(file, "\ -Facets reversed : %5d\n", stl->stats.facets_reversed); - fprintf(file, "\ -Backwards edges : %5d\n", stl->stats.backwards_edges); - fprintf(file, "\ -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"); - 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); - stl->error = 1; - return; - } - - fprintf(fp, "solid %s\n", label); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, " facet normal % .8E % .8E % .8E\n", - stl->facet_start[i].normal.x, stl->facet_start[i].normal.y, - stl->facet_start[i].normal.z); - fprintf(fp, " outer loop\n"); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); - fprintf(fp, " endloop\n"); - fprintf(fp, " endfacet\n"); - } - - fprintf(fp, "endsolid %s\n", label); - - fclose(fp); -} - -void -stl_print_neighbors(stl_file *stl, char *file) { - int i; - FILE *fp; - char *error_msg; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - for(i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", - i, - stl->neighbors_start[i].neighbor[0], - (int)stl->neighbors_start[i].which_vertex_not[0], - stl->neighbors_start[i].neighbor[1], - (int)stl->neighbors_start[i].which_vertex_not[1], - stl->neighbors_start[i].neighbor[2], - (int)stl->neighbors_start[i].which_vertex_not[2]); - } - fclose(fp); -} - -void -stl_write_binary(stl_file *stl, const char *file, const char *label) { - FILE *fp; - int i; - char *error_msg; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - fprintf(fp, "%s", label); - for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); - - fseek(fp, LABEL_SIZE, SEEK_SET); - fwrite(&stl->stats.number_of_facets, 4, 1, fp); - for(i = 0; i < stl->stats.number_of_facets; i++) - fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); - fclose(fp); -} - -void -stl_write_vertex(stl_file *stl, int facet, int vertex) { - if (stl->error) return; - printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, - stl->facet_start[facet].vertex[vertex].x, - stl->facet_start[facet].vertex[vertex].y, - stl->facet_start[facet].vertex[vertex].z); -} - -void -stl_write_facet(stl_file *stl, char *label, int facet) { - if (stl->error) return; - printf("facet (%d)/ %s\n", facet, label); - stl_write_vertex(stl, facet, 0); - stl_write_vertex(stl, facet, 1); - stl_write_vertex(stl, facet, 2); -} - -void -stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) { - if (stl->error) return; - printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label); - if(edge.which_edge < 3) { - stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); - stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); - } else { - stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); - stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); - } -} - -void -stl_write_neighbor(stl_file *stl, int facet) { - if (stl->error) return; - printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, - stl->neighbors_start[facet].neighbor[0], - stl->neighbors_start[facet].neighbor[1], - stl->neighbors_start[facet].neighbor[2], - stl->neighbors_start[facet].which_vertex_not[0], - stl->neighbors_start[facet].which_vertex_not[1], - stl->neighbors_start[facet].which_vertex_not[2]); -} - -void -stl_write_quad_object(stl_file *stl, char *file) { - FILE *fp; - int i; - int j; - char *error_msg; - stl_vertex connect_color; - stl_vertex uncon_1_color; - stl_vertex uncon_2_color; - stl_vertex uncon_3_color; - stl_vertex color; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - connect_color.x = 0.0; - connect_color.y = 0.0; - connect_color.z = 1.0; - uncon_1_color.x = 0.0; - uncon_1_color.y = 1.0; - uncon_1_color.z = 0.0; - uncon_2_color.x = 1.0; - uncon_2_color.y = 1.0; - uncon_2_color.z = 1.0; - uncon_3_color.x = 1.0; - uncon_3_color.y = 0.0; - uncon_3_color.z = 0.0; - - fprintf(fp, "CQUAD\n"); - for(i = 0; i < stl->stats.number_of_facets; i++) { - j = ((stl->neighbors_start[i].neighbor[0] == -1) + - (stl->neighbors_start[i].neighbor[1] == -1) + - (stl->neighbors_start[i].neighbor[2] == -1)); - if(j == 0) { - color = connect_color; - } else if(j == 1) { - color = uncon_1_color; - } else if(j == 2) { - color = uncon_2_color; - } else { - color = uncon_3_color; - } - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[0].x, - stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z, color.x, color.y, color.z); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[1].x, - stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z, color.x, color.y, color.z); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2].x, - stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2].x, - stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); - } - fclose(fp); -} - -void -stl_write_dxf(stl_file *stl, char *file, char *label) { - int i; - FILE *fp; - char *error_msg; - - if (stl->error) return; - - /* Open the file */ - fp = 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); - stl->error = 1; - return; - } - - fprintf(fp, "999\n%s\n", label); - fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); - fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\ -0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n"); - fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); - - fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "0\n3DFACE\n8\n0\n"); - fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", - stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, - stl->facet_start[i].vertex[0].z); - fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", - stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, - stl->facet_start[i].vertex[1].z); - fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); - fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", - stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, - stl->facet_start[i].vertex[2].z); - } - - fprintf(fp, "0\nENDSEC\n0\nEOF\n"); - - fclose(fp); -} - -void -stl_clear_error(stl_file *stl) { - stl->error = 0; -} - -void -stl_exit_on_error(stl_file *stl) { - if (!stl->error) return; - stl->error = 0; - stl_close(stl); - exit(1); -} - -int -stl_get_error(stl_file *stl) { - return stl->error; -} diff --git a/xs/src/admesh/stl_io.cpp b/xs/src/admesh/stl_io.cpp new file mode 100644 index 000000000..f58d6b4f6 --- /dev/null +++ b/xs/src/admesh/stl_io.cpp @@ -0,0 +1,421 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include +#include "stl.h" +#include "config.h" + +#include + +#if !defined(SEEK_SET) +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +void +stl_print_edges(stl_file *stl, FILE *file) { + int i; + int edges_allocated; + + if (stl->error) return; + + edges_allocated = stl->stats.number_of_facets * 3; + for(i = 0; i < edges_allocated; i++) { + fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n", + stl->edge_start[i].facet_number, + stl->edge_start[i].p1.x, stl->edge_start[i].p1.y, + stl->edge_start[i].p1.z, stl->edge_start[i].p2.x, + stl->edge_start[i].p2.y, stl->edge_start[i].p2.z); + } +} + + +void +stl_stats_out(stl_file *stl, FILE *file, char *input_file) { + if (stl->error) return; + + /* this is here for Slic3r, without our config.h + it won't use this part of the code anyway */ +#ifndef VERSION +#define VERSION "unknown" +#endif + fprintf(file, "\n\ +================= Results produced by ADMesh version " VERSION " ================\n"); + fprintf(file, "\ +Input file : %s\n", input_file); + if(stl->stats.type == binary) { + fprintf(file, "\ +File type : Binary STL file\n"); + } else { + fprintf(file, "\ +File type : ASCII STL file\n"); + } + fprintf(file, "\ +Header : %s\n", stl->stats.header); + fprintf(file, "============== Size ==============\n"); + fprintf(file, "Min X = % f, Max X = % f\n", + stl->stats.min.x, stl->stats.max.x); + fprintf(file, "Min Y = % f, Max Y = % f\n", + stl->stats.min.y, stl->stats.max.y); + fprintf(file, "Min Z = % f, Max Z = % f\n", + stl->stats.min.z, stl->stats.max.z); + + fprintf(file, "\ +========= Facet Status ========== Original ============ Final ====\n"); + fprintf(file, "\ +Number of facets : %5d %5d\n", + stl->stats.original_num_facets, stl->stats.number_of_facets); + fprintf(file, "\ +Facets with 1 disconnected edge : %5d %5d\n", + stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - + stl->stats.connected_facets_3_edge); + fprintf(file, "\ +Facets with 2 disconnected edges : %5d %5d\n", + stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - + stl->stats.connected_facets_2_edge); + fprintf(file, "\ +Facets with 3 disconnected edges : %5d %5d\n", + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - + stl->stats.connected_facets_1_edge); + fprintf(file, "\ +Total disconnected facets : %5d %5d\n", + stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - + stl->stats.connected_facets_3_edge); + + fprintf(file, + "=== Processing Statistics === ===== Other Statistics =====\n"); + fprintf(file, "\ +Number of parts : %5d Volume : % f\n", + stl->stats.number_of_parts, stl->stats.volume); + fprintf(file, "\ +Degenerate facets : %5d\n", stl->stats.degenerate_facets); + fprintf(file, "\ +Edges fixed : %5d\n", stl->stats.edges_fixed); + fprintf(file, "\ +Facets removed : %5d\n", stl->stats.facets_removed); + fprintf(file, "\ +Facets added : %5d\n", stl->stats.facets_added); + fprintf(file, "\ +Facets reversed : %5d\n", stl->stats.facets_reversed); + fprintf(file, "\ +Backwards edges : %5d\n", stl->stats.backwards_edges); + fprintf(file, "\ +Normals fixed : %5d\n", stl->stats.normals_fixed); +} + +void +stl_write_ascii(stl_file *stl, const char *file, const char *label) { + int i; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + FILE *fp = boost::nowide::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); + stl->error = 1; + return; + } + + fprintf(fp, "solid %s\n", label); + + for(i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, " facet normal % .8E % .8E % .8E\n", + stl->facet_start[i].normal.x, stl->facet_start[i].normal.y, + stl->facet_start[i].normal.z); + fprintf(fp, " outer loop\n"); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, + stl->facet_start[i].vertex[0].z); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, + stl->facet_start[i].vertex[1].z); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, + stl->facet_start[i].vertex[2].z); + fprintf(fp, " endloop\n"); + fprintf(fp, " endfacet\n"); + } + + fprintf(fp, "endsolid %s\n", label); + + fclose(fp); +} + +void +stl_print_neighbors(stl_file *stl, char *file) { + int i; + FILE *fp; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + for(i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", + i, + stl->neighbors_start[i].neighbor[0], + (int)stl->neighbors_start[i].which_vertex_not[0], + stl->neighbors_start[i].neighbor[1], + (int)stl->neighbors_start[i].which_vertex_not[1], + stl->neighbors_start[i].neighbor[2], + (int)stl->neighbors_start[i].which_vertex_not[2]); + } + fclose(fp); +} + +void +stl_write_binary(stl_file *stl, const char *file, const char *label) { + FILE *fp; + int i; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + fprintf(fp, "%s", label); + for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); + + fseek(fp, LABEL_SIZE, SEEK_SET); + fwrite(&stl->stats.number_of_facets, 4, 1, fp); + for(i = 0; i < stl->stats.number_of_facets; i++) + fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); + fclose(fp); +} + +void +stl_write_vertex(stl_file *stl, int facet, int vertex) { + if (stl->error) return; + printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, + stl->facet_start[facet].vertex[vertex].x, + stl->facet_start[facet].vertex[vertex].y, + stl->facet_start[facet].vertex[vertex].z); +} + +void +stl_write_facet(stl_file *stl, char *label, int facet) { + if (stl->error) return; + printf("facet (%d)/ %s\n", facet, label); + stl_write_vertex(stl, facet, 0); + stl_write_vertex(stl, facet, 1); + stl_write_vertex(stl, facet, 2); +} + +void +stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) { + if (stl->error) return; + printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label); + if(edge.which_edge < 3) { + stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); + stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); + } else { + stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); + stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); + } +} + +void +stl_write_neighbor(stl_file *stl, int facet) { + if (stl->error) return; + printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, + stl->neighbors_start[facet].neighbor[0], + stl->neighbors_start[facet].neighbor[1], + stl->neighbors_start[facet].neighbor[2], + stl->neighbors_start[facet].which_vertex_not[0], + stl->neighbors_start[facet].which_vertex_not[1], + stl->neighbors_start[facet].which_vertex_not[2]); +} + +void +stl_write_quad_object(stl_file *stl, char *file) { + FILE *fp; + int i; + int j; + char *error_msg; + stl_vertex connect_color; + stl_vertex uncon_1_color; + stl_vertex uncon_2_color; + stl_vertex uncon_3_color; + stl_vertex color; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + connect_color.x = 0.0; + connect_color.y = 0.0; + connect_color.z = 1.0; + uncon_1_color.x = 0.0; + uncon_1_color.y = 1.0; + uncon_1_color.z = 0.0; + uncon_2_color.x = 1.0; + uncon_2_color.y = 1.0; + uncon_2_color.z = 1.0; + uncon_3_color.x = 1.0; + uncon_3_color.y = 0.0; + uncon_3_color.z = 0.0; + + fprintf(fp, "CQUAD\n"); + for(i = 0; i < stl->stats.number_of_facets; i++) { + j = ((stl->neighbors_start[i].neighbor[0] == -1) + + (stl->neighbors_start[i].neighbor[1] == -1) + + (stl->neighbors_start[i].neighbor[2] == -1)); + if(j == 0) { + color = connect_color; + } else if(j == 1) { + color = uncon_1_color; + } else if(j == 2) { + color = uncon_2_color; + } else { + color = uncon_3_color; + } + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + stl->facet_start[i].vertex[0].x, + stl->facet_start[i].vertex[0].y, + stl->facet_start[i].vertex[0].z, color.x, color.y, color.z); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + stl->facet_start[i].vertex[1].x, + stl->facet_start[i].vertex[1].y, + stl->facet_start[i].vertex[1].z, color.x, color.y, color.z); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + stl->facet_start[i].vertex[2].x, + stl->facet_start[i].vertex[2].y, + stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + stl->facet_start[i].vertex[2].x, + stl->facet_start[i].vertex[2].y, + stl->facet_start[i].vertex[2].z, color.x, color.y, color.z); + } + fclose(fp); +} + +void +stl_write_dxf(stl_file *stl, char *file, char *label) { + int i; + FILE *fp; + char *error_msg; + + if (stl->error) return; + + /* Open the file */ + fp = boost::nowide::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); + stl->error = 1; + return; + } + + fprintf(fp, "999\n%s\n", label); + fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); + fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\ +0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n"); + fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); + + fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); + + for(i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, "0\n3DFACE\n8\n0\n"); + fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", + stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y, + stl->facet_start[i].vertex[0].z); + fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", + stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y, + stl->facet_start[i].vertex[1].z); + fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", + stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, + stl->facet_start[i].vertex[2].z); + fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", + stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y, + stl->facet_start[i].vertex[2].z); + } + + fprintf(fp, "0\nENDSEC\n0\nEOF\n"); + + fclose(fp); +} + +void +stl_clear_error(stl_file *stl) { + stl->error = 0; +} + +void +stl_exit_on_error(stl_file *stl) { + if (!stl->error) return; + stl->error = 0; + stl_close(stl); + exit(1); +} + +int +stl_get_error(stl_file *stl) { + return stl->error; +} diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c deleted file mode 100644 index 13f1f43af..000000000 --- a/xs/src/admesh/stlinit.c +++ /dev/null @@ -1,426 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include -#include -#include -#include - -#include "stl.h" - -#ifndef SEEK_SET -#error "SEEK_SET not defined" -#endif - -void -stl_open(stl_file *stl, const char *file) { - stl_initialize(stl); - stl_count_facets(stl, file); - stl_allocate(stl); - stl_read(stl, 0, 1); - if (!stl->error) fclose(stl->fp); -} - - -void -stl_initialize(stl_file *stl) { - memset(stl, 0, sizeof(stl_file)); - stl->stats.volume = -1.0; -} - -void -stl_count_facets(stl_file *stl, const char *file) { - long file_size; - int header_num_facets; - int num_facets; - int i; - size_t s; - unsigned char chtest[128]; - int num_lines = 1; - char *error_msg; - - if (stl->error) return; - - /* Open the file in binary mode first */ - stl->fp = 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); - stl->error = 1; - return; - } - /* Find size of file */ - fseek(stl->fp, 0, SEEK_END); - file_size = ftell(stl->fp); - - /* Check for binary or ASCII file */ - fseek(stl->fp, HEADER_SIZE, SEEK_SET); - if (!fread(chtest, sizeof(chtest), 1, stl->fp)) { - perror("The input is an empty file"); - stl->error = 1; - return; - } - stl->stats.type = ascii; - for(s = 0; s < sizeof(chtest); s++) { - if(chtest[s] > 127) { - stl->stats.type = binary; - break; - } - } - rewind(stl->fp); - - /* Get the header and the number of facets in the .STL file */ - /* If the .STL file is binary, then do the following */ - if(stl->stats.type == binary) { - /* Test if the STL file has the right size */ - if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) - || (file_size < STL_MIN_FILE_SIZE)) { - fprintf(stderr, "The file %s has the wrong size.\n", file); - stl->error = 1; - return; - } - num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; - - /* Read the header */ - if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) { - stl->stats.header[80] = '\0'; - } - - /* Read the int following the header. This should contain # of facets */ - if((!fread(&header_num_facets, sizeof(int), 1, stl->fp)) || (num_facets != header_num_facets)) { - fprintf(stderr, - "Warning: File size doesn't match number of facets in the header\n"); - } - } - /* Otherwise, if the .STL file is ASCII, then do the following */ - else { - /* 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); - - // 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); - stl->error = 1; - return; - } - - /* Find the number of facets */ - char linebuf[100]; - while (fgets(linebuf, 100, stl->fp) != NULL) { - /* don't count short lines */ - if (strlen(linebuf) <= 4) continue; - - /* skip solid/endsolid lines as broken STL file generators may put several of them */ - if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue; - - ++num_lines; - } - - rewind(stl->fp); - - /* Get the header */ - for(i = 0; - (i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++); - stl->stats.header[i] = '\0'; /* Lose the '\n' */ - stl->stats.header[80] = '\0'; - - num_facets = num_lines / ASCII_LINES_PER_FACET; - } - stl->stats.number_of_facets += num_facets; - stl->stats.original_num_facets = stl->stats.number_of_facets; -} - -void -stl_allocate(stl_file *stl) { - if (stl->error) return; - - /* Allocate memory for the entire .STL file */ - stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets, - sizeof(stl_facet)); - if(stl->facet_start == NULL) perror("stl_initialize"); - stl->stats.facets_malloced = stl->stats.number_of_facets; - - /* Allocate memory for the neighbors list */ - stl->neighbors_start = (stl_neighbors*) - calloc(stl->stats.number_of_facets, sizeof(stl_neighbors)); - if(stl->facet_start == NULL) perror("stl_initialize"); -} - -void -stl_open_merge(stl_file *stl, char *file_to_merge) { - int num_facets_so_far; - stl_type origStlType; - FILE *origFp; - stl_file stl_to_merge; - - if (stl->error) return; - - /* Record how many facets we have so far from the first file. We will start putting - facets in the next position. Since we're 0-indexed, it'l be the same position. */ - num_facets_so_far = stl->stats.number_of_facets; - - /* Record the file type we started with: */ - origStlType=stl->stats.type; - /* Record the file pointer too: */ - origFp=stl->fp; - - /* Initialize the sturucture with zero stats, header info and sizes: */ - stl_initialize(&stl_to_merge); - stl_count_facets(&stl_to_merge, file_to_merge); - - /* Copy what we need to into stl so that we can read the file_to_merge directly into it - using stl_read: Save the rest of the valuable info: */ - stl->stats.type=stl_to_merge.stats.type; - stl->fp=stl_to_merge.fp; - - /* Add the number of facets we already have in stl with what we we found in stl_to_merge but - haven't read yet. */ - stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets; - - /* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */ - stl_reallocate(stl); - - /* Read the file to merge directly into stl, adding it to what we have already. - Start at num_facets_so_far, the index to the first unused facet. Also say - that this isn't our first time so we should augment stats like min and max - instead of erasing them. */ - stl_read(stl, num_facets_so_far, 0); - - /* Restore the stl information we overwrote (for stl_read) so that it still accurately - reflects the subject part: */ - stl->stats.type=origStlType; - stl->fp=origFp; -} - -extern void -stl_reallocate(stl_file *stl) { - if (stl->error) return; - /* Reallocate more memory for the .STL file(s) */ - stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets * - sizeof(stl_facet)); - if(stl->facet_start == NULL) perror("stl_initialize"); - stl->stats.facets_malloced = stl->stats.number_of_facets; - - /* Reallocate more memory for the neighbors list */ - stl->neighbors_start = (stl_neighbors*) - realloc(stl->neighbors_start, stl->stats.number_of_facets * - sizeof(stl_neighbors)); - if(stl->facet_start == NULL) perror("stl_initialize"); -} - - -/* Reads the contents of the file pointed to by stl->fp into the stl structure, - starting at facet first_facet. The second argument says if it's our first - time running this for the stl and therefore we should reset our max and min stats. */ -void -stl_read(stl_file *stl, int first_facet, int first) { - stl_facet facet; - int i; - - if (stl->error) return; - - if(stl->stats.type == binary) { - fseek(stl->fp, HEADER_SIZE, SEEK_SET); - } else { - rewind(stl->fp); - } - - char normal_buf[3][32]; - for(i = first_facet; i < stl->stats.number_of_facets; i++) { - if(stl->stats.type == binary) - /* Read a single facet from a binary .STL file */ - { - /* we assume little-endian architecture! */ - if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) { - stl->error = 1; - return; - } - } else - /* Read a single facet from an ASCII .STL file */ - { - // skip solid/endsolid - // (in this order, otherwise it won't work when they are paired in the middle of a file) - fscanf(stl->fp, "endsolid\n"); - fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") - // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. - int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); - assert(res_normal == 3); - int res_outer_loop = fscanf(stl->fp, " outer loop"); - assert(res_outer_loop == 0); - int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); - assert(res_vertex1 == 3); - int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); - assert(res_vertex2 == 3); - int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); - assert(res_vertex3 == 3); - int res_endloop = fscanf(stl->fp, " endloop"); - assert(res_endloop == 0); - // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines. - int res_endfacet = fscanf(stl->fp, " endfacet "); - if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { - perror("Something is syntactically very wrong with this ASCII STL!"); - stl->error = 1; - return; - } - - // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. - if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || - sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || - sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { - // Normal was mangled. Maybe denormals or "not a number" were stored? - // Just reset the normal and silently ignore it. - memset(&facet.normal, 0, sizeof(facet.normal)); - } - } - -#if 0 - // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, - // close to zero values may be represented with singificantly higher precision than the rest of the vertices. - // It may be worth to round these numbers to zero during loading to reduce the number of errors reported - // during the STL import. - for (size_t j = 0; j < 3; ++ j) { - if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) - printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); - if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) - printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); - if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) - printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); - } -#endif - -#if 1 - { - // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. - // When using a memcmp on raw floats, those numbers report to be different. - // Unify all +0 and -0 to +0 to make the floats equal under memcmp. - uint32_t *f = (uint32_t*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f == 0x80000000) - // Negative zero, switch to positive zero. - *f = 0; - } -#else - { - // Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision - // than the rest of the vertices. Round them to zero. - float *f = (float*)&facet; - for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats - if (*f > -1e-12f && *f < 1e-12f) - // Negative zero, switch to positive zero. - *f = 0; - } -#endif - /* Write the facet into memory. */ - memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); - stl_facet_stats(stl, facet, first); - first = 0; - } - stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; - stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; - stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; - stl->stats.bounding_diameter = sqrt( - stl->stats.size.x * stl->stats.size.x + - stl->stats.size.y * stl->stats.size.y + - stl->stats.size.z * stl->stats.size.z - ); -} - -void -stl_facet_stats(stl_file *stl, stl_facet facet, int first) { - float diff_x; - float diff_y; - float diff_z; - float max_diff; - - if (stl->error) return; - - /* while we are going through all of the facets, let's find the */ - /* maximum and minimum values for x, y, and z */ - - /* Initialize the max and min values the first time through*/ - if (first) { - stl->stats.max.x = facet.vertex[0].x; - stl->stats.min.x = facet.vertex[0].x; - stl->stats.max.y = facet.vertex[0].y; - stl->stats.min.y = facet.vertex[0].y; - stl->stats.max.z = facet.vertex[0].z; - stl->stats.min.z = facet.vertex[0].z; - - diff_x = ABS(facet.vertex[0].x - facet.vertex[1].x); - diff_y = ABS(facet.vertex[0].y - facet.vertex[1].y); - diff_z = ABS(facet.vertex[0].z - facet.vertex[1].z); - max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - stl->stats.shortest_edge = max_diff; - - first = 0; - } - - /* now find the max and min values */ - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[0].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[0].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[0].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[0].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[0].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[0].z); - - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[1].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[1].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[1].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[1].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[1].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[1].z); - - stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[2].x); - stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[2].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[2].y); - stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[2].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[2].z); - stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[2].z); -} - -void -stl_close(stl_file *stl) { - if (stl->error) return; - - if(stl->neighbors_start != NULL) - free(stl->neighbors_start); - if(stl->facet_start != NULL) - free(stl->facet_start); - if(stl->v_indices != NULL) - free(stl->v_indices); - if(stl->v_shared != NULL) - free(stl->v_shared); -} - diff --git a/xs/src/admesh/stlinit.cpp b/xs/src/admesh/stlinit.cpp new file mode 100644 index 000000000..f5110d394 --- /dev/null +++ b/xs/src/admesh/stlinit.cpp @@ -0,0 +1,428 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include +#include +#include +#include + +#include + +#include "stl.h" + +#ifndef SEEK_SET +#error "SEEK_SET not defined" +#endif + +void +stl_open(stl_file *stl, const char *file) { + stl_initialize(stl); + stl_count_facets(stl, file); + stl_allocate(stl); + stl_read(stl, 0, 1); + if (!stl->error) fclose(stl->fp); +} + + +void +stl_initialize(stl_file *stl) { + memset(stl, 0, sizeof(stl_file)); + stl->stats.volume = -1.0; +} + +void +stl_count_facets(stl_file *stl, const char *file) { + long file_size; + int header_num_facets; + int num_facets; + int i; + size_t s; + unsigned char chtest[128]; + int num_lines = 1; + char *error_msg; + + if (stl->error) return; + + /* Open the file in binary mode first */ + 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 */ + sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", + file); + perror(error_msg); + free(error_msg); + stl->error = 1; + return; + } + /* Find size of file */ + fseek(stl->fp, 0, SEEK_END); + file_size = ftell(stl->fp); + + /* Check for binary or ASCII file */ + fseek(stl->fp, HEADER_SIZE, SEEK_SET); + if (!fread(chtest, sizeof(chtest), 1, stl->fp)) { + perror("The input is an empty file"); + stl->error = 1; + return; + } + stl->stats.type = ascii; + for(s = 0; s < sizeof(chtest); s++) { + if(chtest[s] > 127) { + stl->stats.type = binary; + break; + } + } + rewind(stl->fp); + + /* Get the header and the number of facets in the .STL file */ + /* If the .STL file is binary, then do the following */ + if(stl->stats.type == binary) { + /* Test if the STL file has the right size */ + if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) + || (file_size < STL_MIN_FILE_SIZE)) { + fprintf(stderr, "The file %s has the wrong size.\n", file); + stl->error = 1; + return; + } + num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; + + /* Read the header */ + if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) { + stl->stats.header[80] = '\0'; + } + + /* Read the int following the header. This should contain # of facets */ + if((!fread(&header_num_facets, sizeof(int), 1, stl->fp)) || (num_facets != header_num_facets)) { + fprintf(stderr, + "Warning: File size doesn't match number of facets in the header\n"); + } + } + /* Otherwise, if the .STL file is ASCII, then do the following */ + else { + /* 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 = boost::nowide::freopen(file, "r", stl->fp); + + // 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); + stl->error = 1; + return; + } + + /* Find the number of facets */ + char linebuf[100]; + while (fgets(linebuf, 100, stl->fp) != NULL) { + /* don't count short lines */ + if (strlen(linebuf) <= 4) continue; + + /* skip solid/endsolid lines as broken STL file generators may put several of them */ + if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue; + + ++num_lines; + } + + rewind(stl->fp); + + /* Get the header */ + for(i = 0; + (i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++); + stl->stats.header[i] = '\0'; /* Lose the '\n' */ + stl->stats.header[80] = '\0'; + + num_facets = num_lines / ASCII_LINES_PER_FACET; + } + stl->stats.number_of_facets += num_facets; + stl->stats.original_num_facets = stl->stats.number_of_facets; +} + +void +stl_allocate(stl_file *stl) { + if (stl->error) return; + + /* Allocate memory for the entire .STL file */ + stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets, + sizeof(stl_facet)); + if(stl->facet_start == NULL) perror("stl_initialize"); + stl->stats.facets_malloced = stl->stats.number_of_facets; + + /* Allocate memory for the neighbors list */ + stl->neighbors_start = (stl_neighbors*) + calloc(stl->stats.number_of_facets, sizeof(stl_neighbors)); + if(stl->facet_start == NULL) perror("stl_initialize"); +} + +void +stl_open_merge(stl_file *stl, char *file_to_merge) { + int num_facets_so_far; + stl_type origStlType; + FILE *origFp; + stl_file stl_to_merge; + + if (stl->error) return; + + /* Record how many facets we have so far from the first file. We will start putting + facets in the next position. Since we're 0-indexed, it'l be the same position. */ + num_facets_so_far = stl->stats.number_of_facets; + + /* Record the file type we started with: */ + origStlType=stl->stats.type; + /* Record the file pointer too: */ + origFp=stl->fp; + + /* Initialize the sturucture with zero stats, header info and sizes: */ + stl_initialize(&stl_to_merge); + stl_count_facets(&stl_to_merge, file_to_merge); + + /* Copy what we need to into stl so that we can read the file_to_merge directly into it + using stl_read: Save the rest of the valuable info: */ + stl->stats.type=stl_to_merge.stats.type; + stl->fp=stl_to_merge.fp; + + /* Add the number of facets we already have in stl with what we we found in stl_to_merge but + haven't read yet. */ + stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets; + + /* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */ + stl_reallocate(stl); + + /* Read the file to merge directly into stl, adding it to what we have already. + Start at num_facets_so_far, the index to the first unused facet. Also say + that this isn't our first time so we should augment stats like min and max + instead of erasing them. */ + stl_read(stl, num_facets_so_far, 0); + + /* Restore the stl information we overwrote (for stl_read) so that it still accurately + reflects the subject part: */ + stl->stats.type=origStlType; + stl->fp=origFp; +} + +extern void +stl_reallocate(stl_file *stl) { + if (stl->error) return; + /* Reallocate more memory for the .STL file(s) */ + stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets * + sizeof(stl_facet)); + if(stl->facet_start == NULL) perror("stl_initialize"); + stl->stats.facets_malloced = stl->stats.number_of_facets; + + /* Reallocate more memory for the neighbors list */ + stl->neighbors_start = (stl_neighbors*) + realloc(stl->neighbors_start, stl->stats.number_of_facets * + sizeof(stl_neighbors)); + if(stl->facet_start == NULL) perror("stl_initialize"); +} + + +/* Reads the contents of the file pointed to by stl->fp into the stl structure, + starting at facet first_facet. The second argument says if it's our first + time running this for the stl and therefore we should reset our max and min stats. */ +void +stl_read(stl_file *stl, int first_facet, int first) { + stl_facet facet; + int i; + + if (stl->error) return; + + if(stl->stats.type == binary) { + fseek(stl->fp, HEADER_SIZE, SEEK_SET); + } else { + rewind(stl->fp); + } + + char normal_buf[3][32]; + for(i = first_facet; i < stl->stats.number_of_facets; i++) { + if(stl->stats.type == binary) + /* Read a single facet from a binary .STL file */ + { + /* we assume little-endian architecture! */ + if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) { + stl->error = 1; + return; + } + } else + /* Read a single facet from an ASCII .STL file */ + { + // skip solid/endsolid + // (in this order, otherwise it won't work when they are paired in the middle of a file) + fscanf(stl->fp, "endsolid\n"); + fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") + // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. + int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); + assert(res_normal == 3); + int res_outer_loop = fscanf(stl->fp, " outer loop"); + assert(res_outer_loop == 0); + int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); + assert(res_vertex1 == 3); + int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); + assert(res_vertex2 == 3); + int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); + assert(res_vertex3 == 3); + int res_endloop = fscanf(stl->fp, " endloop"); + assert(res_endloop == 0); + // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines. + int res_endfacet = fscanf(stl->fp, " endfacet "); + if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { + perror("Something is syntactically very wrong with this ASCII STL!"); + stl->error = 1; + return; + } + + // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. + if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || + sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || + sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { + // Normal was mangled. Maybe denormals or "not a number" were stored? + // Just reset the normal and silently ignore it. + memset(&facet.normal, 0, sizeof(facet.normal)); + } + } + +#if 0 + // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, + // close to zero values may be represented with singificantly higher precision than the rest of the vertices. + // It may be worth to round these numbers to zero during loading to reduce the number of errors reported + // during the STL import. + for (size_t j = 0; j < 3; ++ j) { + if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) + printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); + if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) + printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); + if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) + printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); + } +#endif + +#if 1 + { + // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. + // When using a memcmp on raw floats, those numbers report to be different. + // Unify all +0 and -0 to +0 to make the floats equal under memcmp. + uint32_t *f = (uint32_t*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f == 0x80000000) + // Negative zero, switch to positive zero. + *f = 0; + } +#else + { + // Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision + // than the rest of the vertices. Round them to zero. + float *f = (float*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f > -1e-12f && *f < 1e-12f) + // Negative zero, switch to positive zero. + *f = 0; + } +#endif + /* Write the facet into memory. */ + memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); + stl_facet_stats(stl, facet, first); + first = 0; + } + stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; + stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; + stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; + stl->stats.bounding_diameter = sqrt( + stl->stats.size.x * stl->stats.size.x + + stl->stats.size.y * stl->stats.size.y + + stl->stats.size.z * stl->stats.size.z + ); +} + +void +stl_facet_stats(stl_file *stl, stl_facet facet, int first) { + float diff_x; + float diff_y; + float diff_z; + float max_diff; + + if (stl->error) return; + + /* while we are going through all of the facets, let's find the */ + /* maximum and minimum values for x, y, and z */ + + /* Initialize the max and min values the first time through*/ + if (first) { + stl->stats.max.x = facet.vertex[0].x; + stl->stats.min.x = facet.vertex[0].x; + stl->stats.max.y = facet.vertex[0].y; + stl->stats.min.y = facet.vertex[0].y; + stl->stats.max.z = facet.vertex[0].z; + stl->stats.min.z = facet.vertex[0].z; + + diff_x = ABS(facet.vertex[0].x - facet.vertex[1].x); + diff_y = ABS(facet.vertex[0].y - facet.vertex[1].y); + diff_z = ABS(facet.vertex[0].z - facet.vertex[1].z); + max_diff = STL_MAX(diff_x, diff_y); + max_diff = STL_MAX(diff_z, max_diff); + stl->stats.shortest_edge = max_diff; + + first = 0; + } + + /* now find the max and min values */ + stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[0].x); + stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[0].x); + stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[0].y); + stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[0].y); + stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[0].z); + stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[0].z); + + stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[1].x); + stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[1].x); + stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[1].y); + stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[1].y); + stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[1].z); + stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[1].z); + + stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[2].x); + stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[2].x); + stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[2].y); + stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[2].y); + stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[2].z); + stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[2].z); +} + +void +stl_close(stl_file *stl) { + if (stl->error) return; + + if(stl->neighbors_start != NULL) + free(stl->neighbors_start); + if(stl->facet_start != NULL) + free(stl->facet_start); + if(stl->v_indices != NULL) + free(stl->v_indices); + if(stl->v_shared != NULL) + free(stl->v_shared); +} + diff --git a/xs/src/admesh/util.c b/xs/src/admesh/util.c deleted file mode 100644 index cabb8a778..000000000 --- a/xs/src/admesh/util.c +++ /dev/null @@ -1,573 +0,0 @@ -/* ADMesh -- process triangulated solid meshes - * Copyright (C) 1995, 1996 Anthony D. Martin - * Copyright (C) 2013, 2014 several contributors, see AUTHORS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Questions, comments, suggestions, etc to - * https://github.com/admesh/admesh/issues - */ - -#include -#include -#include -#include - -#include "stl.h" - -static void stl_rotate(float *x, float *y, const double c, const double s); -static float get_area(stl_facet *facet); -static float get_volume(stl_file *stl); - - -void -stl_verify_neighbors(stl_file *stl) { - int i; - int j; - stl_edge edge_a; - stl_edge edge_b; - int neighbor; - int vnot; - - if (stl->error) return; - - stl->stats.backwards_edges = 0; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - edge_a.p1 = stl->facet_start[i].vertex[j]; - edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; - neighbor = stl->neighbors_start[i].neighbor[j]; - vnot = stl->neighbors_start[i].which_vertex_not[j]; - - if(neighbor == -1) - continue; /* this edge has no neighbor... Continue. */ - if(vnot < 3) { - edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; - edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; - } else { - stl->stats.backwards_edges += 1; - edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; - edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; - } - if(memcmp(&edge_a, &edge_b, SIZEOF_EDGE_SORT) != 0) { - /* These edges should match but they don't. Print results. */ - printf("edge %d of facet %d doesn't match edge %d of facet %d\n", - j, i, vnot + 1, neighbor); - stl_write_facet(stl, (char*)"first facet", i); - stl_write_facet(stl, (char*)"second facet", neighbor); - } - } - } -} - -void -stl_translate(stl_file *stl, float x, float y, float z) { - int i; - int j; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x -= (stl->stats.min.x - x); - stl->facet_start[i].vertex[j].y -= (stl->stats.min.y - y); - stl->facet_start[i].vertex[j].z -= (stl->stats.min.z - z); - } - } - stl->stats.max.x -= (stl->stats.min.x - x); - stl->stats.max.y -= (stl->stats.min.y - y); - stl->stats.max.z -= (stl->stats.min.z - z); - stl->stats.min.x = x; - stl->stats.min.y = y; - stl->stats.min.z = z; - - stl_invalidate_shared_vertices(stl); -} - -/* Translates the stl by x,y,z, relatively from wherever it is currently */ -void -stl_translate_relative(stl_file *stl, float x, float y, float z) { - int i; - int j; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x += x; - stl->facet_start[i].vertex[j].y += y; - stl->facet_start[i].vertex[j].z += z; - } - } - stl->stats.min.x += x; - stl->stats.min.y += y; - stl->stats.min.z += z; - stl->stats.max.x += x; - stl->stats.max.y += y; - stl->stats.max.z += z; - - stl_invalidate_shared_vertices(stl); -} - -void -stl_scale_versor(stl_file *stl, float versor[3]) { - int i; - int j; - - if (stl->error) return; - - /* scale extents */ - stl->stats.min.x *= versor[0]; - stl->stats.min.y *= versor[1]; - stl->stats.min.z *= versor[2]; - stl->stats.max.x *= versor[0]; - stl->stats.max.y *= versor[1]; - stl->stats.max.z *= versor[2]; - - /* scale size */ - stl->stats.size.x *= versor[0]; - stl->stats.size.y *= versor[1]; - stl->stats.size.z *= versor[2]; - - /* scale volume */ - if (stl->stats.volume > 0.0) { - stl->stats.volume *= (versor[0] * versor[1] * versor[2]); - } - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x *= versor[0]; - stl->facet_start[i].vertex[j].y *= versor[1]; - stl->facet_start[i].vertex[j].z *= versor[2]; - } - } - - stl_invalidate_shared_vertices(stl); -} - -void -stl_scale(stl_file *stl, float factor) { - float versor[3]; - - if (stl->error) return; - - versor[0] = factor; - versor[1] = factor; - versor[2] = factor; - stl_scale_versor(stl, versor); -} - -static void calculate_normals(stl_file *stl) { - long i; - float normal[3]; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl_calculate_normal(normal, &stl->facet_start[i]); - stl_normalize_vector(normal); - stl->facet_start[i].normal.x = normal[0]; - stl->facet_start[i].normal.y = normal[1]; - stl->facet_start[i].normal.z = normal[2]; - } -} - -void stl_transform(stl_file *stl, float *trafo3x4) { - int i_face, i_vertex, i, j; - if (stl->error) - return; - for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { - stl_vertex *vertices = stl->facet_start[i_face].vertex; - for (i_vertex = 0; i_vertex < 3; ++ i_vertex) { - stl_vertex &v_dst = vertices[i_vertex]; - stl_vertex v_src = v_dst; - v_dst.x = trafo3x4[0] * v_src.x + trafo3x4[1] * v_src.y + trafo3x4[2] * v_src.z + trafo3x4[3]; - v_dst.y = trafo3x4[4] * v_src.x + trafo3x4[5] * v_src.y + trafo3x4[6] * v_src.z + trafo3x4[7]; - v_dst.z = trafo3x4[8] * v_src.x + trafo3x4[9] * v_src.y + trafo3x4[10] * v_src.z + trafo3x4[11]; - } - } - stl_get_size(stl); - calculate_normals(stl); -} - -void -stl_rotate_x(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].y, - &stl->facet_start[i].vertex[j].z, c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); -} - -void -stl_rotate_y(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].z, - &stl->facet_start[i].vertex[j].x, c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); -} - -void -stl_rotate_z(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j].x, - &stl->facet_start[i].vertex[j].y, c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); -} - - - -static void -stl_rotate(float *x, float *y, const double c, const double s) { - double xold = *x; - double yold = *y; - *x = float(c * xold - s * yold); - *y = float(s * xold + c * yold); -} - -extern void -stl_get_size(stl_file *stl) { - int i; - int j; - - if (stl->error) return; - if (stl->stats.number_of_facets == 0) return; - - stl->stats.min.x = stl->facet_start[0].vertex[0].x; - stl->stats.min.y = stl->facet_start[0].vertex[0].y; - stl->stats.min.z = stl->facet_start[0].vertex[0].z; - stl->stats.max.x = stl->facet_start[0].vertex[0].x; - stl->stats.max.y = stl->facet_start[0].vertex[0].y; - stl->stats.max.z = stl->facet_start[0].vertex[0].z; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->stats.min.x = STL_MIN(stl->stats.min.x, - stl->facet_start[i].vertex[j].x); - stl->stats.min.y = STL_MIN(stl->stats.min.y, - stl->facet_start[i].vertex[j].y); - stl->stats.min.z = STL_MIN(stl->stats.min.z, - stl->facet_start[i].vertex[j].z); - stl->stats.max.x = STL_MAX(stl->stats.max.x, - stl->facet_start[i].vertex[j].x); - stl->stats.max.y = STL_MAX(stl->stats.max.y, - stl->facet_start[i].vertex[j].y); - stl->stats.max.z = STL_MAX(stl->stats.max.z, - stl->facet_start[i].vertex[j].z); - } - } - stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; - stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; - stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; - stl->stats.bounding_diameter = sqrt( - stl->stats.size.x * stl->stats.size.x + - stl->stats.size.y * stl->stats.size.y + - stl->stats.size.z * stl->stats.size.z - ); -} - -void -stl_mirror_xy(stl_file *stl) { - int i; - int j; - float temp_size; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].z *= -1.0; - } - } - temp_size = stl->stats.min.z; - stl->stats.min.z = stl->stats.max.z; - stl->stats.max.z = temp_size; - stl->stats.min.z *= -1.0; - stl->stats.max.z *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ -} - -void -stl_mirror_yz(stl_file *stl) { - int i; - int j; - float temp_size; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].x *= -1.0; - } - } - temp_size = stl->stats.min.x; - stl->stats.min.x = stl->stats.max.x; - stl->stats.max.x = temp_size; - stl->stats.min.x *= -1.0; - stl->stats.max.x *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ -} - -void -stl_mirror_xz(stl_file *stl) { - int i; - int j; - float temp_size; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j].y *= -1.0; - } - } - temp_size = stl->stats.min.y; - stl->stats.min.y = stl->stats.max.y; - stl->stats.max.y = temp_size; - stl->stats.min.y *= -1.0; - stl->stats.max.y *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ -} - -static float get_volume(stl_file *stl) { - long i; - stl_vertex p0; - stl_vertex p; - stl_normal n; - float height; - float area; - float volume = 0.0; - - if (stl->error) return 0; - - /* Choose a point, any point as the reference */ - p0.x = stl->facet_start[0].vertex[0].x; - p0.y = stl->facet_start[0].vertex[0].y; - p0.z = stl->facet_start[0].vertex[0].z; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - p.x = stl->facet_start[i].vertex[0].x - p0.x; - p.y = stl->facet_start[i].vertex[0].y - p0.y; - p.z = stl->facet_start[i].vertex[0].z - p0.z; - /* Do dot product to get distance from point to plane */ - 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; - } - return volume; -} - -void stl_calculate_volume(stl_file *stl) { - if (stl->error) return; - stl->stats.volume = get_volume(stl); - if(stl->stats.volume < 0.0) { - stl_reverse_all_facets(stl); - stl->stats.volume = -stl->stats.volume; - } -} - -static float get_area(stl_facet *facet) { - double cross[3][3]; - float sum[3]; - float n[3]; - float area; - int i; - - /* cast to double before calculating cross product because large coordinates - can result in overflowing product - (bad area is responsible for bad volume and bad facets reversal) */ - for(i = 0; i < 3; i++) { - cross[i][0]=(((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].z) - - ((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].y)); - cross[i][1]=(((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].x) - - ((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].z)); - cross[i][2]=(((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].y) - - ((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].x)); - } - - sum[0] = cross[0][0] + cross[1][0] + cross[2][0]; - sum[1] = cross[0][1] + cross[1][1] + cross[2][1]; - sum[2] = cross[0][2] + cross[1][2] + cross[2][2]; - - /* This should already be done. But just in case, let's do it again */ - stl_calculate_normal(n, facet); - stl_normalize_vector(n); - - area = 0.5 * (n[0] * sum[0] + n[1] * sum[1] + n[2] * sum[2]); - return area; -} - -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) { - - int i; - int last_edges_fixed = 0; - - if (stl->error) return; - - if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag - || fill_holes_flag || normal_directions_flag) { - if (verbose_flag) - printf("Checking exact...\n"); - exact_flag = 1; - stl_check_facets_exact(stl); - stl->stats.facets_w_1_bad_edge = - (stl->stats.connected_facets_2_edge - - stl->stats.connected_facets_3_edge); - stl->stats.facets_w_2_bad_edge = - (stl->stats.connected_facets_1_edge - - stl->stats.connected_facets_2_edge); - stl->stats.facets_w_3_bad_edge = - (stl->stats.number_of_facets - - stl->stats.connected_facets_1_edge); - } - - if(nearby_flag || fixall_flag) { - if(!tolerance_flag) { - tolerance = stl->stats.shortest_edge; - } - if(!increment_flag) { - increment = stl->stats.bounding_diameter / 10000.0; - } - - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - for(i = 0; i < iterations; i++) { - if(stl->stats.connected_facets_3_edge < - stl->stats.number_of_facets) { - if (verbose_flag) - printf("\ -Checking nearby. Tolerance= %f Iteration=%d of %d...", - tolerance, i + 1, iterations); - stl_check_facets_nearby(stl, tolerance); - if (verbose_flag) - printf(" Fixed %d edges.\n", - stl->stats.edges_fixed - last_edges_fixed); - last_edges_fixed = stl->stats.edges_fixed; - tolerance += increment; - } else { - if (verbose_flag) - printf("\ -All facets connected. No further nearby check necessary.\n"); - break; - } - } - } else { - if (verbose_flag) - printf("All facets connected. No nearby check necessary.\n"); - } - } - - if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - if (verbose_flag) - printf("Removing unconnected facets...\n"); - stl_remove_unconnected_facets(stl); - } else - if (verbose_flag) - printf("No unconnected need to be removed.\n"); - } - - if(fill_holes_flag || fixall_flag) { - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - if (verbose_flag) - printf("Filling holes...\n"); - stl_fill_holes(stl); - } else - if (verbose_flag) - printf("No holes need to be filled.\n"); - } - - if(reverse_all_flag) { - if (verbose_flag) - printf("Reversing all facets...\n"); - stl_reverse_all_facets(stl); - } - - if(normal_directions_flag || fixall_flag) { - if (verbose_flag) - printf("Checking normal directions...\n"); - stl_fix_normal_directions(stl); - } - - if(normal_values_flag || fixall_flag) { - if (verbose_flag) - printf("Checking normal values...\n"); - stl_fix_normal_values(stl); - } - - /* Always calculate the volume. It shouldn't take too long */ - if (verbose_flag) - printf("Calculating volume...\n"); - stl_calculate_volume(stl); - - if(exact_flag) { - if (verbose_flag) - printf("Verifying neighbors...\n"); - stl_verify_neighbors(stl); - } -} diff --git a/xs/src/admesh/util.cpp b/xs/src/admesh/util.cpp new file mode 100644 index 000000000..b0c31469d --- /dev/null +++ b/xs/src/admesh/util.cpp @@ -0,0 +1,573 @@ +/* ADMesh -- process triangulated solid meshes + * Copyright (C) 1995, 1996 Anthony D. Martin + * Copyright (C) 2013, 2014 several contributors, see AUTHORS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Questions, comments, suggestions, etc to + * https://github.com/admesh/admesh/issues + */ + +#include +#include +#include +#include + +#include "stl.h" + +static void stl_rotate(float *x, float *y, const double c, const double s); +static float get_area(stl_facet *facet); +static float get_volume(stl_file *stl); + + +void +stl_verify_neighbors(stl_file *stl) { + int i; + int j; + stl_edge edge_a; + stl_edge edge_b; + int neighbor; + int vnot; + + if (stl->error) return; + + stl->stats.backwards_edges = 0; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + edge_a.p1 = stl->facet_start[i].vertex[j]; + edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; + neighbor = stl->neighbors_start[i].neighbor[j]; + vnot = stl->neighbors_start[i].which_vertex_not[j]; + + if(neighbor == -1) + continue; /* this edge has no neighbor... Continue. */ + if(vnot < 3) { + edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; + edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; + } else { + stl->stats.backwards_edges += 1; + edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; + edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; + } + if(memcmp(&edge_a, &edge_b, SIZEOF_EDGE_SORT) != 0) { + /* These edges should match but they don't. Print results. */ + printf("edge %d of facet %d doesn't match edge %d of facet %d\n", + j, i, vnot + 1, neighbor); + stl_write_facet(stl, (char*)"first facet", i); + stl_write_facet(stl, (char*)"second facet", neighbor); + } + } + } +} + +void +stl_translate(stl_file *stl, float x, float y, float z) { + int i; + int j; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].x -= (stl->stats.min.x - x); + stl->facet_start[i].vertex[j].y -= (stl->stats.min.y - y); + stl->facet_start[i].vertex[j].z -= (stl->stats.min.z - z); + } + } + stl->stats.max.x -= (stl->stats.min.x - x); + stl->stats.max.y -= (stl->stats.min.y - y); + stl->stats.max.z -= (stl->stats.min.z - z); + stl->stats.min.x = x; + stl->stats.min.y = y; + stl->stats.min.z = z; + + stl_invalidate_shared_vertices(stl); +} + +/* Translates the stl by x,y,z, relatively from wherever it is currently */ +void +stl_translate_relative(stl_file *stl, float x, float y, float z) { + int i; + int j; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].x += x; + stl->facet_start[i].vertex[j].y += y; + stl->facet_start[i].vertex[j].z += z; + } + } + stl->stats.min.x += x; + stl->stats.min.y += y; + stl->stats.min.z += z; + stl->stats.max.x += x; + stl->stats.max.y += y; + stl->stats.max.z += z; + + stl_invalidate_shared_vertices(stl); +} + +void +stl_scale_versor(stl_file *stl, float versor[3]) { + int i; + int j; + + if (stl->error) return; + + /* scale extents */ + stl->stats.min.x *= versor[0]; + stl->stats.min.y *= versor[1]; + stl->stats.min.z *= versor[2]; + stl->stats.max.x *= versor[0]; + stl->stats.max.y *= versor[1]; + stl->stats.max.z *= versor[2]; + + /* scale size */ + stl->stats.size.x *= versor[0]; + stl->stats.size.y *= versor[1]; + stl->stats.size.z *= versor[2]; + + /* scale volume */ + if (stl->stats.volume > 0.0) { + stl->stats.volume *= (versor[0] * versor[1] * versor[2]); + } + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].x *= versor[0]; + stl->facet_start[i].vertex[j].y *= versor[1]; + stl->facet_start[i].vertex[j].z *= versor[2]; + } + } + + stl_invalidate_shared_vertices(stl); +} + +void +stl_scale(stl_file *stl, float factor) { + float versor[3]; + + if (stl->error) return; + + versor[0] = factor; + versor[1] = factor; + versor[2] = factor; + stl_scale_versor(stl, versor); +} + +static void calculate_normals(stl_file *stl) { + long i; + float normal[3]; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + stl_calculate_normal(normal, &stl->facet_start[i]); + stl_normalize_vector(normal); + stl->facet_start[i].normal.x = normal[0]; + stl->facet_start[i].normal.y = normal[1]; + stl->facet_start[i].normal.z = normal[2]; + } +} + +void stl_transform(stl_file *stl, float *trafo3x4) { + int i_face, i_vertex; + if (stl->error) + return; + for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { + stl_vertex *vertices = stl->facet_start[i_face].vertex; + for (i_vertex = 0; i_vertex < 3; ++ i_vertex) { + stl_vertex &v_dst = vertices[i_vertex]; + stl_vertex v_src = v_dst; + v_dst.x = trafo3x4[0] * v_src.x + trafo3x4[1] * v_src.y + trafo3x4[2] * v_src.z + trafo3x4[3]; + v_dst.y = trafo3x4[4] * v_src.x + trafo3x4[5] * v_src.y + trafo3x4[6] * v_src.z + trafo3x4[7]; + v_dst.z = trafo3x4[8] * v_src.x + trafo3x4[9] * v_src.y + trafo3x4[10] * v_src.z + trafo3x4[11]; + } + } + stl_get_size(stl); + calculate_normals(stl); +} + +void +stl_rotate_x(stl_file *stl, float angle) { + int i; + int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl_rotate(&stl->facet_start[i].vertex[j].y, + &stl->facet_start[i].vertex[j].z, c, s); + } + } + stl_get_size(stl); + calculate_normals(stl); +} + +void +stl_rotate_y(stl_file *stl, float angle) { + int i; + int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl_rotate(&stl->facet_start[i].vertex[j].z, + &stl->facet_start[i].vertex[j].x, c, s); + } + } + stl_get_size(stl); + calculate_normals(stl); +} + +void +stl_rotate_z(stl_file *stl, float angle) { + int i; + int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl_rotate(&stl->facet_start[i].vertex[j].x, + &stl->facet_start[i].vertex[j].y, c, s); + } + } + stl_get_size(stl); + calculate_normals(stl); +} + + + +static void +stl_rotate(float *x, float *y, const double c, const double s) { + double xold = *x; + double yold = *y; + *x = float(c * xold - s * yold); + *y = float(s * xold + c * yold); +} + +extern void +stl_get_size(stl_file *stl) { + int i; + int j; + + if (stl->error) return; + if (stl->stats.number_of_facets == 0) return; + + stl->stats.min.x = stl->facet_start[0].vertex[0].x; + stl->stats.min.y = stl->facet_start[0].vertex[0].y; + stl->stats.min.z = stl->facet_start[0].vertex[0].z; + stl->stats.max.x = stl->facet_start[0].vertex[0].x; + stl->stats.max.y = stl->facet_start[0].vertex[0].y; + stl->stats.max.z = stl->facet_start[0].vertex[0].z; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->stats.min.x = STL_MIN(stl->stats.min.x, + stl->facet_start[i].vertex[j].x); + stl->stats.min.y = STL_MIN(stl->stats.min.y, + stl->facet_start[i].vertex[j].y); + stl->stats.min.z = STL_MIN(stl->stats.min.z, + stl->facet_start[i].vertex[j].z); + stl->stats.max.x = STL_MAX(stl->stats.max.x, + stl->facet_start[i].vertex[j].x); + stl->stats.max.y = STL_MAX(stl->stats.max.y, + stl->facet_start[i].vertex[j].y); + stl->stats.max.z = STL_MAX(stl->stats.max.z, + stl->facet_start[i].vertex[j].z); + } + } + stl->stats.size.x = stl->stats.max.x - stl->stats.min.x; + stl->stats.size.y = stl->stats.max.y - stl->stats.min.y; + stl->stats.size.z = stl->stats.max.z - stl->stats.min.z; + stl->stats.bounding_diameter = sqrt( + stl->stats.size.x * stl->stats.size.x + + stl->stats.size.y * stl->stats.size.y + + stl->stats.size.z * stl->stats.size.z + ); +} + +void +stl_mirror_xy(stl_file *stl) { + int i; + int j; + float temp_size; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].z *= -1.0; + } + } + temp_size = stl->stats.min.z; + stl->stats.min.z = stl->stats.max.z; + stl->stats.max.z = temp_size; + stl->stats.min.z *= -1.0; + stl->stats.max.z *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ +} + +void +stl_mirror_yz(stl_file *stl) { + int i; + int j; + float temp_size; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].x *= -1.0; + } + } + temp_size = stl->stats.min.x; + stl->stats.min.x = stl->stats.max.x; + stl->stats.max.x = temp_size; + stl->stats.min.x *= -1.0; + stl->stats.max.x *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ +} + +void +stl_mirror_xz(stl_file *stl) { + int i; + int j; + float temp_size; + + if (stl->error) return; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + for(j = 0; j < 3; j++) { + stl->facet_start[i].vertex[j].y *= -1.0; + } + } + temp_size = stl->stats.min.y; + stl->stats.min.y = stl->stats.max.y; + stl->stats.max.y = temp_size; + stl->stats.min.y *= -1.0; + stl->stats.max.y *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ +} + +static float get_volume(stl_file *stl) { + long i; + stl_vertex p0; + stl_vertex p; + stl_normal n; + float height; + float area; + float volume = 0.0; + + if (stl->error) return 0; + + /* Choose a point, any point as the reference */ + p0.x = stl->facet_start[0].vertex[0].x; + p0.y = stl->facet_start[0].vertex[0].y; + p0.z = stl->facet_start[0].vertex[0].z; + + for(i = 0; i < stl->stats.number_of_facets; i++) { + p.x = stl->facet_start[i].vertex[0].x - p0.x; + p.y = stl->facet_start[i].vertex[0].y - p0.y; + p.z = stl->facet_start[i].vertex[0].z - p0.z; + /* Do dot product to get distance from point to plane */ + 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.0f; + } + return volume; +} + +void stl_calculate_volume(stl_file *stl) { + if (stl->error) return; + stl->stats.volume = get_volume(stl); + if(stl->stats.volume < 0.0) { + stl_reverse_all_facets(stl); + stl->stats.volume = -stl->stats.volume; + } +} + +static float get_area(stl_facet *facet) { + double cross[3][3]; + float sum[3]; + float n[3]; + float area; + int i; + + /* cast to double before calculating cross product because large coordinates + can result in overflowing product + (bad area is responsible for bad volume and bad facets reversal) */ + for(i = 0; i < 3; i++) { + cross[i][0]=(((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].z) - + ((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].y)); + cross[i][1]=(((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].x) - + ((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].z)); + cross[i][2]=(((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].y) - + ((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].x)); + } + + sum[0] = cross[0][0] + cross[1][0] + cross[2][0]; + sum[1] = cross[0][1] + cross[1][1] + cross[2][1]; + sum[2] = cross[0][2] + cross[1][2] + cross[2][2]; + + /* This should already be done. But just in case, let's do it again */ + stl_calculate_normal(n, facet); + stl_normalize_vector(n); + + area = 0.5 * (n[0] * sum[0] + n[1] * sum[1] + n[2] * sum[2]); + return area; +} + +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) { + + int i; + int last_edges_fixed = 0; + + if (stl->error) return; + + if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag + || fill_holes_flag || normal_directions_flag) { + if (verbose_flag) + printf("Checking exact...\n"); + exact_flag = 1; + stl_check_facets_exact(stl); + stl->stats.facets_w_1_bad_edge = + (stl->stats.connected_facets_2_edge - + stl->stats.connected_facets_3_edge); + stl->stats.facets_w_2_bad_edge = + (stl->stats.connected_facets_1_edge - + stl->stats.connected_facets_2_edge); + stl->stats.facets_w_3_bad_edge = + (stl->stats.number_of_facets - + stl->stats.connected_facets_1_edge); + } + + if(nearby_flag || fixall_flag) { + if(!tolerance_flag) { + tolerance = stl->stats.shortest_edge; + } + if(!increment_flag) { + increment = stl->stats.bounding_diameter / 10000.0; + } + + if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + for(i = 0; i < iterations; i++) { + if(stl->stats.connected_facets_3_edge < + stl->stats.number_of_facets) { + if (verbose_flag) + printf("\ +Checking nearby. Tolerance= %f Iteration=%d of %d...", + tolerance, i + 1, iterations); + stl_check_facets_nearby(stl, tolerance); + if (verbose_flag) + printf(" Fixed %d edges.\n", + stl->stats.edges_fixed - last_edges_fixed); + last_edges_fixed = stl->stats.edges_fixed; + tolerance += increment; + } else { + if (verbose_flag) + printf("\ +All facets connected. No further nearby check necessary.\n"); + break; + } + } + } else { + if (verbose_flag) + printf("All facets connected. No nearby check necessary.\n"); + } + } + + if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { + if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + if (verbose_flag) + printf("Removing unconnected facets...\n"); + stl_remove_unconnected_facets(stl); + } else + if (verbose_flag) + printf("No unconnected need to be removed.\n"); + } + + if(fill_holes_flag || fixall_flag) { + if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + if (verbose_flag) + printf("Filling holes...\n"); + stl_fill_holes(stl); + } else + if (verbose_flag) + printf("No holes need to be filled.\n"); + } + + if(reverse_all_flag) { + if (verbose_flag) + printf("Reversing all facets...\n"); + stl_reverse_all_facets(stl); + } + + if(normal_directions_flag || fixall_flag) { + if (verbose_flag) + printf("Checking normal directions...\n"); + stl_fix_normal_directions(stl); + } + + if(normal_values_flag || fixall_flag) { + if (verbose_flag) + printf("Checking normal values...\n"); + stl_fix_normal_values(stl); + } + + /* Always calculate the volume. It shouldn't take too long */ + if (verbose_flag) + printf("Calculating volume...\n"); + stl_calculate_volume(stl); + + if(exact_flag) { + if (verbose_flag) + printf("Verifying neighbors...\n"); + stl_verify_neighbors(stl); + } +} 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: -- cgit v1.2.3