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:
Alessandro Ranellucci 2013-06-07 23:16:02 +02:00
parent 9ea55497c2
commit 510c2092df
8 changed files with 151 additions and 32 deletions

View File

@ -30,6 +30,7 @@ lib/Slic3r/GCode/MotionPlanner.pm
lib/Slic3r/GCode/Reader.pm lib/Slic3r/GCode/Reader.pm
lib/Slic3r/GCode/SpiralVase.pm lib/Slic3r/GCode/SpiralVase.pm
lib/Slic3r/Geometry.pm lib/Slic3r/Geometry.pm
lib/Slic3r/Geometry/BoundingBox.pm
lib/Slic3r/Geometry/Clipper.pm lib/Slic3r/Geometry/Clipper.pm
lib/Slic3r/GUI.pm lib/Slic3r/GUI.pm
lib/Slic3r/GUI/AboutDialog.pm lib/Slic3r/GUI/AboutDialog.pm

View File

@ -51,6 +51,8 @@ use Slic3r::GCode::MotionPlanner;
use Slic3r::GCode::Reader; use Slic3r::GCode::Reader;
use Slic3r::GCode::SpiralVase; use Slic3r::GCode::SpiralVase;
use Slic3r::Geometry qw(PI); use Slic3r::Geometry qw(PI);
use Slic3r::Geometry::BoundingBox;
use Slic3r::Geometry::Clipper;
use Slic3r::Layer; use Slic3r::Layer;
use Slic3r::Layer::Region; use Slic3r::Layer::Region;
use Slic3r::Line; use Slic3r::Line;

View File

