diff --git a/lib/Slic3r/Format/OBJ.pm b/lib/Slic3r/Format/OBJ.pm index 616b38cdc..7a92fec82 100644 --- a/lib/Slic3r/Format/OBJ.pm +++ b/lib/Slic3r/Format/OBJ.pm @@ -21,7 +21,7 @@ sub read_file { my $mesh = Slic3r::TriangleMesh->new; $mesh->ReadFromPerl($vertices, $facets); - $mesh->repair; + $mesh->check_topology; my $model = Slic3r::Model->new; diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 11928d8b2..a3b859104 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -12,7 +12,7 @@ sub read_file { my $mesh = Slic3r::TriangleMesh->new; $mesh->ReadSTLFile($path); - $mesh->repair; + $mesh->check_topology; die "This STL file couldn't be read because it's empty.\n" if $mesh->facets_count == 0; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 9acf8f1d4..a8190b934 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -179,6 +179,13 @@ sub _init_menubar { # File menu my $fileMenu = Wx::Menu->new; { + $self->_append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub { + $self->{plater}->add if $self->{plater}; + }, undef, 'brick_add.png'); + $self->_append_menu_item($fileMenu, "Open 2.5D TIN mesh…", 'Import a 2.5D TIN mesh', sub { + $self->{plater}->add_tin if $self->{plater}; + }, undef, 'map_add.png'); + $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub { $self->load_config_file; }, undef, 'plugin_add.png'); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4a75390a9..b27a3e0cf 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -510,6 +510,36 @@ sub add { $self->load_file($_) for @input_files; } +sub add_tin { + my $self = shift; + + my @input_files = wxTheApp->open_model($self); + return if !@input_files; + + my $offset = Wx::GetNumberFromUser("", "Enter the minimum thickness in mm (i.e. the offset from the lowest point):", "2.5D TIN", + 5, 0, 1000000, $self); + return if $offset < 0; + + foreach my $input_file (@input_files) { + my $model = eval { Slic3r::Model->read_from_file($input_file) }; + Slic3r::GUI::show_error($self, $@) if $@; + next if !$model; + + if ($model->looks_like_multipart_object) { + Slic3r::GUI::show_error($self, "Multi-part models cannot be opened as 2.5D TIN files. Please load a single continuous mesh."); + next; + } + + my $model_object = $model->get_object(0); + eval { + $model_object->get_volume(0)->extrude_tin($offset); + }; + Slic3r::GUI::show_error($self, $@) if $@; + + $self->load_model_objects($model_object); + } +} + sub load_file { my $self = shift; my ($input_file) = @_; @@ -555,6 +585,7 @@ sub load_model_objects { my @obj_idx = (); foreach my $model_object (@model_objects) { my $o = $self->{model}->add_object($model_object); + $o->repair; push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( name => basename($model_object->input_file), diff --git a/slic3r.pl b/slic3r.pl index 6f40e57a3..962562530 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -186,6 +186,7 @@ if (@ARGV) { # slicing from command line } else { $model = Slic3r::Model->read_from_file($input_file); } + $model->repair; if ($opt{info}) { $model->print_info; diff --git a/src/utils/extrude-tin.cpp b/src/utils/extrude-tin.cpp index d2b9b49e6..0043b42e8 100644 --- a/src/utils/extrude-tin.cpp +++ b/src/utils/extrude-tin.cpp @@ -34,59 +34,12 @@ main(const int argc, const char **argv) for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) { TriangleMesh mesh; Slic3r::IO::STL::read(*it, &mesh); - calculate_normals(&mesh.stl); - - if (mesh.facets_count() == 0) { - printf("Error: file is empty: %s\n", it->c_str()); - continue; - } - - float z = mesh.stl.stats.min.z - config.option("offset", true)->getFloat(); - printf("min.z = %f, z = %f\n", mesh.stl.stats.min.z, z); - TriangleMesh mesh2 = mesh; - - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { - const stl_facet &facet = mesh.stl.facet_start[i]; - - if (facet.normal.z < 0) { - printf("Invalid 2.5D mesh / TIN (one facet points downwards = %f)\n", facet.normal.z); - exit(1); - } - - for (int j = 0; j < 3; ++j) { - if (mesh.stl.neighbors_start[i].neighbor[j] == -1) { - stl_facet new_facet; - float normal[3]; - - // first triangle - new_facet.vertex[0] = new_facet.vertex[2] = facet.vertex[(j+1)%3]; - new_facet.vertex[1] = facet.vertex[j]; - new_facet.vertex[2].z = z; - stl_calculate_normal(normal, &new_facet); - stl_normalize_vector(normal); - new_facet.normal.x = normal[0]; - new_facet.normal.y = normal[1]; - new_facet.normal.z = normal[2]; - stl_add_facet(&mesh2.stl, &new_facet); - - // second triangle - new_facet.vertex[0] = new_facet.vertex[1] = facet.vertex[j]; - new_facet.vertex[2] = facet.vertex[(j+1)%3]; - new_facet.vertex[1].z = new_facet.vertex[2].z = z; - new_facet.normal.x = normal[0]; - new_facet.normal.y = normal[1]; - new_facet.normal.z = normal[2]; - stl_add_facet(&mesh2.stl, &new_facet); - } - } - } - - mesh2.repair(); + mesh.extrude_tin(config.option("offset", true)->getFloat()); std::string outfile = config.option("output", true)->getString(); if (outfile.empty()) outfile = *it + "_extruded.stl"; - Slic3r::IO::STL::write(mesh2, outfile); + Slic3r::IO::STL::write(mesh, outfile); printf("Extruded mesh written to %s\n", outfile.c_str()); } diff --git a/var/map_add.png b/var/map_add.png new file mode 100755 index 000000000..2b72da06a Binary files /dev/null and b/var/map_add.png differ diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index d800e8f9c..12b3fa772 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -321,7 +321,8 @@ stl_read(stl_file *stl, int first_facet, int first) { // 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 + int j; + for (j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats if (*f == 0x80000000) // Negative zero, switch to positive zero. *f = 0; diff --git a/xs/src/admesh/util.c b/xs/src/admesh/util.c index b2282d3bc..0ee6b209c 100644 --- a/xs/src/admesh/util.c +++ b/xs/src/admesh/util.c @@ -251,8 +251,8 @@ 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); + *x = c * xold - s * yold; + *y = s * xold + c * yold; } extern void diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c1d7a9edf..26432c569 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef SLIC3R_DEBUG #include "SVG.hpp" @@ -426,6 +427,55 @@ TriangleMesh::require_shared_vertices() if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl)); } +void +TriangleMesh::extrude_tin(float offset) +{ + calculate_normals(&this->stl); + + const int number_of_facets = this->stl.stats.number_of_facets; + if (number_of_facets == 0) + throw std::runtime_error("Error: file is empty"); + + const float z = this->stl.stats.min.z - offset; + + for (int i = 0; i < number_of_facets; ++i) { + const stl_facet &facet = this->stl.facet_start[i]; + + if (facet.normal.z < 0) + throw std::runtime_error("Invalid 2.5D mesh: at least one facet points downwards."); + + for (int j = 0; j < 3; ++j) { + if (this->stl.neighbors_start[i].neighbor[j] == -1) { + stl_facet new_facet; + float normal[3]; + + // first triangle + new_facet.vertex[0] = new_facet.vertex[2] = facet.vertex[(j+1)%3]; + new_facet.vertex[1] = facet.vertex[j]; + new_facet.vertex[2].z = z; + stl_calculate_normal(normal, &new_facet); + stl_normalize_vector(normal); + new_facet.normal.x = normal[0]; + new_facet.normal.y = normal[1]; + new_facet.normal.z = normal[2]; + stl_add_facet(&this->stl, &new_facet); + + // second triangle + new_facet.vertex[0] = new_facet.vertex[1] = facet.vertex[j]; + new_facet.vertex[2] = facet.vertex[(j+1)%3]; + new_facet.vertex[1].z = new_facet.vertex[2].z = z; + new_facet.normal.x = normal[0]; + new_facet.normal.y = normal[1]; + new_facet.normal.z = normal[2]; + stl_add_facet(&this->stl, &new_facet); + } + } + } + stl_get_size(&this->stl); + + this->repair(); +} + void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 2c538c3f5..dc8450a51 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -55,6 +55,7 @@ class TriangleMesh void reset_repair_stats(); bool needed_repair() const; size_t facets_count() const; + void extrude_tin(float offset); stl_file stl; bool repaired; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 187b57e68..83576c814 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -71,6 +71,7 @@ void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); void print_info(); + void repair(); }; @@ -142,6 +143,8 @@ ModelMaterial::attributes() void clear_volumes(); int volumes_count() %code%{ RETVAL = THIS->volumes.size(); %}; + Ref get_volume(int idx) + %code%{ RETVAL = THIS->volumes.at(idx); %}; %name{_add_instance} Ref add_instance(); Ref _add_instance_clone(ModelInstance* other) @@ -198,6 +201,7 @@ ModelMaterial::attributes() %}; void print_info(); + void repair(); }; @@ -225,6 +229,15 @@ ModelMaterial::attributes() %code%{ THIS->modifier = modifier; %}; ModelMaterial* assign_unique_material(); + + void extrude_tin(float offset) + %code%{ + try { + THIS->mesh.extrude_tin(offset); + } catch (std::exception& e) { + croak("%s", e.what()); + } + %}; };