diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 99713e059..e496e1233 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -618,7 +618,7 @@ sub load_model_objects { $o->repair; push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( - name => basename($model_object->input_file), + name => $model_object->name || basename($model_object->input_file), ); push @obj_idx, $#{ $self->{objects} }; @@ -1649,9 +1649,14 @@ sub object_cut_dialog { return unless $dlg->ShowModal == wxID_OK; if (my @new_objects = $dlg->NewModelObjects) { + my $process_dialog = Wx::ProgressDialog->new('Loading…', "Loading new objects…", 100, $self, 0); + $process_dialog->Pulse; + $self->remove($obj_idx); $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; + $self->arrange if @new_objects <= 2; # don't arrange for grid cuts + + $process_dialog->Destroy; } } diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 74dcae834..14a3500e0 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -6,6 +6,8 @@ use strict; use warnings; use utf8; +use POSIX qw(ceil); +use Scalar::Util qw(looks_like_number); use Slic3r::Geometry qw(PI X Y Z); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); @@ -109,8 +111,14 @@ sub new { )); { my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL); + $self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize); + $self->{btn_cut}->SetDefault; $cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10); + + $self->{btn_cut_grid} = Wx::Button->new($self, -1, "Cut by grid…", wxDefaultPosition, wxDefaultSize); + $cut_button_sizer->Add($self->{btn_cut_grid}, 0, wxALIGN_RIGHT | wxALL, 10); + $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( sizer => $cut_button_sizer, )); @@ -144,14 +152,14 @@ sub new { $self->_perform_cut() unless $self->{mesh_cut_valid}; # Adjust position / orientation of the split object halves. - if ($self->{new_model_objects}{lower}) { + if (my $lower = $self->{new_model_objects}[0]) { if ($self->{cut_options}{rotate_lower} && $self->{cut_options}{axis} == Z) { - $self->{new_model_objects}{lower}->rotate(PI, X); + $lower->rotate(PI, X); } - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 + $lower->center_around_origin; # align to Z = 0 } - if ($self->{new_model_objects}{upper}) { - $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 + if (my $upper = $self->{new_model_objects}[1]) { + $upper->center_around_origin; # align to Z = 0 } # Note that the window was already closed, so a pending update will not be executed. @@ -159,6 +167,48 @@ sub new { $self->EndModal(wxID_OK); $self->Destroy(); }); + + EVT_BUTTON($self, $self->{btn_cut_grid}, sub { + my $grid_x = Wx::GetTextFromUser("Enter the width of the desired tiles along the X axis:", + "Cut by Grid", 100, $self); + return if !looks_like_number($grid_x) || $grid_x <= 0; + + my $grid_y = Wx::GetTextFromUser("Enter the width of the desired tiles along the Y axis:", + "Cut by Grid", 100, $self); + return if !looks_like_number($grid_y) || $grid_y <= 0; + + my $process_dialog = Wx::ProgressDialog->new('Cutting…', "Cutting model by grid…", 100, $self, 0); + $process_dialog->Pulse; + + my $meshes = $self->{model_object}->mesh->cut_by_grid(Slic3r::Pointf->new($grid_x, $grid_y)); + $self->{new_model_objects} = []; + + my $bb = $self->{model_object}->bounding_box; + $self->{new_model} = my $model = Slic3r::Model->new; + for my $i (0..$#$meshes) { + push @{$self->{new_model_objects}}, my $o = $model->add_object( + name => sprintf('%s (%d)', $self->{model_object}->name, $i+1), + ); + my $v = $o->add_volume( + mesh => $meshes->[$i], + name => $o->name, + ); + my $min = $v->mesh->bounding_box->min_point->clone; + $o->center_around_origin; + my $i = $o->add_instance(offset => Slic3r::Pointf->new(@{$min}[X,Y])); + $i->offset->translate( + 5 * ceil(($i->offset->x - $bb->center->x) / $grid_x), + 5 * ceil(($i->offset->y - $bb->center->y) / $grid_y), + ); + } + + $process_dialog->Destroy; + + # Note that the window was already closed, so a pending update will not be executed. + $self->{already_closed} = 1; + $self->EndModal(wxID_OK); + $self->Destroy(); + }); EVT_CLOSE($self, sub { # Note that the window was already closed, so a pending update will not be executed. @@ -208,12 +258,12 @@ sub _perform_cut my ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z); my ($upper_object, $lower_object) = @{$new_model->objects}; $self->{new_model} = $new_model; - $self->{new_model_objects} = {}; + $self->{new_model_objects} = []; if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { - $self->{new_model_objects}{upper} = $upper_object; + $self->{new_model_objects}[1] = $upper_object; } if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { - $self->{new_model_objects}{lower} = $lower_object; + $self->{new_model_objects}[0] = $lower_object; } $self->{mesh_cut_valid} = 1; @@ -241,7 +291,7 @@ sub _update { # get volumes to render my @objects = (); if ($life_preview_active) { - push @objects, values %{$self->{new_model_objects}}; + push @objects, grep defined, @{$self->{new_model_objects}}; } else { push @objects, $self->{model_object}; } @@ -311,7 +361,7 @@ sub _update { sub NewModelObjects { my ($self) = @_; - return values %{ $self->{new_model_objects} }; + return grep defined, @{ $self->{new_model_objects} }; } 1; diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 82f536691..3192fb618 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -160,41 +160,12 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - const BoundingBoxf3 bb = mesh.bounding_box(); - mesh.translate(0, 0, -bb.min.z); - - const Sizef3 size = bb.size(); - const size_t x_parts = ceil((size.x - EPSILON)/cli_config.cut_grid.value.x); - const size_t y_parts = ceil((size.y - EPSILON)/cli_config.cut_grid.value.y); - - for (size_t i = 1; i <= x_parts; ++i) { - TriangleMesh curr; - if (i == x_parts) { - curr = mesh; - } else { - TriangleMesh next; - TriangleMeshSlicer(&mesh).cut(bb.min.x + (cli_config.cut_grid.value.x * i), &next, &curr); - curr.repair(); - next.repair(); - mesh = next; - } - - for (size_t j = 1; j <= y_parts; ++j) { - TriangleMesh tile; - if (j == y_parts) { - tile = curr; - } else { - TriangleMesh next; - TriangleMeshSlicer(&curr).cut(bb.min.y + (cli_config.cut_grid.value.y * j), &next, &tile); - tile.repair(); - next.repair(); - curr = next; - } - - std::ostringstream ss; - ss << model->objects.front()->input_file << "_" << i << "_" << j << ".stl"; - IO::STL::write(tile, ss.str()); - } + TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value); + for (TriangleMeshPtrs::iterator m = meshes.begin(); m != meshes.end(); ++m) { + std::ostringstream ss; + ss << model->objects.front()->input_file << "_" << (m - meshes.begin()) << ".stl"; + IO::STL::write(**m, ss.str()); + delete *m; } } else { std::cerr << "error: command not supported" << std::endl; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 5ce86817d..2a52c865f 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -395,6 +395,47 @@ TriangleMesh::split() const return meshes; } +TriangleMeshPtrs +TriangleMesh::cut_by_grid(const Pointf &grid) const +{ + TriangleMesh mesh = *this; + const BoundingBoxf3 bb = mesh.bounding_box(); + const Sizef3 size = bb.size(); + const size_t x_parts = ceil((size.x - EPSILON)/grid.x); + const size_t y_parts = ceil((size.y - EPSILON)/grid.y); + + TriangleMeshPtrs meshes; + for (size_t i = 1; i <= x_parts; ++i) { + TriangleMesh curr; + if (i == x_parts) { + curr = mesh; + } else { + TriangleMesh next; + TriangleMeshSlicer(&mesh).cut(bb.min.x + (grid.x * i), &next, &curr); + curr.repair(); + next.repair(); + mesh = next; + } + + for (size_t j = 1; j <= y_parts; ++j) { + TriangleMesh* tile; + if (j == y_parts) { + tile = new TriangleMesh(curr); + } else { + TriangleMesh next; + tile = new TriangleMesh; + TriangleMeshSlicer(&curr).cut(bb.min.y + (grid.y * j), &next, tile); + tile->repair(); + next.repair(); + curr = next; + } + + meshes.push_back(tile); + } + } + return meshes; +} + void TriangleMesh::merge(const TriangleMesh &mesh) { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 225d744d4..cba8ba43e 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -49,6 +49,7 @@ class TriangleMesh void center_around_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split() const; + TriangleMeshPtrs cut_by_grid(const Pointf &grid) const; void merge(const TriangleMesh &mesh); ExPolygons horizontal_projection() const; Polygon convex_hull(); @@ -112,8 +113,6 @@ class TriangleMeshSlicer void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; - static void cut(TriangleMesh* mesh, Axis axis, float z, TriangleMesh* upper, TriangleMesh* lower); - private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index a3b1bf4a0..373cba12e 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -30,6 +30,8 @@ void align_to_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split(); + TriangleMeshPtrs cut_by_grid(Pointf* grid) + %code{% RETVAL = THIS->cut_by_grid(*grid); %}; void merge(TriangleMesh* mesh) %code{% THIS->merge(*mesh); %}; ExPolygons horizontal_projection();