From 22ba87c3b3efc2dd292561f8db98eb8e6b15deb8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 1 Mar 2017 22:19:30 +0100 Subject: [PATCH] Finished porting importers and read_from_file() to C++ --- lib/Slic3r.pm | 3 - lib/Slic3r/Format/AMF.pm | 13 --- lib/Slic3r/Format/AMF/Parser.pm | 162 -------------------------------- lib/Slic3r/Format/OBJ.pm | 13 --- lib/Slic3r/Format/STL.pm | 26 ----- lib/Slic3r/GUI/Plater.pm | 4 +- lib/Slic3r/Model.pm | 16 ---- slic3r.pl | 2 +- src/slic3r.cpp | 3 +- utils/amf-to-stl.pl | 4 +- utils/dump-stl.pl | 4 +- utils/split_stl.pl | 4 +- utils/stl-to-amf.pl | 4 +- xs/src/libslic3r/IO.cpp | 23 ++++- xs/src/libslic3r/IO.hpp | 2 + xs/src/libslic3r/Model.cpp | 29 +++++- xs/src/libslic3r/Model.hpp | 1 + xs/xsp/Model.xsp | 14 +++ 18 files changed, 76 insertions(+), 251 deletions(-) delete mode 100644 lib/Slic3r/Format/AMF.pm delete mode 100644 lib/Slic3r/Format/AMF/Parser.pm delete mode 100644 lib/Slic3r/Format/OBJ.pm delete mode 100644 lib/Slic3r/Format/STL.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 81cbc7eb5d..294b623ecc 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -52,9 +52,6 @@ use Slic3r::ExPolygon; use Slic3r::ExtrusionLoop; use Slic3r::ExtrusionPath; use Slic3r::Flow; -use Slic3r::Format::AMF; -use Slic3r::Format::OBJ; -use Slic3r::Format::STL; use Slic3r::GCode::ArcFitting; use Slic3r::GCode::MotionPlanner; use Slic3r::GCode::PressureRegulator; diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm deleted file mode 100644 index 551573c214..0000000000 --- a/lib/Slic3r/Format/AMF.pm +++ /dev/null @@ -1,13 +0,0 @@ -package Slic3r::Format::AMF; -use Moo; - -sub read_file { - my $self = shift; - my ($file) = @_; - - my $model = Slic3r::Model->new; - $model->read_amf($file); - return $model; -} - -1; diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm deleted file mode 100644 index ade2c44ac3..0000000000 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ /dev/null @@ -1,162 +0,0 @@ -package Slic3r::Format::AMF::Parser; -use strict; -use warnings; - -use base 'XML::SAX::Base'; - -my %xyz_index = (x => 0, y => 1, z => 2); #= - -sub new { - my $self = shift->SUPER::new(@_); - $self->{_tree} = []; - $self->{_objects_map} = {}; # this hash maps AMF object IDs to object indexes in $model->objects - $self->{_instances} = {}; # apply these lazily to make sure all objects have been parsed - $self; -} - -sub start_element { - my $self = shift; - my $data = shift; - - if ($data->{LocalName} eq 'object') { - $self->{_object} = $self->{_model}->add_object; - $self->{_object_vertices} = []; - $self->{_objects_map}{ $self->_get_attribute($data, 'id') } = $#{ $self->{_model}->objects }; - } elsif ($data->{LocalName} eq 'vertex') { - $self->{_vertex} = ["", "", ""]; - } elsif ($self->{_vertex} && $data->{LocalName} =~ /^[xyz]$/ && $self->{_tree}[-1] eq 'coordinates') { - $self->{_coordinate} = $data->{LocalName}; - } elsif ($data->{LocalName} eq 'volume') { - $self->{_volume} = $self->{_object}->add_volume( - material_id => $self->_get_attribute($data, 'materialid') // undef, - mesh => Slic3r::TriangleMesh->new, - ); - $self->{_volume_facets} = []; - } elsif ($data->{LocalName} eq 'triangle') { - $self->{_triangle} = ["", "", ""]; - } elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') { - $self->{_vertex_idx} = $1-1; - } elsif ($data->{LocalName} eq 'material') { - my $material_id = $self->_get_attribute($data, 'id') // '_'; - $self->{_material} = $self->{_model}->set_material($material_id); - } elsif ($data->{LocalName} eq 'metadata') { - $self->{_metadata_type} = $self->_get_attribute($data, 'type'); - $self->{_metadata_value} = ''; - } elsif ($data->{LocalName} eq 'constellation') { - $self->{_constellation} = 1; # we merge all constellations as we don't support more than one - } elsif ($data->{LocalName} eq 'instance' && $self->{_constellation}) { - my $object_id = $self->_get_attribute($data, 'objectid'); - $self->{_instances}{$object_id} ||= []; - push @{ $self->{_instances}{$object_id} }, $self->{_instance} = {}; - } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { - $self->{_instance_property} = $data->{LocalName}; - } - - push @{$self->{_tree}}, $data->{LocalName}; -} - -sub characters { - my $self = shift; - my $data = shift; - - if ($self->{_vertex} && $self->{_coordinate}) { - $self->{_vertex}[ $xyz_index{$self->{_coordinate}} ] .= $data->{Data}; - } elsif ($self->{_triangle} && defined $self->{_vertex_idx}) { - $self->{_triangle}[ $self->{_vertex_idx} ] .= $data->{Data}; - } elsif ($self->{_metadata_type}) { - $self->{_metadata_value} .= $data->{Data}; - } elsif ($self->{_instance_property}) { - $self->{_instance}{ $self->{_instance_property} } .= $data->{Data}; - } -} - -sub end_element { - my $self = shift; - my $data = shift; - - pop @{$self->{_tree}}; - - if ($data->{LocalName} eq 'object') { - $self->{_object} = undef; - $self->{_object_vertices} = undef; - } elsif ($data->{LocalName} eq 'vertex') { - push @{$self->{_object_vertices}}, $self->{_vertex}; - $self->{_vertex} = undef; - } elsif ($self->{_coordinate} && $data->{LocalName} =~ /^[xyz]$/) { - $self->{_coordinate} = undef; - } elsif ($data->{LocalName} eq 'volume') { - $self->{_volume}->mesh->ReadFromPerl($self->{_object_vertices}, $self->{_volume_facets}); - $self->{_volume}->mesh->repair; - $self->{_volume} = undef; - $self->{_volume_facets} = undef; - } elsif ($data->{LocalName} eq 'triangle') { - push @{$self->{_volume_facets}}, $self->{_triangle}; - $self->{_triangle} = undef; - } elsif (defined $self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) { - $self->{_vertex_idx} = undef; - } elsif ($data->{LocalName} eq 'material') { - $self->{_material} = undef; - } elsif ($data->{LocalName} eq 'metadata') { - my $value = $self->{_metadata_value}; - if ($self->{_metadata_type} =~ /^slic3r\.(.+)/) { - my $opt_key = $1; - if (exists $Slic3r::Config::Options->{$opt_key}) { - my $config; - if ($self->{_material}) { - $config = $self->{_material}->config; - } elsif ($self->{_volume}) { - $config = $self->{_volume}->config; - } elsif ($self->{_object}) { - $config = $self->{_object}->config; - } - - $config->set_deserialize($opt_key, $value) if defined $config; - } elsif ($opt_key eq 'modifier' && $self->{_volume}) { - $self->{_volume}->set_modifier($value); - } - } elsif ($self->{_metadata_type} eq 'name' && $self->{_volume}) { - $self->{_volume}->set_name($value); - } elsif ($self->{_metadata_type} eq 'name' && $self->{_object}) { - $self->{_object}->set_name($value); - } elsif ($self->{_material}) { - $self->{_material}->set_attribute($self->{_metadata_type}, $value); - } - $self->{_metadata_type} = undef; - $self->{_metadata_value} = undef; - } elsif ($data->{LocalName} eq 'constellation') { - $self->{_constellation} = undef; - } elsif ($data->{LocalName} eq 'instance') { - $self->{_instance} = undef; - } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { - $self->{_instance_property} = undef; - } -} - -sub end_document { - my $self = shift; - - foreach my $object_id (keys %{ $self->{_instances} }) { - my $new_object_id = $self->{_objects_map}{$object_id}; - if (!defined $new_object_id) { - warn "Undefined object $object_id referenced in constellation\n"; - next; - } - - foreach my $instance (@{ $self->{_instances}{$object_id} }) { - next if !defined($instance->{deltax}) || !defined($instance->{deltay}); - $self->{_model}->objects->[$new_object_id]->add_instance( - rotation => $instance->{rz} || 0, - offset => Slic3r::Pointf->new($instance->{deltax} || 0, $instance->{deltay} || 0), - ); - } - } -} - -sub _get_attribute { - my $self = shift; - my ($data, $name) = @_; - - return +(map $_->{Value}, grep $_->{Name} eq $name, values %{$data->{Attributes}})[0]; -} - -1; diff --git a/lib/Slic3r/Format/OBJ.pm b/lib/Slic3r/Format/OBJ.pm deleted file mode 100644 index 14b086c9f4..0000000000 --- a/lib/Slic3r/Format/OBJ.pm +++ /dev/null @@ -1,13 +0,0 @@ -package Slic3r::Format::OBJ; -use Moo; - -sub read_file { - my $self = shift; - my ($file) = @_; - - my $model = Slic3r::Model->new; - $model->read_obj($file); - return $model; -} - -1; diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm deleted file mode 100644 index 5a9359df66..0000000000 --- a/lib/Slic3r/Format/STL.pm +++ /dev/null @@ -1,26 +0,0 @@ -package Slic3r::Format::STL; -use Moo; - -sub read_file { - my $self = shift; - my ($file) = @_; - - my $model = Slic3r::Model->new; - $model->read_stl($file); - return $model; -} - -sub write_file { - my $self = shift; - my ($file, $mesh, %params) = @_; - - $mesh = $mesh->mesh if $mesh->isa('Slic3r::Model'); - - my $path = Slic3r::encode_path($file); - - $params{binary} - ? $mesh->write_binary($path) - : $mesh->write_ascii($path); -} - -1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 77f50391dd..8ab170ee69 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1420,7 +1420,7 @@ sub export_stl { return if !@{$self->{objects}}; my $output_file = $self->_get_export_file('STL') or return; - Slic3r::Format::STL->write_file($output_file, $self->{model}, binary => 1); + $self->{model}->write_stl($output_file, 1); $self->statusbar->SetStatusText("STL file exported to $output_file"); } @@ -1467,7 +1467,7 @@ sub export_object_stl { my $model_object = $self->{model}->objects->[$obj_idx]; my $output_file = $self->_get_export_file('STL') or return; - Slic3r::Format::STL->write_file($output_file, $model_object->mesh, binary => 1); + $model_object->mesh->write_binary($output_file); $self->statusbar->SetStatusText("STL file exported to $output_file"); } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index e852a4ab3f..99c69cd4a3 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -4,22 +4,6 @@ package Slic3r::Model; use List::Util qw(first max any); use Slic3r::Geometry qw(X Y Z move_points); -sub read_from_file { - my $class = shift; - my ($input_file) = @_; - - my $model = $input_file =~ /\.stl$/i ? Slic3r::Format::STL->read_file($input_file) - : $input_file =~ /\.obj$/i ? Slic3r::Format::OBJ->read_file($input_file) - : $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file) - : die "Input file must have .stl, .obj or .amf(.xml) extension\n"; - - die "The supplied file couldn't be read because it's empty.\n" - if $model->objects_count == 0; - - $_->set_input_file($input_file) for @{$model->objects}; - return $model; -} - sub merge { my $class = shift; my @models = @_; diff --git a/slic3r.pl b/slic3r.pl index 9f3ca42fe4..9c2eba7a74 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -217,7 +217,7 @@ 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); - Slic3r::Format::STL->write_file($output_file, $new_mesh, binary => 1); + $new_mesh->write_binary($output_file); } } exit; diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 3192fb618a..e194117b36 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -69,9 +69,8 @@ main(const int argc, const char **argv) } Model model; - // TODO: read other file formats with Model::read_from_file() try { - IO::STL::read(*it, &model); + model = Model::read_from_file(*it); } catch (std::exception &e) { std::cout << *it << ": " << e.what() << std::endl; exit(1); diff --git a/utils/amf-to-stl.pl b/utils/amf-to-stl.pl index f9599704fb..56df2e2317 100755 --- a/utils/amf-to-stl.pl +++ b/utils/amf-to-stl.pl @@ -26,12 +26,12 @@ my %opt = (); } { - my $model = Slic3r::Format::AMF->read_file($ARGV[0]); + my $model = Slic3r::Model->read_from_file($ARGV[0]); my $output_file = $ARGV[0]; $output_file =~ s/\.amf(?:\.xml)?$/\.stl/i; printf "Writing to %s\n", basename($output_file); - Slic3r::Format::STL->write_file($output_file, $model, binary => !$opt{ascii}); + $model->write_stl($output_file, !$opt{ascii}); } diff --git a/utils/dump-stl.pl b/utils/dump-stl.pl index 0c7f609dec..6da275abc9 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::Format::STL->read_file($ARGV[0]); + my $model = Slic3r::Model->read_from_file($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"; - Slic3r::Format::STL->write_file($ARGV[1], $model); + $model->write_stl($ARGV[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 e9990b8fc6..eb659b9cae 100755 --- a/utils/split_stl.pl +++ b/utils/split_stl.pl @@ -26,7 +26,7 @@ my %opt = (); } { - my $model = Slic3r::Format::STL->read_file($ARGV[0]); + my $model = Slic3r::Model->read_from_file($ARGV[0]); my $basename = $ARGV[0]; $basename =~ s/\.stl$//i; @@ -44,7 +44,7 @@ my %opt = (); my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count; printf "Writing to %s\n", basename($output_file); - Slic3r::Format::STL->write_file($output_file, $new_model, binary => !$opt{ascii}); + $new_model->write_stl($output_file, !$opt{ascii}); } } diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index f4e133ce15..cb0418937d 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -26,7 +26,7 @@ my %opt = (); } { - my @models = map Slic3r::Format::STL->read_file($_), @ARGV; + my @models = map Slic3r::Model->read_from_file($_), @ARGV; my $output_file = $ARGV[0]; $output_file =~ s/\.stl$/.amf.xml/i; @@ -54,7 +54,7 @@ my %opt = (); } printf "Writing to %s\n", basename($output_file); - Slic3r::Format::AMF->write_file($output_file, $new_model); + $new_model->write_amf($output_file); } diff --git a/xs/src/libslic3r/IO.cpp b/xs/src/libslic3r/IO.cpp index 7c958dc54d..b75b613359 100644 --- a/xs/src/libslic3r/IO.cpp +++ b/xs/src/libslic3r/IO.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" @@ -33,15 +34,22 @@ STL::read(std::string input_file, Model* model) throw std::runtime_error("This STL file couldn't be read because it's empty."); ModelObject* object = model->add_object(); - object->name = input_file; // TODO: use basename() + object->name = boost::filesystem::path(input_file).filename().string(); object->input_file = input_file; ModelVolume* volume = object->add_volume(mesh); - volume->name = input_file; // TODO: use basename() + volume->name = object->name; return true; } +bool +STL::write(Model& model, std::string output_file, bool binary) +{ + TriangleMesh mesh = model.mesh(); + return STL::write(mesh, output_file, binary); +} + bool STL::write(TriangleMesh& mesh, std::string output_file, bool binary) { @@ -83,7 +91,7 @@ OBJ::read(std::string input_file, Model* model) throw std::runtime_error("Error while reading OBJ file"); ModelObject* object = model->add_object(); - object->name = input_file; // TODO: use basename() + object->name = boost::filesystem::path(input_file).filename().string(); object->input_file = input_file; // Loop over shapes and add a volume for each one. @@ -118,12 +126,19 @@ OBJ::read(std::string input_file, Model* model) TriangleMesh mesh(points, facets); mesh.check_topology(); ModelVolume* volume = object->add_volume(mesh); - volume->name = input_file; // TODO: use basename() + volume->name = object->name; } return true; } +bool +OBJ::write(Model& model, std::string output_file) +{ + TriangleMesh mesh = model.mesh(); + return OBJ::write(mesh, output_file); +} + bool OBJ::write(TriangleMesh& mesh, std::string output_file) { diff --git a/xs/src/libslic3r/IO.hpp b/xs/src/libslic3r/IO.hpp index 4a6ffab011..5d02b171d3 100644 --- a/xs/src/libslic3r/IO.hpp +++ b/xs/src/libslic3r/IO.hpp @@ -13,6 +13,7 @@ class STL public: static bool read(std::string input_file, TriangleMesh* mesh); static bool read(std::string input_file, Model* model); + static bool write(Model& model, std::string output_file, bool binary = true); static bool write(TriangleMesh& mesh, std::string output_file, bool binary = true); }; @@ -21,6 +22,7 @@ class OBJ public: static bool read(std::string input_file, TriangleMesh* mesh); static bool read(std::string input_file, Model* model); + static bool write(Model& model, std::string output_file); static bool write(TriangleMesh& mesh, std::string output_file); }; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 0fe0722185..3b02c36d51 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -1,7 +1,9 @@ #include "Model.hpp" #include "Geometry.hpp" +#include "IO.hpp" #include -#include "boost/filesystem.hpp" +#include +#include namespace Slic3r { @@ -38,6 +40,31 @@ Model::~Model() this->clear_materials(); } +Model +Model::read_from_file(std::string input_file) +{ + Model model; + + if (boost::algorithm::iends_with(input_file, ".stl")) { + IO::STL::read(input_file, &model); + } else if (boost::algorithm::iends_with(input_file, ".obj")) { + IO::OBJ::read(input_file, &model); + } else if (boost::algorithm::iends_with(input_file, ".amf") + || boost::algorithm::iends_with(input_file, ".amf.xml")) { + IO::AMF::read(input_file, &model); + } else { + throw std::runtime_error("Unknown file format"); + } + + if (model.objects.empty()) + throw std::runtime_error("The supplied file couldn't be read because it's empty"); + + for (ModelObjectPtrs::const_iterator o = model.objects.begin(); o != model.objects.end(); ++o) + (*o)->input_file = input_file; + + return model; +} + ModelObject* Model::add_object() { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 16ea811552..9c8e0d1e20 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -47,6 +47,7 @@ class Model Model& operator= (Model other); void swap(Model &other); ~Model(); + static Model read_from_file(std::string input_file); ModelObject* add_object(); ModelObject* add_object(const ModelObject &other, bool copy_volumes = true); void delete_object(size_t idx); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index a3d0948bc9..c5c7006068 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -10,6 +10,15 @@ %name{Slic3r::Model} class Model { Model(); ~Model(); + + %name{read_from_file} Model(std::string input_file) + %code%{ + try { + RETVAL = new Model(Model::read_from_file(input_file)); + } catch (std::exception& e) { + croak("Error while opening %s: %s\n", input_file.c_str(), e.what()); + } + %}; Clone clone() %code%{ RETVAL = THIS; %}; @@ -20,6 +29,11 @@ %code%{ RETVAL = Slic3r::IO::OBJ::read(input_file, THIS); %}; bool read_amf(std::string input_file) %code%{ RETVAL = Slic3r::IO::AMF::read(input_file, THIS); %}; + + bool write_stl(std::string output_file, bool binary = false) + %code%{ RETVAL = Slic3r::IO::STL::write(*THIS, output_file, binary); %}; + bool write_obj(std::string output_file) + %code%{ RETVAL = Slic3r::IO::OBJ::write(*THIS, output_file); %}; bool write_amf(std::string output_file) %code%{ RETVAL = Slic3r::IO::AMF::write(*THIS, output_file); %};