Finish GUI decoupling

This commit is contained in:
Alessandro Ranellucci 2012-09-12 16:30:44 +02:00
parent 8382eeef0a
commit e40f32995f
11 changed files with 223 additions and 142 deletions

View File

@ -72,7 +72,7 @@ sub write_file {
foreach my $instance (@{$object->instances}) { foreach my $instance (@{$object->instances}) {
$instances .= sprintf qq{ <instance objectid="%d">\n}, $object_id; $instances .= sprintf qq{ <instance objectid="%d">\n}, $object_id;
$instances .= sprintf qq{ <deltax>%s</deltax>\n}, $instance->offset->[X]; $instances .= sprintf qq{ <deltax>%s</deltax>\n}, $instance->offset->[X];
$instances .= sprintf qq{ <deltax>%s</deltax>\n}, $instance->offset->[Y]; $instances .= sprintf qq{ <deltay>%s</deltay>\n}, $instance->offset->[Y];
$instances .= sprintf qq{ <rz>%s</rz>\n}, $instance->rotation; $instances .= sprintf qq{ <rz>%s</rz>\n}, $instance->rotation;
$instances .= sprintf qq{ </instance>\n}; $instances .= sprintf qq{ </instance>\n};
} }

View File

@ -107,7 +107,7 @@ sub end_document {
foreach my $object_id (keys %{ $self->{_instances} }) { foreach my $object_id (keys %{ $self->{_instances} }) {
my $new_object_id = $self->{_objects_map}{$object_id}; my $new_object_id = $self->{_objects_map}{$object_id};
if (!$new_object_id) { if (!defined $new_object_id) {
warn "Undefined object $object_id referenced in constellation\n"; warn "Undefined object $object_id referenced in constellation\n";
next; next;
} }
@ -115,7 +115,7 @@ sub end_document {
foreach my $instance (@{ $self->{_instances}{$object_id} }) { foreach my $instance (@{ $self->{_instances}{$object_id} }) {
$self->{_model}->objects->[$new_object_id]->add_instance( $self->{_model}->objects->[$new_object_id]->add_instance(
rotation => $instance->{rz} || 0, rotation => $instance->{rz} || 0,
offset => [ $instance->{deltax} || 0, $instance->{deltay} ], offset => [ $instance->{deltax} || 0, $instance->{deltay} || 0 ],
); );
} }
} }

View File

@ -5,6 +5,7 @@ use utf8;
use File::Basename qw(basename dirname); use File::Basename qw(basename dirname);
use List::Util qw(max sum); use List::Util qw(max sum);
use Math::ConvexHull qw(convex_hull);
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2); use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2);
use Slic3r::Geometry::Clipper qw(JT_ROUND); use Slic3r::Geometry::Clipper qw(JT_ROUND);
use threads::shared qw(shared_clone); use threads::shared qw(shared_clone);
@ -39,6 +40,8 @@ sub new {
$self->{config} = Slic3r::Config->new_from_defaults(qw( $self->{config} = Slic3r::Config->new_from_defaults(qw(
bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance
)); ));
$self->{objects} = [];
$self->{selected_objects} = [];
$self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, CANVAS_SIZE, wxTAB_TRAVERSAL); $self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, CANVAS_SIZE, wxTAB_TRAVERSAL);
$self->{canvas}->SetBackgroundColour(Wx::wxWHITE); $self->{canvas}->SetBackgroundColour(Wx::wxWHITE);
@ -183,8 +186,6 @@ sub new {
}); });
$self->_update_bed_size; $self->_update_bed_size;
$self->{objects} = [];
$self->{selected_objects} = [];
$self->recenter; $self->recenter;
{ {
@ -298,7 +299,7 @@ sub load_file {
my $model = Slic3r::Model->read_from_file($input_file); my $model = Slic3r::Model->read_from_file($input_file);
for my $i (0 .. $#{$model->objects}) { for my $i (0 .. $#{$model->objects}) {
my $object = Slic3r::GUI::Plater::Object->new( my $object = Slic3r::GUI::Plater::Object->new(
name => basebane($input_file), name => basename($input_file),
input_file => $input_file, input_file => $input_file,
input_file_object_id => $i, input_file_object_id => $i,
mesh => $model->objects->[$i]->mesh, mesh => $model->objects->[$i]->mesh,
@ -308,8 +309,13 @@ sub load_file {
: [0,0], : [0,0],
], ],
); );
# we only consider the rotation of the first instance for now
$object->set_rotation($model->objects->[$i]->instances->[0]->rotation)
if $model->objects->[$i]->instances;
push @{ $self->{objects} }, $object; push @{ $self->{objects} }, $object;
$self->object_loaded($#{ $self->{objects} }); $self->object_loaded($#{ $self->{objects} }, no_arrange => (@{$object->instances} > 1));
} }
$process_dialog->Destroy; $process_dialog->Destroy;
@ -377,13 +383,18 @@ sub decrease {
my $self = shift; my $self = shift;
my ($obj_idx, $object) = $self->selected_object; my ($obj_idx, $object) = $self->selected_object;
$self->{selected_objects} = [ +(grep { $_->[0] == $obj_idx } @{$self->{object_previews}})[-1] ]; if ($object->instances_count >= 2) {
$self->remove; pop @{$object->instances};
} else {
$self->remove;
}
if ($self->{objects}[$obj_idx]) { if ($self->{objects}[$obj_idx]) {
$self->{list}->Select($obj_idx, 0); $self->{list}->Select($obj_idx, 0);
$self->{list}->Select($obj_idx, 1); $self->{list}->Select($obj_idx, 1);
} }
$self->recenter;
$self->{canvas}->Refresh;
} }
sub rotate { sub rotate {
@ -397,7 +408,7 @@ sub rotate {
return if !$angle || $angle == -1; return if !$angle || $angle == -1;
} }
$object->set_rotation($angle); $object->set_rotation($object->rotate + $angle);
$self->recenter; $self->recenter;
$self->{canvas}->Refresh; $self->{canvas}->Refresh;
} }
@ -411,7 +422,8 @@ sub changescale {
my $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 5000, $self); my $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 5000, $self);
return if !$scale || $scale == -1; return if !$scale || $scale == -1;
$object->set_scale($scale); $self->{list}->SetItem($obj_idx, 2, "$scale%");
$object->set_scale($scale / 100);
$self->arrange; $self->arrange;
} }
@ -421,13 +433,16 @@ 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 $_->thumbnail->size->[$a], @{$self->{objects}}); $size[$a] = $self->to_units(max(map $_->thumbnail->size->[$a], @{$self->{objects}}));
} }
eval { eval {
my $config = $self->skeinpanel->config; my $config = $self->skeinpanel->config;
my @positions = Slic3r::Geometry::arrange my @positions = Slic3r::Geometry::arrange
($total_parts, @size, @{$config->bed_size}, $config->min_object_distance); ($total_parts, @size, @{$config->bed_size}, $config->min_object_distance, $self->skeinpanel->config);
@$_ = @{shift @positions}
for map @{$_->instances}, @{$self->{objects}};
}; };
# ignore arrange warnings on purpose # ignore arrange warnings on purpose
@ -440,8 +455,7 @@ sub split_object {
my ($obj_idx, $current_object) = $self->selected_object; my ($obj_idx, $current_object) = $self->selected_object;
my $current_copies_num = $current_object->instances_count; my $current_copies_num = $current_object->instances_count;
my $mesh = $current_object->mesh->clone; my $mesh = $current_object->get_mesh;
$mesh->scale(&Slic3r::SCALING_FACTOR);
my @new_meshes = $mesh->split_mesh; my @new_meshes = $mesh->split_mesh;
if (@new_meshes == 1) { if (@new_meshes == 1) {
@ -455,11 +469,15 @@ sub split_object {
$self->remove($obj_idx); $self->remove($obj_idx);
foreach my $mesh (@new_meshes) { foreach my $mesh (@new_meshes) {
my $object = $self->{print}->add_object_from_mesh($mesh); my $object = Slic3r::GUI::Plater::Object->new(
$object->input_file($current_object->input_file); name => basename($current_object->input_file),
my $new_obj_idx = $#{$self->{print}->objects}; input_file => $current_object->input_file,
push @{$self->{print}->copies->[$new_obj_idx]}, [0,0] for 2..$current_copies_num; input_file_object_id => undef,
$self->object_loaded($new_obj_idx, no_arrange => 1); mesh => $mesh,
instances => [ map [0,0], 1..$current_copies_num ],
);
push @{ $self->{objects} }, $object;
$self->object_loaded($#{ $self->{objects} }, no_arrange => 1);
} }
$self->arrange; $self->arrange;
@ -473,15 +491,13 @@ sub export_gcode {
return; return;
} }
# set this before spawning the thread because ->config needs GetParent and it's not available there # get config before spawning the thread because ->config needs GetParent and it's not available there
$self->{print}->config($self->skeinpanel->config); my $print = $self->_init_print;
$self->{print}->extra_variables->{"${_}_preset"} = $self->skeinpanel->{options_tabs}{$_}->current_preset->{name}
for qw(print filament printer);
# select output file # select output file
$self->{output_file} = $main::opt{output}; $self->{output_file} = $main::opt{output};
{ {
$self->{output_file} = $self->{print}->expanded_output_filepath($self->{output_file}); $self->{output_file} = $print->expanded_output_filepath($self->{output_file}, $self->{objects}[0]->input_file);
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}), my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}),
basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE);
if ($dlg->ShowModal != wxID_OK) { if ($dlg->ShowModal != wxID_OK) {
@ -496,6 +512,7 @@ sub export_gcode {
if ($Slic3r::have_threads) { if ($Slic3r::have_threads) {
$self->{export_thread} = threads->create(sub { $self->{export_thread} = threads->create(sub {
$self->export_gcode2( $self->export_gcode2(
$print,
$self->{output_file}, $self->{output_file},
progressbar => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) }, progressbar => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) },
message_dialog => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) }, message_dialog => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) },
@ -516,6 +533,7 @@ sub export_gcode {
}); });
} else { } else {
$self->export_gcode2( $self->export_gcode2(
$print,
$self->{output_file}, $self->{output_file},
progressbar => sub { progressbar => sub {
my ($percent, $message) = @_; my ($percent, $message) = @_;
@ -529,9 +547,20 @@ sub export_gcode {
} }
} }
sub _init_print {
my $self = shift;
return Slic3r::Print->new(
config => $self->skeinpanel->config,
extra_variables => {
map { $_ => $self->skeinpanel->{options_tabs}{$_}->current_preset->{name} } qw(print filament printer),
},
);
}
sub export_gcode2 { sub export_gcode2 {
my $self = shift; my $self = shift;
my ($output_file, %params) = @_; my ($print, $output_file, %params) = @_;
$Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new;
local $SIG{'KILL'} = sub { local $SIG{'KILL'} = sub {
Slic3r::debugf "Exporting cancelled; exiting thread...\n"; Slic3r::debugf "Exporting cancelled; exiting thread...\n";
@ -539,8 +568,8 @@ sub export_gcode2 {
} if $Slic3r::have_threads; } if $Slic3r::have_threads;
eval { eval {
my $print = $self->{print};
$print->config->validate; $print->config->validate;
$print->add_model($self->make_model);
$print->validate; $print->validate;
{ {
@ -549,7 +578,6 @@ sub export_gcode2 {
my %params = ( my %params = (
output_file => $output_file, output_file => $output_file,
status_cb => sub { $params{progressbar}->(@_) }, status_cb => sub { $params{progressbar}->(@_) },
keep_meshes => 1,
); );
if ($params{export_svg}) { if ($params{export_svg}) {
$print->export_svg(%params); $print->export_svg(%params);
@ -621,7 +649,7 @@ sub _get_export_file {
my $output_file = $main::opt{output}; my $output_file = $main::opt{output};
{ {
$output_file = $self->{print}->expanded_output_filepath($output_file); $output_file = $self->_init_print->expanded_output_filepath($output_file, $self->{objects}[0]->input_file);
$output_file =~ s/\.gcode$/$suffix/i; $output_file =~ s/\.gcode$/$suffix/i;
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -639,18 +667,21 @@ sub make_model {
my $self = shift; my $self = shift;
my $model = Slic3r::Model->new; my $model = Slic3r::Model->new;
for my $obj_idx (0 .. $#{$self->{objects}}) { foreach my $object (@{$self->{objects}}) {
my $object = $self->{objects}[$obj_idx]; my $mesh = $object->get_mesh;
# TODO: reload file $mesh->scale($object->scale);
my $mesh = $self->{print}->[$obj_idx]->mesh->clone; my $model_object = $model->add_object(
$mesh->scale(&Slic3r::SCALING_FACTOR); vertices => $mesh->vertices,
my $model_object = $model->add_object(vertices => $mesh->vertices); input_file => $object->input_file,
$model_object->add_volume(facets => $mesh->facets); );
for my $copy (@{$self->{print}->copies->[$obj_idx]}) { $model_object->add_volume(
$object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]); facets => $mesh->facets,
} );
$model_object->add_instance(
rotation => $object->rotate,
offset => [ @$_ ],
) for @{$object->instances};
} }
# TODO: $model->align_to_origin;
return $model; return $model;
} }
@ -661,7 +692,7 @@ sub make_thumbnail {
my $cb = sub { my $cb = sub {
my $object = $self->{objects}[$obj_idx]; my $object = $self->{objects}[$obj_idx];
my $thumbnail = $object->make_thumbnail; my $thumbnail = $object->make_thumbnail(scaling_factor => $self->{scaling_factor});
if ($Slic3r::have_threads) { if ($Slic3r::have_threads) {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $thumbnail ]))); Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $thumbnail ])));
@ -683,20 +714,21 @@ sub on_thumbnail_made {
sub recenter { sub recenter {
my $self = shift; my $self = shift;
return unless @{$self->{objects}};
# calculate displacement needed to center the print # calculate displacement needed to center the print
my @print_bb = (0,0,0,0); my @print_bb = Slic3r::Geometry::bounding_box([
foreach my $object (@{$self->{objects}}) { map {
foreach my $instance (@{$object->instances}) { my $obj = $_;
my @bb = (@$instance, map $instance->[$_] + $object->size->[$_], X,Y); map {
$print_bb[X1] = $bb[X1] if $bb[X1] < $print_bb[X1]; my $instance = $_;
$print_bb[Y1] = $bb[Y1] if $bb[Y1] < $print_bb[Y1]; $instance, [ map $instance->[$_] + $obj->rotated_size->[$_], X,Y ];
$print_bb[X2] = $bb[X2] if $bb[X2] > $print_bb[X2]; } @{$obj->instances};
$print_bb[Y2] = $bb[Y2] if $bb[Y2] > $print_bb[Y2]; } @{$self->{objects}},
} ]);
}
$self->{shift} = [ $self->{shift} = [
($self->{canvas}->GetSize->GetWidth - ($self->to_pixel($print_bb[X2] + $print_bb[X1]))) / 2, ($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, ($self->{canvas}->GetSize->GetHeight - $self->to_pixel($print_bb[Y2] + $print_bb[Y1])) / 2,
]; ];
} }
@ -729,7 +761,6 @@ sub _update_bed_size {
my $bed_size = $self->{config}->bed_size; my $bed_size = $self->{config}->bed_size;
my $canvas_side = CANVAS_SIZE->[X]; # when the canvas is not rendered yet, its GetSize() method returns 0,0 my $canvas_side = CANVAS_SIZE->[X]; # when the canvas is not rendered yet, its GetSize() method returns 0,0
my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y]; 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; $self->{scaling_factor} = $canvas_side / $bed_largest_side;
} }
@ -769,7 +800,7 @@ sub repaint {
$dc->DrawRectangle(0, 0, @size); $dc->DrawRectangle(0, 0, @size);
# draw text if plate is empty # draw text if plate is empty
if (@{$parent->{objects}}) { if (!@{$parent->{objects}}) {
$dc->SetTextForeground(Wx::Colour->new(150,50,50)); $dc->SetTextForeground(Wx::Colour->new(150,50,50));
$dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL));
$dc->DrawLabel(CANVAS_TEXT, Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); $dc->DrawLabel(CANVAS_TEXT, Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
@ -779,14 +810,15 @@ sub repaint {
$dc->SetPen(wxBLACK_PEN); $dc->SetPen(wxBLACK_PEN);
@{$parent->{object_previews}} = (); @{$parent->{object_previews}} = ();
for my $obj_idx (0 .. $#{$parent->{objects}}) { for my $obj_idx (0 .. $#{$parent->{objects}}) {
next unless $parent->{thumbnails}[$obj_idx]; my $object = $parent->{objects}[$obj_idx];
for my $copy_idx (0 .. $#{$print->copies->[$obj_idx]}) { next unless $object->thumbnail;
my $copy = $print->copies->[$obj_idx][$copy_idx]; for my $instance_idx (0 .. $#{$object->instances}) {
push @{$parent->{object_previews}}, [ $obj_idx, $copy_idx, $parent->{thumbnails}[$obj_idx]->clone ]; my $instance = $object->instances->[$instance_idx];
$parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($copy->[$_]) + $parent->{shift}[$_], (X,Y)); push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $object->thumbnail->clone ];
$parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y));
my $drag_object = $self->{drag_object}; my $drag_object = $self->{drag_object};
if (defined $drag_object && $obj_idx == $drag_object->[0] && $copy_idx == $drag_object->[1]) { if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) {
$dc->SetBrush($parent->{dragged_brush}); $dc->SetBrush($parent->{dragged_brush});
} elsif (grep { $_->[0] == $obj_idx } @{$parent->{selected_objects}}) { } elsif (grep { $_->[0] == $obj_idx } @{$parent->{selected_objects}}) {
$dc->SetBrush($parent->{selected_brush}); $dc->SetBrush($parent->{selected_brush});
@ -796,7 +828,7 @@ sub repaint {
$dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0);
# if sequential printing is enabled and we have more than one object # if sequential printing is enabled and we have more than one object
if ($parent->{config}->complete_objects && (map @$_, @{$print->copies}) > 1) { if ($parent->{config}->complete_objects && (map @{$_->instances}, @{$parent->{objects}}) > 1) {
my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0];
$dc->SetPen($parent->{clearance_pen}); $dc->SetPen($parent->{clearance_pen});
$dc->SetBrush($parent->{transparent_brush}); $dc->SetBrush($parent->{transparent_brush});
@ -820,7 +852,6 @@ sub repaint {
sub mouse_event { sub mouse_event {
my ($self, $event) = @_; my ($self, $event) = @_;
my $parent = $self->GetParent; my $parent = $self->GetParent;
my $print = $parent->{print};
my $point = $event->GetPosition; my $point = $event->GetPosition;
my $pos = $parent->_y([[$point->x, $point->y]])->[0]; #]] my $pos = $parent->_y([[$point->x, $point->y]])->[0]; #]]
@ -829,12 +860,13 @@ sub mouse_event {
$parent->{list}->Select($parent->{list}->GetFirstSelected, 0); $parent->{list}->Select($parent->{list}->GetFirstSelected, 0);
$parent->selection_changed(0); $parent->selection_changed(0);
for my $preview (@{$parent->{object_previews}}) { for my $preview (@{$parent->{object_previews}}) {
if ($preview->[2]->encloses_point($pos)) { my ($obj_idx, $instance_idx, $thumbnail) = @$preview;
$parent->{selected_objects} = [$preview]; if ($thumbnail->encloses_point($pos)) {
$parent->{list}->Select($preview->[0], 1); $parent->{selected_objects} = [ [$obj_idx, $instance_idx] ];
$parent->{list}->Select($obj_idx, 1);
$parent->selection_changed(1); $parent->selection_changed(1);
my $copy = $print->copies->[ $preview->[0] ]->[ $preview->[1] ]; my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx];
$self->{drag_start_pos} = [ map $pos->[$_] - $parent->{shift}[$_] - $parent->to_pixel($copy->[$_]), X,Y ]; # displacement between the click and the copy's origin $self->{drag_start_pos} = [ map $pos->[$_] - $parent->{shift}[$_] - $parent->to_pixel($instance->[$_]), X,Y ]; # displacement between the click and the instance origin
$self->{drag_object} = $preview; $self->{drag_object} = $preview;
} }
} }
@ -847,9 +879,11 @@ sub mouse_event {
$self->SetCursor(wxSTANDARD_CURSOR); $self->SetCursor(wxSTANDARD_CURSOR);
} elsif ($event->Dragging) { } elsif ($event->Dragging) {
return if !$self->{drag_start_pos}; # concurrency problems return if !$self->{drag_start_pos}; # concurrency problems
for my $obj ($self->{drag_object}) { for my $preview ($self->{drag_object}) {
my $copy = $print->copies->[ $obj->[0] ]->[ $obj->[1] ]; my ($obj_idx, $instance_idx, $thumbnail) = @$preview;
$copy->[$_] = $parent->to_scaled($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y; my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx];
$instance->[$_] = $parent->to_units($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y;
$instance = $parent->_y([$instance])->[0];
$parent->Refresh; $parent->Refresh;
} }
} elsif ($event->Moving) { } elsif ($event->Moving) {
@ -886,7 +920,7 @@ sub list_item_selected {
sub object_list_changed { sub object_list_changed {
my $self = shift; my $self = shift;
my $method = $self->{print} && @{$self->{print}->objects} ? 'Enable' : 'Disable'; my $method = @{$self->{objects}} ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method $self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl); for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl);
} }
@ -921,7 +955,7 @@ sub to_pixel {
return $_[0] * $self->{scaling_factor}; return $_[0] * $self->{scaling_factor};
} }
sub to_scaled { sub to_units {
my $self = shift; my $self = shift;
return $_[0] / $self->{scaling_factor}; return $_[0] / $self->{scaling_factor};
} }
@ -967,17 +1001,25 @@ use Slic3r::Geometry qw(X Y);
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', required => 1); has 'input_file_object_id' => (is => 'rw'); # undef means keep mesh
has 'mesh' => (is => 'rw', required => 1, trigger => 1); has 'mesh' => (is => 'rw', required => 1, trigger => 1);
has 'size' => (is => 'rw'); has 'size' => (is => 'rw');
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 });
has 'instances' => (is => 'rw', default => sub { [] }); has 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis
has 'thumbnail' => (is => 'rw'); has 'thumbnail' => (is => 'rw');
sub _trigger_mesh { sub _trigger_mesh {
my $self = shift; my $self = shift;
$self->size($self->mesh->size) if $self->mesh; $self->size([$self->mesh->size]) if $self->mesh;
}
sub get_mesh {
my $self = shift;
return $self->mesh->clone if $self->mesh;
my $model = Slic3r::Model->read_from_file($self->input_file);
return $model->objects->[$self->input_file_object_id]->mesh;
} }
sub instances_count { sub instances_count {
@ -987,16 +1029,21 @@ sub instances_count {
sub make_thumbnail { sub make_thumbnail {
my $self = shift; my $self = shift;
my %params = @_;
my @points = map [ @$_[X,Y] ], @{$self->mesh->vertices}; my @points = map [ @$_[X,Y] ], @{$self->mesh->vertices};
my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
for (@$convex_hull) { for (@$convex_hull) {
@$_ = map $self->to_pixel($_), @$_; @$_ = map $_ * $params{scaling_factor}, @$_;
} }
$convex_hull->simplify(0.3); $convex_hull->simplify(0.3);
$convex_hull->rotate(Slic3r::Geometry::deg2rad($self->rotate));
$convex_hull->scale($self->scale);
$convex_hull->align_to_origin;
$self->thumbnail($convex_hull); # ignored in multi-threaded environments $self->thumbnail($convex_hull); # ignored in multi-threaded environments
$self->mesh(undef); $self->mesh(undef) if defined $self->input_file_object_id;
return $convex_hull; return $convex_hull;
} }
@ -1004,7 +1051,10 @@ sub set_rotation {
my $self = shift; my $self = shift;
my ($angle) = @_; my ($angle) = @_;
$self->thumbnail->rotate($angle - $self->rotate); if ($self->thumbnail) {
$self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate));
$self->thumbnail->align_to_origin;
}
$self->rotate($angle); $self->rotate($angle);
} }
@ -1012,8 +1062,22 @@ sub set_scale {
my $self = shift; my $self = shift;
my ($scale) = @_; my ($scale) = @_;
$self->thumbnail->scale($scale - $self->scale); my $factor = $scale / $self->scale;
return if $factor == 1;
$self->size->[$_] *= $factor for X,Y;
if ($self->thumbnail) {
$self->thumbnail->scale($factor);
$self->thumbnail->align_to_origin;
}
$self->scale($scale); $self->scale($scale);
} }
sub rotated_size {
my $self = shift;
return Slic3r::Polygon->new([0,0], [$self->size->[X], 0], [@{$self->size}], [0, $self->size->[Y]])
->rotate(Slic3r::Geometry::deg2rad($self->rotate))
->size;
}
1; 1;

View File

@ -57,8 +57,8 @@ sub do_slice {
$config->validate; $config->validate;
# confirm slicing of more than one copies # confirm slicing of more than one copies
my $copies = $Slic3r::Config->duplicate_grid->[X] * $Slic3r::Config->duplicate_grid->[Y]; my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y];
$copies = $Slic3r::Config->duplicate if $Slic3r::Config->duplicate > 1; $copies = $config->duplicate if $config->duplicate > 1;
if ($copies > 1) { if ($copies > 1) {
my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?",
'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); 'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL);

View File

@ -882,7 +882,7 @@ sub douglas_peucker2 {
} }
sub arrange { sub arrange {
my ($total_parts, $partx, $party, $areax, $areay, $dist) = @_; my ($total_parts, $partx, $party, $areax, $areay, $dist, $Config) = @_;
my $linint = sub { my $linint = sub {
my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_; my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_;
@ -895,8 +895,13 @@ sub arrange {
# margin needed for the skirt # margin needed for the skirt
my $skirt_margin; my $skirt_margin;
if ($Slic3r::Config->skirts > 0) { if ($Config->skirts > 0) {
$skirt_margin = ($Slic3r::flow->spacing * $Slic3r::Config->skirts + $Slic3r::Config->skirt_distance) * 2; my $flow = Slic3r::Flow->new(
layer_height => $Config->first_layer_height,
nozzle_diameter => $Config->nozzle_diameter->[0], # TODO: actually look for the extruder used for skirt
width => $Config->first_layer_extrusion_width,
);
$skirt_margin = ($flow->spacing * $Config->skirts + $Config->skirt_distance) * 2;
} else { } else {
$skirt_margin = 0; $skirt_margin = 0;
} }

View File

@ -30,24 +30,21 @@ sub add_object {
sub mesh { sub mesh {
my $self = shift; my $self = shift;
my $vertices = []; my @meshes = ();
my $facets = [];
foreach my $object (@{$self->objects}) { foreach my $object (@{$self->objects}) {
my $mesh = $object->mesh; my @instances = $object->instances ? @{$object->instances} : (undef);
foreach my $instance (@instances) {
my $v_offset = @$vertices; my $mesh = $object->mesh->clone;
push @$vertices, @{$mesh->vertices}; if ($instance) {
push @$facets, map { $mesh->rotate($instance->rotation);
my $f = [@$_]; $mesh->align_to_origin;
$f->[$_] += $v_offset for -3..-1; $mesh->move(@{$instance->offset});
$f; }
} @{$mesh->facets}; push @meshes, $mesh;
}
} }
return Slic3r::TriangleMesh->new( return Slic3r::TriangleMesh->merge(@meshes);
vertices => $vertices,
facets => $facets,
);
} }
package Slic3r::Model::Material; package Slic3r::Model::Material;
@ -61,6 +58,7 @@ use Moo;
use Slic3r::Geometry qw(X Y Z); use Slic3r::Geometry qw(X Y Z);
has 'input_file' => (is => 'rw');
has 'model' => (is => 'ro', weak_ref => 1, required => 1); has 'model' => (is => 'ro', weak_ref => 1, required => 1);
has 'vertices' => (is => 'ro', default => sub { [] }); has 'vertices' => (is => 'ro', default => sub { [] });
has 'volumes' => (is => 'ro', default => sub { [] }); has 'volumes' => (is => 'ro', default => sub { [] });
@ -88,38 +86,11 @@ sub mesh {
my $vertices = []; my $vertices = [];
my $facets = []; 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( return Slic3r::TriangleMesh->new(
vertices => $vertices, vertices => $self->vertices,
facets => $facets, facets => [ map @{$_->facets}, @{$self->volumes} ],
); );
} }

View File

@ -6,7 +6,7 @@ use warnings;
use parent 'Slic3r::Polyline'; use parent 'Slic3r::Polyline';
use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges
scale polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon); polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon);
use Slic3r::Geometry::Clipper qw(JT_MITER); use Slic3r::Geometry::Clipper qw(JT_MITER);
sub lines { sub lines {
@ -111,7 +111,7 @@ sub is_printable {
# detect them and we would be discarding them. # detect them and we would be discarding them.
my $p = $self->clone; my $p = $self->clone;
$p->make_counter_clockwise; $p->make_counter_clockwise;
return $p->offset(scale($flow_width || $Slic3r::flow->width) / 2) ? 1 : 0; return $p->offset(Slic3r::Geometry::scale($flow_width || $Slic3r::flow->width) / 2) ? 1 : 0;
} }
sub is_valid { sub is_valid {

View File

@ -4,7 +4,7 @@ use warnings;
use Math::Clipper qw(); use Math::Clipper qw();
use Scalar::Util qw(reftype); use Scalar::Util qw(reftype);
use Slic3r::Geometry qw(A B X Y MIN MAX polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2 polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
polyline_lines move_points same_point); polyline_lines move_points same_point);
# the constructor accepts an array(ref) of points # the constructor accepts an array(ref) of points
@ -144,7 +144,13 @@ sub size {
my $self = shift; my $self = shift;
my @extents = $self->bounding_box; my @extents = $self->bounding_box;
return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y); return [$extents[X2] - $extents[X1], $extents[Y2] - $extents[Y1]];
}
sub align_to_origin {
my $self = shift;
my @bb = $self->bounding_box;
return $self->translate(-$bb[X1], -$bb[Y1]);
} }
sub rotate { sub rotate {
@ -163,4 +169,16 @@ sub translate {
return $self; return $self;
} }
sub scale {
my $self = shift;
my ($factor) = @_;
return if $factor == 1;
# transform point coordinates
foreach my $point (@$self) {
$point->[$_] *= $factor for X,Y;
}
return $self;
}
1; 1;

View File

@ -108,11 +108,11 @@ sub add_model {
$mesh->rotate($object->instances->[0]->rotation); $mesh->rotate($object->instances->[0]->rotation);
} }
push @print_objects, $self->add_object_from_mesh($mesh); push @print_objects, $self->add_object_from_mesh($mesh, input_file => $object->input_file);
if ($object->instances) { if ($object->instances) {
# replace the default [0,0] instance with the custom ones # replace the default [0,0] instance with the custom ones
@{$self->copies->[-1]} = map [ scale $_->offset->[X], scale $_->offset->[X] ], @{$object->instances}; @{$self->copies->[-1]} = map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances};
} }
} }
@ -121,7 +121,7 @@ sub add_model {
sub add_object_from_mesh { sub add_object_from_mesh {
my $self = shift; my $self = shift;
my ($mesh) = @_; my ($mesh, %attributes) = @_;
$mesh->rotate($Slic3r::Config->rotate); $mesh->rotate($Slic3r::Config->rotate);
$mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR); $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR);
@ -131,6 +131,7 @@ sub add_object_from_mesh {
my $object = Slic3r::Print::Object->new( my $object = Slic3r::Print::Object->new(
mesh => $mesh, mesh => $mesh,
size => [ $mesh->size ], size => [ $mesh->size ],
%attributes,
); );
push @{$self->objects}, $object; push @{$self->objects}, $object;
@ -236,7 +237,7 @@ sub arrange_objects {
my $party = max(map $_->size->[Y], @{$self->objects}); my $party = max(map $_->size->[Y], @{$self->objects});
my @positions = Slic3r::Geometry::arrange my @positions = Slic3r::Geometry::arrange
($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $Slic3r::Config->min_object_distance); ($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $Slic3r::Config->min_object_distance, $self->config);
for my $obj_idx (0..$#{$self->objects}) { for my $obj_idx (0..$#{$self->objects}) {
@{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]}; @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]};
@ -788,11 +789,14 @@ sub total_extrusion_volume {
return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000; return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000;
} }
# this method will return the value of $self->output_file after expanding its # this method will return the supplied input file path after expanding its
# format variables with their values # format variables with their values
sub expanded_output_filepath { sub expanded_output_filepath {
my $self = shift; my $self = shift;
my ($path) = @_; my ($path, $input_file) = @_;
# if no input file was supplied, take the first one from our objects
$input_file ||= $self->objects->[0]->input_file;
# if output path is an existing directory, we take that and append # if output path is an existing directory, we take that and append
# the specified filename format # the specified filename format
@ -800,7 +804,6 @@ sub expanded_output_filepath {
# if no explicit output file was defined, we take the input # if no explicit output file was defined, we take the input
# file directory and append the specified filename format # file directory and append the specified filename format
my $input_file = $self->objects->[0]->input_file;
$path ||= (fileparse($input_file))[1] . $Slic3r::Config->output_filename_format; $path ||= (fileparse($input_file))[1] . $Slic3r::Config->output_filename_format;
my $input_filename = my $input_filename_base = basename($input_file); my $input_filename = my $input_filename_base = basename($input_file);

View File

@ -70,6 +70,26 @@ sub BUILD {
} }
} }
sub merge {
my $class = shift;
my @meshes = @_;
my $vertices = [];
my $facets = [];
foreach my $mesh (@meshes) {
my $v_offset = @$vertices;
push @$vertices, @{$mesh->vertices};
push @$facets, map {
my $f = [@$_];
$f->[$_] += $v_offset for -3..-1;
$f;
} @{$mesh->facets};
}
return $class->new(vertices => $vertices, facets => $facets);
}
sub clone { sub clone {
my $self = shift; my $self = shift;
return (ref $self)->new( return (ref $self)->new(

View File

@ -36,7 +36,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]), expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]),
); );
foreach my $angle (0, 45) { foreach my $angle (0, 45) {
$surface->expolygon->rotate($angle, [0,0]); $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4);
is scalar @paths, 1, 'one continuous path'; is scalar @paths, 1, 'one continuous path';
} }