Unfinished work for decoupling GUI from the Print object (goal = more speed for manipulation, less memory usage)

This commit is contained in:
Alessandro Ranellucci 2012-08-29 19:37:27 +02:00
parent f29d455319
commit 191de5d078
5 changed files with 223 additions and 166 deletions

View File

@ -1131,6 +1131,15 @@ sub replace_options {
return $string;
}
# min object distance is max(duplicate_distance, clearance_radius)
sub min_object_distance {
my $self = shift;
return ($self->complete_objects && $self->extruder_clearance_radius > $self->duplicate_distance)
? $self->extruder_clearance_radius
: $self->duplicate_distance;
}
# CLASS METHODS:
sub write_ini {

View File

@ -4,7 +4,7 @@ use warnings;
use utf8;
use File::Basename qw(basename dirname);
use Math::ConvexHull qw(convex_hull);
use List::Util qw(max sum);
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale);
use Slic3r::Geometry::Clipper qw(JT_ROUND);
use threads::shared qw(shared_clone);
@ -155,8 +155,9 @@ sub new {
EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub {
my ($self, $event) = @_;
my ($obj_idx, $thumbnail) = @{$event->GetData};
$self->{thumbnails}[$obj_idx] = $thumbnail;
$self->make_thumbnail2;
$self->{objects}[$obj_idx]->thumbnail($thumbnail);
$self->mesh(undef);
$self->on_thumbnail_made;
});
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
@ -182,10 +183,7 @@ sub new {
});
$self->_update_bed_size;
$self->{print} = Slic3r::Print->new;
$self->{thumbnails} = []; # polygons, each one aligned to 0,0
$self->{scale} = [];
$self->{object_previews} = []; # [ obj_idx, copy_idx, positioned polygon ]
$self->{objects} = [];
$self->{selected_objects} = [];
$self->recenter;
@ -295,12 +293,26 @@ sub load_file {
my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0);
$process_dialog->Pulse;
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
$self->{print}->add_objects_from_file($input_file);
my $obj_idx = $#{$self->{print}->objects};
$process_dialog->Destroy;
$self->object_loaded($obj_idx);
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
my $model = Slic3r::Model->read_from_file($input_file);
for my $i (0 .. $#{$model->objects}) {
my $object = Slic3r::GUI::Plater::Object->new(
name => basebane($input_file),
input_file => $input_file,
input_file_object_id => $i,
mesh => $model->objects->[$i]->mesh,
instances => [
$model->objects->[$i]->instances
? (map $_->offset, @{$model->objects->[$i]->instances})
: [0,0],
],
);
push @{ $self->{objects} }, $object;
$self->object_loaded($#{ $self->{objects} });
}
$process_dialog->Destroy;
$self->statusbar->SetStatusText("Loaded $input_file");
}
@ -308,11 +320,10 @@ sub object_loaded {
my $self = shift;
my ($obj_idx, %params) = @_;
my $object = $self->{print}->objects->[$obj_idx];
$self->{list}->InsertStringItem($obj_idx, basename($object->input_file));
$self->{list}->SetItem($obj_idx, 1, "1");
$self->{list}->SetItem($obj_idx, 2, "100%");
push @{$self->{scale}}, 1;
my $object = $self->{objects}[$obj_idx];
$self->{list}->InsertStringItem($obj_idx, $object->name);
$self->{list}->SetItem($obj_idx, 1, $object->instances_count);
$self->{list}->SetItem($obj_idx, 2, ($object->scale * 100) . "%");
$self->make_thumbnail($obj_idx);
$self->arrange unless $params{no_arrange};
@ -325,36 +336,13 @@ sub remove {
my $self = shift;
my ($obj_idx) = @_;
if (defined $obj_idx) {
$self->{print}->copies->[$obj_idx][$_] = undef
for 0 .. $#{ $self->{print}->copies->[$obj_idx] };
} else {
foreach my $pobj (@{$self->{selected_objects}}) {
my ($obj_idx, $copy_idx) = ($pobj->[0], $pobj->[1]);
$self->{print}->copies->[$obj_idx][$copy_idx] = undef;
}
# if no object index is supplied, remove the selected one
if (!defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
}
my @objects_to_remove = ();
for my $obj_idx (0 .. $#{$self->{print}->objects}) {
my $copies = $self->{print}->copies->[$obj_idx];
# filter out removed copies
@$copies = grep defined $_, @$copies;
# update copies count in list
$self->{list}->SetItem($obj_idx, 1, scalar @$copies);
# if no copies are left, remove the object itself
push @objects_to_remove, $obj_idx if !@$copies;
}
for my $obj_idx (sort { $b <=> $a } @objects_to_remove) {
splice @{$self->{print}->objects}, $obj_idx, 1;
splice @{$self->{print}->copies}, $obj_idx, 1;
splice @{$self->{thumbnails}}, $obj_idx, 1;
splice @{$self->{scale}}, $obj_idx, 1;
splice @{$self->{objects}}, $obj_idx, 1;
$self->{list}->DeleteItem($obj_idx);
}
$self->{selected_objects} = [];
$self->selection_changed(0);
@ -366,10 +354,7 @@ sub remove {
sub reset {
my $self = shift;
@{$self->{print}->objects} = ();
@{$self->{print}->copies} = ();
@{$self->{thumbnails}} = ();
@{$self->{scale}} = ();
@{$self->{objects}} = ();
$self->{list}->DeleteAllItems;
$self->{selected_objects} = [];
@ -381,21 +366,21 @@ sub reset {
sub increase {
my $self = shift;
my $obj_idx = $self->selected_object_idx;
my $copies = $self->{print}->copies->[$obj_idx];
push @$copies, [ $copies->[-1]->[X] + scale 10, $copies->[-1]->[Y] + scale 10 ];
$self->{list}->SetItem($obj_idx, 1, scalar @$copies);
my ($obj_idx, $object) = $self->selected_object;
my $instances = $object->instances;
push @$instances, [ $instances->[-1]->[X] + scale 10, $instances->[-1]->[Y] + scale 10 ];
$self->{list}->SetItem($obj_idx, 1, $object->instances_count);
$self->arrange;
}
sub decrease {
my $self = shift;
my $obj_idx = $self->selected_object_idx;
my ($obj_idx, $object) = $self->selected_object;
$self->{selected_objects} = [ +(grep { $_->[0] == $obj_idx } @{$self->{object_previews}})[-1] ];
$self->remove;
if ($self->{print}->objects->[$obj_idx]) {
if ($self->{objects}[$obj_idx]) {
$self->{list}->Select($obj_idx, 0);
$self->{list}->Select($obj_idx, 1);
}
@ -405,37 +390,14 @@ sub rotate {
my $self = shift;
my ($angle) = @_;
my $obj_idx = $self->selected_object_idx;
my $object = $self->{print}->objects->[$obj_idx];
my ($obj_idx, $object) = $self->selected_object;
if (!defined $angle) {
$angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", 0, -364, 364, $self);
$angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", $object->rotate, -364, 364, $self);
return if !$angle || $angle == -1;
}
$self->statusbar->SetStatusText("Rotating object…");
$self->statusbar->StartBusy;
# rotate, realign to 0,0 and update size
$object->mesh->rotate($angle);
$object->mesh->align_to_origin;
$object->size([ $object->mesh->size ]);
$self->make_thumbnail($obj_idx);
$self->recenter;
$self->{canvas}->Refresh;
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("");
}
sub arrange {
my $self = shift;
eval {
$self->{print}->arrange_objects;
};
# ignore arrange warnings on purpose
$object->set_rotation($angle);
$self->recenter;
$self->{canvas}->Refresh;
}
@ -443,36 +405,41 @@ sub arrange {
sub changescale {
my $self = shift;
my $obj_idx = $self->selected_object_idx;
my $scale = $self->{scale}[$obj_idx];
my ($obj_idx, $object) = $self->selected_object;
# max scale factor should be above 2540 to allow importing files exported in inches
$scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $scale*100, 0, 5000, $self);
$scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 5000, $self);
return if !$scale || $scale == -1;
$self->statusbar->SetStatusText("Scaling object…");
$self->statusbar->StartBusy;
my $object = $self->{print}->objects->[$obj_idx];
my $mesh = $object->mesh;
$mesh->scale($scale/100 / $self->{scale}[$obj_idx]);
$object->mesh->align_to_origin;
$object->size([ $object->mesh->size ]);
$self->{scale}[$obj_idx] = $scale/100;
$self->{list}->SetItem($obj_idx, 2, "$scale%");
$self->make_thumbnail($obj_idx);
$object->set_scale($scale);
$self->arrange;
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("");
}
sub arrange {
my $self = shift;
my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return;
my @size = ();
for my $a (X,Y) {
$size[$a] = max(map $_->thumbnail->size->[$a], @{$self->{objects}});
}
eval {
my $config = $self->skeinpanel->config;
my @positions = Slic3r::Geometry::arrange
($total_parts, @size, @{$config->bed_size}, $config->min_object_distance);
};
# ignore arrange warnings on purpose
$self->recenter;
$self->{canvas}->Refresh;
}
sub split_object {
my $self = shift;
my $obj_idx = $self->selected_object_idx;
my $current_object = $self->{print}->objects->[$obj_idx];
my $current_copies_num = @{$self->{print}->copies->[$obj_idx]};
my ($obj_idx, $current_object) = $self->selected_object;
my $current_copies_num = $current_object->instances_count;
my $mesh = $current_object->mesh->clone;
$mesh->scale(&Slic3r::SCALING_FACTOR);
@ -670,8 +637,10 @@ sub make_model {
my $self = shift;
my $model = Slic3r::Model->new;
for my $obj_idx (0 .. $#{$self->{print}->objects}) {
my $mesh = $self->{print}->objects->[$obj_idx]->mesh->clone;
for my $obj_idx (0 .. $#{$self->{objects}}) {
my $object = $self->{objects}[$obj_idx];
# TODO: reload file
my $mesh = $self->{print}->[$obj_idx]->mesh->clone;
$mesh->scale(&Slic3r::SCALING_FACTOR);
my $object = $model->add_object(vertices => $mesh->vertices);
$object->add_volume(facets => $mesh->facets);
@ -689,27 +658,21 @@ sub make_thumbnail {
my ($obj_idx) = @_;
my $cb = sub {
my $object = $self->{print}->objects->[$obj_idx];
my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices};
my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
for (@$convex_hull) {
@$_ = map $self->to_pixel($_), @$_;
}
$convex_hull->simplify(0.3);
$self->{thumbnails}->[$obj_idx] = $convex_hull; # ignored in multithread environment
my $object = $self->{objects}[$obj_idx];
my $thumbnail = $object->make_thumbnail;
if ($Slic3r::have_threads) {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $convex_hull ])));
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $thumbnail ])));
threads->exit;
} else {
$self->make_thumbnail2;
$self->on_thumbnail_made;
}
};
$Slic3r::have_threads ? threads->create($cb)->detach : $cb->();
}
sub make_thumbnail2 {
sub on_thumbnail_made {
my $self = shift;
$self->recenter;
$self->{canvas}->Refresh;
@ -719,8 +682,8 @@ sub recenter {
my $self = shift;
# calculate displacement needed to center the print
my @print_bb = $self->{print}->bounding_box;
@print_bb = (0,0,0,0) if !defined $print_bb[0];
my @print_bb = (0,0,0,0);
@print_bb = if !defined $print_bb[0];
$self->{shift} = [
($self->{canvas}->GetSize->GetWidth - ($self->to_pixel($print_bb[X2] + $print_bb[X1]))) / 2,
($self->{canvas}->GetSize->GetHeight - ($self->to_pixel($print_bb[Y2] + $print_bb[Y1]))) / 2,
@ -758,9 +721,6 @@ sub _update_bed_size {
my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y];
my $old_scaling_factor = $self->{scaling_factor};
$self->{scaling_factor} = $canvas_side / $bed_largest_side;
if (defined $old_scaling_factor && $self->{scaling_factor} != $old_scaling_factor) {
$self->make_thumbnail($_) for 0..$#{$self->{thumbnails}};
}
}
# this is called on the canvas
@ -936,9 +896,10 @@ sub selection_changed {
}
}
sub selected_object_idx {
sub selected_object {
my $self = shift;
return $self->{selected_objects}[0] ? $self->{selected_objects}[0][0] : $self->{list}->GetFirstSelected;
my $obj_idx = $self->{selected_objects}[0] ? $self->{selected_objects}[0][0] : $self->{list}->GetFirstSelected;
return ($obj_idx, $self->{objects}[$obj_idx]),
}
sub statusbar {
@ -989,4 +950,60 @@ sub OnDropFiles {
$self->{window}->load_file($_) for @$filenames;
}
package Slic3r::GUI::Plater::Object;
use Moo;
use Math::ConvexHull qw(convex_hull);
has 'name' => (is => 'rw', required => 1);
has 'input_file' => (is => 'rw', required => 1);
has 'input_file_object_id' => (is => 'rw', required => 1);
has 'mesh' => (is => 'rw', required => 1, trigger => 1);
has 'size' => (is => 'rw');
has 'scale' => (is => 'rw', default => sub { 1 });
has 'rotate' => (is => 'rw', default => sub { 0 });
has 'instances' => (is => 'rw', default => sub { [] });
has 'thumbnail' => (is => 'rw');
sub _trigger_mesh {
my $self = shift;
$self->size($mesh->size) if $self->mesh;
}
sub instances_count {
my $self = shift;
return scalar @{$self->instances};
}
sub make_thumbnail {
my $self = shift;
my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices};
my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
for (@$convex_hull) {
@$_ = map $self->to_pixel($_), @$_;
}
$convex_hull->simplify(0.3);
$self->thumbnail($convex_hull); # ignored in multi-threaded environments
$self->mesh(undef);
return $convex_hull;
}
sub set_rotation {
my $self = shift;
my ($angle) = @_;
$self->thumbnail->rotate($angle - $self->rotate);
$self->rotate($angle);
}
sub set_scale {
my $self = shift;
my ($scale) = @_;
$self->thumbnail->scale($scale - $self->scale);
$self->scale($scale);
}
1;

View File

@ -6,6 +6,18 @@ use Slic3r::Geometry qw(X Y Z);
has 'materials' => (is => 'ro', default => sub { {} });
has 'objects' => (is => 'ro', default => sub { [] });
sub read_from_file {
my $class = shift;
my ($input_file) = @_;
my $model = $input_file =~ /\.stl$/i ? Slic3r::Format::STL->read_file($input_file)
: $input_file =~ /\.obj$/i ? Slic3r::Format::OBJ->read_file($input_file)
: $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file)
: die "Input file must have .stl, .obj or .amf(.xml) extension\n";
return $model;
}
sub add_object {
my $self = shift;
@ -21,34 +33,15 @@ sub mesh {
my $vertices = [];
my $facets = [];
foreach my $object (@{$self->objects}) {
my @instances = $object->instances ? @{$object->instances} : (undef);
foreach my $instance (@instances) {
my @vertices = @{$object->vertices};
if ($instance) {
# save Z coordinates, as rotation and translation discard them
my @z = map $_->[Z], @vertices;
if ($instance->rotation) {
# transform vertex coordinates
my $rad = Slic3r::Geometry::deg2rad($instance->rotation);
@vertices = Slic3r::Geometry::rotate_points($rad, undef, @vertices);
}
@vertices = Slic3r::Geometry::move_points($instance->offset, @vertices);
# reapply Z coordinates
$vertices[$_][Z] = $z[$_] for 0 .. $#z;
}
my $mesh = $object->mesh;
my $v_offset = @$vertices;
push @$vertices, @vertices;
foreach my $volume (@{$object->volumes}) {
push @$vertices, @{$mesh->vertices};
push @$facets, map {
my $f = [@$_];
$f->[$_] += $v_offset for -3..-1;
$f;
} @{$volume->facets};
}
}
} @{$mesh->facets};
}
return Slic3r::TriangleMesh->new(
@ -87,6 +80,47 @@ sub add_instance {
return $self->instances->[-1];
}
sub mesh {
my $self = shift;
my $vertices = [];
my $facets = [];
my @instances = $self->instances ? @{$self->instances} : (undef);
foreach my $instance (@instances) {
my @vertices = @{$self->vertices};
if ($instance) {
# save Z coordinates, as rotation and translation discard them
my @z = map $_->[Z], @vertices;
if ($instance->rotation) {
# transform vertex coordinates
my $rad = Slic3r::Geometry::deg2rad($instance->rotation);
@vertices = Slic3r::Geometry::rotate_points($rad, undef, @vertices);
}
@vertices = Slic3r::Geometry::move_points($instance->offset, @vertices);
# reapply Z coordinates
$vertices[$_][Z] = $z[$_] for 0 .. $#z;
}
my $v_offset = @$vertices;
push @$vertices, @vertices;
foreach my $volume (@{$self->volumes}) {
push @$facets, map {
my $f = [@$_];
$f->[$_] += $v_offset for -3..-1;
$f;
} @{$volume->facets};
}
}
return Slic3r::TriangleMesh->new(
vertices => $vertices,
facets => $facets,
);
}
package Slic3r::Model::Volume;
use Moo;

View File

@ -4,7 +4,7 @@ use warnings;
use Math::Clipper qw();
use Scalar::Util qw(reftype);
use Slic3r::Geometry qw(A B polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
use Slic3r::Geometry qw(A B X Y MIN MAX polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polyline_lines move_points same_point);
# the constructor accepts an array(ref) of points
@ -140,6 +140,13 @@ sub bounding_box {
return Slic3r::Geometry::bounding_box($self);
}
sub size {
my $self = shift;
my @extents = $self->bounding_box;
return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y);
}
sub rotate {
my $self = shift;
my ($angle, $center) = @_;

View File

@ -3,6 +3,7 @@ use Moo;
use File::Basename qw(basename fileparse);
use File::Spec;
use List::Util qw(max);
use Math::ConvexHull 1.0.4 qw(convex_hull);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points);
@ -83,10 +84,7 @@ sub add_objects_from_file {
my $self = shift;
my ($input_file) = @_;
my $model = $input_file =~ /\.stl$/i ? Slic3r::Format::STL->read_file($input_file)
: $input_file =~ /\.obj$/i ? Slic3r::Format::OBJ->read_file($input_file)
: $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file)
: die "Input file must have .stl, .obj or .amf(.xml) extension\n";
my $model = Slic3r::Model->read_from_file($input_file);
my @print_objects = $self->add_model($model);
$_->input_file($input_file) for @print_objects;
@ -231,19 +229,11 @@ sub arrange_objects {
my $self = shift;
my $total_parts = scalar map @$_, @{$self->copies};
my $partx = my $party = 0;
foreach my $object (@{$self->objects}) {
$partx = $object->size->[X] if $object->size->[X] > $partx;
$party = $object->size->[Y] if $object->size->[Y] > $party;
}
# object distance is max(duplicate_distance, clearance_radius)
my $distance = $Slic3r::Config->complete_objects && $Slic3r::Config->extruder_clearance_radius > $Slic3r::Config->duplicate_distance
? $Slic3r::Config->extruder_clearance_radius
: $Slic3r::Config->duplicate_distance;
my $partx = max(map $_->size->[X], @{$self->objects});
my $party = max(map $_->size->[Y], @{$self->objects});
my @positions = Slic3r::Geometry::arrange
($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $distance);
($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $Slic3r::Config->min_object_distance);
for my $obj_idx (0..$#{$self->objects}) {
@{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]};