@ -335,11 +335,25 @@ sub align_to_origin {
my @bb = Slic3r::Geometry::bounding_box([ map @$_, map @$_, @{$self->expolygons} ]); my @bb = Slic3r::Geometry::bounding_box([ map @$_, map @$_, @{$self->expolygons} ]);
$_->translate(-$bb[X1], -$bb[Y1]) for @{$self->expolygons}; $_->translate(-$bb[X1], -$bb[Y1]) for @{$self->expolygons};
$self;
}
sub scale {
my $self = shift;
$_->scale(@_) for @{$self->expolygons};
$self;
} }
sub rotate { sub rotate {
my $self = shift; my $self = shift;
$_->rotate(@_) for @{$self->expolygons}; $_->rotate(@_) for @{$self->expolygons};
$self;
}
sub translate {
my $self = shift;
$_->translate(@_) for @{$self->expolygons};
$self;
} }
sub size { sub size {

View File

@ -452,7 +452,7 @@ sub arrange {
my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return; my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return;
my @size = (); my @size = ();
for my $a (X,Y) { for my $a (X,Y) {
$size[$a] = max(map $_->size->[$a], @{$self->{objects}}); $size[$a] = max(map $_->transformed_size->[$a], @{$self->{objects}});
} }
eval { eval {
@ -769,10 +769,9 @@ sub recenter {
my @print_bb = Slic3r::Geometry::bounding_box([ my @print_bb = Slic3r::Geometry::bounding_box([
map { map {
my $obj = $_; my $obj = $_;
map { my $bb = $obj->transformed_bounding_box;
my $instance = $_; my @points = ($bb->min_point, $bb->max_point);
$instance, [ map $instance->[$_] + $obj->size->[$_], X,Y ]; map Slic3r::Geometry::move_points($_, @points), @{$obj->instances};
} @{$obj->instances};
} @{$self->{objects}}, } @{$self->{objects}},
]); ]);
$self->{shift} = [ $self->{shift} = [
@ -867,9 +866,12 @@ sub repaint {
next unless $object->thumbnail && @{$object->thumbnail->expolygons}; next unless $object->thumbnail && @{$object->thumbnail->expolygons};
for my $instance_idx (0 .. $#{$object->instances}) { for my $instance_idx (0 .. $#{$object->instances}) {
my $instance = $object->instances->[$instance_idx]; 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)) my $thumbnail = $object->thumbnail
for @{$parent->{object_previews}->[-1][2]->expolygons}; ->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}; my $drag_object = $self->{drag_object};
if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) { 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 Moo;
use Math::ConvexHull::MonotoneChain qw(convex_hull); 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 'name' => (is => 'rw', required => 1);
has 'input_file' => (is => 'rw', required => 1); has 'input_file' => (is => 'rw', required => 1);
has 'input_file_object_id' => (is => 'rw'); # undef means keep model object has 'input_file_object_id' => (is => 'rw'); # undef means keep model object
has 'model_object' => (is => 'rw', required => 1, trigger => 1); 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 '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 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis
has 'thumbnail' => (is => 'rw'); has 'thumbnail' => (is => 'rw');
has 'thumbnail_scaling_factor' => (is => 'rw'); has 'thumbnail_scaling_factor' => (is => 'rw');
@ -1088,8 +1090,9 @@ has 'is_manifold' => (is => 'rw');
sub _trigger_model_object { sub _trigger_model_object {
my $self = shift; my $self = shift;
if ($self->model_object) { if ($self->model_object) {
$self->model_object->align_to_origin;
my $mesh = $self->model_object->mesh; my $mesh = $self->model_object->mesh;
$self->size([$mesh->size]); $self->bounding_box($mesh->bounding_box);
$self->facets(scalar @{$mesh->facets}); $self->facets(scalar @{$mesh->facets});
$self->vertices(scalar @{$mesh->vertices}); $self->vertices(scalar @{$mesh->vertices});
$self->materials($self->model_object->materials_count); $self->materials($self->model_object->materials_count);
@ -1131,22 +1134,22 @@ sub make_thumbnail {
my $self = shift; my $self = shift;
my $mesh = $self->model_object->mesh; my $mesh = $self->model_object->mesh;
$mesh->align_to_origin;
my $thumbnail = Slic3r::ExPolygon::Collection->new( my $thumbnail = Slic3r::ExPolygon::Collection->new(
expolygons => (@{$mesh->facets} <= 5000) expolygons => (@{$mesh->facets} <= 5000)
? $mesh->horizontal_projection ? $mesh->horizontal_projection
: [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ], : [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ],
); );
for (map @$_, map @$_, @{$thumbnail->expolygons}) { $thumbnail->scale($self->thumbnail_scaling_factor);
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
}
# only simplify expolygons larger than the threshold # only simplify expolygons larger than the threshold
@{$thumbnail->expolygons} = map { ($_->area >= 1) ? $_->simplify(0.5) : $_ } @{$thumbnail->expolygons}; @{$thumbnail->expolygons} = grep @$_,
foreach my $expolygon (@{$thumbnail->expolygons}) { map { ($_->area >= 1) ? $_->simplify(0.5) : $_ }
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate)); @{$thumbnail->expolygons};
$expolygon->scale($self->scale);
} $thumbnail->rotate(deg2rad($self->rotate)); # TODO: around center
@{$thumbnail->expolygons} = grep @$_, @{$thumbnail->expolygons}; $thumbnail->scale($self->scale);
$thumbnail->align_to_origin;
$self->thumbnail($thumbnail); # ignored in multi-threaded environments $self->thumbnail($thumbnail); # ignored in multi-threaded environments
$self->free_model_object; $self->free_model_object;
@ -1158,10 +1161,8 @@ sub set_rotation {
my ($angle) = @_; my ($angle) = @_;
if ($self->thumbnail) { if ($self->thumbnail) {
$self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate)); # rotate around object centerpoint
$self->thumbnail->align_to_origin; $self->thumbnail->rotate(deg2rad($angle - $self->rotate), $self->bounding_box->center_2D->clone->scale($self->thumbnail_scaling_factor));
my $z_size = $self->size->[Z];
$self->size([ (map $_ / $self->thumbnail_scaling_factor, @{$self->thumbnail->size}), $z_size ]);
} }
$self->rotate($angle); $self->rotate($angle);
} }
@ -1170,14 +1171,25 @@ sub set_scale {
my $self = shift; my $self = shift;
my ($scale) = @_; my ($scale) = @_;
my $factor = $scale / $self->scale;
return if $factor == 1;
$self->size->[$_] *= $factor for X,Y,Z;
if ($self->thumbnail) { if ($self->thumbnail) {
$_->scale($factor) for @{$self->thumbnail->expolygons}; $self->thumbnail->scale($scale / $self->scale);
$self->thumbnail->align_to_origin;
} }
$self->scale($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; 1;

View File

@ -78,7 +78,7 @@ sub get_properties {
my $object = $self->{object}; my $object = $self->{object};
return [ return [
['Name' => $object->name], ['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], ['Facets' => $object->facets],
['Vertices' => $object->vertices], ['Vertices' => $object->vertices],
['Materials' => $object->materials], ['Materials' => $object->materials],

View 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;

View File

@ -35,6 +35,13 @@ sub distance_to {
return Slic3r::Geometry::distance_between_points($self, $point); return Slic3r::Geometry::distance_between_points($self, $point);
} }
sub scale {
my $self = shift;
my ($factor) = @_;
$_ *= $factor for @$self;
$self;
}
sub rotate { sub rotate {
my $self = shift; my $self = shift;
my ($angle, $center) = @_; my ($angle, $center) = @_;

View File

@ -408,6 +408,11 @@ sub extents {
return Slic3r::Geometry::bounding_box_3D($self->used_vertices); 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 { sub size {
my $self = shift; my $self = shift;
return Slic3r::Geometry::size_3D($self->used_vertices); return Slic3r::Geometry::size_3D($self->used_vertices);