From af1b64a08611c09790fd49e383506becfa2909b1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 14:59:34 +0200 Subject: [PATCH 01/31] Added unit testing to prevent regression about disconnected infill paths --- lib/Slic3r/Polyline.pm | 2 ++ t/fill.t | 33 ++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 786957dab..da9cc0479 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -144,6 +144,7 @@ sub rotate { my $self = shift; my ($angle, $center) = @_; @$self = Slic3r::Geometry::rotate_points($angle, $center, @$self); + bless $_, 'Slic3r::Point' for @$self; return $self; } @@ -151,6 +152,7 @@ sub translate { my $self = shift; my ($x, $y) = @_; @$self = Slic3r::Geometry::move_points([$x, $y], @$self); + bless $_, 'Slic3r::Point' for @$self; return $self; } diff --git a/t/fill.t b/t/fill.t index f45aab2da..0e725698a 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 2; +plan tests => 4; BEGIN { use FindBin; @@ -10,13 +10,16 @@ BEGIN { } use Slic3r; +use Slic3r::Geometry qw(scale X Y); +use Slic3r::Surface qw(:types); -my $print = Slic3r::Print->new( - x_length => 50, - y_length => 50, -); +sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { + my $print = Slic3r::Print->new( + x_length => 50, + y_length => 50, + ); my $filler = Slic3r::Fill::Rectilinear->new(print => $print); my $surface_width = 250; my $distance = $filler->adjust_solid_spacing( @@ -27,4 +30,24 @@ my $print = Slic3r::Print->new( is $surface_width % $distance, 0, 'adjusted solid distance'; } +{ + my $print = Slic3r::Print->new( + x_length => scale 100, + y_length => scale 100, + ); + my $filler = Slic3r::Fill::Rectilinear->new( + print => $print, + max_print_dimension => scale 100, + ); + my $surface = Slic3r::Surface->new( + surface_type => S_TYPE_TOP, + 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]); + my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); + is scalar @paths, 1, 'one continuous path'; + } +} + __END__ From 0c22250740986a702cdf3d9d19db51ca423ce1bd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 16:30:11 +0200 Subject: [PATCH 02/31] New option to disable retraction when moving between infill paths inside the same island. #29 --- README.markdown | 3 +++ lib/Slic3r/Config.pm | 7 +++++++ lib/Slic3r/GCode.pm | 14 ++++++-------- lib/Slic3r/GUI/Tab.pm | 2 +- slic3r.pl | 3 +++ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/README.markdown b/README.markdown index a335f0cb9..46c779a83 100644 --- a/README.markdown +++ b/README.markdown @@ -170,6 +170,9 @@ The author of the Silk icon set is Mark James. --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) + --only-retract-when-crossing-perimeters + Disable retraction when travelling between infill paths inside the same island. + (default: no) --solid-infill-below-area Force solid infill when a region has a smaller area than this threshold (mm^2, default: 70) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 36aaa5ae7..97992d5b4 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -470,6 +470,13 @@ our $Options = { type => 'bool', default => 1, }, + 'only_retract_when_crossing_perimeters' => { + label => 'Only retract when crossing perimeters', + tooltip => 'Disables retraction when travelling between infill paths inside the same island.', + cli => 'only-retract-when-crossing-perimeters!', + type => 'bool', + default => 0, + }, 'support_material' => { label => 'Generate support material', tooltip => 'Enable support material generation.', diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b5e020ace..ae6d63c6b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -132,15 +132,13 @@ sub extrude_path { my $gcode = ""; # retract if distance from previous position is greater or equal to the one - # specified by the user *and* to the maximum distance between infill lines + # specified by the user { - my $distance_from_last_pos = $self->last_pos->distance_to($path->points->[0]) * &Slic3r::SCALING_FACTOR; - my $distance_threshold = $self->extruder->retract_before_travel; - $distance_threshold = 2 * ($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) / $Slic3r::Config->fill_density * sqrt(2) - if 0 && $Slic3r::Config->fill_density > 0 && $description =~ /fill/; - - if ($distance_from_last_pos >= $distance_threshold) { - $gcode .= $self->retract(travel_to => $path->points->[0]); + 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}) { + $gcode .= $self->retract(travel_to => $path->points->[0]); + } } } diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index cd68593ae..16107b548 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -401,7 +401,7 @@ sub build { }, { title => 'Advanced', - options => [qw(infill_every_layers fill_angle solid_infill_below_area)], + options => [qw(infill_every_layers fill_angle solid_infill_below_area only_retract_when_crossing_perimeters)], }, ]); diff --git a/slic3r.pl b/slic3r.pl index 0f9baa58c..2e9c87072 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -213,6 +213,9 @@ $j --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) + --only-retract-when-crossing-perimeters + Disable retraction when travelling between infill paths inside the same island. + (default: no) --solid-infill-below-area Force solid infill when a region has a smaller area than this threshold (mm^2, default: $config->{solid_infill_below_area}) From 873ea938329fd0e3a51d2a34c4f39e1588257c12 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 17:18:12 +0200 Subject: [PATCH 03/31] Fix missing module inclusion preventing the recently introduced option from working --- lib/Slic3r/GCode.pm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index ae6d63c6b..94b065dec 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -1,8 +1,9 @@ package Slic3r::GCode; use Moo; +use List::Util qw(first); use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale); +use Slic3r::Geometry qw(scale unscale points_coincide PI X Y); has 'layer' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); @@ -48,8 +49,6 @@ my %role_speeds = ( &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', ); -use Slic3r::Geometry qw(points_coincide PI X Y); - sub extruder { my $self = shift; return $Slic3r::extruders->[$self->extruder_idx]; From fe6c8fabdb11f275dbf9edc6b32fdc846b10fc7c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 19:06:14 +0200 Subject: [PATCH 04/31] Make Josef Prusa and Petr Ledvinka happy while we figure out how to handle localized Windows file paths --- lib/Slic3r/GUI.pm | 2 +- slic3r.pl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 8bd7fa684..09d1071ea 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -47,7 +47,7 @@ sub OnInit { $self->{notifier} = Slic3r::GUI::Notifier->new; # locate or create data directory - $datadir = Wx::StandardPaths::Get->GetUserDataDir; + $datadir ||= Wx::StandardPaths::Get->GetUserDataDir; Slic3r::debugf "Data directory: %s\n", $datadir; my $run_wizard = (-d $datadir) ? 0 : 1; for ($datadir, "$datadir/print", "$datadir/filament", "$datadir/printer") { diff --git a/slic3r.pl b/slic3r.pl index 2e9c87072..cc0af0a60 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -27,6 +27,7 @@ my %cli_options = (); 'save=s' => \$opt{save}, 'load=s@' => \$opt{load}, 'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config}, + 'datadir=s' => \$opt{datadir}, 'export-svg' => \$opt{export_svg}, 'merge|m' => \$opt{merge}, ); @@ -71,6 +72,10 @@ if ($opt{save}) { 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->{skeinpanel}->load_config_file($_) for @{$opt{load}}; $gui->{skeinpanel}->load_config($cli_config); $gui->MainLoop; From 896641cb7e66f99bf652025bd84edd6c92978c78 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 20:04:29 +0200 Subject: [PATCH 05/31] Fixed regression causing some bridges not to be detected correctly. #629 --- lib/Slic3r/ExPolygon.pm | 21 ++++++--------------- lib/Slic3r/Geometry/Clipper.pm | 19 +++++++++++++++++-- lib/Slic3r/Layer.pm | 19 ++++++++++++++++++- lib/Slic3r/Polygon.pm | 8 +------- lib/Slic3r/Print.pm | 2 +- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 5f9d23577..e5f96d381 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -61,13 +61,12 @@ sub boost_polygon { sub offset { my $self = shift; - my ($distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= &Slic3r::SCALING_FACTOR * 1000000; - $joinType = JT_MITER if !defined $joinType; - $miterLimit ||= 2; - - my $offsets = Math::Clipper::offset($self, $distance, $scale, $joinType, $miterLimit); - return @$offsets; + return Slic3r::Geometry::Clipper::offset($self, @_); +} + +sub offset_ex { + my $self = shift; + return Slic3r::Geometry::Clipper::offset_ex($self, @_); } sub safety_offset { @@ -83,14 +82,6 @@ sub safety_offset { ); } -sub offset_ex { - my $self = shift; - my @offsets = $self->offset(@_); - - # apply holes to the right contours - return @{ union_ex(\@offsets) }; -} - sub encloses_point { my $self = shift; my ($point) = @_; diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index a76048fc4..84c57dcf6 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -4,11 +4,11 @@ use warnings; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset +our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset offset_ex diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND JT_SQUARE is_counter_clockwise); -use Math::Clipper 1.09 ':all'; +use Math::Clipper 1.09 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); use Slic3r::Geometry qw(scale); our $clipper = Math::Clipper->new; @@ -27,6 +27,21 @@ sub safety_offset { return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100, JT_MITER, 2); } +sub offset { + my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; + $scale ||= &Slic3r::SCALING_FACTOR * 1000000; + $joinType = JT_MITER if !defined $joinType; + $miterLimit ||= 2; + + my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit); + return @$offsets; +} + +sub offset_ex { + # offset polygons and then apply holes to the right contours + return @{ union_ex([ offset(@_) ]) }; +} + sub diff_ex { my ($subject, $clip, $safety_offset) = @_; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index fc33cf420..cb47fa484 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -358,13 +358,30 @@ sub prepare_fill_surfaces { if ($Slic3r::Config->fill_density == 0) { @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; } + + # remove unprintable regions (they would slow down the infill process and also cause + # some weird failures during bridge neighbor detection) + { + my $distance = scale $self->infill_flow->spacing / 2; + @surfaces = map { + my $surface = $_; + + # offset inwards + my @offsets = $surface->expolygon->offset_ex(-$distance); + @offsets = @{union_ex(Math::Clipper::offset([ map @$_, @offsets ], $distance, 100, JT_MITER))}; + map Slic3r::Surface->new( + expolygon => $_, + surface_type => $surface->surface_type, + ), @offsets; + } @surfaces; + } # turn too small internal regions into solid regions { my $min_area = scale scale $Slic3r::Config->solid_infill_below_area; # scaling an area requires two calls! my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @surfaces; $_->surface_type(S_TYPE_INTERNALSOLID) for @small; - Slic3r::debugf "identified %d small surfaces at layer %d\n", scalar(@small), $self->id if @small > 0; + Slic3r::debugf "identified %d small solid surfaces at layer %d\n", scalar(@small), $self->id if @small > 0; } $self->fill_surfaces([@surfaces]); diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index fa7ee712d..ba09dcc2d 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -71,13 +71,7 @@ sub safety_offset { sub offset { my $self = shift; - my ($distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= &Slic3r::SCALING_FACTOR * 1000000; - $joinType = JT_MITER if !defined $joinType; - $miterLimit ||= 2; - - my $offsets = Math::Clipper::offset([$self], $distance, $scale, $joinType, $miterLimit); - return map Slic3r::Polygon->new($_), @$offsets; + return map Slic3r::Polygon->new($_), Slic3r::Geometry::Clipper::offset([$self], @_); } # this method subdivides the polygon segments to that no one of them diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index c5aec01a7..487a5c312 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -497,7 +497,7 @@ sub make_skirt { my @skirt = (); for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) { my $distance = scale ($Slic3r::Config->skirt_distance + ($flow->spacing * $i)); - my $outline = offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND); + my $outline = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND); push @skirt, Slic3r::ExtrusionLoop->pack( polygon => Slic3r::Polygon->new(@{$outline->[0]}), role => EXTR_ROLE_SKIRT, From a1ac866b84414f5f20bf8c767345f0cbce96f730 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 20:14:01 +0200 Subject: [PATCH 06/31] Refactoring: turn x_length and y_length in size --- lib/Slic3r/GUI/Plater.pm | 8 ++------ lib/Slic3r/Print.pm | 20 +++++++++----------- lib/Slic3r/Print/Object.pm | 3 +-- t/fill.t | 12 ++---------- 4 files changed, 14 insertions(+), 29 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 7155f21ee..9c6a900da 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -419,9 +419,7 @@ sub rotate { # rotate, realign to 0,0 and update size $object->mesh->rotate($angle); $object->mesh->align_to_origin; - my @size = $object->mesh->size; - $object->x_length($size[X]); - $object->y_length($size[Y]); + $object->size([ $object->mesh->size ]); $self->make_thumbnail($obj_idx); $self->recenter; @@ -458,9 +456,7 @@ sub changescale { my $mesh = $object->mesh; $mesh->scale($scale/100 / $self->{scale}[$obj_idx]); $object->mesh->align_to_origin; - my @size = $object->mesh->size; - $object->x_length($size[X]); - $object->y_length($size[Y]); + $object->size([ $object->mesh->size ]); $self->{scale}[$obj_idx] = $scale/100; $self->{list}->SetItem($obj_idx, 2, "$scale%"); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 487a5c312..f015416de 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -112,11 +112,9 @@ sub add_object_from_mesh { $mesh->align_to_origin; # initialize print object - my @size = $mesh->size; my $object = Slic3r::Print::Object->new( - mesh => $mesh, - x_length => $size[X], - y_length => $size[Y], + mesh => $mesh, + size => [ $mesh->size ], ); push @{$self->objects}, $object; @@ -201,8 +199,8 @@ sub duplicate { for my $x_copy (1..$Slic3r::Config->duplicate_grid->[X]) { for my $y_copy (1..$Slic3r::Config->duplicate_grid->[Y]) { push @{$self->copies->[0]}, [ - ($object->x_length + $dist) * ($x_copy-1), - ($object->y_length + $dist) * ($y_copy-1), + ($object->size->[X] + $dist) * ($x_copy-1), + ($object->size->[Y] + $dist) * ($y_copy-1), ]; } } @@ -220,8 +218,8 @@ sub arrange_objects { my $total_parts = scalar map @$_, @{$self->copies}; my $partx = my $party = 0; foreach my $object (@{$self->objects}) { - $partx = $object->x_length if $object->x_length > $partx; - $party = $object->y_length if $object->y_length > $party; + $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) @@ -246,9 +244,9 @@ sub bounding_box { foreach my $copy (@{$self->copies->[$obj_idx]}) { push @points, [ $copy->[X], $copy->[Y] ], - [ $copy->[X] + $object->x_length, $copy->[Y] ], - [ $copy->[X] + $object->x_length, $copy->[Y] + $object->y_length ], - [ $copy->[X], $copy->[Y] + $object->y_length ]; + [ $copy->[X] + $object->size->[X], $copy->[Y] ], + [ $copy->[X] + $object->size->[X], $copy->[Y] + $object->size->[Y] ], + [ $copy->[X], $copy->[Y] + $object->size->[Y] ]; } } return Slic3r::Geometry::bounding_box(\@points); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index c06358b83..696d72ca3 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -8,8 +8,7 @@ use Slic3r::Surface ':types'; has 'input_file' => (is => 'rw', required => 0); has 'mesh' => (is => 'rw', required => 0); -has 'x_length' => (is => 'rw', required => 1); -has 'y_length' => (is => 'rw', required => 1); +has 'size' => (is => 'rw', required => 1); has 'layers' => ( traits => ['Array'], diff --git a/t/fill.t b/t/fill.t index 0e725698a..2a7ef1f93 100644 --- a/t/fill.t +++ b/t/fill.t @@ -16,11 +16,7 @@ use Slic3r::Surface qw(:types); sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { - my $print = Slic3r::Print->new( - x_length => 50, - y_length => 50, - ); - my $filler = Slic3r::Fill::Rectilinear->new(print => $print); + my $filler = Slic3r::Fill::Rectilinear->new(print => Slic3r::Print->new); my $surface_width = 250; my $distance = $filler->adjust_solid_spacing( width => $surface_width, @@ -31,12 +27,8 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } } { - my $print = Slic3r::Print->new( - x_length => scale 100, - y_length => scale 100, - ); my $filler = Slic3r::Fill::Rectilinear->new( - print => $print, + print => Slic3r::Print->new, max_print_dimension => scale 100, ); my $surface = Slic3r::Surface->new( From a1a4d97f9f0866d1edf1dc97d1fed425f6780f49 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 20:14:52 +0200 Subject: [PATCH 07/31] Remove useless subroutines --- lib/Slic3r/Geometry/Clipper.pm | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 84c57dcf6..5a1dfdd91 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -4,7 +4,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset offset_ex +our @EXPORT_OK = qw(safety_offset offset offset_ex diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND JT_SQUARE is_counter_clockwise); @@ -12,16 +12,6 @@ use Math::Clipper 1.09 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockw use Slic3r::Geometry qw(scale); our $clipper = Math::Clipper->new; -sub explode_expolygon { - my ($expolygon) = @_; - return ($expolygon->{outer}, @{ $expolygon->{holes} }); -} - -sub explode_expolygons { - my ($expolygons) = @_; - return map explode_expolygon($_), @$expolygons; -} - sub safety_offset { my ($polygons, $factor) = @_; return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100, JT_MITER, 2); From 08270022dd9bebfb199448db3acc631e9049d133 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 20:40:44 +0200 Subject: [PATCH 08/31] Refactoring: initialize all layers at once and avoid duplication of slicing height math --- lib/Slic3r/Print.pm | 2 +- lib/Slic3r/Print/Object.pm | 41 ++++++++++++++++++++------------------ lib/Slic3r/TriangleMesh.pm | 6 ++---- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f015416de..e701bd1c4 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -477,7 +477,7 @@ sub make_skirt { $skirt_height = $self->layer_count if $skirt_height > $self->layer_count; my @points = (); foreach my $obj_idx (0 .. $#{$self->objects}) { - my @layers = map $self->objects->[$obj_idx]->layer($_), 0..($skirt_height-1); + my @layers = map $self->objects->[$obj_idx]->layers->[$_], 0..($skirt_height-1); my @layer_points = ( (map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers), (map @$_, map @{$_->thin_walls}, @layers), diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 696d72ca3..d8f2c531b 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -2,39 +2,42 @@ package Slic3r::Print::Object; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale deg2rad); +use Slic3r::Geometry qw(Z scale unscale deg2rad); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); use Slic3r::Surface ':types'; has 'input_file' => (is => 'rw', required => 0); has 'mesh' => (is => 'rw', required => 0); has 'size' => (is => 'rw', required => 1); +has 'layers' => (is => 'rw', default => sub { [] } ); -has 'layers' => ( - traits => ['Array'], - is => 'rw', - #isa => 'ArrayRef[Slic3r::Layer]', - default => sub { [] }, -); +sub BUILD { + my $self = shift; + + # make layers + while (!@{$self->layers} || $self->layers->[-1]->slice_z < $self->size->[Z]) { + push @{$self->layers}, Slic3r::Layer->new(id => $#{$self->layers} + 1); + } +} sub layer_count { my $self = shift; return scalar @{ $self->layers }; } -sub layer { +sub get_layer_range { my $self = shift; - my ($layer_id) = @_; + my ($min_z, $max_z) = @_; - # extend our print by creating all necessary layers - - if ($self->layer_count < $layer_id + 1) { - for (my $i = $self->layer_count; $i <= $layer_id; $i++) { - push @{ $self->layers }, Slic3r::Layer->new(id => $i); + my ($min_layer, $max_layer) = (0, undef); + for my $layer (@{$self->layers}) { + $min_layer = $layer->id if $layer->slice_z <= $min_z; + if ($layer->slice_z >= $max_z) { + $max_layer = $layer->id; + last; } } - - return $self->layers->[$layer_id]; + return ($min_layer, $max_layer); } sub slice { @@ -46,7 +49,7 @@ sub slice { my $apply_lines = sub { my $lines = shift; foreach my $layer_id (keys %$lines) { - my $layer = $self->layer($layer_id); + my $layer = $self->layers->[$layer_id]; push @{$layer->lines}, @{$lines->{$layer_id}}; } }; @@ -399,7 +402,7 @@ sub combine_infill { # start from bottom, skip first layer for (my $i = 1; $i < $self->layer_count; $i++) { - my $layer = $self->layer($i); + my $layer = $self->layers->[$i]; # skip layer if no internal fill surfaces next if !grep $_->surface_type == S_TYPE_INTERNAL, @{$layer->fill_surfaces}; @@ -408,7 +411,7 @@ sub combine_infill { # we do this from the greater depth to the smaller for (my $d = $Slic3r::Config->infill_every_layers - 1; $d >= 1; $d--) { next if ($i - $d) < 0; - my $lower_layer = $self->layer($i - 1); + my $lower_layer = $self->layers->[$i - 1]; # select surfaces of the lower layer having the depth we're looking for my @lower_surfaces = grep $_->depth_layers == $d && $_->surface_type == S_TYPE_INTERNAL, diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 6f6589450..b48f91981 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -401,14 +401,12 @@ sub slice_facet { } # calculate the layer extents - my $min_layer = int((unscale($min_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) - 2; - $min_layer = 0 if $min_layer < 0; - my $max_layer = int((unscale($max_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) + 2; + my ($min_layer, $max_layer) = $print_object->get_layer_range($min_z, $max_z); Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer; my $lines = {}; # layer_id => [ lines ] for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) { - my $layer = $print_object->layer($layer_id); + my $layer = $print_object->layers->[$layer_id]; $lines->{$layer_id} ||= []; push @{ $lines->{$layer_id} }, $self->intersect_facet($facet_id, $layer->slice_z); } From 9bd1b0f6ba29f5e9a4d4b6317b5e7c155a623c50 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 26 Aug 2012 17:47:00 +0200 Subject: [PATCH 09/31] Revert "Refactoring: initialize all layers at once and avoid duplication of slicing height math". #637 This reverts commit 08270022dd9bebfb199448db3acc631e9049d133. --- lib/Slic3r/Print.pm | 2 +- lib/Slic3r/Print/Object.pm | 41 ++++++++++++++++++-------------------- lib/Slic3r/TriangleMesh.pm | 6 ++++-- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e701bd1c4..f015416de 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -477,7 +477,7 @@ sub make_skirt { $skirt_height = $self->layer_count if $skirt_height > $self->layer_count; my @points = (); foreach my $obj_idx (0 .. $#{$self->objects}) { - my @layers = map $self->objects->[$obj_idx]->layers->[$_], 0..($skirt_height-1); + my @layers = map $self->objects->[$obj_idx]->layer($_), 0..($skirt_height-1); my @layer_points = ( (map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers), (map @$_, map @{$_->thin_walls}, @layers), diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index d8f2c531b..696d72ca3 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -2,42 +2,39 @@ package Slic3r::Print::Object; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(Z scale unscale deg2rad); +use Slic3r::Geometry qw(scale unscale deg2rad); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); use Slic3r::Surface ':types'; has 'input_file' => (is => 'rw', required => 0); has 'mesh' => (is => 'rw', required => 0); has 'size' => (is => 'rw', required => 1); -has 'layers' => (is => 'rw', default => sub { [] } ); -sub BUILD { - my $self = shift; - - # make layers - while (!@{$self->layers} || $self->layers->[-1]->slice_z < $self->size->[Z]) { - push @{$self->layers}, Slic3r::Layer->new(id => $#{$self->layers} + 1); - } -} +has 'layers' => ( + traits => ['Array'], + is => 'rw', + #isa => 'ArrayRef[Slic3r::Layer]', + default => sub { [] }, +); sub layer_count { my $self = shift; return scalar @{ $self->layers }; } -sub get_layer_range { +sub layer { my $self = shift; - my ($min_z, $max_z) = @_; + my ($layer_id) = @_; - my ($min_layer, $max_layer) = (0, undef); - for my $layer (@{$self->layers}) { - $min_layer = $layer->id if $layer->slice_z <= $min_z; - if ($layer->slice_z >= $max_z) { - $max_layer = $layer->id; - last; + # extend our print by creating all necessary layers + + if ($self->layer_count < $layer_id + 1) { + for (my $i = $self->layer_count; $i <= $layer_id; $i++) { + push @{ $self->layers }, Slic3r::Layer->new(id => $i); } } - return ($min_layer, $max_layer); + + return $self->layers->[$layer_id]; } sub slice { @@ -49,7 +46,7 @@ sub slice { my $apply_lines = sub { my $lines = shift; foreach my $layer_id (keys %$lines) { - my $layer = $self->layers->[$layer_id]; + my $layer = $self->layer($layer_id); push @{$layer->lines}, @{$lines->{$layer_id}}; } }; @@ -402,7 +399,7 @@ sub combine_infill { # start from bottom, skip first layer for (my $i = 1; $i < $self->layer_count; $i++) { - my $layer = $self->layers->[$i]; + my $layer = $self->layer($i); # skip layer if no internal fill surfaces next if !grep $_->surface_type == S_TYPE_INTERNAL, @{$layer->fill_surfaces}; @@ -411,7 +408,7 @@ sub combine_infill { # we do this from the greater depth to the smaller for (my $d = $Slic3r::Config->infill_every_layers - 1; $d >= 1; $d--) { next if ($i - $d) < 0; - my $lower_layer = $self->layers->[$i - 1]; + my $lower_layer = $self->layer($i - 1); # select surfaces of the lower layer having the depth we're looking for my @lower_surfaces = grep $_->depth_layers == $d && $_->surface_type == S_TYPE_INTERNAL, diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index b48f91981..6f6589450 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -401,12 +401,14 @@ sub slice_facet { } # calculate the layer extents - my ($min_layer, $max_layer) = $print_object->get_layer_range($min_z, $max_z); + my $min_layer = int((unscale($min_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) - 2; + $min_layer = 0 if $min_layer < 0; + my $max_layer = int((unscale($max_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) + 2; Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer; my $lines = {}; # layer_id => [ lines ] for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) { - my $layer = $print_object->layers->[$layer_id]; + my $layer = $print_object->layer($layer_id); $lines->{$layer_id} ||= []; push @{ $lines->{$layer_id} }, $self->intersect_facet($facet_id, $layer->slice_z); } From c0322ec703ab065fa5fece6eac711b4c020f1555 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Tue, 28 Aug 2012 15:39:32 +0200 Subject: [PATCH 10/31] Add example costs calculaton. #644 --- utils/post-processing/filament-weight.pl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/utils/post-processing/filament-weight.pl b/utils/post-processing/filament-weight.pl index c564b4d80..5ed836461 100755 --- a/utils/post-processing/filament-weight.pl +++ b/utils/post-processing/filament-weight.pl @@ -1,20 +1,30 @@ #!/usr/bin/perl -i # -# Post-processing script for adding weight of required filament to -# G-code output. +# Post-processing script for adding weight and cost of required +# filament to G-code output. use strict; use warnings; # example densities, adjust according to filament specifications -use constant PLA => 1.25; # g/cm3 -use constant ABS => 1.05; # g/cm3 +use constant PLA_P => 1.25; # g/cm3 +use constant ABS_P => 1.05; # g/cm3 + +# example costs, adjust according to filament prices +use constant PLA_PRICE => 0.05; # EUR/g +use constant ABS_PRICE => 0.02; # EUR/g +use constant CURRENCY => "EUR"; while (<>) { if (/^(;\s+filament\s+used\s+=\s.*\((\d+(?:\.\d+)?)cm3)\)/) { - my $pla = $2 * PLA; - my $abs = $2 * ABS; - printf "%s or %.2fg PLA/%.2fg ABS)\n", $1, $pla, $abs; + my $pla_weight = $2 * PLA_P; + my $abs_weight = $2 * ABS_P; + + my $pla_costs = $pla_weight * PLA_PRICE; + my $abs_costs = $abs_weight * ABS_PRICE; + + printf "%s or %.2fg PLA/%.2fg ABS)\n", $1, $pla_weight, $abs_weight; + printf "; costs = %s %.2f (PLA), %s %.2f (ABS)\n", CURRENCY, $pla_costs, CURRENCY, $abs_costs; } else { print; } From f90520ed0666533842ca2512d8dd52fb70327250 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 29 Aug 2012 16:49:38 +0200 Subject: [PATCH 11/31] Refactoring: new Slic3r::Model class to represent files --- MANIFEST | 1 + lib/Slic3r.pm | 1 + lib/Slic3r/Format/AMF.pm | 65 +++++++++--------- lib/Slic3r/Format/AMF/Parser.pm | 2 +- lib/Slic3r/Format/OBJ.pm | 5 +- lib/Slic3r/Format/STL.pm | 13 ++-- lib/Slic3r/GUI/Plater.pm | 35 +++++----- lib/Slic3r/GUI/SkeinPanel.pm | 2 +- lib/Slic3r/Model.pm | 112 ++++++++++++++++++++++++++++++++ lib/Slic3r/Print.pm | 49 +++++++++----- slic3r.pl | 4 +- utils/amf-to-stl.pl | 4 +- utils/split_stl.pl | 11 +++- utils/stl-to-amf.pl | 39 ++++++++--- 14 files changed, 255 insertions(+), 88 deletions(-) create mode 100644 lib/Slic3r/Model.pm diff --git a/MANIFEST b/MANIFEST index 19de72498..5a5a80df1 100644 --- a/MANIFEST +++ b/MANIFEST @@ -15,6 +15,7 @@ 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 diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index dc9776121..19117f4b8 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -41,6 +41,7 @@ use Slic3r::GCode; use Slic3r::Geometry qw(PI); use Slic3r::Layer; use Slic3r::Line; +use Slic3r::Model; use Slic3r::Point; use Slic3r::Polygon; use Slic3r::Polyline; diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index ce388ec2e..369503c78 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -25,15 +25,21 @@ sub read_file { close $fh; - $_ = Slic3r::TriangleMesh->new(vertices => $vertices, facets => $_) - for values %$meshes_by_material; - - return $materials, $meshes_by_material; + my $model = Slic3r::Model->new; + my $object = $model->add_object(vertices => $vertices); + foreach my $material (keys %$meshes_by_material) { + push @{$model->materials}, $material; # TODO: we should not add duplicate materials + $object->add_volume( + material_id => $#{$model->materials}, + facets => $meshes_by_material->{$material}, + ); + } + return $model; } sub write_file { my $self = shift; - my ($file, $materials, $meshes_by_material) = @_; + my ($file, $model, %params) = @_; my %vertices_offset = (); @@ -42,20 +48,20 @@ sub write_file { printf $fh qq{\n}; printf $fh qq{\n}; printf $fh qq{ Slic3r %s\n}, $Slic3r::VERSION; - foreach my $material_id (keys %$materials) { - printf $fh qq{ \n}, $material_id; - for (keys %{$materials->{$material_id}}) { - printf $fh qq{ %s\n}, $_, $materials->{$material_id}{$_}; + for my $material_id (0 .. $#{ $model->materials }) { + my $material = $model->materials->[$material_id]; + printf $fh qq{ \n}, $material_id; + for (keys %$material) { + printf $fh qq{ %s\n}, $_, $material->{$_}; } printf $fh qq{ \n}; } - printf $fh qq{ \n}; - printf $fh qq{ \n}; - printf $fh qq{ \n}; - my $vertices_count = 0; - foreach my $mesh (values %$meshes_by_material) { - $vertices_offset{$mesh} = $vertices_count; - foreach my $vertex (@{$mesh->vertices}, ) { + for my $object_id (0 .. $#{ $model->objects }) { + my $object = $model->objects->[$object_id]; + printf $fh qq{ \n}, $object_id; + printf $fh qq{ \n}; + printf $fh qq{ \n}; + foreach my $vertex (@{$object->vertices}, ) { printf $fh qq{ \n}; printf $fh qq{ \n}; printf $fh qq{ %s\n}, $vertex->[X]; @@ -63,24 +69,21 @@ sub write_file { printf $fh qq{ %s\n}, $vertex->[Z]; printf $fh qq{ \n}; printf $fh qq{ \n}; - $vertices_count++; } - } - printf $fh qq{ \n}; - foreach my $material_id (sort keys %$meshes_by_material) { - my $mesh = $meshes_by_material->{$material_id}; - printf $fh qq{ \n}, - ($material_id eq '_') ? '' : " materialid=\"$material_id\""; - foreach my $facet (@{$mesh->facets}) { - printf $fh qq{ \n}; - printf $fh qq{ %d\n}, $_, $facet->[$_] + $vertices_offset{$mesh}, $_ - for -3..-1; - printf $fh qq{ \n}; + printf $fh qq{ \n}; + foreach my $volume (@{ $object->volumes }) { + printf $fh qq{ \n}, + (!defined $volume->material_id) ? '' : (sprintf ' materialid="%s"', $volume->material_id); + foreach my $facet (@{$volume->facets}) { + printf $fh qq{ \n}; + printf $fh qq{ %d\n}, $_, $facet->[$_], $_ for -3..-1; + printf $fh qq{ \n}; + } + printf $fh qq{ \n}; } - printf $fh qq{ \n}; + printf $fh qq{ \n}; + printf $fh qq{ \n}; } - printf $fh qq{ \n}; - printf $fh qq{ \n}; printf $fh qq{\n}; close $fh; } diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index 1d8d4dc39..7c100b777 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -71,7 +71,7 @@ sub end_element { } elsif ($data->{LocalName} eq 'triangle') { push @{$self->{_volume}}, $self->{_triangle}; $self->{_triangle} = undef; - } elsif ($self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) { + } elsif (defined $self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) { $self->{_vertex_idx} = undef; } elsif ($data->{LocalName} eq 'material') { $self->{_materials}{ $self->{_material_id} } = $self->{_material}; diff --git a/lib/Slic3r/Format/OBJ.pm b/lib/Slic3r/Format/OBJ.pm index 210853025..d663cfb5a 100644 --- a/lib/Slic3r/Format/OBJ.pm +++ b/lib/Slic3r/Format/OBJ.pm @@ -17,7 +17,10 @@ sub read_file { } close $fh; - return Slic3r::TriangleMesh->new(vertices => $vertices, facets => $facets); + my $model = Slic3r::Model->new; + my $object = $model->add_object(vertices => $vertices); + my $volume = $object->add_volume(facets => $facets); + return $model; } 1; diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 9706fd00d..5325a4ea6 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -117,7 +117,10 @@ sub read_file { } } - return Slic3r::TriangleMesh->new(vertices => $vertices, facets => $facets); + my $model = Slic3r::Model->new; + my $object = $model->add_object(vertices => $vertices); + my $volume = $object->add_volume(facets => $facets); + return $model; } sub _read_ascii { @@ -161,13 +164,13 @@ sub _read_binary { sub write_file { my $self = shift; - my ($file, $mesh, $binary) = @_; + my ($file, $model, %params) = @_; open my $fh, '>', $file; - $binary - ? _write_binary($fh, $mesh) - : _write_ascii($fh, $mesh); + $params{binary} + ? _write_binary($fh, $model->mesh) + : _write_ascii($fh, $model->mesh); close $fh; } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9c6a900da..2efb7e018 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -296,7 +296,7 @@ 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_object_from_file($input_file); + $self->{print}->add_objects_from_file($input_file); my $obj_idx = $#{$self->{print}->objects}; $process_dialog->Destroy; @@ -630,13 +630,11 @@ sub on_export_failed { sub export_stl { my $self = shift; - - my $print = $self->{print}; # select output file my $output_file = $main::opt{output}; { - $output_file = $print->expanded_output_filepath($output_file); + $output_file = $self->{print}->expanded_output_filepath($output_file); $output_file =~ s/\.gcode$/.stl/i; my $dlg = Wx::FileDialog->new($self, 'Save STL file as:', dirname($output_file), basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); @@ -648,21 +646,26 @@ sub export_stl { $dlg->Destroy; } - my $mesh = Slic3r::TriangleMesh->new(facets => [], vertices => []); - for my $obj_idx (0 .. $#{$print->objects}) { - for my $copy (@{$print->copies->[$obj_idx]}) { - my $cloned_mesh = $print->objects->[$obj_idx]->mesh->clone; - $cloned_mesh->move(@$copy); - my $vertices_offset = scalar @{$mesh->vertices}; - push @{$mesh->vertices}, @{$cloned_mesh->vertices}; - push @{$mesh->facets}, map [ $_->[0], map $vertices_offset + $_, @$_[-3..-1] ], @{$cloned_mesh->facets}; + Slic3r::Format::STL->write_file($output_file, $self->make_model, binary => 1); + $self->statusbar->SetStatusText("STL file exported to $output_file"); +} + +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; + $mesh->scale(&Slic3r::SCALING_FACTOR); + my $object = $model->add_object(vertices => $mesh->vertices); + $object->add_volume(facets => $mesh->facets); + for my $copy (@{$self->{print}->copies->[$obj_idx]}) { + $object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]); } } - $mesh->scale(&Slic3r::SCALING_FACTOR); - $mesh->align_to_origin; + # TODO: $model->align_to_origin; - Slic3r::Format::STL->write_file($output_file, $mesh, 1); - $self->statusbar->SetStatusText("STL file exported to $output_file"); + return $model; } sub make_thumbnail { diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 764f60a81..babd1cfbc 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -96,7 +96,7 @@ sub do_slice { Slic3r::GUI->save_settings; my $print = Slic3r::Print->new(config => $config); - $print->add_object_from_file($input_file); + $print->add_objects_from_file($input_file); $print->validate; # select output file diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm new file mode 100644 index 000000000..48979da31 --- /dev/null +++ b/lib/Slic3r/Model.pm @@ -0,0 +1,112 @@ +package Slic3r::Model; +use Moo; + +use Slic3r::Geometry qw(X Y Z); + +has 'materials' => (is => 'ro', default => sub { [] }); +has 'objects' => (is => 'ro', default => sub { [] }); + +sub add_object { + my $self = shift; + + my $object = Slic3r::Model::Object->new(model => $self, @_); + push @{$self->objects}, $object; + return $object; +} + +# flattens everything to a single mesh +sub mesh { + my $self = shift; + + 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}; + } + } + } + + return Slic3r::TriangleMesh->new( + vertices => $vertices, + facets => $facets, + ); +} + +package Slic3r::Model::Material; +use Moo; + +has 'model' => (is => 'ro', weak_ref => 1, required => 1); +has 'attributes' => (is => 'rw', default => sub { {} }); + +package Slic3r::Model::Object; +use Moo; + +has 'model' => (is => 'ro', weak_ref => 1, required => 1); +has 'vertices' => (is => 'ro', default => sub { [] }); +has 'volumes' => (is => 'ro', default => sub { [] }); +has 'instances' => (is => 'rw'); + +sub add_volume { + my $self = shift; + + my $volume = Slic3r::Model::Volume->new(object => $self, @_); + push @{$self->volumes}, $volume; + return $volume; +} + +sub add_instance { + my $self = shift; + + $self->instances([]) if !defined $self->instances; + push @{$self->instances}, Slic3r::Model::Instance->new(object => $self, @_); + return $self->instances->[-1]; +} + +package Slic3r::Model::Volume; +use Moo; + +has 'object' => (is => 'ro', weak_ref => 1, required => 1); +has 'material_id' => (is => 'rw'); +has 'facets' => (is => 'rw', default => sub { [] }); + +sub mesh { + my $self = shift; + return Slic3r::TriangleMesh->new( + vertices => $self->object->vertices, + facets => $self->facets, + ); +} + +package Slic3r::Model::Instance; +use Moo; + +has 'object' => (is => 'ro', weak_ref => 1, required => 1); +has 'rotation' => (is => 'rw', default => sub { 0 }); +has 'offset' => (is => 'rw'); + +1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f015416de..bfa7c2c36 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -79,28 +79,43 @@ sub _trigger_config { $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); } -sub add_object_from_file { +sub add_objects_from_file { my $self = shift; my ($input_file) = @_; - my $object; - if ($input_file =~ /\.stl$/i) { - my $mesh = Slic3r::Format::STL->read_file($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 @print_objects = $self->add_model($model); + $_->input_file($input_file) for @print_objects; +} + +sub add_model { + my $self = shift; + my ($model) = @_; + + my @print_objects = (); + foreach my $object (@{ $model->objects }) { + my $mesh = $object->volumes->[0]->mesh; $mesh->check_manifoldness; - $object = $self->add_object_from_mesh($mesh); - } elsif ($input_file =~ /\.obj$/i) { - my $mesh = Slic3r::Format::OBJ->read_file($input_file); - $mesh->check_manifoldness; - $object = $self->add_object_from_mesh($mesh); - } elsif ( $input_file =~ /\.amf(\.xml)?$/i) { - my ($materials, $meshes_by_material) = Slic3r::Format::AMF->read_file($input_file); - $_->check_manifoldness for values %$meshes_by_material; - $object = $self->add_object_from_mesh($meshes_by_material->{_} || +(values %$meshes_by_material)[0]); - } else { - die "Input file must have .stl, .obj or .amf(.xml) extension\n"; + + if ($object->instances) { + # we ignore the per-instance rotation currently and only + # consider the first one + $mesh->rotate($object->instances->[0]->rotation); + } + + push @print_objects, $self->add_object_from_mesh($mesh); + + 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}; + } } - $object->input_file($input_file); - return $object; + + return @print_objects; } sub add_object_from_mesh { diff --git a/slic3r.pl b/slic3r.pl index cc0af0a60..234ce5c24 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -88,9 +88,9 @@ if (@ARGV) { # slicing from command line while (my $input_file = shift @ARGV) { my $print = Slic3r::Print->new(config => $config); - $print->add_object_from_file($input_file); + $print->add_objects_from_file($input_file); if ($opt{merge}) { - $print->add_object_from_file($_) for splice @ARGV, 0; + $print->add_objects_from_file($_) for splice @ARGV, 0; } $print->duplicate; $print->arrange_objects if @{$print->objects} > 1; diff --git a/utils/amf-to-stl.pl b/utils/amf-to-stl.pl index 847e5e749..b421dd33a 100755 --- a/utils/amf-to-stl.pl +++ b/utils/amf-to-stl.pl @@ -25,12 +25,12 @@ my %opt = (); } { - my $mesh = Slic3r::Format::AMF->read_file($ARGV[0]); + my $model = Slic3r::Format::AMF->read_file($ARGV[0]); my $output_file = $ARGV[0]; $output_file =~ s/\.amf(?:\.xml)?$/\.stl/i; printf "Writing to %s\n", basename($output_file); - Slic3r::Format::STL->write_file($output_file, $mesh, !$opt{ascii}); + Slic3r::Format::STL->write_file($output_file, $model, binary => !$opt{ascii}); } diff --git a/utils/split_stl.pl b/utils/split_stl.pl index af9890116..42d2926bd 100755 --- a/utils/split_stl.pl +++ b/utils/split_stl.pl @@ -25,15 +25,20 @@ my %opt = (); } { - my $mesh = Slic3r::Format::STL->read_file($ARGV[0]); + my $model = Slic3r::Format::STL->read_file($ARGV[0]); my $basename = $ARGV[0]; $basename =~ s/\.stl$//i; my $part_count = 0; - foreach my $new_mesh ($mesh->split_mesh) { + foreach my $new_mesh ($model->mesh->split_mesh) { + my $new_model = Slic3r::Model->new; + $new_model + ->add_object(vertices => $new_mesh->vertices) + ->add_volume(facets => $new_mesh->facets); + my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count; printf "Writing to %s\n", basename($output_file); - Slic3r::Format::STL->write_file($output_file, $new_mesh, !$opt{ascii}); + Slic3r::Format::STL->write_file($output_file, $new_model, binary => !$opt{ascii}); } } diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index f4805369b..90aacc535 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -18,29 +18,49 @@ my %opt = (); { my %options = ( 'help' => sub { usage() }, + 'distinct-materials' => \$opt{distinct_materials}, ); GetOptions(%options) or usage(1); $ARGV[0] or usage(1); } { - my @meshes = map Slic3r::Format::STL->read_file($_), @ARGV; + my @models = map Slic3r::Format::STL->read_file($_), @ARGV; my $output_file = $ARGV[0]; $output_file =~ s/\.stl$/.amf.xml/i; - my $materials = {}; - my $meshes_by_material = {}; - if (@meshes == 1) { - $meshes_by_material->{_} = $meshes[0]; + my $new_model = Slic3r::Model->new; + + if ($opt{distinct_materials} && @models > 1) { + my $new_object = $new_model->add_object; + for my $m (0 .. $#models) { + my $model = $models[$m]; + my $v_offset = @{$new_object->vertices}; + push @{$new_object->vertices}, @{$model->objects->[0]->vertices}; + my @new_facets = map { + my $f = [@$_]; + $f->[$_] += $v_offset for -3..-1; + $f; + } @{ $model->objects->[0]->volumes->[0]->facets }; + + push @{$new_model->materials}, { Name => basename($ARGV[$m]) }; + $new_object->add_volume( + material_id => $#{$new_model->materials}, + facets => [@new_facets], + ); + } } else { - for (0..$#meshes) { - $materials->{$_+1} = { Name => basename($ARGV[$_]) }; - $meshes_by_material->{$_+1} = $meshes[$_]; + foreach my $model (@models) { + $new_model->add_object( + vertices => $model->objects->[0]->vertices, + )->add_volume( + facets => $model->objects->[0]->volumes->[0]->facets, + ); } } printf "Writing to %s\n", basename($output_file); - Slic3r::Format::AMF->write_file($output_file, $materials, $meshes_by_material); + Slic3r::Format::AMF->write_file($output_file, $new_model); } @@ -51,6 +71,7 @@ sub usage { Usage: amf-to-stl.pl [ OPTIONS ] file.stl [ file2.stl [ file3.stl ] ] --help Output this usage screen and exit + --distinct-materials Assign each STL file to a different material EOF exit ($exit_code || 0); From 23f6842bd45999731f367dac23c3ce3deb4654bb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 29 Aug 2012 17:11:56 +0200 Subject: [PATCH 12/31] Export plates as AMF constellations --- lib/Slic3r/Format/AMF.pm | 17 ++++++++++++++++- lib/Slic3r/GUI.pm | 16 ++++++++++++++++ lib/Slic3r/GUI/Plater.pm | 30 +++++++++++++++++++++++------- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index 369503c78..ce3fbb474 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -56,6 +56,7 @@ sub write_file { } printf $fh qq{ \n}; } + my $instances = ''; for my $object_id (0 .. $#{ $model->objects }) { my $object = $model->objects->[$object_id]; printf $fh qq{ \n}, $object_id; @@ -76,13 +77,27 @@ sub write_file { (!defined $volume->material_id) ? '' : (sprintf ' materialid="%s"', $volume->material_id); foreach my $facet (@{$volume->facets}) { printf $fh qq{ \n}; - printf $fh qq{ %d\n}, $_, $facet->[$_], $_ for -3..-1; + printf $fh qq{ %d\n}, (4+$_), $facet->[$_], (4+$_) for -3..-1; printf $fh qq{ \n}; } printf $fh qq{ \n}; } printf $fh qq{ \n}; printf $fh qq{ \n}; + if ($object->instances) { + foreach my $instance (@{$object->instances}) { + $instances .= sprintf qq{ \n}, $object_id; + $instances .= sprintf qq{ %s\n}, $instance->offset->[X]; + $instances .= sprintf qq{ %s\n}, $instance->offset->[Y]; + $instances .= sprintf qq{ %s\n}, $instance->rotation; + $instances .= sprintf qq{ \n}; + } + } + } + if ($instances) { + printf $fh qq{ \n}; + printf $fh $instances; + printf $fh qq{ \n}; } printf $fh qq{\n}; close $fh; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 09d1071ea..887d35320 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -22,6 +22,10 @@ use constant MI_REPEAT_QUICK => &Wx::NewId; use constant MI_QUICK_SAVE_AS => &Wx::NewId; use constant MI_SLICE_SVG => &Wx::NewId; +use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId; +use constant MI_PLATER_EXPORT_STL => &Wx::NewId; +use constant MI_PLATER_EXPORT_AMF => &Wx::NewId; + use constant MI_TAB_PLATER => &Wx::NewId; use constant MI_TAB_PRINT => &Wx::NewId; use constant MI_TAB_FILAMENT => &Wx::NewId; @@ -98,6 +102,17 @@ sub OnInit { EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)}); } + # Plater menu + my $platerMenu = Wx::Menu->new; + { + $platerMenu->Append(MI_PLATER_EXPORT_GCODE, "Export G-code...", 'Export current plate as G-code'); + $platerMenu->Append(MI_PLATER_EXPORT_STL, "Export STL...", 'Export current plate as STL'); + $platerMenu->Append(MI_PLATER_EXPORT_AMF, "Export AMF...", 'Export current plate as AMF'); + EVT_MENU($frame, MI_PLATER_EXPORT_GCODE, sub { $self->{skeinpanel}{plater}->export_gcode }); + EVT_MENU($frame, MI_PLATER_EXPORT_STL, sub { $self->{skeinpanel}{plater}->export_stl }); + EVT_MENU($frame, MI_PLATER_EXPORT_AMF, sub { $self->{skeinpanel}{plater}->export_amf }); + } + # Window menu my $windowMenu = Wx::Menu->new; { @@ -128,6 +143,7 @@ sub OnInit { { my $menubar = Wx::MenuBar->new; $menubar->Append($fileMenu, "&File"); + $menubar->Append($platerMenu, "&Plater"); $menubar->Append($windowMenu, "&Window"); $menubar->Append($helpMenu, "&Help"); $frame->SetMenuBar($menubar); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 2efb7e018..a1b8c3406 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -631,23 +631,39 @@ sub on_export_failed { sub export_stl { my $self = shift; - # select output file + my $output_file = $self->_get_export_file('STL') or return; + Slic3r::Format::STL->write_file($output_file, $self->make_model, binary => 1); + $self->statusbar->SetStatusText("STL file exported to $output_file"); +} + +sub export_amf { + my $self = shift; + + my $output_file = $self->_get_export_file('AMF') or return; + Slic3r::Format::AMF->write_file($output_file, $self->make_model); + $self->statusbar->SetStatusText("AMF file exported to $output_file"); +} + +sub _get_export_file { + my $self = shift; + my ($format) = @_; + + my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml'; + my $output_file = $main::opt{output}; { $output_file = $self->{print}->expanded_output_filepath($output_file); - $output_file =~ s/\.gcode$/.stl/i; - my $dlg = Wx::FileDialog->new($self, 'Save STL file as:', dirname($output_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); if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; - return; + return undef; } $output_file = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath; $dlg->Destroy; } - - Slic3r::Format::STL->write_file($output_file, $self->make_model, binary => 1); - $self->statusbar->SetStatusText("STL file exported to $output_file"); + return $output_file; } sub make_model { From f29d4553191d96ea10ec39cefbb36190e7e0b4ce Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 29 Aug 2012 18:23:34 +0200 Subject: [PATCH 13/31] Refactor the AMF parser so that it popolates a Model object directly. Also read constellations --- lib/Slic3r/Format/AMF.pm | 24 +++---------- lib/Slic3r/Format/AMF/Parser.pm | 63 ++++++++++++++++++++++++++------- lib/Slic3r/Model.pm | 2 +- utils/stl-to-amf.pl | 5 +-- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index ce3fbb474..552f1fdbd 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -12,28 +12,12 @@ sub read_file { open my $fh, '<', $file or die "Failed to open $file\n"; - my $vertices = []; - my $materials = {}; - my $meshes_by_material = {}; + my $model = Slic3r::Model->new; XML::SAX::PurePerl - ->new(Handler => Slic3r::Format::AMF::Parser->new( - _vertices => $vertices, - _materials => $materials, - _meshes_by_material => $meshes_by_material, - )) + ->new(Handler => Slic3r::Format::AMF::Parser->new(_model => $model)) ->parse_file($fh); - close $fh; - my $model = Slic3r::Model->new; - my $object = $model->add_object(vertices => $vertices); - foreach my $material (keys %$meshes_by_material) { - push @{$model->materials}, $material; # TODO: we should not add duplicate materials - $object->add_volume( - material_id => $#{$model->materials}, - facets => $meshes_by_material->{$material}, - ); - } return $model; } @@ -48,8 +32,8 @@ sub write_file { printf $fh qq{\n}; printf $fh qq{\n}; printf $fh qq{ Slic3r %s\n}, $Slic3r::VERSION; - for my $material_id (0 .. $#{ $model->materials }) { - my $material = $model->materials->[$material_id]; + for my $material_id (sort keys %{ $model->materials }) { + my $material = $model->materials->{$material_id}; printf $fh qq{ \n}, $material_id; for (keys %$material) { printf $fh qq{ %s\n}, $_, $material->{$_}; diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index 7c100b777..93b35765e 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -11,6 +11,8 @@ my %xyz_index = (x => 0, y => 1, z => 2); #= sub new { my $self = shift->SUPER::new(@_); $self->{_tree} = []; + $self->{_objects_map} = {}; # this hash maps AMF object IDs to object indexes in $model->objects + $self->{_instances} = {}; # apply these lazily to make sure all objects have been parsed $self; } @@ -18,23 +20,35 @@ sub start_element { my $self = shift; my $data = shift; - if ($data->{LocalName} eq 'vertex') { + if ($data->{LocalName} eq 'object') { + $self->{_object} = $self->{_model}->add_object; + $self->{_objects_map}{ $self->_get_attribute($data, 'id') } = $#{ $self->{_model}->objects }; + } elsif ($data->{LocalName} eq 'vertex') { $self->{_vertex} = ["", "", ""]; } elsif ($self->{_vertex} && $data->{LocalName} =~ /^[xyz]$/ && $self->{_tree}[-1] eq 'coordinates') { $self->{_coordinate} = $data->{LocalName}; } elsif ($data->{LocalName} eq 'volume') { - $self->{_volume_materialid} = $self->_get_attribute($data, 'materialid') || '_'; - $self->{_volume} = []; + $self->{_volume} = $self->{_object}->add_volume( + material_id => $self->_get_attribute($data, 'materialid') || undef, + ); } elsif ($data->{LocalName} eq 'triangle') { $self->{_triangle} = ["", "", ""]; } elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') { $self->{_vertex_idx} = $1-1; } elsif ($data->{LocalName} eq 'material') { - $self->{_material_id} = $self->_get_attribute($data, 'id') || '_'; - $self->{_material} = {}; + my $material_id = $self->_get_attribute($data, 'id') || '_'; + $self->{_material} = $self->{_model}->materials->{ $material_id } = {}; } elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') { $self->{_material_metadata_type} = $self->_get_attribute($data, 'type'); $self->{_material}{ $self->{_material_metadata_type} } = ""; + } elsif ($data->{LocalName} eq 'constellation') { + $self->{_constellation} = 1; # we merge all constellations as we don't support more than one + } elsif ($data->{LocalName} eq 'instance' && $self->{_constellation}) { + my $object_id = $self->_get_attribute($data, 'objectid'); + $self->{_instances}{$object_id} ||= []; + push @{ $self->{_instances}{$object_id} }, $self->{_instance} = {}; + } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { + $self->{_instance_property} = $data->{LocalName}; } push @{$self->{_tree}}, $data->{LocalName}; @@ -50,6 +64,8 @@ sub characters { $self->{_triangle}[ $self->{_vertex_idx} ] .= $data->{Data}; } elsif ($self->{_material_metadata_type}) { $self->{_material}{ $self->{_material_metadata_type} } .= $data->{Data}; + } elsif ($self->{_instance_property}) { + $self->{_instance}{ $self->{_instance_property} } .= $data->{Data}; } } @@ -59,26 +75,49 @@ sub end_element { pop @{$self->{_tree}}; - if ($data->{LocalName} eq 'vertex') { - push @{$self->{_vertices}}, $self->{_vertex}; + if ($data->{LocalName} eq 'object') { + $self->{_object} = undef; + } elsif ($data->{LocalName} eq 'vertex') { + push @{$self->{_object}->vertices}, $self->{_vertex}; $self->{_vertex} = undef; } elsif ($self->{_coordinate} && $data->{LocalName} =~ /^[xyz]$/) { $self->{_coordinate} = undef; } elsif ($data->{LocalName} eq 'volume') { - $self->{_meshes_by_material}{ $self->{_volume_materialid} } ||= []; - push @{ $self->{_meshes_by_material}{ $self->{_volume_materialid} } }, @{$self->{_volume}}; $self->{_volume} = undef; } elsif ($data->{LocalName} eq 'triangle') { - push @{$self->{_volume}}, $self->{_triangle}; + push @{$self->{_volume}->facets}, $self->{_triangle}; $self->{_triangle} = undef; } elsif (defined $self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) { $self->{_vertex_idx} = undef; } elsif ($data->{LocalName} eq 'material') { - $self->{_materials}{ $self->{_material_id} } = $self->{_material}; - $self->{_material_id} = undef; $self->{_material} = undef; } elsif ($data->{LocalName} eq 'metadata' && $self->{_material}) { $self->{_material_metadata_type} = undef; + } elsif ($data->{LocalName} eq 'constellation') { + $self->{_constellation} = undef; + } elsif ($data->{LocalName} eq 'instance') { + $self->{_instance} = undef; + } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { + $self->{_instance_property} = undef; + } +} + +sub end_document { + my $self = shift; + + foreach my $object_id (keys %{ $self->{_instances} }) { + my $new_object_id = $self->{_objects_map}{$object_id}; + if (!$new_object_id) { + warn "Undefined object $object_id referenced in constellation\n"; + next; + } + + 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} ], + ); + } } } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 48979da31..b648d4b34 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -3,7 +3,7 @@ use Moo; use Slic3r::Geometry qw(X Y Z); -has 'materials' => (is => 'ro', default => sub { [] }); +has 'materials' => (is => 'ro', default => sub { {} }); has 'objects' => (is => 'ro', default => sub { [] }); sub add_object { diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index 90aacc535..78e5989b4 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -43,9 +43,10 @@ my %opt = (); $f; } @{ $model->objects->[0]->volumes->[0]->facets }; - push @{$new_model->materials}, { Name => basename($ARGV[$m]) }; + my $material_id = scalar keys %{$new_model->materials}; + $new_model->materials->{$material_id} = { Name => basename($ARGV[$m]) }; $new_object->add_volume( - material_id => $#{$new_model->materials}, + material_id => $material_id, facets => [@new_facets], ); } From b37af86befc09c075fe7eae9c4b1d79148f7abd8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 30 Aug 2012 23:04:56 +0200 Subject: [PATCH 14/31] 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 bfa7c2c36..444af3510 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -42,6 +42,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)) { @@ -71,12 +79,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 5017f17171f7e38046026559993f301157ee8a6d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 30 Aug 2012 23:13:28 +0200 Subject: [PATCH 15/31] 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 25f647a60cd94eddde490d9d3316456c740c7d0b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 30 Aug 2012 23:15:42 +0200 Subject: [PATCH 16/31] 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 8d7b7814739b7ccc8dfb0fc52ad6efeaf2fe83a3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 31 Aug 2012 00:12:13 +0200 Subject: [PATCH 17/31] 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 d5ed65a63cc03e843b201ad59f0279b68ff71439 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Sat, 1 Sep 2012 08:36:54 +0200 Subject: [PATCH 18/31] 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 e0a96c00b349173ffff24b8b255d23ea97e22586 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 6 Sep 2012 10:44:48 +0200 Subject: [PATCH 19/31] 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 444af3510..26448bb82 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -584,15 +584,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 1053947c1910b4fe971f0959f5a5e5a1648868e7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 6 Sep 2012 10:56:42 +0200 Subject: [PATCH 20/31] 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 f41a6af343dce297773fcf6647f917b44c87dedb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 6 Sep 2012 11:01:44 +0200 Subject: [PATCH 21/31] 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 97992d5b4..5ded4a592 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 26448bb82..7c1515e35 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -653,6 +653,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 628cc52338ec5fc21ed781031c7d6aa1dd9cd049 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 6 Sep 2012 11:28:24 +0200 Subject: [PATCH 22/31] 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 7c1515e35..87f94c1d1 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -5,7 +5,7 @@ use File::Basename qw(basename fileparse); use File::Spec; 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 36796645cbcb2b3d7d5f7a6eb817979d0adc5c5c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 11 Sep 2012 16:02:26 +0200 Subject: [PATCH 23/31] 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 bb5f00cf0cec1aa3f5517ac7831280d59c6f250c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 12:13:43 +0200 Subject: [PATCH 24/31] 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 41d5412fcb9c8c32002c1433d3efc07e00f3557d Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 12 Sep 2012 15:40:55 +0300 Subject: [PATCH 25/31] 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 9934f8957f2fdc821eed3a08556b5845be1c783d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:19:47 +0200 Subject: [PATCH 26/31] 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 e0d5f4e528e76a18148f8a988d932e0013b92f37 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:22:43 +0200 Subject: [PATCH 27/31] 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 7a5071f66caf96e2fe7673900e72fbe6a0e49508 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:29:44 +0200 Subject: [PATCH 28/31] 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 a1b8c3406..ff98c7458 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -508,6 +508,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 87f94c1d1..479cdc752 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -10,6 +10,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'); @@ -818,6 +819,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 12b38ce69424a1ddf871bf17279cca4bd3e01c99 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:40:57 +0200 Subject: [PATCH 29/31] 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 f4260ae93d25df113cbbb9c1995c508c44587d56 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:53:24 +0200 Subject: [PATCH 30/31] 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 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 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 2a51cad0c39adeb028b0fc90e0df6fb5092ae68d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 12 Sep 2012 15:58:01 +0200 Subject: [PATCH 31/31] 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 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 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;