Fix rotation and scaling in plater producing mispositioned objects in G-code after recent changes. Includes a large refactoring and the new Slic3r::Geometry::BoundingBox class. #1171 #1191
This commit is contained in:
parent
9ea55497c2
commit
510c2092df
1
MANIFEST
1
MANIFEST
@ -30,6 +30,7 @@ lib/Slic3r/GCode/MotionPlanner.pm
|
||||
lib/Slic3r/GCode/Reader.pm
|
||||
lib/Slic3r/GCode/SpiralVase.pm
|
||||
lib/Slic3r/Geometry.pm
|
||||
lib/Slic3r/Geometry/BoundingBox.pm
|
||||
lib/Slic3r/Geometry/Clipper.pm
|
||||
lib/Slic3r/GUI.pm
|
||||
lib/Slic3r/GUI/AboutDialog.pm
|
||||
|
@ -51,6 +51,8 @@ use Slic3r::GCode::MotionPlanner;
|
||||
use Slic3r::GCode::Reader;
|
||||
use Slic3r::GCode::SpiralVase;
|
||||
use Slic3r::Geometry qw(PI);
|
||||
use Slic3r::Geometry::BoundingBox;
|
||||
use Slic3r::Geometry::Clipper;
|
||||
use Slic3r::Layer;
|
||||
use Slic3r::Layer::Region;
|
||||
use Slic3r::Line;
|
||||
|
@ -335,11 +335,25 @@ sub align_to_origin {
|
||||
|
||||
my @bb = Slic3r::Geometry::bounding_box([ map @$_, map @$_, @{$self->expolygons} ]);
|
||||
$_->translate(-$bb[X1], -$bb[Y1]) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
$_->scale(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub rotate {
|
||||
my $self = shift;
|
||||
$_->rotate(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub translate {
|
||||
my $self = shift;
|
||||
$_->translate(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub size {
|
||||
|
@ -452,7 +452,7 @@ sub arrange {
|
||||
my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return;
|
||||
my @size = ();
|
||||
for my $a (X,Y) {
|
||||
$size[$a] = max(map $_->size->[$a], @{$self->{objects}});
|
||||
$size[$a] = max(map $_->transformed_size->[$a], @{$self->{objects}});
|
||||
}
|
||||
|
||||
eval {
|
||||
@ -769,10 +769,9 @@ sub recenter {
|
||||
my @print_bb = Slic3r::Geometry::bounding_box([
|
||||
map {
|
||||
my $obj = $_;
|
||||
map {
|
||||
my $instance = $_;
|
||||
$instance, [ map $instance->[$_] + $obj->size->[$_], X,Y ];
|
||||
} @{$obj->instances};
|
||||
my $bb = $obj->transformed_bounding_box;
|
||||
my @points = ($bb->min_point, $bb->max_point);
|
||||
map Slic3r::Geometry::move_points($_, @points), @{$obj->instances};
|
||||
} @{$self->{objects}},
|
||||
]);
|
||||
$self->{shift} = [
|
||||
@ -867,9 +866,12 @@ sub repaint {
|
||||
next unless $object->thumbnail && @{$object->thumbnail->expolygons};
|
||||
for my $instance_idx (0 .. $#{$object->instances}) {
|
||||
my $instance = $object->instances->[$instance_idx];
|
||||
push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $object->thumbnail->clone ];
|
||||
$_->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y))
|
||||
for @{$parent->{object_previews}->[-1][2]->expolygons};
|
||||
|
||||
my $thumbnail = $object->thumbnail
|
||||
->clone
|
||||
->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y));
|
||||
|
||||
push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $thumbnail ];
|
||||
|
||||
my $drag_object = $self->{drag_object};
|
||||
if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) {
|
||||
@ -1065,15 +1067,15 @@ package Slic3r::GUI::Plater::Object;
|
||||
use Moo;
|
||||
|
||||
use Math::ConvexHull::MonotoneChain qw(convex_hull);
|
||||
use Slic3r::Geometry qw(X Y Z);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad);
|
||||
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'input_file' => (is => 'rw', required => 1);
|
||||
has 'input_file_object_id' => (is => 'rw'); # undef means keep model object
|
||||
has 'model_object' => (is => 'rw', required => 1, trigger => 1);
|
||||
has 'size' => (is => 'rw');
|
||||
has 'bounding_box' => (is => 'rw'); # 3D bb of original object (aligned to origin) with no rotation or scaling
|
||||
has 'scale' => (is => 'rw', default => sub { 1 });
|
||||
has 'rotate' => (is => 'rw', default => sub { 0 });
|
||||
has 'rotate' => (is => 'rw', default => sub { 0 }); # around object center point
|
||||
has 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis
|
||||
has 'thumbnail' => (is => 'rw');
|
||||
has 'thumbnail_scaling_factor' => (is => 'rw');
|
||||
@ -1088,8 +1090,9 @@ has 'is_manifold' => (is => 'rw');
|
||||
sub _trigger_model_object {
|
||||
my $self = shift;
|
||||
if ($self->model_object) {
|
||||
$self->model_object->align_to_origin;
|
||||
my $mesh = $self->model_object->mesh;
|
||||
$self->size([$mesh->size]);
|
||||
$self->bounding_box($mesh->bounding_box);
|
||||
$self->facets(scalar @{$mesh->facets});
|
||||
$self->vertices(scalar @{$mesh->vertices});
|
||||
$self->materials($self->model_object->materials_count);
|
||||
@ -1131,22 +1134,22 @@ sub make_thumbnail {
|
||||
my $self = shift;
|
||||
|
||||
my $mesh = $self->model_object->mesh;
|
||||
$mesh->align_to_origin;
|
||||
my $thumbnail = Slic3r::ExPolygon::Collection->new(
|
||||
expolygons => (@{$mesh->facets} <= 5000)
|
||||
? $mesh->horizontal_projection
|
||||
: [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ],
|
||||
);
|
||||
for (map @$_, map @$_, @{$thumbnail->expolygons}) {
|
||||
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
|
||||
}
|
||||
$thumbnail->scale($self->thumbnail_scaling_factor);
|
||||
|
||||
# only simplify expolygons larger than the threshold
|
||||
@{$thumbnail->expolygons} = map { ($_->area >= 1) ? $_->simplify(0.5) : $_ } @{$thumbnail->expolygons};
|
||||
foreach my $expolygon (@{$thumbnail->expolygons}) {
|
||||
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate));
|
||||
$expolygon->scale($self->scale);
|
||||
}
|
||||
@{$thumbnail->expolygons} = grep @$_, @{$thumbnail->expolygons};
|
||||
$thumbnail->align_to_origin;
|
||||
@{$thumbnail->expolygons} = grep @$_,
|
||||
map { ($_->area >= 1) ? $_->simplify(0.5) : $_ }
|
||||
@{$thumbnail->expolygons};
|
||||
|
||||
$thumbnail->rotate(deg2rad($self->rotate)); # TODO: around center
|
||||
$thumbnail->scale($self->scale);
|
||||
|
||||
$self->thumbnail($thumbnail); # ignored in multi-threaded environments
|
||||
$self->free_model_object;
|
||||
|
||||
@ -1158,10 +1161,8 @@ sub set_rotation {
|
||||
my ($angle) = @_;
|
||||
|
||||
if ($self->thumbnail) {
|
||||
$self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate));
|
||||
$self->thumbnail->align_to_origin;
|
||||
my $z_size = $self->size->[Z];
|
||||
$self->size([ (map $_ / $self->thumbnail_scaling_factor, @{$self->thumbnail->size}), $z_size ]);
|
||||
# rotate around object centerpoint
|
||||
$self->thumbnail->rotate(deg2rad($angle - $self->rotate), $self->bounding_box->center_2D->clone->scale($self->thumbnail_scaling_factor));
|
||||
}
|
||||
$self->rotate($angle);
|
||||
}
|
||||
@ -1170,14 +1171,25 @@ sub set_scale {
|
||||
my $self = shift;
|
||||
my ($scale) = @_;
|
||||
|
||||
my $factor = $scale / $self->scale;
|
||||
return if $factor == 1;
|
||||
$self->size->[$_] *= $factor for X,Y,Z;
|
||||
if ($self->thumbnail) {
|
||||
$_->scale($factor) for @{$self->thumbnail->expolygons};
|
||||
$self->thumbnail->align_to_origin;
|
||||
$self->thumbnail->scale($scale / $self->scale);
|
||||
}
|
||||
$self->scale($scale);
|
||||
}
|
||||
|
||||
# bounding box with applied rotation and scaling
|
||||
sub transformed_bounding_box {
|
||||
my $self = shift;
|
||||
|
||||
return $self->bounding_box
|
||||
->clone
|
||||
->rotate(deg2rad($self->rotate), $self->bounding_box->center)
|
||||
->scale($self->scale);
|
||||
}
|
||||
|
||||
sub transformed_size {
|
||||
my $self = shift;
|
||||
return $self->transformed_bounding_box->size;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -78,7 +78,7 @@ sub get_properties {
|
||||
my $object = $self->{object};
|
||||
return [
|
||||
['Name' => $object->name],
|
||||
['Size' => sprintf "%.2f x %.2f x %.2f", @{$object->size}],
|
||||
['Size' => sprintf "%.2f x %.2f x %.2f", @{$object->transformed_size}],
|
||||
['Facets' => $object->facets],
|
||||
['Vertices' => $object->vertices],
|
||||
['Materials' => $object->materials],
|
||||
|
78
lib/Slic3r/Geometry/BoundingBox.pm
Normal file
78
lib/Slic3r/Geometry/BoundingBox.pm
Normal file
@ -0,0 +1,78 @@
|
||||
package Slic3r::Geometry::BoundingBox;
|
||||
use Moo;
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX X1 Y1 X2 Y2);
|
||||
use Storable qw();
|
||||
|
||||
has 'extents' => (is => 'ro', required => 1);
|
||||
|
||||
sub clone { Storable::dclone($_[0]) }
|
||||
|
||||
# four-arguments 2D bb
|
||||
sub bb {
|
||||
my $self = shift;
|
||||
my $extents = $self->extents;
|
||||
return [ $extents->[X][MIN], $extents->[Y][MIN], $extents->[X][MAX], $extents->[Y][MAX] ];
|
||||
}
|
||||
|
||||
sub polygon {
|
||||
my $self = shift;
|
||||
return Slic3r::Polygon->new_from_bounding_box($self->bb);
|
||||
}
|
||||
|
||||
sub rotate {
|
||||
my $self = shift;
|
||||
my ($angle, $center) = @_;
|
||||
|
||||
# rotate the 2D bounding box polygon and leave Z unaltered
|
||||
my $bb_p = $self->polygon;
|
||||
$bb_p->rotate($angle, $center);
|
||||
my @bb = $bb_p->bounding_box;
|
||||
$self->extents->[X][MIN] = $bb[X1];
|
||||
$self->extents->[Y][MIN] = $bb[Y1];
|
||||
$self->extents->[X][MAX] = $bb[X2];
|
||||
$self->extents->[Y][MAX] = $bb[Y2];
|
||||
|
||||
$self;
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
my ($factor) = @_;
|
||||
|
||||
$_ *= $factor
|
||||
for map @$_[MIN,MAX],
|
||||
grep $_, @{$self->extents}[X,Y,Z];
|
||||
|
||||
$self;
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
|
||||
my $extents = $self->extents;
|
||||
return [ map $extents->[$_][MAX] - $extents->[$_][MIN], grep $extents->[$_], (X,Y,Z) ];
|
||||
}
|
||||
|
||||
sub center {
|
||||
my $self = shift;
|
||||
|
||||
my $extents = $self->extents;
|
||||
return [ map +($extents->[$_][MAX] + $extents->[$_][MIN])/2, grep $extents->[$_], (X,Y,Z) ];
|
||||
}
|
||||
|
||||
sub center_2D {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new(@{$self->center}[X,Y]);
|
||||
}
|
||||
|
||||
sub min_point {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new($self->extents->[X][MIN], $self->extents->[Y][MIN]);
|
||||
}
|
||||
|
||||
sub max_point {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new($self->extents->[X][MAX], $self->extents->[Y][MAX]);
|
||||
}
|
||||
|
||||
1;
|
@ -35,6 +35,13 @@ sub distance_to {
|
||||
return Slic3r::Geometry::distance_between_points($self, $point);
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
my ($factor) = @_;
|
||||
$_ *= $factor for @$self;
|
||||
$self;
|
||||
}
|
||||
|
||||
sub rotate {
|
||||
my $self = shift;
|
||||
my ($angle, $center) = @_;
|
||||
|
@ -408,6 +408,11 @@ sub extents {
|
||||
return Slic3r::Geometry::bounding_box_3D($self->used_vertices);
|
||||
}
|
||||
|
||||
sub bounding_box {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::BoundingBox->new(extents => [ $self->extents ]);
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::size_3D($self->used_vertices);
|
||||
|
Loading…
Reference in New Issue
Block a user