From 191de5d0781698f1d30d63adf9cd129d3114bccf Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 29 Aug 2012 19:37:27 +0200 Subject: [PATCH 01/22] Unfinished work for decoupling GUI from the Print object (goal = more speed for manipulation, less memory usage) --- lib/Slic3r/Config.pm | 9 ++ lib/Slic3r/GUI/Plater.pm | 261 +++++++++++++++++++++------------------ lib/Slic3r/Model.pm | 90 +++++++++----- lib/Slic3r/Polyline.pm | 9 +- lib/Slic3r/Print.pm | 20 +-- 5 files changed, 223 insertions(+), 166 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 97992d5b4..71363c726 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -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 { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index a1b8c3406..712b736b0 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -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; - $self->{list}->DeleteItem($obj_idx); - } + 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; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index b648d4b34..3a4aec49d 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -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 $v_offset = @$vertices; - push @$vertices, @vertices; - foreach my $volume (@{$object->volumes}) { - push @$facets, map { - my $f = [@$_]; - $f->[$_] += $v_offset for -3..-1; - $f; - } @{$volume->facets}; - } - } + my $mesh = $object->mesh; + + my $v_offset = @$vertices; + push @$vertices, @{$mesh->vertices}; + push @$facets, map { + my $f = [@$_]; + $f->[$_] += $v_offset for -3..-1; + $f; + } @{$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; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index da9cc0479..ce13e7786 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -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) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index bfa7c2c36..735a31681 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -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]}; From 94a096f1d21d6d1e86e04b1fbef968ad452f1b1d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 30 Aug 2012 23:04:56 +0200 Subject: [PATCH 02/22] Support legacy config files not containing first_layer_height #631 --- lib/Slic3r/Print.pm | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 735a31681..397bc9bea 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -43,6 +43,14 @@ sub _trigger_config { # store config in a handy place $Slic3r::Config = $self->config; + # legacy with existing config files + $self->config->set('first_layer_height', $self->config->layer_height) + if !$self->config->first_layer_height; + $self->config->set_ifndef('small_perimeter_speed', $self->config->perimeter_speed); + $self->config->set_ifndef('bridge_speed', $self->config->infill_speed); + $self->config->set_ifndef('solid_infill_speed', $self->config->infill_speed); + $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); + # initialize extruder(s) $Slic3r::extruders = []; for my $t (0, map $_-1, map $self->config->get($_), qw(perimeter_extruder infill_extruder support_material_extruder)) { @@ -72,12 +80,6 @@ sub _trigger_config { # G-code flavors $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3'; $self->config->set('extrusion_axis', '') if $self->config->gcode_flavor eq 'no-extrusion'; - - # legacy with existing config files - $self->config->set_ifndef('small_perimeter_speed', $self->config->perimeter_speed); - $self->config->set_ifndef('bridge_speed', $self->config->infill_speed); - $self->config->set_ifndef('solid_infill_speed', $self->config->infill_speed); - $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); } sub add_objects_from_file { From 202bfa91c8e96cce10cc3194cd30d0093dc9a468 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 30 Aug 2012 23:13:28 +0200 Subject: [PATCH 03/22] Adjust M-codes for temperature handling for Teacup. #539 --- lib/Slic3r/GCode.pm | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 94b065dec..499502696 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -421,25 +421,34 @@ sub set_temperature { return "" if $wait && $Slic3r::Config->gcode_flavor eq 'makerbot'; - my ($code, $comment) = $wait + my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup') ? ('M109', 'wait for temperature to be reached') : ('M104', 'set temperature'); - return sprintf "$code %s%d %s; $comment\n", + my $gcode = sprintf "$code %s%d %s; $comment\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, (defined $tool && $tool != $self->extruder_idx) ? "T$tool " : ""; + + $gcode .= "M116 ; wait for temperature to be reached\n" + if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait; + + return $gcode; } sub set_bed_temperature { my $self = shift; my ($temperature, $wait) = @_; - my ($code, $comment) = $wait + my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup') ? (($Slic3r::Config->gcode_flavor eq 'makerbot' ? 'M109' - : $Slic3r::Config->gcode_flavor eq 'teacup' ? 'M109 P1' : 'M190'), 'wait for bed temperature to be reached') : ('M140', 'set bed temperature'); - return sprintf "$code %s%d ; $comment\n", + my $gcode = sprintf "$code %s%d ; $comment\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; + + $gcode .= "M116 ; wait for bed temperature to be reached\n" + if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait; + + return $gcode; } 1; From d7e801fd32712244aebc7260f9db39fdaca8bee2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 30 Aug 2012 23:15:42 +0200 Subject: [PATCH 04/22] Releasing 0.9.2 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 19117f4b8..085bd2123 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.2-dev"; +our $VERSION = "0.9.2"; our $debug = 0; sub debugf { From 152c714b879743bb56c3a103862180ab94a39319 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Fri, 31 Aug 2012 00:12:13 +0200 Subject: [PATCH 05/22] Bump version number --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 085bd2123..746176f26 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.2"; +our $VERSION = "0.9.3-dev"; our $debug = 0; sub debugf { From dc0d87f2bb5d13008841771b9a8232748b94bc36 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen <henrik@brixandersen.dk> Date: Sat, 1 Sep 2012 08:36:54 +0200 Subject: [PATCH 06/22] Fix MANIFEST. #656 --- MANIFEST | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST b/MANIFEST index 5a5a80df1..ff89293dd 100644 --- a/MANIFEST +++ b/MANIFEST @@ -15,7 +15,6 @@ lib/Slic3r/Fill/Flowsnake.pm lib/Slic3r/Fill/HilbertCurve.pm lib/Slic3r/Fill/Honeycomb.pm lib/Slic3r/Fill/Line.pm -lib/Slic3r/Fill/Model.pm lib/Slic3r/Fill/OctagramSpiral.pm lib/Slic3r/Fill/PlanePath.pm lib/Slic3r/Fill/Rectilinear.pm @@ -36,6 +35,7 @@ lib/Slic3r/GUI/SkeinPanel.pm lib/Slic3r/GUI/Tab.pm lib/Slic3r/Layer.pm lib/Slic3r/Line.pm +lib/Slic3r/Model.pm lib/Slic3r/Point.pm lib/Slic3r/Polygon.pm lib/Slic3r/Polyline.pm From 47c5d410ea52e2258b3c28a0316b72fce23f1c9f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 6 Sep 2012 10:44:48 +0200 Subject: [PATCH 07/22] Remove the initial retraction compensation. Not only it is not very good for multi-head setups, but it also caused wrong first layer Z when lift was enabled. #655 --- lib/Slic3r/Print.pm | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 397bc9bea..f9aac1505 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -574,15 +574,6 @@ sub write_gcode { print $fh $gcodegen->set_tool(0); print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers; - # this spits out some platic at start from each extruder when they are first used; - # the primary extruder will compensate by the normal retraction length, while - # the others will compensate for their toolchange length + restart extra. - # this is a temporary solution as all extruders should use some kind of skirt - # to be put into a consistent state. - $_->retracted($_->retract_length_toolchange + $_->retract_restart_extra_toolchange) - for @{$Slic3r::extruders}[1 .. $#{$Slic3r::extruders}]; - $gcodegen->retract; - # write start commands to file printf $fh $gcodegen->set_bed_temperature($Slic3r::Config->first_layer_bed_temperature, 1), if $Slic3r::Config->first_layer_bed_temperature && $Slic3r::Config->start_gcode !~ /M190/i; From 3b38e392ad7c3e83fc67dd4c4b6c2bece0ceb11e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 6 Sep 2012 10:56:42 +0200 Subject: [PATCH 08/22] Consider single walls as external perimeters. #661 --- lib/Slic3r/Layer.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index cb47fa484..a6560b66e 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -318,7 +318,7 @@ sub make_perimeters { { my @thin_paths = (); my %properties = ( - role => EXTR_ROLE_PERIMETER, + role => EXTR_ROLE_EXTERNAL_PERIMETER, flow_spacing => $self->perimeter_flow->spacing, ); for (@{ $self->thin_walls }) { From 37ae2e1e2d067c22c47e78a08f6e47ef8ae48f56 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 6 Sep 2012 11:01:44 +0200 Subject: [PATCH 09/22] Use support material extruder for brim. #653 --- lib/Slic3r/Config.pm | 3 +++ lib/Slic3r/Print.pm | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 71363c726..312352312 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -178,6 +178,7 @@ our $Options = { # extruder mapping 'perimeter_extruder' => { label => 'Perimeter extruder', + tooltip => 'The extruder to use when printing perimeters.', cli => 'perimeter-extruder=i', type => 'i', aliases => [qw(perimeters_extruder)], @@ -185,12 +186,14 @@ our $Options = { }, 'infill_extruder' => { label => 'Infill extruder', + tooltip => 'The extruder to use when printing infill.', cli => 'infill-extruder=i', type => 'i', default => 1, }, 'support_material_extruder' => { label => 'Support material extruder', + tooltip => 'The extruder to use when printing support material. This affects brim too.', cli => 'support-material-extruder=i', type => 'i', default => 1, diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f9aac1505..e38b20a71 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -643,6 +643,7 @@ sub write_gcode { # extrude brim if ($layer_id == 0 && !$brim_done) { + $gcode .= $gcodegen->set_tool($Slic3r::Config->support_material_extruder-1); $gcodegen->shift_x($shift[X]); $gcodegen->shift_y($shift[Y]); $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; From 6adf98edafd6d13d76252de6b6b6ca79292f07be Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Thu, 6 Sep 2012 11:28:24 +0200 Subject: [PATCH 10/22] Fix regression causing SVG/DLP support material to crash the application. #668 --- lib/Slic3r/Print.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e38b20a71..d0173a8a4 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -6,7 +6,7 @@ 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); +use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points nearest_point); use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex offset JT_ROUND JT_SQUARE); use Time::HiRes qw(gettimeofday tv_interval); From e89f64e9d313d74b6c1aa3ca0fd9afadaf50eba0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Tue, 11 Sep 2012 18:11:46 +0200 Subject: [PATCH 11/22] More unfinished work --- lib/Slic3r/GUI/Plater.pm | 36 ++++++++++++++++++++++-------------- lib/Slic3r/Model.pm | 2 ++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 712b736b0..9488fc4bc 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -5,7 +5,7 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(max sum); -use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale); +use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2); use Slic3r::Geometry::Clipper qw(JT_ROUND); use threads::shared qw(shared_clone); use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); @@ -368,7 +368,7 @@ sub increase { my ($obj_idx, $object) = $self->selected_object; my $instances = $object->instances; - push @$instances, [ $instances->[-1]->[X] + scale 10, $instances->[-1]->[Y] + scale 10 ]; + push @$instances, [ $instances->[-1]->[X] + 10, $instances->[-1]->[Y] + 10 ]; $self->{list}->SetItem($obj_idx, 1, $object->instances_count); $self->arrange; } @@ -408,7 +408,7 @@ sub changescale { 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", $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; $object->set_scale($scale); @@ -642,8 +642,8 @@ sub make_model { # 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); + my $model_object = $model->add_object(vertices => $mesh->vertices); + $model_object->add_volume(facets => $mesh->facets); for my $copy (@{$self->{print}->copies->[$obj_idx]}) { $object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]); } @@ -683,7 +683,15 @@ sub recenter { # calculate displacement needed to center the print my @print_bb = (0,0,0,0); - @print_bb = if !defined $print_bb[0]; + foreach my $object (@{$self->{objects}}) { + foreach my $instance (@{$object->instances}) { + my @bb = (@$instance, map $instance->[$_] + $object->size->[$_], X,Y); + $print_bb[X1] = $bb[X1] if $bb[X1] < $print_bb[X1]; + $print_bb[Y1] = $bb[Y1] if $bb[Y1] < $print_bb[Y1]; + $print_bb[X2] = $bb[X2] if $bb[X2] > $print_bb[X2]; + $print_bb[Y2] = $bb[Y2] if $bb[Y2] > $print_bb[Y2]; + } + } $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, @@ -727,7 +735,6 @@ sub _update_bed_size { sub repaint { my ($self, $event) = @_; my $parent = $self->GetParent; - my $print = $parent->{print}; my $dc = Wx::PaintDC->new($self); my $size = $self->GetSize; @@ -744,7 +751,7 @@ sub repaint { } # draw print center - if (@{$print->objects}) { + if (@{$parent->{objects}}) { $dc->SetPen($parent->{print_center_pen}); $dc->DrawLine($size[X]/2, 0, $size[X]/2, $size[Y]); $dc->DrawLine(0, $size[Y]/2, $size[X], $size[Y]/2); @@ -760,7 +767,7 @@ sub repaint { $dc->DrawRectangle(0, 0, @size); # draw text if plate is empty - if (!@{$print->objects}) { + if (@{$parent->{objects}}) { $dc->SetTextForeground(Wx::Colour->new(150,50,50)); $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); @@ -769,7 +776,7 @@ sub repaint { # draw thumbnails $dc->SetPen(wxBLACK_PEN); @{$parent->{object_previews}} = (); - for my $obj_idx (0 .. $#{$print->objects}) { + for my $obj_idx (0 .. $#{$parent->{objects}}) { next unless $parent->{thumbnails}[$obj_idx]; for my $copy_idx (0 .. $#{$print->copies->[$obj_idx]}) { my $copy = $print->copies->[$obj_idx][$copy_idx]; @@ -909,12 +916,12 @@ sub statusbar { sub to_pixel { my $self = shift; - return unscale $_[0] * $self->{scaling_factor}; + return $_[0] * $self->{scaling_factor}; } sub to_scaled { my $self = shift; - return scale $_[0] / $self->{scaling_factor}; + return $_[0] / $self->{scaling_factor}; } sub _y { @@ -954,6 +961,7 @@ package Slic3r::GUI::Plater::Object; use Moo; use Math::ConvexHull qw(convex_hull); +use Slic3r::Geometry qw(X Y); has 'name' => (is => 'rw', required => 1); has 'input_file' => (is => 'rw', required => 1); @@ -967,7 +975,7 @@ has 'thumbnail' => (is => 'rw'); sub _trigger_mesh { my $self = shift; - $self->size($mesh->size) if $self->mesh; + $self->size($self->mesh->size) if $self->mesh; } sub instances_count { @@ -978,7 +986,7 @@ sub instances_count { sub make_thumbnail { my $self = shift; - my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices}; + my @points = map [ @$_[X,Y] ], @{$self->mesh->vertices}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); for (@$convex_hull) { @$_ = map $self->to_pixel($_), @$_; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 3a4aec49d..ead574aee 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -59,6 +59,8 @@ has 'attributes' => (is => 'rw', default => sub { {} }); package Slic3r::Model::Object; use Moo; +use Slic3r::Geometry qw(X Y Z); + has 'model' => (is => 'ro', weak_ref => 1, required => 1); has 'vertices' => (is => 'ro', default => sub { [] }); has 'volumes' => (is => 'ro', default => sub { [] }); From d848e2d4c9c84a71a96b8de963671ca2fd266cc5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Tue, 11 Sep 2012 16:02:26 +0200 Subject: [PATCH 12/22] Warn the user when Slic3r is being run under 5.16 --- lib/Slic3r.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 746176f26..f6bd643c4 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -21,6 +21,9 @@ BEGIN { $have_threads = $Config{useithreads} && eval "use threads; use Thread::Queue; 1"; } +warn "Running Slic3r under Perl >= 5.16 is not supported nor recommended\n" + if $^V >= v5.16; + use FindBin; our $var = "$FindBin::Bin/var"; From f314cedd8f63e8c100d8b35ac68af58a1e38fab9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 12:13:43 +0200 Subject: [PATCH 13/22] Bugfix: extrusion width setting for support material was only affecting the path spacing but not the actual flow. #666 --- lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/Fill/Concentric.pm | 2 +- lib/Slic3r/Fill/Honeycomb.pm | 3 ++- lib/Slic3r/Fill/PlanePath.pm | 2 +- lib/Slic3r/Fill/Rectilinear.pm | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index e30eec65f..7d0556f89 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -170,7 +170,7 @@ sub make_fill { ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) : EXTR_ROLE_FILL), depth_layers => $surface->depth_layers, - flow_spacing => $params->{flow_spacing} || $flow_spacing, + flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"), ), @paths, ], ); diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index 94866e2ae..c0d172cfe 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -18,7 +18,7 @@ sub fill_surface { my $min_spacing = scale $params{flow_spacing}; my $distance = $min_spacing / $params{density}; - my $flow_spacing; + my $flow_spacing = $params{flow_spacing}; if ($params{density} == 1) { $distance = $self->adjust_solid_spacing( width => $bounding_box->[X2] - $bounding_box->[X1], diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 229c72568..1e407e2e6 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -94,7 +94,8 @@ sub fill_surface { paths => [ map Slic3r::ExtrusionPath->pack(polyline => $_, role => -1), @paths ], ); - return {}, map $_->polyline, $collection->shortest_path; + return { flow_spacing => $params{flow_spacing} }, + map $_->polyline, $collection->shortest_path; } 1; diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm index daeb13dea..d33b6e6fc 100644 --- a/lib/Slic3r/Fill/PlanePath.pm +++ b/lib/Slic3r/Fill/PlanePath.pm @@ -60,7 +60,7 @@ sub fill_surface { # paths must be rotated back $self->rotate_points_back(\@paths, $rotate_vector); - return {}, @paths; + return { flow_spacing => $params{flow_spacing} }, @paths; } 1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 2de7872a6..bac965c15 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -22,7 +22,7 @@ sub fill_surface { my $distance_between_lines = $min_spacing / $params{density}; my $line_oscillation = $distance_between_lines - $min_spacing; - my $flow_spacing; + my $flow_spacing = $params{flow_spacing}; if ($params{density} == 1) { $distance_between_lines = $self->adjust_solid_spacing( width => $bounding_box->[X2] - $bounding_box->[X1], From e61055774ffa46712f6001a0511cb4c4b8dca255 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen <henrik@brixandersen.dk> Date: Wed, 12 Sep 2012 15:40:55 +0300 Subject: [PATCH 14/22] Move FAQs to the wiki, add link to Documentation --- README.markdown | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/README.markdown b/README.markdown index 46c779a83..1dfe6552b 100644 --- a/README.markdown +++ b/README.markdown @@ -9,8 +9,8 @@ A: Yes. Slic3r is a G-code generator for 3D printers. It's compatible with RepRaps, Makerbots, Ultimakers and many more machines. -See the [project homepage](http://slic3r.org/) at slic3r.org -for more information. +See the [project homepage](http://slic3r.org/) at slic3r.org and the +[documentation](https://github.com/alexrj/Slic3r/wiki/Documentation) on the Slic3r wiki for more information. ## What language is it written in? @@ -275,38 +275,3 @@ If you want to change a preset file, just do If you want to slice a file overriding an option contained in your preset file: slic3r.pl --load config.ini --layer-height 0.25 file.stl - -## How can I integrate Slic3r with Pronterface? - -Put this into *slicecommand*: - - slic3r.pl $s --load config.ini --output $o - -And this into *sliceoptscommand*: - - slic3r.pl --load config.ini --ignore-nonexistent-config - -Replace `slic3r.pl` with the full path to the slic3r executable and `config.ini` -with the full path of your config file (put it in your home directory or where -you like). -On Mac, the executable has a path like this: - - /Applications/Slic3r.app/Contents/MacOS/slic3r - -## How can I specify a custom filename format for output G-code files? - -You can specify a filename format by using any of the config options. -Just enclose them in square brackets, and Slic3r will replace them upon -exporting. -The additional `[input_filename]` and `[input_filename_base]` options will -be replaced by the input file name (in the second case, the .stl extension -is stripped). - -The default format is `[input_filename_base].gcode`, meaning that if you slice -a *foo.stl* file, the output will be saved to *foo.gcode*. - -See below for more complex examples: - - [input_filename_base]_h[layer_height]_p[perimeters]_s[solid_layers].gcode - [input_filename]_center[print_center]_[layer_height]layers.gcode - From 87912cb3b0a93eddbc9d32a8d75ae4d2ec2a76a7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:19:47 +0200 Subject: [PATCH 15/22] Bugfix: only_retract_when_crossing_perimeters was almost not working. #680 --- lib/Slic3r/GCode.pm | 4 ++-- lib/Slic3r/Geometry.pm | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 499502696..4f29fa0b9 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -3,7 +3,7 @@ use Moo; use List::Util qw(first); use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale points_coincide PI X Y); +use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y); has 'layer' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); @@ -135,7 +135,7 @@ sub extrude_path { { my $travel = Slic3r::Line->new($self->last_pos, $path->points->[0]); if ($travel->length >= scale $self->extruder->retract_before_travel) { - if (!$Slic3r::Config->only_retract_when_crossing_perimeters || $path->role != EXTR_ROLE_FILL || !first { $_->expolygon->encloses_line($travel) } @{$self->layer->slices}) { + if (!$Slic3r::Config->only_retract_when_crossing_perimeters || $path->role != EXTR_ROLE_FILL || !first { $_->expolygon->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { $gcode .= $self->retract(travel_to => $path->points->[0]); } } diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 979a2eb0e..dfad17ad0 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -20,6 +20,7 @@ our @EXPORT_OK = qw( shortest_path collinear scale unscale merge_collinear_lines rad2deg_dir bounding_box_center line_intersects_any douglas_peucker polyline_remove_short_segments normal triangle_normal polygon_is_convex + scaled_epsilon ); @@ -38,6 +39,7 @@ use constant MAX => 1; our $parallel_degrees_limit = abs(deg2rad(3)); sub epsilon () { 1E-4 } +sub scaled_epsilon () { epsilon / &Slic3r::SCALING_FACTOR } sub scale ($) { $_[0] / &Slic3r::SCALING_FACTOR } sub unscale ($) { $_[0] * &Slic3r::SCALING_FACTOR } From 4fbb8ffb9d697895a3dfb943925496b3b64a4442 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:22:43 +0200 Subject: [PATCH 16/22] Replace 'scale epsilon' with 'scaled_epsilon' --- lib/Slic3r/Fill/Rectilinear.pm | 8 ++++---- t/arcs.t | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index bac965c15..121249eaa 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -3,7 +3,7 @@ use Moo; extends 'Slic3r::Fill::Base'; -use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale epsilon); +use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon); sub fill_surface { my $self = shift; @@ -36,7 +36,7 @@ sub fill_surface { my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); my @vertical_lines = (); - for (my $i = 0; $x <= $bounding_box->[X2] + scale epsilon; $i++) { + for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); if ($is_line_pattern && $i % 2) { $vertical_line->[A][X] += $line_oscillation; @@ -49,7 +49,7 @@ sub fill_surface { # clip paths against a slightly offsetted expolygon, so that the first and last paths # are kept even if the expolygon has vertical sides my @paths = @{ Boost::Geometry::Utils::polygon_linestring_intersection( - +($expolygon->offset_ex(scale epsilon))[0]->boost_polygon, # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object + +($expolygon->offset_ex(scaled_epsilon))[0]->boost_polygon, # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object Boost::Geometry::Utils::linestring(@vertical_lines), ) }; for (@paths) { @@ -64,7 +64,7 @@ sub fill_surface { ); @paths = (); - my $tolerance = 10 * scale epsilon; + my $tolerance = 10 * scaled_epsilon; my $diagonal_distance = $distance_between_lines * 5; my $can_connect = $is_line_pattern ? sub { diff --git a/t/arcs.t b/t/arcs.t index 1ffcd87b0..972620402 100644 --- a/t/arcs.t +++ b/t/arcs.t @@ -11,7 +11,7 @@ BEGIN { use Slic3r; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(epsilon scale X Y); +use Slic3r::Geometry qw(scaled_epsilon scale X Y); { my $path = Slic3r::ExtrusionPath->new(polyline => Slic3r::Polyline->new( @@ -61,17 +61,17 @@ use Slic3r::Geometry qw(epsilon scale X Y); isa_ok $collection2->paths->[0], 'Slic3r::ExtrusionPath::Arc', 'path'; my $expected_length = scale 7.06858347057701; - ok abs($collection1->paths->[0]->length - $expected_length) < scale epsilon, 'cw oriented arc has correct length'; - ok abs($collection2->paths->[0]->length - $expected_length) < scale epsilon, 'ccw oriented arc has correct length'; + ok abs($collection1->paths->[0]->length - $expected_length) < scaled_epsilon, 'cw oriented arc has correct length'; + ok abs($collection2->paths->[0]->length - $expected_length) < scaled_epsilon, 'ccw oriented arc has correct length'; is $collection1->paths->[0]->orientation, 'cw', 'cw orientation was correctly detected'; is $collection2->paths->[0]->orientation, 'ccw', 'ccw orientation was correctly detected'; my $center1 = [ map sprintf('%.0f', $_), @{ $collection1->paths->[0]->center } ]; - ok abs($center1->[X] - scale 10) < scale epsilon && abs($center1->[Y] - scale 10) < scale epsilon, 'center was correctly detected'; + ok abs($center1->[X] - scale 10) < scaled_epsilon && abs($center1->[Y] - scale 10) < scaled_epsilon, 'center was correctly detected'; my $center2 = [ map sprintf('%.0f', $_), @{ $collection2->paths->[0]->center } ]; - ok abs($center2->[X] - scale 10) < scale epsilon && abs($center1->[Y] - scale 10) < scale epsilon, 'center was correctly detected'; + ok abs($center2->[X] - scale 10) < scaled_epsilon && abs($center1->[Y] - scale 10) < scaled_epsilon, 'center was correctly detected'; } #========================================================== From 757515ba170b2f741528b94428ebf34b04e6afc0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:29:44 +0200 Subject: [PATCH 17/22] New [print_preset], [filament_preset] and [printer_preset] variables. #675 --- lib/Slic3r/GUI/Plater.pm | 2 ++ lib/Slic3r/Print.pm | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9488fc4bc..4996e68ec 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -475,6 +475,8 @@ sub export_gcode { # set this before spawning the thread because ->config needs GetParent and it's not available there $self->{print}->config($self->skeinpanel->config); + $self->{print}->extra_variables->{"${_}_preset"} = $self->skeinpanel->{options_tabs}{$_}->current_preset->{name} + for qw(print filament printer); # select output file $self->{output_file} = $main::opt{output}; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d0173a8a4..868b75846 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -11,6 +11,7 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex offset JT_ROUN use Time::HiRes qw(gettimeofday tv_interval); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }, trigger => 1); +has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'copies' => (is => 'rw', default => sub {[]}); # obj_idx => [copies...] has 'total_extrusion_length' => (is => 'rw'); @@ -808,6 +809,7 @@ sub expanded_output_filepath { return $Slic3r::Config->replace_options($path, { input_filename => $input_filename, input_filename_base => $input_filename_base, + %{ $self->extra_variables }, }); } From ca44cba6c376114a62e830552a2c2c207aa44836 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:40:57 +0200 Subject: [PATCH 18/22] Bugfix: --datadir was ignored. #651 --- slic3r.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slic3r.pl b/slic3r.pl index 234ce5c24..e5c63b99e 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -71,11 +71,11 @@ if ($opt{save}) { # launch GUI my $gui; if (!@ARGV && !$opt{save} && eval "require Slic3r::GUI; 1") { - $gui = Slic3r::GUI->new; { no warnings 'once'; $Slic3r::GUI::datadir = $opt{datadir} if $opt{datadir}; } + $gui = Slic3r::GUI->new; $gui->{skeinpanel}->load_config_file($_) for @{$opt{load}}; $gui->{skeinpanel}->load_config($cli_config); $gui->MainLoop; From f659dcf0888f7e1b3ffc72d08971b1a8896f06fb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:53:24 +0200 Subject: [PATCH 19/22] Patch for --gcode-arcs (kindly submitted by Paul Howes) --- lib/Slic3r/ExtrusionPath.pm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index a006d8712..224cdf4be 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -187,10 +187,14 @@ sub detect_arcs { my $s2_angle = $s2->atan; my $s3_angle = $s3->atan; $s1_angle += 2*PI if $s1_angle < 0; - $s2_angle += 2*PI if $s2_angle < 0; - $s3_angle += 2*PI if $s3_angle < 0; my $s1s2_angle = $s2_angle - $s1_angle; my $s2s3_angle = $s3_angle - $s2_angle; + # allow -ve angles but constrain angles differences to 0<difference<2PI + $s1s2_angle -= 2*PI if ($s1s2_angle > 2*PI); + $s1s2_angle += 2*PI if ($s1s2_angle < -2*PI); + $s2s3_angle -= 2*PI if ($s2s3_angle > 2*PI); + $s2s3_angle += 2*PI if ($s2s3_angle < -2*PI); + next if abs($s1s2_angle - $s2s3_angle) > $Slic3r::Geometry::parallel_degrees_limit; next if abs($s1s2_angle) < $Slic3r::Geometry::parallel_degrees_limit; # ignore parallel lines next if $s1s2_angle > $max_angle; # ignore too sharp vertices @@ -203,10 +207,15 @@ sub detect_arcs { my $line = Slic3r::Line->new($points[$j], $points[$j+1]); last if abs($line->length - $s1_len) > $len_epsilon; my $line_angle = $line->atan; - $line_angle += 2*PI if $line_angle < 0; my $anglediff = $line_angle - $last_line_angle; + # allow -ve angles but constrain angle differences to 0<difference<2PI + $anglediff -= 2*PI if ($anglediff > 2*PI); + $anglediff += 2*PI if ($anglediff < -2*PI); + last if abs($s1s2_angle - $anglediff) > $Slic3r::Geometry::parallel_degrees_limit; - + # Do not try to be too ambitious. Just detect arcs up to 60 degrees. + # The algorithm for finding the center is not accurate enough for more than this + last if abs($s1_angle - $line_angle) > PI/6; # point $j+1 belongs to the arc $arc_points[-1] = $points[$j+1]; $last_j = $j+1; From 8382eeef0a1a18ff728a49019ce24ab5e631dadc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 15:58:01 +0200 Subject: [PATCH 20/22] Revert "Patch for --gcode-arcs (kindly submitted by Paul Howes)" This reverts commit f4260ae93d25df113cbbb9c1995c508c44587d56. --- lib/Slic3r/ExtrusionPath.pm | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 224cdf4be..a006d8712 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -187,14 +187,10 @@ sub detect_arcs { my $s2_angle = $s2->atan; my $s3_angle = $s3->atan; $s1_angle += 2*PI if $s1_angle < 0; + $s2_angle += 2*PI if $s2_angle < 0; + $s3_angle += 2*PI if $s3_angle < 0; my $s1s2_angle = $s2_angle - $s1_angle; my $s2s3_angle = $s3_angle - $s2_angle; - # allow -ve angles but constrain angles differences to 0<difference<2PI - $s1s2_angle -= 2*PI if ($s1s2_angle > 2*PI); - $s1s2_angle += 2*PI if ($s1s2_angle < -2*PI); - $s2s3_angle -= 2*PI if ($s2s3_angle > 2*PI); - $s2s3_angle += 2*PI if ($s2s3_angle < -2*PI); - next if abs($s1s2_angle - $s2s3_angle) > $Slic3r::Geometry::parallel_degrees_limit; next if abs($s1s2_angle) < $Slic3r::Geometry::parallel_degrees_limit; # ignore parallel lines next if $s1s2_angle > $max_angle; # ignore too sharp vertices @@ -207,15 +203,10 @@ sub detect_arcs { my $line = Slic3r::Line->new($points[$j], $points[$j+1]); last if abs($line->length - $s1_len) > $len_epsilon; my $line_angle = $line->atan; + $line_angle += 2*PI if $line_angle < 0; my $anglediff = $line_angle - $last_line_angle; - # allow -ve angles but constrain angle differences to 0<difference<2PI - $anglediff -= 2*PI if ($anglediff > 2*PI); - $anglediff += 2*PI if ($anglediff < -2*PI); - last if abs($s1s2_angle - $anglediff) > $Slic3r::Geometry::parallel_degrees_limit; - # Do not try to be too ambitious. Just detect arcs up to 60 degrees. - # The algorithm for finding the center is not accurate enough for more than this - last if abs($s1_angle - $line_angle) > PI/6; + # point $j+1 belongs to the arc $arc_points[-1] = $points[$j+1]; $last_j = $j+1; From e40f32995f034962a7310dcbaf6ddc6eb1327cab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 12 Sep 2012 16:30:44 +0200 Subject: [PATCH 21/22] Finish GUI decoupling --- lib/Slic3r/Format/AMF.pm | 2 +- lib/Slic3r/Format/AMF/Parser.pm | 4 +- lib/Slic3r/GUI/Plater.pm | 218 +++++++++++++++++++++----------- lib/Slic3r/GUI/SkeinPanel.pm | 4 +- lib/Slic3r/Geometry.pm | 11 +- lib/Slic3r/Model.pm | 61 +++------ lib/Slic3r/Polygon.pm | 4 +- lib/Slic3r/Polyline.pm | 22 +++- lib/Slic3r/Print.pm | 17 ++- lib/Slic3r/TriangleMesh.pm | 20 +++ t/fill.t | 2 +- 11 files changed, 223 insertions(+), 142 deletions(-) diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index 552f1fdbd..78d2656c5 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -72,7 +72,7 @@ sub write_file { foreach my $instance (@{$object->instances}) { $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->[Y]; + $instances .= sprintf qq{ <deltay>%s</deltay>\n}, $instance->offset->[Y]; $instances .= sprintf qq{ <rz>%s</rz>\n}, $instance->rotation; $instances .= sprintf qq{ </instance>\n}; } diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index 93b35765e..5e0b28178 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -107,7 +107,7 @@ sub end_document { foreach my $object_id (keys %{ $self->{_instances} }) { 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"; next; } @@ -115,7 +115,7 @@ sub end_document { foreach my $instance (@{ $self->{_instances}{$object_id} }) { $self->{_model}->objects->[$new_object_id]->add_instance( rotation => $instance->{rz} || 0, - offset => [ $instance->{deltax} || 0, $instance->{deltay} ], + offset => [ $instance->{deltax} || 0, $instance->{deltay} || 0 ], ); } } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4996e68ec..89db0241f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -5,6 +5,7 @@ use utf8; use File::Basename qw(basename dirname); 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::Clipper qw(JT_ROUND); use threads::shared qw(shared_clone); @@ -39,6 +40,8 @@ sub new { $self->{config} = Slic3r::Config->new_from_defaults(qw( 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}->SetBackgroundColour(Wx::wxWHITE); @@ -183,8 +186,6 @@ sub new { }); $self->_update_bed_size; - $self->{objects} = []; - $self->{selected_objects} = []; $self->recenter; { @@ -298,7 +299,7 @@ sub load_file { 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), + name => basename($input_file), input_file => $input_file, input_file_object_id => $i, mesh => $model->objects->[$i]->mesh, @@ -308,8 +309,13 @@ sub load_file { : [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; - $self->object_loaded($#{ $self->{objects} }); + $self->object_loaded($#{ $self->{objects} }, no_arrange => (@{$object->instances} > 1)); } $process_dialog->Destroy; @@ -377,13 +383,18 @@ sub decrease { my $self = shift; my ($obj_idx, $object) = $self->selected_object; - $self->{selected_objects} = [ +(grep { $_->[0] == $obj_idx } @{$self->{object_previews}})[-1] ]; - $self->remove; + if ($object->instances_count >= 2) { + pop @{$object->instances}; + } else { + $self->remove; + } if ($self->{objects}[$obj_idx]) { $self->{list}->Select($obj_idx, 0); $self->{list}->Select($obj_idx, 1); } + $self->recenter; + $self->{canvas}->Refresh; } sub rotate { @@ -397,7 +408,7 @@ sub rotate { return if !$angle || $angle == -1; } - $object->set_rotation($angle); + $object->set_rotation($object->rotate + $angle); $self->recenter; $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); return if !$scale || $scale == -1; - $object->set_scale($scale); + $self->{list}->SetItem($obj_idx, 2, "$scale%"); + $object->set_scale($scale / 100); $self->arrange; } @@ -421,13 +433,16 @@ sub arrange { 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}}); + $size[$a] = $self->to_units(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); + ($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 @@ -440,8 +455,7 @@ sub split_object { 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); + my $mesh = $current_object->get_mesh; my @new_meshes = $mesh->split_mesh; if (@new_meshes == 1) { @@ -455,11 +469,15 @@ sub split_object { $self->remove($obj_idx); foreach my $mesh (@new_meshes) { - my $object = $self->{print}->add_object_from_mesh($mesh); - $object->input_file($current_object->input_file); - my $new_obj_idx = $#{$self->{print}->objects}; - push @{$self->{print}->copies->[$new_obj_idx]}, [0,0] for 2..$current_copies_num; - $self->object_loaded($new_obj_idx, no_arrange => 1); + my $object = Slic3r::GUI::Plater::Object->new( + name => basename($current_object->input_file), + input_file => $current_object->input_file, + input_file_object_id => undef, + mesh => $mesh, + instances => [ map [0,0], 1..$current_copies_num ], + ); + push @{ $self->{objects} }, $object; + $self->object_loaded($#{ $self->{objects} }, no_arrange => 1); } $self->arrange; @@ -473,15 +491,13 @@ sub export_gcode { return; } - # set this before spawning the thread because ->config needs GetParent and it's not available there - $self->{print}->config($self->skeinpanel->config); - $self->{print}->extra_variables->{"${_}_preset"} = $self->skeinpanel->{options_tabs}{$_}->current_preset->{name} - for qw(print filament printer); + # get config before spawning the thread because ->config needs GetParent and it's not available there + my $print = $self->_init_print; # select output file $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}), basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); if ($dlg->ShowModal != wxID_OK) { @@ -496,6 +512,7 @@ sub export_gcode { if ($Slic3r::have_threads) { $self->{export_thread} = threads->create(sub { $self->export_gcode2( + $print, $self->{output_file}, 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([@_]))) }, @@ -516,6 +533,7 @@ sub export_gcode { }); } else { $self->export_gcode2( + $print, $self->{output_file}, progressbar => sub { 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 { my $self = shift; - my ($output_file, %params) = @_; + my ($print, $output_file, %params) = @_; $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; local $SIG{'KILL'} = sub { Slic3r::debugf "Exporting cancelled; exiting thread...\n"; @@ -539,8 +568,8 @@ sub export_gcode2 { } if $Slic3r::have_threads; eval { - my $print = $self->{print}; $print->config->validate; + $print->add_model($self->make_model); $print->validate; { @@ -549,7 +578,6 @@ sub export_gcode2 { my %params = ( output_file => $output_file, status_cb => sub { $params{progressbar}->(@_) }, - keep_meshes => 1, ); if ($params{export_svg}) { $print->export_svg(%params); @@ -621,7 +649,7 @@ sub _get_export_file { 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; 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); @@ -639,18 +667,21 @@ sub make_model { my $self = shift; my $model = Slic3r::Model->new; - 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 $model_object = $model->add_object(vertices => $mesh->vertices); - $model_object->add_volume(facets => $mesh->facets); - for my $copy (@{$self->{print}->copies->[$obj_idx]}) { - $object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]); - } + foreach my $object (@{$self->{objects}}) { + my $mesh = $object->get_mesh; + $mesh->scale($object->scale); + my $model_object = $model->add_object( + vertices => $mesh->vertices, + input_file => $object->input_file, + ); + $model_object->add_volume( + facets => $mesh->facets, + ); + $model_object->add_instance( + rotation => $object->rotate, + offset => [ @$_ ], + ) for @{$object->instances}; } - # TODO: $model->align_to_origin; return $model; } @@ -661,7 +692,7 @@ sub make_thumbnail { my $cb = sub { 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) { 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 { my $self = shift; + return unless @{$self->{objects}}; + # calculate displacement needed to center the print - my @print_bb = (0,0,0,0); - foreach my $object (@{$self->{objects}}) { - foreach my $instance (@{$object->instances}) { - my @bb = (@$instance, map $instance->[$_] + $object->size->[$_], X,Y); - $print_bb[X1] = $bb[X1] if $bb[X1] < $print_bb[X1]; - $print_bb[Y1] = $bb[Y1] if $bb[Y1] < $print_bb[Y1]; - $print_bb[X2] = $bb[X2] if $bb[X2] > $print_bb[X2]; - $print_bb[Y2] = $bb[Y2] if $bb[Y2] > $print_bb[Y2]; - } - } + my @print_bb = Slic3r::Geometry::bounding_box([ + map { + my $obj = $_; + map { + my $instance = $_; + $instance, [ map $instance->[$_] + $obj->rotated_size->[$_], X,Y ]; + } @{$obj->instances}; + } @{$self->{objects}}, + ]); $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, + ($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, ]; } @@ -729,7 +761,6 @@ sub _update_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 $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; } @@ -769,7 +800,7 @@ sub repaint { $dc->DrawRectangle(0, 0, @size); # draw text if plate is empty - if (@{$parent->{objects}}) { + if (!@{$parent->{objects}}) { $dc->SetTextForeground(Wx::Colour->new(150,50,50)); $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); @@ -779,14 +810,15 @@ sub repaint { $dc->SetPen(wxBLACK_PEN); @{$parent->{object_previews}} = (); for my $obj_idx (0 .. $#{$parent->{objects}}) { - next unless $parent->{thumbnails}[$obj_idx]; - for my $copy_idx (0 .. $#{$print->copies->[$obj_idx]}) { - my $copy = $print->copies->[$obj_idx][$copy_idx]; - push @{$parent->{object_previews}}, [ $obj_idx, $copy_idx, $parent->{thumbnails}[$obj_idx]->clone ]; - $parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($copy->[$_]) + $parent->{shift}[$_], (X,Y)); + my $object = $parent->{objects}[$obj_idx]; + next unless $object->thumbnail; + for my $instance_idx (0 .. $#{$object->instances}) { + my $instance = $object->instances->[$instance_idx]; + 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}; - 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}); } elsif (grep { $_->[0] == $obj_idx } @{$parent->{selected_objects}}) { $dc->SetBrush($parent->{selected_brush}); @@ -796,7 +828,7 @@ sub repaint { $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); # 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]; $dc->SetPen($parent->{clearance_pen}); $dc->SetBrush($parent->{transparent_brush}); @@ -820,7 +852,6 @@ sub repaint { sub mouse_event { my ($self, $event) = @_; my $parent = $self->GetParent; - my $print = $parent->{print}; my $point = $event->GetPosition; my $pos = $parent->_y([[$point->x, $point->y]])->[0]; #]] @@ -829,12 +860,13 @@ sub mouse_event { $parent->{list}->Select($parent->{list}->GetFirstSelected, 0); $parent->selection_changed(0); for my $preview (@{$parent->{object_previews}}) { - if ($preview->[2]->encloses_point($pos)) { - $parent->{selected_objects} = [$preview]; - $parent->{list}->Select($preview->[0], 1); + my ($obj_idx, $instance_idx, $thumbnail) = @$preview; + if ($thumbnail->encloses_point($pos)) { + $parent->{selected_objects} = [ [$obj_idx, $instance_idx] ]; + $parent->{list}->Select($obj_idx, 1); $parent->selection_changed(1); - my $copy = $print->copies->[ $preview->[0] ]->[ $preview->[1] ]; - $self->{drag_start_pos} = [ map $pos->[$_] - $parent->{shift}[$_] - $parent->to_pixel($copy->[$_]), X,Y ]; # displacement between the click and the copy's origin + my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx]; + $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; } } @@ -847,9 +879,11 @@ sub mouse_event { $self->SetCursor(wxSTANDARD_CURSOR); } elsif ($event->Dragging) { return if !$self->{drag_start_pos}; # concurrency problems - for my $obj ($self->{drag_object}) { - my $copy = $print->copies->[ $obj->[0] ]->[ $obj->[1] ]; - $copy->[$_] = $parent->to_scaled($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y; + for my $preview ($self->{drag_object}) { + my ($obj_idx, $instance_idx, $thumbnail) = @$preview; + 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; } } elsif ($event->Moving) { @@ -886,7 +920,7 @@ sub list_item_selected { sub object_list_changed { my $self = shift; - my $method = $self->{print} && @{$self->{print}->objects} ? 'Enable' : 'Disable'; + my $method = @{$self->{objects}} ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl); } @@ -921,7 +955,7 @@ sub to_pixel { return $_[0] * $self->{scaling_factor}; } -sub to_scaled { +sub to_units { my $self = shift; return $_[0] / $self->{scaling_factor}; } @@ -967,17 +1001,25 @@ use Slic3r::Geometry qw(X Y); has 'name' => (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 'size' => (is => 'rw'); has 'scale' => (is => 'rw', default => sub { 1 }); 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'); sub _trigger_mesh { 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 { @@ -987,16 +1029,21 @@ sub instances_count { sub make_thumbnail { my $self = shift; + my %params = @_; my @points = map [ @$_[X,Y] ], @{$self->mesh->vertices}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); for (@$convex_hull) { - @$_ = map $self->to_pixel($_), @$_; + @$_ = map $_ * $params{scaling_factor}, @$_; } $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->mesh(undef); + $self->mesh(undef) if defined $self->input_file_object_id; + return $convex_hull; } @@ -1004,7 +1051,10 @@ sub set_rotation { my $self = shift; 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); } @@ -1012,8 +1062,22 @@ sub set_scale { my $self = shift; 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); } +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; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index babd1cfbc..6ef5a64de 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -57,8 +57,8 @@ sub do_slice { $config->validate; # confirm slicing of more than one copies - my $copies = $Slic3r::Config->duplicate_grid->[X] * $Slic3r::Config->duplicate_grid->[Y]; - $copies = $Slic3r::Config->duplicate if $Slic3r::Config->duplicate > 1; + my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y]; + $copies = $config->duplicate if $config->duplicate > 1; if ($copies > 1) { my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", 'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index dfad17ad0..cd1e59b32 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -882,7 +882,7 @@ sub douglas_peucker2 { } sub arrange { - my ($total_parts, $partx, $party, $areax, $areay, $dist) = @_; + my ($total_parts, $partx, $party, $areax, $areay, $dist, $Config) = @_; my $linint = sub { my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_; @@ -895,8 +895,13 @@ sub arrange { # margin needed for the skirt my $skirt_margin; - if ($Slic3r::Config->skirts > 0) { - $skirt_margin = ($Slic3r::flow->spacing * $Slic3r::Config->skirts + $Slic3r::Config->skirt_distance) * 2; + if ($Config->skirts > 0) { + 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 { $skirt_margin = 0; } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index ead574aee..2d7a33155 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -30,24 +30,21 @@ sub add_object { sub mesh { my $self = shift; - my $vertices = []; - my $facets = []; + my @meshes = (); foreach my $object (@{$self->objects}) { - my $mesh = $object->mesh; - - my $v_offset = @$vertices; - push @$vertices, @{$mesh->vertices}; - push @$facets, map { - my $f = [@$_]; - $f->[$_] += $v_offset for -3..-1; - $f; - } @{$mesh->facets}; + my @instances = $object->instances ? @{$object->instances} : (undef); + foreach my $instance (@instances) { + my $mesh = $object->mesh->clone; + if ($instance) { + $mesh->rotate($instance->rotation); + $mesh->align_to_origin; + $mesh->move(@{$instance->offset}); + } + push @meshes, $mesh; + } } - return Slic3r::TriangleMesh->new( - vertices => $vertices, - facets => $facets, - ); + return Slic3r::TriangleMesh->merge(@meshes); } package Slic3r::Model::Material; @@ -61,6 +58,7 @@ use Moo; use Slic3r::Geometry qw(X Y Z); +has 'input_file' => (is => 'rw'); has 'model' => (is => 'ro', weak_ref => 1, required => 1); has 'vertices' => (is => 'ro', default => sub { [] }); has 'volumes' => (is => 'ro', default => sub { [] }); @@ -88,38 +86,11 @@ sub mesh { 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, + vertices => $self->vertices, + facets => [ map @{$_->facets}, @{$self->volumes} ], ); } diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index ba09dcc2d..ee6f193d9 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -6,7 +6,7 @@ use warnings; use parent 'Slic3r::Polyline'; 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); sub lines { @@ -111,7 +111,7 @@ sub is_printable { # detect them and we would be discarding them. my $p = $self->clone; $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 { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index ce13e7786..474b55d94 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -4,7 +4,7 @@ use warnings; use Math::Clipper qw(); 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); # the constructor accepts an array(ref) of points @@ -144,7 +144,13 @@ sub size { my $self = shift; 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 { @@ -163,4 +169,16 @@ sub translate { 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; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 868b75846..8accf1dd9 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -108,11 +108,11 @@ sub add_model { $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) { # 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 { my $self = shift; - my ($mesh) = @_; + my ($mesh, %attributes) = @_; $mesh->rotate($Slic3r::Config->rotate); $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR); @@ -131,6 +131,7 @@ sub add_object_from_mesh { my $object = Slic3r::Print::Object->new( mesh => $mesh, size => [ $mesh->size ], + %attributes, ); push @{$self->objects}, $object; @@ -236,7 +237,7 @@ sub arrange_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 $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}) { @{$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; } -# 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 sub expanded_output_filepath { 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 # the specified filename format @@ -800,7 +804,6 @@ sub expanded_output_filepath { # if no explicit output file was defined, we take the input # 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; my $input_filename = my $input_filename_base = basename($input_file); diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 6f6589450..b17148fa8 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -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 { my $self = shift; return (ref $self)->new( diff --git a/t/fill.t b/t/fill.t index 2a7ef1f93..dd8cdf938 100644 --- a/t/fill.t +++ b/t/fill.t @@ -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] ]), ); 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); is scalar @paths, 1, 'one continuous path'; } From f638558167c3a8cf0b7fef62f560c3e2b1bdedef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Fri, 21 Sep 2012 15:35:32 +0200 Subject: [PATCH 22/22] Preserve position when splitting objects in plater --- lib/Slic3r/GUI/Plater.pm | 9 ++++++--- lib/Slic3r/TriangleMesh.pm | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 89db0241f..5f83117c4 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -6,7 +6,7 @@ use utf8; use File::Basename qw(basename dirname); 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 MIN MAX); use Slic3r::Geometry::Clipper qw(JT_ROUND); use threads::shared qw(shared_clone); use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); @@ -456,6 +456,7 @@ sub split_object { my ($obj_idx, $current_object) = $self->selected_object; my $current_copies_num = $current_object->instances_count; my $mesh = $current_object->get_mesh; + $mesh->align_to_origin; my @new_meshes = $mesh->split_mesh; if (@new_meshes == 1) { @@ -469,18 +470,20 @@ sub split_object { $self->remove($obj_idx); foreach my $mesh (@new_meshes) { + my @extents = $mesh->extents; my $object = Slic3r::GUI::Plater::Object->new( name => basename($current_object->input_file), input_file => $current_object->input_file, input_file_object_id => undef, mesh => $mesh, - instances => [ map [0,0], 1..$current_copies_num ], + instances => [ map [$extents[X][MIN], $extents[Y][MIN]], 1..$current_copies_num ], ); push @{ $self->{objects} }, $object; $self->object_loaded($#{ $self->{objects} }, no_arrange => 1); } - $self->arrange; + $self->recenter; + $self->{canvas}->Refresh; } sub export_gcode { diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index b17148fa8..7f9d929b1 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -355,7 +355,7 @@ sub align_to_origin { # calculate the displacements needed to # have lowest value for each axis at coordinate 0 - my @extents = $self->bounding_box; + my @extents = $self->extents; $self->move(map -$extents[$_][MIN], X,Y,Z); } @@ -379,7 +379,7 @@ sub duplicate { $self->BUILD; } -sub bounding_box { +sub extents { my $self = shift; my @extents = (map [undef, undef], X,Y,Z); foreach my $vertex (@{$self->vertices}) { @@ -394,7 +394,7 @@ sub bounding_box { sub size { my $self = shift; - my @extents = $self->bounding_box; + my @extents = $self->extents; return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y,Z); }