diff --git a/README.md b/README.md index 167c3f33..2c71475d 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ The author of the Silk icon set is Mark James. Non-slicing actions (no G-code will be generated): --repair Repair given STL files and save them as _fixed.obj + --cut Cut given input files at given Z (relative) and export + them as _upper.stl and _lower.stl --info Output information about the supplied file(s) and exit -j, --threads Number of threads to use (1+, default: 2) diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index d53a61ad..c21f9b85 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -25,13 +25,15 @@ sub read_file { sub write_file { my $self = shift; - my ($file, $model, %params) = @_; + my ($file, $mesh, %params) = @_; + + $mesh = $mesh->mesh if $mesh->isa('Slic3r::Model'); my $path = Slic3r::encode_path($file); $params{binary} - ? $model->mesh->write_binary($path) - : $model->mesh->write_ascii($path); + ? $mesh->write_binary($path) + : $mesh->write_ascii($path); } 1; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index a39fd165..b80bc5ed 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -191,6 +191,21 @@ sub has_objects_with_no_instances { return (first { !defined $_->instances } @{$self->objects}) ? 1 : 0; } +# makes sure all objects have at least one instance +sub add_default_instances { + my ($self) = @_; + + # apply a default position to all objects not having one + my $added = 0; + foreach my $object (@{$self->objects}) { + if (!defined $object->instances) { + $object->add_instance(offset => [0,0]); + $added = 1; + } + } + return $added; +} + # this returns the bounding box of the *transformed* instances sub bounding_box { my $self = shift; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 8fa9889f..794661d8 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -46,13 +46,8 @@ sub set_model { # make method idempotent so that the object is reusable $self->_print->delete_all_objects; - my $need_arrange = $model->has_objects_with_no_instances; - if ($need_arrange) { - # apply a default position to all objects not having one - foreach my $object (@{$model->objects}) { - $object->add_instance(offset => [0,0]) if !defined $object->instances; - } - } + # make sure all objects have at least one defined instance + my $need_arrange = $model->add_default_instances; # apply scaling and rotation supplied from command line if any foreach my $instance (map @{$_->instances}, @{$model->objects}) { diff --git a/slic3r.pl b/slic3r.pl index f9e6f637..73b6c373 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -36,6 +36,7 @@ my %cli_options = (); 'export-svg' => \$opt{export_svg}, 'merge|m' => \$opt{merge}, 'repair' => \$opt{repair}, + 'cut=f' => \$opt{cut}, 'info' => \$opt{info}, 'scale=f' => \$opt{scale}, @@ -120,6 +121,25 @@ if (@ARGV) { # slicing from command line exit; } + if ($opt{cut}) { + foreach my $file (@ARGV) { + my $model = Slic3r::Model->read_from_file($file); + $model->add_default_instances; + my $mesh = $model->mesh; + $mesh->translate(0, 0, -$mesh->bounding_box->z_min); + my $upper = Slic3r::TriangleMesh->new; + my $lower = Slic3r::TriangleMesh->new; + $mesh->cut($opt{cut}, $upper, $lower); + $upper->repair; + $lower->repair; + Slic3r::Format::STL->write_file("${file}_upper.stl", $upper, binary => 0) + if $upper->facets_count > 0; + Slic3r::Format::STL->write_file("${file}_lower.stl", $lower, binary => 0) + if $lower->facets_count > 0; + } + exit; + } + while (my $input_file = shift @ARGV) { my $model; if ($opt{merge}) { @@ -203,6 +223,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ... Non-slicing actions (no G-code will be generated): --repair Repair given STL files and save them as _fixed.obj + --cut Cut given input files at given Z (relative) and export + them as _upper.stl and _lower.stl --info Output information about the supplied file(s) and exit $j diff --git a/xs/src/TriangleMesh.cpp b/xs/src/TriangleMesh.cpp index 005ffd0d..5b2332c6 100644 --- a/xs/src/TriangleMesh.cpp +++ b/xs/src/TriangleMesh.cpp @@ -755,7 +755,7 @@ TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPoly void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) { - std::vector lines; + std::vector upper_lines, lower_lines; float scaled_z = scale_(z); for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { @@ -766,13 +766,26 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); // intersect facet with cutting plane + std::vector lines; this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &lines); + // save intersection lines for generating correct triangulations + for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->edge_type == feTop) { + lower_lines.push_back(*it); + } else if (it->edge_type == feBottom) { + upper_lines.push_back(*it); + } else if (it->edge_type != feHorizontal) { + lower_lines.push_back(*it); + upper_lines.push_back(*it); + } + } + if (min_z > z || (min_z == z && max_z > min_z)) { - // facet is above the cut plane but does not belong to it + // facet is above the cut plane and does not belong to it if (upper != NULL) stl_add_facet(&upper->stl, facet); } else if (max_z < z || (max_z == z && max_z > min_z)) { - // facet is below the cut plane but does not belong to it + // facet is below the cut plane and does not belong to it if (lower != NULL) stl_add_facet(&lower->stl, facet); } else if (min_z < z && max_z > z) { // facet is cut by the slicing plane @@ -803,18 +816,21 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) // build the triangular facet stl_facet triangle; + triangle.normal = facet->normal; triangle.vertex[0] = *v0; triangle.vertex[1] = v0v1; triangle.vertex[2] = v2v0; // build the facets forming a quadrilateral on the other side stl_facet quadrilateral[2]; + quadrilateral[0].normal = facet->normal; quadrilateral[0].vertex[0] = *v1; quadrilateral[0].vertex[1] = *v2; quadrilateral[0].vertex[2] = v0v1; + quadrilateral[1].normal = facet->normal; quadrilateral[1].vertex[0] = *v2; - quadrilateral[1].vertex[0] = v2v0; - quadrilateral[1].vertex[0] = v0v1; + quadrilateral[1].vertex[1] = v2v0; + quadrilateral[1].vertex[2] = v0v1; if (v0->z > z) { if (upper != NULL) stl_add_facet(&upper->stl, &triangle); @@ -832,9 +848,53 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) } } - // compute shape of section - ExPolygons section; - this->make_expolygons(lines, §ion); + // triangulate holes of upper mesh + if (upper != NULL) { + // compute shape of section + ExPolygons section; + this->make_expolygons(upper_lines, §ion); + + // triangulate section + Polygons triangles; + for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) + expolygon->triangulate(&triangles); + + // convert triangles to facets and append them to mesh + for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { + Polygon p = *polygon; + p.reverse(); + stl_facet facet; + for (size_t i = 0; i <= 2; ++i) { + facet.vertex[i].x = unscale(p.points[i].x); + facet.vertex[i].y = unscale(p.points[i].y); + facet.vertex[i].z = z; + } + //stl_add_facet(&upper->stl, &facet); + } + } + + // triangulate holes of lower mesh + if (lower != NULL) { + // compute shape of section + ExPolygons section; + this->make_expolygons(lower_lines, §ion); + + // triangulate section + Polygons triangles; + for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) + expolygon->triangulate(&triangles); + + // convert triangles to facets and append them to mesh + for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { + stl_facet facet; + for (size_t i = 0; i <= 2; ++i) { + facet.vertex[i].x = unscale(polygon->points[i].x); + facet.vertex[i].y = unscale(polygon->points[i].y); + facet.vertex[i].z = z; + } + //stl_add_facet(&lower->stl, &facet); + } + } /* stl_get_size(&(upper->stl)); diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 9b1f2175..5c18de5a 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -108,7 +108,7 @@ my $cube = { my $lower = Slic3r::TriangleMesh->new; $m->cut(0, $upper, $lower); #$upper->repair; $lower->repair; - is $upper->facets_count, 10, 'upper mesh has all facets except those belonging to the slicing plane'; + is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane'; is $lower->facets_count, 0, 'lower mesh has no facets'; } { @@ -116,8 +116,8 @@ my $cube = { my $lower = Slic3r::TriangleMesh->new; $m->cut(10, $upper, $lower); #$upper->repair; $lower->repair; - is $upper->facets_count, 14, 'upper mesh has the right number of facets'; - is $lower->facets_count, 14, 'lower mesh has the right number of facets'; + is $upper->facets_count, 16, 'upper mesh has the right number of facets'; + is $lower->facets_count, 16, 'lower mesh has the right number of facets'; } }