From 5a96bad8c2e282a9fb3e67d7b4efca5b59c974dd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 8 Aug 2014 21:48:59 +0200 Subject: [PATCH 1/4] Ported some more methods to C++ --- lib/Slic3r.pm | 1 - lib/Slic3r/Model.pm | 25 --------------------- lib/Slic3r/TriangleMesh.pm | 22 ------------------- xs/src/libslic3r/Model.cpp | 36 +++++++++++++++++++++++++++++++ xs/src/libslic3r/Model.hpp | 5 +++-- xs/src/libslic3r/TriangleMesh.cpp | 11 ++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 1 + xs/xsp/Model.xsp | 4 ++++ xs/xsp/TriangleMesh.xsp | 6 ++++++ 9 files changed, 61 insertions(+), 50 deletions(-) delete mode 100644 lib/Slic3r/TriangleMesh.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index c8422a1ba..25db38c30 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -71,7 +71,6 @@ use Slic3r::Print::Object; use Slic3r::Print::Simple; use Slic3r::Print::SupportMaterial; use Slic3r::Surface; -use Slic3r::TriangleMesh; our $build = eval "use Slic3r::Build; 1"; use Thread::Semaphore; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 5f9a808b6..0399c236f 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -158,21 +158,6 @@ sub _arrange { ); } -# 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 ($object->instances_count == 0) { - $object->add_instance(offset => Slic3r::Pointf->new(0,0)); - $added = 1; - } - } - return $added; -} - # this returns the bounding box of the *transformed* instances sub bounding_box { my $self = shift; @@ -535,16 +520,6 @@ sub unique_materials { return sort keys %materials; } -sub facets_count { - my $self = shift; - return sum(map $_->mesh->facets_count, grep !$_->modifier, @{$self->volumes}); -} - -sub needed_repair { - my $self = shift; - return (first { !$_->mesh->needed_repair } grep !$_->modifier, @{$self->volumes}) ? 0 : 1; -} - sub mesh_stats { my $self = shift; diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm deleted file mode 100644 index b06ba281e..000000000 --- a/lib/Slic3r/TriangleMesh.pm +++ /dev/null @@ -1,22 +0,0 @@ -package Slic3r::TriangleMesh; -use strict; -use warnings; - -use List::Util qw(first); -use Slic3r::Geometry qw(X Y); -use Slic3r::Geometry::Clipper qw(union_ex offset); - -sub needed_repair { - my $self = shift; - - my $stats = $self->stats; - return (first { $stats->{$_} > 0 } - qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)) ? 1 : 0; -} - -sub center { - my $self = shift; - return $self->bounding_box->center; -} - -1; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index cf7d9d1c5..a63cb3c46 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -158,6 +158,21 @@ Model::has_objects_with_no_instances() const return false; } +// makes sure all objects have at least one instance +bool +Model::add_default_instances() +{ + bool added = false; + // apply a default position to all objects not having one + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + if ((*o)->instances.empty()) { + (*o)->add_instance(); + added = true; + } + } + return true; +} + #ifdef SLIC3RXS REGISTER_CLASS(Model, "Model"); #endif @@ -321,6 +336,27 @@ ModelObject::raw_mesh(TriangleMesh* mesh) const } } +size_t +ModelObject::facets_count() const +{ + size_t num = 0; + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + if ((*v)->modifier) continue; + num += (*v)->mesh.stl.stats.number_of_facets; + } + return num; +} + +bool +ModelObject::needed_repair() const +{ + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + if ((*v)->modifier) continue; + if ((*v)->mesh.needed_repair()) return true; + } + return false; +} + #ifdef SLIC3RXS REGISTER_CLASS(ModelObject, "Model::Object"); #endif diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index da21759d4..4c193d980 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -53,6 +53,7 @@ class Model // void arrange_objects(coordf_t distance, const BoundingBox &bb); // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); bool has_objects_with_no_instances() const; + bool add_default_instances(); // void bounding_box(BoundingBox* bb) const; // void align_to_origin(); // void center_instances_around_point(const Pointf &point); @@ -121,8 +122,8 @@ class ModelObject //void translate(coordf_t x, coordf_t y, coordf_t z); //size_t materials_count() const; //void unique_materials(std::vector* materials) const; - //size_t facets_count() const; - //bool needed_repair() const; + size_t facets_count() const; + bool needed_repair() const; private: Model* model; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 04e892592..01e008efd 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -153,6 +153,17 @@ TriangleMesh::reset_repair_stats() { this->stl.stats.normals_fixed = 0; } +bool +TriangleMesh::needed_repair() const +{ + return this->stl.stats.degenerate_facets > 0 + || this->stl.stats.edges_fixed > 0 + || this->stl.stats.facets_removed > 0 + || this->stl.stats.facets_added > 0 + || this->stl.stats.facets_reversed > 0 + || this->stl.stats.backwards_edges > 0; +} + void TriangleMesh::WriteOBJFile(char* output_file) { stl_generate_shared_vertices(&stl); diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index c35300c39..2099f5e1e 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -45,6 +45,7 @@ class TriangleMesh void convex_hull(Polygon* hull); void bounding_box(BoundingBoxf3* bb) const; void reset_repair_stats(); + bool needed_repair() const; stl_file stl; bool repaired; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index d83b92b5e..04b7c8207 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -56,6 +56,7 @@ // void arrange_objects(coordf_t distance, const BoundingBox &bb); // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); bool has_objects_with_no_instances() const; + bool add_default_instances(); // void bounding_box(BoundingBox* bb) const; // void center_instances_around_point(const Pointf &point); // void align_instances_to_origin(); @@ -163,6 +164,9 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->origin_translation; %}; void set_origin_translation(Pointf* point) %code%{ THIS->origin_translation = *point; %}; + + bool needed_repair() const; + int facets_count(); }; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index ba5cdd0ce..d763fd98a 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -38,6 +38,12 @@ RETVAL = new BoundingBoxf3(); THIS->bounding_box(RETVAL); %}; + Pointf3* center() + %code{% + BoundingBoxf3 bb; + THIS->bounding_box(&bb); + RETVAL = new Pointf3(bb.center()); + %}; int facets_count() %code{% RETVAL = THIS->stl.stats.number_of_facets; %}; void reset_repair_stats(); From 73b3c0636158036b8a1d3805ba91da6eddacbe87 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 21 Sep 2014 10:51:36 +0200 Subject: [PATCH 2/4] Ported more things to XS --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/Plater.pm | 3 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 7 +- lib/Slic3r/Model.pm | 242 ----------------------- lib/Slic3r/Print/Simple.pm | 2 +- lib/Slic3r/Test.pm | 2 +- slic3r.pl | 4 +- xs/src/libslic3r/BoundingBox.cpp | 40 ++-- xs/src/libslic3r/BoundingBox.hpp | 11 +- xs/src/libslic3r/Model.cpp | 239 ++++++++++++++++++++++ xs/src/libslic3r/Model.hpp | 29 +-- xs/src/libslic3r/TriangleMesh.cpp | 22 ++- xs/src/libslic3r/TriangleMesh.hpp | 4 +- xs/t/01_trianglemesh.t | 4 +- xs/t/17_boundingbox.t | 9 +- xs/xsp/BoundingBox.xsp | 3 + xs/xsp/Model.xsp | 51 ++++- xs/xsp/TriangleMesh.xsp | 7 +- 18 files changed, 383 insertions(+), 297 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 25db38c30..d59674251 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -177,6 +177,7 @@ sub thread_cleanup { *Slic3r::Flow::DESTROY = sub {}; *Slic3r::GCode::PlaceholderParser::DESTROY = sub {}; *Slic3r::Geometry::BoundingBox::DESTROY = sub {}; + *Slic3r::Geometry::BoundingBoxf::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {}; *Slic3r::Line::DESTROY = sub {}; *Slic3r::Model::DESTROY = sub {}; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 597eebfb6..3a2a71f63 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -497,6 +497,7 @@ sub objects_loaded { } $self->arrange unless $params{no_arrange}; $self->update; + use XXX; YYY $self->{model}->objects->[0]->instances->[0]->offset->pp; $self->{list}->Update; $self->{list}->Select($obj_idxs->[-1], 1); $self->object_list_changed; @@ -695,7 +696,7 @@ sub changescale { my $versor = [1,1,1]; $versor->[$axis] = $scale/100; - $model_object->scale_xyz($versor); + $model_object->scale_xyz(Slic3r::Pointf3->new(@$versor)); $self->make_thumbnail($obj_idx); } else { # max scale factor should be above 2540 to allow importing files exported in inches diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 75c487043..169fab717 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -148,13 +148,14 @@ sub perform_cut { # and cut dialog but ModelObject::cut() needs Z without any instance transformation my $z = $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; - my ($new_model, $upper_object, $lower_object) = $self->{model_object}->cut($z); + my ($new_model) = $self->{model_object}->cut($z); + my ($upper_object, $lower_object) = @{$new_model->objects}; $self->{new_model} = $new_model; $self->{new_model_objects} = []; - if ($self->{cut_options}{keep_upper} && defined $upper_object) { + if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { push @{$self->{new_model_objects}}, $upper_object; } - if ($self->{cut_options}{keep_lower} && defined $lower_object) { + if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { push @{$self->{new_model_objects}}, $lower_object; if ($self->{cut_options}{rotate_lower}) { $lower_object->rotate(PI, X); diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 0399c236f..1216a724c 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -158,76 +158,6 @@ sub _arrange { ); } -# this returns the bounding box of the *transformed* instances -sub bounding_box { - my $self = shift; - - return undef if !@{$self->objects}; - my $bb = $self->objects->[0]->bounding_box; - $bb->merge($_->bounding_box) for @{$self->objects}[1..$#{$self->objects}]; - return $bb; -} - -# input point is expressed in unscaled coordinates -sub center_instances_around_point { - my ($self, $point) = @_; - - my $bb = $self->bounding_box; - return if !defined $bb; - - my $size = $bb->size; - my @shift = ( - -$bb->x_min + $point->[X] - $size->x/2, - -$bb->y_min + $point->[Y] - $size->y/2, #// - ); - - foreach my $object (@{$self->objects}) { - foreach my $instance (@{$object->instances}) { - $instance->set_offset(Slic3r::Pointf->new( - $instance->offset->x + $shift[X], - $instance->offset->y + $shift[Y], #++ - )); - } - $object->update_bounding_box; - } -} - -sub align_instances_to_origin { - my ($self) = @_; - - my $bb = $self->bounding_box; - return if !defined $bb; - - my $new_center = $bb->size; - $new_center->translate(-$new_center->x/2, -$new_center->y/2); #// - $self->center_instances_around_point($new_center); -} - -sub translate { - my $self = shift; - my @shift = @_; - - $_->translate(@shift) for @{$self->objects}; -} - -# flattens everything to a single mesh -sub mesh { - my $self = shift; - - my $mesh = Slic3r::TriangleMesh->new; - $mesh->merge($_->mesh) for @{$self->objects}; - return $mesh; -} - -# flattens everything to a single mesh -sub raw_mesh { - my $self = shift; - - my $mesh = Slic3r::TriangleMesh->new; - $mesh->merge($_->raw_mesh) for @{$self->objects}; - return $mesh; -} - # this method splits objects into multiple distinct objects by walking their meshes sub split_meshes { my $self = shift; @@ -369,105 +299,6 @@ sub add_instance { } } -sub raw_bounding_box { - my $self = shift; - - my @meshes = map $_->mesh->clone, grep !$_->modifier, @{ $self->volumes }; - die "No meshes found" if !@meshes; - - my $instance = $self->instances->[0]; - $instance->transform_mesh($_, 1) for @meshes; - - my $bb = (shift @meshes)->bounding_box; - $bb->merge($_->bounding_box) for @meshes; - return $bb; -} - -# flattens all volumes and instances into a single mesh -sub mesh { - my $self = shift; - - my $mesh = $self->raw_mesh; - - my @instance_meshes = (); - foreach my $instance (@{ $self->instances }) { - my $m = $mesh->clone; - $instance->transform_mesh($m); - push @instance_meshes, $m; - } - - my $full_mesh = Slic3r::TriangleMesh->new; - $full_mesh->merge($_) for @instance_meshes; - return $full_mesh; -} - -sub update_bounding_box { - my ($self) = @_; - $self->_bounding_box($self->mesh->bounding_box); -} - -# this returns the bounding box of the *transformed* instances -sub bounding_box { - my $self = shift; - - $self->update_bounding_box if !defined $self->_bounding_box; - return $self->_bounding_box->clone; -} - -# this returns the bounding box of the *transformed* given instance -sub instance_bounding_box { - my ($self, $instance_idx) = @_; - - $instance_idx //= 0; - - my $mesh = $self->raw_mesh; - $self->instances->[$instance_idx]->transform_mesh($mesh); - return $mesh->bounding_box; -} - -sub center_around_origin { - my $self = shift; - - # calculate the displacements needed to - # center this object around the origin - my $bb = $self->raw_mesh->bounding_box; - - # first align to origin on XY - my @shift = ( - -$bb->x_min, - -$bb->y_min, - 0, - ); - - # then center it on XY - my $size = $bb->size; - $shift[X] -= $size->x/2; - $shift[Y] -= $size->y/2; #// - - $self->translate(@shift); - $self->origin_translation->translate(@shift[X,Y]); - - if ($self->instances_count > 0) { - foreach my $instance (@{ $self->instances }) { - $instance->set_offset(Slic3r::Pointf->new( - $instance->offset->x - $shift[X], - $instance->offset->y - $shift[Y], #-- - )); - } - $self->update_bounding_box; - } - - return @shift; -} - -sub translate { - my $self = shift; - my @shift = @_; - - $_->mesh->translate(@shift) for @{$self->volumes}; - $self->_bounding_box->translate(@shift) if defined $self->_bounding_box; -} - sub rotate { my ($self, $angle, $axis) = @_; @@ -497,29 +328,6 @@ sub flip { $self->invalidate_bounding_box; } -sub scale_xyz { - my ($self, $versor) = @_; - - $_->mesh->scale_xyz($versor) for @{$self->volumes}; - $self->invalidate_bounding_box; -} - -sub materials_count { - my $self = shift; - - my %materials = map { $_->material_id // '_default' => 1 } @{$self->volumes}; - return scalar keys %materials; -} - -sub unique_materials { - my $self = shift; - - my %materials = (); - $materials{ $_->material_id } = 1 - for grep { defined $_->material_id } @{$self->volumes}; - return sort keys %materials; -} - sub mesh_stats { my $self = shift; @@ -552,54 +360,4 @@ sub print_info { } } -sub cut { - my ($self, $z) = @_; - - # clone this one to duplicate instances, materials etc. - my $model = Slic3r::Model->new; - my $upper = $model->add_object($self); - my $lower = $model->add_object($self); - $upper->clear_volumes; - $lower->clear_volumes; - - foreach my $volume (@{$self->volumes}) { - if ($volume->modifier) { - # don't cut modifiers - $upper->add_volume($volume); - $lower->add_volume($volume); - } else { - my $upper_mesh = Slic3r::TriangleMesh->new; - my $lower_mesh = Slic3r::TriangleMesh->new; - $volume->mesh->cut($z + $volume->mesh->bounding_box->z_min, $upper_mesh, $lower_mesh); - $upper_mesh->repair; - $lower_mesh->repair; - $upper_mesh->reset_repair_stats; - $lower_mesh->reset_repair_stats; - - if ($upper_mesh->facets_count > 0) { - $upper->add_volume( - name => $volume->name, - material_id => $volume->material_id, - mesh => $upper_mesh, - modifier => $volume->modifier, - config => $volume->config, - ); - } - if ($lower_mesh->facets_count > 0) { - $lower->add_volume( - name => $volume->name, - material_id => $volume->material_id, - mesh => $lower_mesh, - modifier => $volume->modifier, - config => $volume->config, - ); - } - } - } - - $upper = undef if !@{$upper->volumes}; - $lower = undef if !@{$lower->volumes}; - return ($model, $upper, $lower); -} - 1; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index a6ed9ba7d..7647bbfc4 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -38,7 +38,7 @@ has 'status_cb' => ( has 'print_center' => ( is => 'rw', - default => sub { [100,100] }, + default => sub { Slic3r::Pointf->new(100,100) }, ); has 'output_file' => ( diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 2a8d4039b..e4a666e0b 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -156,7 +156,7 @@ sub init_print { $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); } $model->arrange_objects($print->config->min_object_distance); - $model->center_instances_around_point($params{print_center} // [100,100]); + $model->center_instances_around_point($params{print_center} // Slic3r::Pointf->new(100,100)); foreach my $model_object (@{$model->objects}) { $print->auto_assign_extruders($model_object); $print->add_model_object($model_object); diff --git a/slic3r.pl b/slic3r.pl index a34104a1d..7379a61dd 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -178,7 +178,7 @@ if (@ARGV) { # slicing from command line $opt{duplicate_grid} = [ split /[,x]/, $opt{duplicate_grid}, 2 ]; } if (defined $opt{print_center}) { - $opt{print_center} = [ split /[,x]/, $opt{print_center}, 2 ]; + $opt{print_center} = Slic3r::Pointf->new(split /[,x]/, $opt{print_center}, 2); } my $sprint = Slic3r::Print::Simple->new( @@ -186,7 +186,7 @@ if (@ARGV) { # slicing from command line rotate => $opt{rotate} // 0, duplicate => $opt{duplicate} // 1, duplicate_grid => $opt{duplicate_grid} // [1,1], - print_center => $opt{print_center} // [100,100], + print_center => $opt{print_center} // Slic3r::Pointf->new(100,100), status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 55c6abbe0..6a0c0d318 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -16,6 +16,7 @@ BoundingBoxBase::BoundingBoxBase(const std::vector &poin this->max.x = std::max(it->x, this->max.x); this->max.y = std::max(it->y, this->max.y); } + this->defined = true; } template BoundingBoxBase::BoundingBoxBase(const std::vector &points); template BoundingBoxBase::BoundingBoxBase(const std::vector &points); @@ -80,10 +81,15 @@ template void BoundingBoxBase::scale(double factor); template void BoundingBoxBase::merge(const PointClass &point) { - this->min.x = std::min(point.x, this->min.x); - this->min.y = std::min(point.y, this->min.y); - this->max.x = std::max(point.x, this->max.x); - this->max.y = std::max(point.y, this->max.y); + if (this->defined) { + this->min.x = std::min(point.x, this->min.x); + this->min.y = std::min(point.y, this->min.y); + this->max.x = std::max(point.x, this->max.x); + this->max.y = std::max(point.y, this->max.y); + } else { + this->min = this->max = point; + this->defined = true; + } } template void BoundingBoxBase::merge(const Point &point); template void BoundingBoxBase::merge(const Pointf &point); @@ -91,10 +97,16 @@ template void BoundingBoxBase::merge(const Pointf &point); template void BoundingBoxBase::merge(const BoundingBoxBase &bb) { - this->min.x = std::min(bb.min.x, this->min.x); - this->min.y = std::min(bb.min.y, this->min.y); - this->max.x = std::max(bb.max.x, this->max.x); - this->max.y = std::max(bb.max.y, this->max.y); + if (this->defined) { + this->min.x = std::min(bb.min.x, this->min.x); + this->min.y = std::min(bb.min.y, this->min.y); + this->max.x = std::max(bb.max.x, this->max.x); + this->max.y = std::max(bb.max.y, this->max.y); + } else { + this->min = bb.min; + this->max = bb.max; + this->defined = true; + } } template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); @@ -102,18 +114,22 @@ template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBox3Base::merge(const PointClass &point) { + if (this->defined) { + this->min.z = std::min(point.z, this->min.z); + this->max.z = std::max(point.z, this->max.z); + } BoundingBoxBase::merge(point); - this->min.z = std::min(point.z, this->min.z); - this->max.z = std::max(point.z, this->max.z); } template void BoundingBox3Base::merge(const Pointf3 &point); template void BoundingBox3Base::merge(const BoundingBox3Base &bb) { + if (this->defined) { + this->min.z = std::min(bb.min.z, this->min.z); + this->max.z = std::max(bb.max.z, this->max.z); + } BoundingBoxBase::merge(bb); - this->min.z = std::min(bb.min.z, this->min.z); - this->max.z = std::max(bb.max.z, this->max.z); } template void BoundingBox3Base::merge(const BoundingBox3Base &bb); diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 0ea81b831..382349afa 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -18,8 +18,9 @@ class BoundingBoxBase public: PointClass min; PointClass max; + bool defined; - BoundingBoxBase() {}; + BoundingBoxBase() : defined(false) {}; BoundingBoxBase(const std::vector &points); void merge(const PointClass &point); void merge(const BoundingBoxBase &bb); @@ -34,7 +35,7 @@ template class BoundingBox3Base : public BoundingBoxBase { public: - BoundingBox3Base() {}; + BoundingBox3Base() : BoundingBoxBase() {}; BoundingBox3Base(const std::vector &points); void merge(const PointClass &point); void merge(const BoundingBox3Base &bb); @@ -50,7 +51,7 @@ class BoundingBox : public BoundingBoxBase void polygon(Polygon* polygon) const; Polygon polygon() const; - BoundingBox() {}; + BoundingBox() : BoundingBoxBase() {}; BoundingBox(const Points &points) : BoundingBoxBase(points) {}; BoundingBox(const Lines &lines); }; @@ -61,13 +62,13 @@ class BoundingBox3 : public BoundingBox3Base {}; class BoundingBoxf : public BoundingBoxBase { public: - BoundingBoxf() {}; + BoundingBoxf() : BoundingBoxBase() {}; BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; }; class BoundingBoxf3 : public BoundingBox3Base { public: - BoundingBoxf3() {}; + BoundingBoxf3() : BoundingBox3Base() {}; BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; }; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index a63cb3c46..dad84cf63 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -173,6 +173,76 @@ Model::add_default_instances() return true; } +// this returns the bounding box of the *transformed* instances +void +Model::bounding_box(BoundingBoxf3* bb) +{ + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + BoundingBoxf3 obb; + (*o)->bounding_box(&obb); + bb->merge(obb); + } +} + +void +Model::center_instances_around_point(const Pointf &point) +{ + BoundingBoxf3 bb; + this->bounding_box(&bb); + + Sizef3 size = bb.size(); + double shift_x = -bb.min.x + point.x - size.x/2; + double shift_y = -bb.min.y + point.y - size.y/2; + + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + for (ModelInstancePtrs::const_iterator i = (*o)->instances.begin(); i != (*o)->instances.end(); ++i) { + (*i)->offset.translate(shift_x, shift_y); + } + (*o)->update_bounding_box(); + } +} + +void +Model::align_instances_to_origin() +{ + BoundingBoxf3 bb; + this->bounding_box(&bb); + + Pointf new_center = (Pointf)bb.size(); + new_center.translate(-new_center.x/2, -new_center.y/2); + this->center_instances_around_point(new_center); +} + +void +Model::translate(coordf_t x, coordf_t y, coordf_t z) +{ + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + (*o)->translate(x, y, z); + } +} + +// flattens everything to a single mesh +void +Model::mesh(TriangleMesh* mesh) const +{ + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + TriangleMesh omesh; + (*o)->mesh(&omesh); + mesh->merge(omesh); + } +} + +// flattens everything to a single mesh +void +Model::raw_mesh(TriangleMesh* mesh) const +{ + for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { + TriangleMesh omesh; + (*o)->raw_mesh(&omesh); + mesh->merge(omesh); + } +} + #ifdef SLIC3RXS REGISTER_CLASS(Model, "Model"); #endif @@ -321,12 +391,45 @@ ModelObject::clear_instances() this->delete_instance(i); } +// this returns the bounding box of the *transformed* instances +void +ModelObject::bounding_box(BoundingBoxf3* bb) +{ + if (!this->_bounding_box_valid) this->update_bounding_box(); + *bb = this->_bounding_box; +} + void ModelObject::invalidate_bounding_box() { this->_bounding_box_valid = false; } +void +ModelObject::update_bounding_box() +{ + TriangleMesh mesh; + this->mesh(&mesh); + + mesh.bounding_box(&this->_bounding_box); + this->_bounding_box_valid = true; +} + +// flattens all volumes and instances into a single mesh +void +ModelObject::mesh(TriangleMesh* mesh) const +{ + TriangleMesh raw_mesh; + this->raw_mesh(&raw_mesh); + + + for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) { + TriangleMesh m = raw_mesh; + (*i)->transform_mesh(&m); + mesh->merge(m); + } +} + void ModelObject::raw_mesh(TriangleMesh* mesh) const { @@ -336,6 +439,94 @@ ModelObject::raw_mesh(TriangleMesh* mesh) const } } +void +ModelObject::raw_bounding_box(BoundingBoxf3* bb) const +{ + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + if ((*v)->modifier) continue; + TriangleMesh mesh = (*v)->mesh; + + if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); + this->instances.front()->transform_mesh(&mesh, true); + + BoundingBoxf3 mbb; + mesh.bounding_box(&mbb); + bb->merge(mbb); + } +} + +// this returns the bounding box of the *transformed* given instance +void +ModelObject::instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const +{ + TriangleMesh mesh; + this->raw_mesh(&mesh); + + this->instances[instance_idx]->transform_mesh(&mesh); + + mesh.bounding_box(bb); +} + +void +ModelObject::center_around_origin() +{ + // calculate the displacements needed to + // center this object around the origin + BoundingBoxf3 bb; + { + TriangleMesh mesh; + this->raw_mesh(&mesh); + mesh.bounding_box(&bb); + } + + // first align to origin on XY + double shift_x = -bb.min.x; + double shift_y = -bb.min.y; + + // then center it on XY + Sizef3 size = bb.size(); + shift_x -= size.x/2; + shift_y -= size.y/2; + + this->translate(shift_x, shift_y, 0); + this->origin_translation.translate(shift_x, shift_y); + + if (!this->instances.empty()) { + for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) { + (*i)->offset.translate(-shift_x, -shift_y); + } + this->update_bounding_box(); + } +} + +void +ModelObject::translate(coordf_t x, coordf_t y, coordf_t z) +{ + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + (*v)->mesh.translate(x, y, z); + } + if (this->_bounding_box_valid) this->_bounding_box.translate(x, y, z); +} + +void +ModelObject::scale(const Pointf3 &versor) +{ + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + (*v)->mesh.scale(versor); + } + this->invalidate_bounding_box(); +} + +size_t +ModelObject::materials_count() const +{ + std::set material_ids; + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + material_ids.insert((*v)->material_id()); + } + return material_ids.size(); +} + size_t ModelObject::facets_count() const { @@ -357,6 +548,47 @@ ModelObject::needed_repair() const return false; } +void +ModelObject::cut(coordf_t z, Model* model) const +{ + // clone this one to duplicate instances, materials etc. + ModelObject* upper = model->add_object(*this); + ModelObject* lower = model->add_object(*this); + upper->clear_volumes(); + lower->clear_volumes(); + + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + ModelVolume* volume = *v; + if (volume->modifier) { + // don't cut modifiers + upper->add_volume(*volume); + lower->add_volume(*volume); + } else { + TriangleMeshSlicer tms(&volume->mesh); + TriangleMesh upper_mesh, lower_mesh; + // TODO: shouldn't we use object bounding box instead of per-volume bb? + tms.cut(z + volume->mesh.bounding_box().min.z, &upper_mesh, &lower_mesh); + upper_mesh.repair(); + lower_mesh.repair(); + upper_mesh.reset_repair_stats(); + lower_mesh.reset_repair_stats(); + + if (upper_mesh.facets_count() > 0) { + ModelVolume* vol = upper->add_volume(upper_mesh); + vol->name = volume->name; + vol->config = volume->config; + vol->set_material(volume->material_id(), *volume->material()); + } + if (lower_mesh.facets_count() > 0) { + ModelVolume* vol = lower->add_volume(lower_mesh); + vol->name = volume->name; + vol->config = volume->config; + vol->set_material(volume->material_id(), *volume->material()); + } + } + } +} + #ifdef SLIC3RXS REGISTER_CLASS(ModelObject, "Model::Object"); #endif @@ -394,6 +626,13 @@ ModelVolume::material() const return this->object->get_model()->get_material(this->_material_id); } +void +ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material) +{ + this->_material_id = material_id; + (void)this->object->get_model()->add_material(material_id, material); +} + ModelMaterial* ModelVolume::assign_unique_material() { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 4c193d980..5fabf2927 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -54,11 +54,12 @@ class Model // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); bool has_objects_with_no_instances() const; bool add_default_instances(); - // void bounding_box(BoundingBox* bb) const; - // void align_to_origin(); - // void center_instances_around_point(const Pointf &point); - // void translate(coordf_t x, coordf_t y, coordf_t z); - // void mesh(TriangleMesh* mesh) const; + void bounding_box(BoundingBoxf3* bb); + void center_instances_around_point(const Pointf &point); + void align_instances_to_origin(); + void translate(coordf_t x, coordf_t y, coordf_t z); + void mesh(TriangleMesh* mesh) const; + void raw_mesh(TriangleMesh* mesh) const; // void split_meshes(); // std::string get_material_name(t_model_material_id material_id); @@ -113,17 +114,21 @@ class ModelObject void delete_last_instance(); void clear_instances(); + void bounding_box(BoundingBoxf3* bb); void invalidate_bounding_box(); + void mesh(TriangleMesh* mesh) const; void raw_mesh(TriangleMesh* mesh) const; - //void mesh(TriangleMesh* mesh) const; - //void instance_bounding_box(size_t instance_idx, BoundingBox* bb) const; - //void center_around_origin(); - //void translate(coordf_t x, coordf_t y, coordf_t z); - //size_t materials_count() const; - //void unique_materials(std::vector* materials) const; + void raw_bounding_box(BoundingBoxf3* bb) const; + void instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const; + void center_around_origin(); + void translate(coordf_t x, coordf_t y, coordf_t z); + void scale(const Pointf3 &versor); + size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; + void cut(coordf_t z, Model* model) const; + void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS private: Model* model; @@ -133,7 +138,6 @@ class ModelObject ModelObject& operator= (ModelObject other); void swap(ModelObject &other); ~ModelObject(); - void update_bounding_box(); }; class ModelVolume @@ -149,6 +153,7 @@ class ModelVolume t_model_material_id material_id() const; void material_id(t_model_material_id material_id); ModelMaterial* material() const; + void set_material(t_model_material_id material_id, const ModelMaterial &material); ModelMaterial* assign_unique_material(); diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 01e008efd..db9b516d8 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -164,6 +164,12 @@ TriangleMesh::needed_repair() const || this->stl.stats.backwards_edges > 0; } +size_t +TriangleMesh::facets_count() const +{ + return this->stl.stats.number_of_facets; +} + void TriangleMesh::WriteOBJFile(char* output_file) { stl_generate_shared_vertices(&stl); @@ -175,12 +181,12 @@ void TriangleMesh::scale(float factor) stl_scale(&(this->stl), factor); } -void TriangleMesh::scale(std::vector versor) +void TriangleMesh::scale(const Pointf3 &versor) { float fversor[3]; - fversor[0] = versor[0]; - fversor[1] = versor[1]; - fversor[2] = versor[2]; + fversor[0] = versor.x; + fversor[1] = versor.y; + fversor[2] = versor.z; stl_scale_versor(&this->stl, fversor); } @@ -355,6 +361,14 @@ TriangleMesh::bounding_box(BoundingBoxf3* bb) const bb->max.z = this->stl.stats.max.z; } +BoundingBoxf3 +TriangleMesh::bounding_box() const +{ + BoundingBoxf3 bb; + this->bounding_box(&bb); + return bb; +} + void TriangleMesh::require_shared_vertices() { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 2099f5e1e..afde8a65d 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -29,7 +29,7 @@ class TriangleMesh void repair(); void WriteOBJFile(char* output_file); void scale(float factor); - void scale(std::vector versor); + void scale(const Pointf3 &versor); void translate(float x, float y, float z); void rotate_x(float angle); void rotate_y(float angle); @@ -44,8 +44,10 @@ class TriangleMesh void horizontal_projection(ExPolygons &retval) const; void convex_hull(Polygon* hull); void bounding_box(BoundingBoxf3* bb) const; + BoundingBoxf3 bounding_box() const; void reset_repair_stats(); bool needed_repair() const; + size_t facets_count() const; stl_file stl; bool repaired; diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 3d286d243..b2f1d2849 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -40,7 +40,7 @@ my $cube = { $m->scale(2); ok abs($m->stats->{volume} - 40*40*40) < 1E-2, 'scale'; - $m->scale_xyz([2,1,1]); + $m->scale_xyz(Slic3r::Pointf3->new(2,1,1)); ok abs($m->stats->{volume} - 2*40*40*40) < 1E-2, 'scale_xyz'; $m->translate(5,10,0); @@ -51,7 +51,7 @@ my $cube = { is_deeply $m->size, [80,40,40], 'size'; - $m->scale_xyz([0.5,1,1]); + $m->scale_xyz(Slic3r::Pointf3->new(0.5,1,1)); $m->rotate(45, Slic3r::Point->new(20,20)); ok abs($m->size->[0] - sqrt(2)*40) < 1E-4, 'rotate'; diff --git a/xs/t/17_boundingbox.t b/xs/t/17_boundingbox.t index 819aefbd2..349e0024d 100644 --- a/xs/t/17_boundingbox.t +++ b/xs/t/17_boundingbox.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 3; +use Test::More tests => 5; { my @points = ( @@ -17,4 +17,11 @@ use Test::More tests => 3; is_deeply $bb->max_point->pp, [500,200], 'max_point'; } +{ + my $bb = Slic3r::Geometry::BoundingBox->new; + $bb->merge_point(Slic3r::Point->new(10, 10)); + is_deeply $bb->min_point->pp, [10,10], 'min_point equals to the only defined point'; + is_deeply $bb->max_point->pp, [10,10], 'max_point equals to the only defined point'; +} + __END__ diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index c368c0c18..61995a328 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -7,6 +7,7 @@ %} %name{Slic3r::Geometry::BoundingBox} class BoundingBox { + BoundingBox(); ~BoundingBox(); Clone clone() %code{% RETVAL = THIS; %}; @@ -41,6 +42,7 @@ new_from_points(CLASS, points) }; %name{Slic3r::Geometry::BoundingBoxf} class BoundingBoxf { + BoundingBoxf(); ~BoundingBoxf(); Clone clone() %code{% RETVAL = THIS; %}; @@ -76,6 +78,7 @@ new_from_points(CLASS, points) }; %name{Slic3r::Geometry::BoundingBoxf3} class BoundingBoxf3 { + BoundingBoxf3(); ~BoundingBoxf3(); Clone clone() %code{% RETVAL = THIS; %}; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 04b7c8207..43d03e9cf 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -55,13 +55,21 @@ // void duplicate_objects(size_t copies_num, coordf_t distance, const BoundingBox &bb); // void arrange_objects(coordf_t distance, const BoundingBox &bb); // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); - bool has_objects_with_no_instances() const; + bool has_objects_with_no_instances(); bool add_default_instances(); - // void bounding_box(BoundingBox* bb) const; - // void center_instances_around_point(const Pointf &point); - // void align_instances_to_origin(); - // void translate(coordf_t x, coordf_t y, coordf_t z); - // void mesh(TriangleMesh* mesh) const; + BoundingBoxf3* bounding_box() + %code%{ + RETVAL = new BoundingBoxf3(); + THIS->bounding_box(RETVAL); + %}; + void center_instances_around_point(Pointf* point) + %code%{ THIS->center_instances_around_point(*point); %}; + void align_instances_to_origin(); + void translate(double x, double y, double z); + TriangleMesh* mesh() + %code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %}; + TriangleMesh* raw_mesh() + %code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %}; // void split_meshes(); // std::string get_material_name(t_model_material_id material_id); @@ -108,8 +116,21 @@ ModelMaterial::attributes() %code%{ RETVAL = &THIS->instances; %}; void invalidate_bounding_box(); + void update_bounding_box(); + TriangleMesh* mesh() + %code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %}; TriangleMesh* raw_mesh() %code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %}; + BoundingBoxf3* raw_bounding_box() + %code%{ + RETVAL = new BoundingBoxf3(); + THIS->raw_bounding_box(RETVAL); + %}; + BoundingBoxf3* instance_bounding_box(int idx) + %code%{ + RETVAL = new BoundingBoxf3(); + THIS->instance_bounding_box(idx, RETVAL); + %}; Ref _bounding_box(BoundingBoxf3* new_bbox = NULL) %code{% @@ -124,6 +145,11 @@ ModelMaterial::attributes() RETVAL = &THIS->_bounding_box; %}; + BoundingBoxf3* bounding_box() + %code%{ + RETVAL = new BoundingBoxf3(); + THIS->bounding_box(RETVAL); + %}; %name{_add_volume} Ref add_volume(TriangleMesh* mesh) %code%{ RETVAL = THIS->add_volume(*mesh); %}; @@ -132,6 +158,8 @@ ModelMaterial::attributes() void delete_volume(size_t idx); void clear_volumes(); + int volumes_count() + %code%{ RETVAL = THIS->volumes.size(); %}; %name{_add_instance} Ref add_instance(); Ref _add_instance_clone(ModelInstance* other) @@ -166,7 +194,18 @@ ModelMaterial::attributes() %code%{ THIS->origin_translation = *point; %}; bool needed_repair() const; + int materials_count() const; int facets_count(); + void center_around_origin(); + void translate(double x, double y, double z); + void scale_xyz(Pointf3* versor) + %code{% THIS->scale(*versor); %}; + + Model* cut(double z) + %code%{ + RETVAL = new Model(); + THIS->cut(z, RETVAL); + %}; }; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index d763fd98a..989719331 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -17,8 +17,8 @@ void repair(); void WriteOBJFile(char* output_file); void scale(float factor); - void scale_xyz(std::vector versor) - %code{% THIS->scale(versor); %}; + void scale_xyz(Pointf3* versor) + %code{% THIS->scale(*versor); %}; void translate(float x, float y, float z); void rotate_x(float angle); void rotate_y(float angle); @@ -44,8 +44,7 @@ THIS->bounding_box(&bb); RETVAL = new Pointf3(bb.center()); %}; - int facets_count() - %code{% RETVAL = THIS->stl.stats.number_of_facets; %}; + int facets_count(); void reset_repair_stats(); %{ From 9daadd1fb63430f989d9eb5955da252af56bff71 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 21 Sep 2014 10:53:00 +0200 Subject: [PATCH 3/4] Removed debugging line --- lib/Slic3r/GUI/Plater.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3a2a71f63..d2e754390 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -497,7 +497,6 @@ sub objects_loaded { } $self->arrange unless $params{no_arrange}; $self->update; - use XXX; YYY $self->{model}->objects->[0]->instances->[0]->offset->pp; $self->{list}->Update; $self->{list}->Select($obj_idxs->[-1], 1); $self->object_list_changed; From d8bb0bff7f80e5efdfe2e7f3178af50fed3ac676 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 21 Sep 2014 10:56:51 +0200 Subject: [PATCH 4/4] Bugfix: reprocess brim and skirt after changing number of copies --- lib/Slic3r/GUI/Plater.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d2e754390..836b3f69b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -565,16 +565,20 @@ sub increase { $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); # only autoarrange if user has autocentering enabled + $self->stop_background_process; if ($Slic3r::GUI::Settings->{_}{autocenter}) { $self->arrange; } else { $self->update; } + $self->schedule_background_process; } sub decrease { my $self = shift; + $self->stop_background_process; + my ($obj_idx, $object) = $self->selected_object; my $model_object = $self->{model}->objects->[$obj_idx]; if ($model_object->instances_count >= 2) { @@ -590,6 +594,7 @@ sub decrease { $self->{list}->Select($obj_idx, 1); } $self->update; + $self->schedule_background_process; } sub rotate {