mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-01 04:42:00 +08:00
Cut by Grid also in GUI
This commit is contained in:
parent
54f782c121
commit
5cbdb7865d
@ -618,7 +618,7 @@ sub load_model_objects {
|
|||||||
$o->repair;
|
$o->repair;
|
||||||
|
|
||||||
push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(
|
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} };
|
push @obj_idx, $#{ $self->{objects} };
|
||||||
|
|
||||||
@ -1649,9 +1649,14 @@ sub object_cut_dialog {
|
|||||||
return unless $dlg->ShowModal == wxID_OK;
|
return unless $dlg->ShowModal == wxID_OK;
|
||||||
|
|
||||||
if (my @new_objects = $dlg->NewModelObjects) {
|
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->remove($obj_idx);
|
||||||
$self->load_model_objects(grep defined($_), @new_objects);
|
$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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
|
use POSIX qw(ceil);
|
||||||
|
use Scalar::Util qw(looks_like_number);
|
||||||
use Slic3r::Geometry qw(PI X Y Z);
|
use Slic3r::Geometry qw(PI X Y Z);
|
||||||
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
||||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||||
@ -109,8 +111,14 @@ sub new {
|
|||||||
));
|
));
|
||||||
{
|
{
|
||||||
my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
|
||||||
$self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize);
|
$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);
|
$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(
|
$optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
|
||||||
sizer => $cut_button_sizer,
|
sizer => $cut_button_sizer,
|
||||||
));
|
));
|
||||||
@ -144,14 +152,14 @@ sub new {
|
|||||||
$self->_perform_cut() unless $self->{mesh_cut_valid};
|
$self->_perform_cut() unless $self->{mesh_cut_valid};
|
||||||
|
|
||||||
# Adjust position / orientation of the split object halves.
|
# 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) {
|
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}) {
|
if (my $upper = $self->{new_model_objects}[1]) {
|
||||||
$self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0
|
$upper->center_around_origin; # align to Z = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Note that the window was already closed, so a pending update will not be executed.
|
# 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->EndModal(wxID_OK);
|
||||||
$self->Destroy();
|
$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 {
|
EVT_CLOSE($self, sub {
|
||||||
# Note that the window was already closed, so a pending update will not be executed.
|
# 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 ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z);
|
||||||
my ($upper_object, $lower_object) = @{$new_model->objects};
|
my ($upper_object, $lower_object) = @{$new_model->objects};
|
||||||
$self->{new_model} = $new_model;
|
$self->{new_model} = $new_model;
|
||||||
$self->{new_model_objects} = {};
|
$self->{new_model_objects} = [];
|
||||||
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
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) {
|
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;
|
$self->{mesh_cut_valid} = 1;
|
||||||
@ -241,7 +291,7 @@ sub _update {
|
|||||||
# get volumes to render
|
# get volumes to render
|
||||||
my @objects = ();
|
my @objects = ();
|
||||||
if ($life_preview_active) {
|
if ($life_preview_active) {
|
||||||
push @objects, values %{$self->{new_model_objects}};
|
push @objects, grep defined, @{$self->{new_model_objects}};
|
||||||
} else {
|
} else {
|
||||||
push @objects, $self->{model_object};
|
push @objects, $self->{model_object};
|
||||||
}
|
}
|
||||||
@ -311,7 +361,7 @@ sub _update {
|
|||||||
|
|
||||||
sub NewModelObjects {
|
sub NewModelObjects {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return values %{ $self->{new_model_objects} };
|
return grep defined, @{ $self->{new_model_objects} };
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -160,41 +160,12 @@ main(const int argc, const char **argv)
|
|||||||
TriangleMesh mesh = model->mesh();
|
TriangleMesh mesh = model->mesh();
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
|
|
||||||
const BoundingBoxf3 bb = mesh.bounding_box();
|
TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value);
|
||||||
mesh.translate(0, 0, -bb.min.z);
|
for (TriangleMeshPtrs::iterator m = meshes.begin(); m != meshes.end(); ++m) {
|
||||||
|
std::ostringstream ss;
|
||||||
const Sizef3 size = bb.size();
|
ss << model->objects.front()->input_file << "_" << (m - meshes.begin()) << ".stl";
|
||||||
const size_t x_parts = ceil((size.x - EPSILON)/cli_config.cut_grid.value.x);
|
IO::STL::write(**m, ss.str());
|
||||||
const size_t y_parts = ceil((size.y - EPSILON)/cli_config.cut_grid.value.y);
|
delete *m;
|
||||||
|
|
||||||
for (size_t i = 1; i <= x_parts; ++i) {
|
|
||||||
TriangleMesh curr;
|
|
||||||
if (i == x_parts) {
|
|
||||||
curr = mesh;
|
|
||||||
} else {
|
|
||||||
TriangleMesh next;
|
|
||||||
TriangleMeshSlicer<X>(&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<Y>(&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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "error: command not supported" << std::endl;
|
std::cerr << "error: command not supported" << std::endl;
|
||||||
|
@ -395,6 +395,47 @@ TriangleMesh::split() const
|
|||||||
return meshes;
|
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<X>(&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<Y>(&curr).cut(bb.min.y + (grid.y * j), &next, tile);
|
||||||
|
tile->repair();
|
||||||
|
next.repair();
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshes.push_back(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return meshes;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TriangleMesh::merge(const TriangleMesh &mesh)
|
TriangleMesh::merge(const TriangleMesh &mesh)
|
||||||
{
|
{
|
||||||
|
@ -49,6 +49,7 @@ class TriangleMesh
|
|||||||
void center_around_origin();
|
void center_around_origin();
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
TriangleMeshPtrs split() const;
|
TriangleMeshPtrs split() const;
|
||||||
|
TriangleMeshPtrs cut_by_grid(const Pointf &grid) const;
|
||||||
void merge(const TriangleMesh &mesh);
|
void merge(const TriangleMesh &mesh);
|
||||||
ExPolygons horizontal_projection() const;
|
ExPolygons horizontal_projection() const;
|
||||||
Polygon convex_hull();
|
Polygon convex_hull();
|
||||||
@ -112,8 +113,6 @@ class TriangleMeshSlicer
|
|||||||
|
|
||||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||||
|
|
||||||
static void cut(TriangleMesh* mesh, Axis axis, float z, TriangleMesh* upper, TriangleMesh* lower);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector< std::vector<int> > t_facets_edges;
|
typedef std::vector< std::vector<int> > t_facets_edges;
|
||||||
t_facets_edges facets_edges;
|
t_facets_edges facets_edges;
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
void align_to_origin();
|
void align_to_origin();
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
TriangleMeshPtrs split();
|
TriangleMeshPtrs split();
|
||||||
|
TriangleMeshPtrs cut_by_grid(Pointf* grid)
|
||||||
|
%code{% RETVAL = THIS->cut_by_grid(*grid); %};
|
||||||
void merge(TriangleMesh* mesh)
|
void merge(TriangleMesh* mesh)
|
||||||
%code{% THIS->merge(*mesh); %};
|
%code{% THIS->merge(*mesh); %};
|
||||||
ExPolygons horizontal_projection();
|
ExPolygons horizontal_projection();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user