Merge branch 'master' into clipper-optimizations
This commit is contained in:
commit
b160feacfd
17 changed files with 195 additions and 123 deletions
2
Build.PL
2
Build.PL
|
@ -7,7 +7,7 @@ my $build = Module::Build->new(
|
||||||
dist_version => '0.1',
|
dist_version => '0.1',
|
||||||
license => 'perl',
|
license => 'perl',
|
||||||
requires => {
|
requires => {
|
||||||
'Boost::Geometry::Utils' => '0.06',
|
'Boost::Geometry::Utils' => '0.08',
|
||||||
'Encode::Locale' => '0',
|
'Encode::Locale' => '0',
|
||||||
'File::Basename' => '0',
|
'File::Basename' => '0',
|
||||||
'File::Spec' => '0',
|
'File::Spec' => '0',
|
||||||
|
|
1
MANIFEST
1
MANIFEST
|
@ -24,6 +24,7 @@ lib/Slic3r/Format/AMF/Parser.pm
|
||||||
lib/Slic3r/Format/OBJ.pm
|
lib/Slic3r/Format/OBJ.pm
|
||||||
lib/Slic3r/Format/STL.pm
|
lib/Slic3r/Format/STL.pm
|
||||||
lib/Slic3r/GCode.pm
|
lib/Slic3r/GCode.pm
|
||||||
|
lib/Slic3r/GCode/CoolingBuffer.pm
|
||||||
lib/Slic3r/GCode/MotionPlanner.pm
|
lib/Slic3r/GCode/MotionPlanner.pm
|
||||||
lib/Slic3r/Geometry.pm
|
lib/Slic3r/Geometry.pm
|
||||||
lib/Slic3r/Geometry/Clipper.pm
|
lib/Slic3r/Geometry/Clipper.pm
|
||||||
|
|
|
@ -29,7 +29,7 @@ our $var = "$FindBin::Bin/var";
|
||||||
|
|
||||||
use Encode;
|
use Encode;
|
||||||
use Encode::Locale;
|
use Encode::Locale;
|
||||||
use Boost::Geometry::Utils 0.06;
|
use Boost::Geometry::Utils 0.08;
|
||||||
use Moo 0.091009;
|
use Moo 0.091009;
|
||||||
|
|
||||||
use Slic3r::Config;
|
use Slic3r::Config;
|
||||||
|
@ -45,6 +45,7 @@ use Slic3r::Format::AMF;
|
||||||
use Slic3r::Format::OBJ;
|
use Slic3r::Format::OBJ;
|
||||||
use Slic3r::Format::STL;
|
use Slic3r::Format::STL;
|
||||||
use Slic3r::GCode;
|
use Slic3r::GCode;
|
||||||
|
use Slic3r::GCode::CoolingBuffer;
|
||||||
use Slic3r::GCode::MotionPlanner;
|
use Slic3r::GCode::MotionPlanner;
|
||||||
use Slic3r::Geometry qw(PI);
|
use Slic3r::Geometry qw(PI);
|
||||||
use Slic3r::Layer;
|
use Slic3r::Layer;
|
||||||
|
|
|
@ -161,11 +161,6 @@ sub bounding_box_polygon {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub bounding_box_center {
|
|
||||||
my $self = shift;
|
|
||||||
return Slic3r::Geometry::bounding_box_center($self->contour);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub clip_line {
|
sub clip_line {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($line) = @_; # line must be a Slic3r::Line object
|
my ($line) = @_; # line must be a Slic3r::Line object
|
||||||
|
|
|
@ -39,10 +39,9 @@ sub filler {
|
||||||
return $FillTypes{$filler}->new;
|
return $FillTypes{$filler}->new;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$self->fillers->{$filler}) {
|
$self->fillers->{$filler} ||= $FillTypes{$filler}->new(
|
||||||
my $f = $self->fillers->{$filler} = $FillTypes{$filler}->new;
|
bounding_box => [ $self->print->bounding_box ],
|
||||||
$f->bounding_box([ $self->print->bounding_box ]) if $f->can('bounding_box');
|
);
|
||||||
}
|
|
||||||
return $self->fillers->{$filler};
|
return $self->fillers->{$filler};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use Slic3r::Geometry qw(PI);
|
||||||
|
|
||||||
has 'layer_id' => (is => 'rw');
|
has 'layer_id' => (is => 'rw');
|
||||||
has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle });
|
has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle });
|
||||||
|
has 'bounding_box' => (is => 'ro', required => 1);
|
||||||
|
|
||||||
sub angles () { [0, PI/2] }
|
sub angles () { [0, PI/2] }
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ sub infill_direction {
|
||||||
# set infill angle
|
# set infill angle
|
||||||
my (@rotate, @shift);
|
my (@rotate, @shift);
|
||||||
$rotate[0] = Slic3r::Geometry::deg2rad($self->angle);
|
$rotate[0] = Slic3r::Geometry::deg2rad($self->angle);
|
||||||
$rotate[1] = $surface->expolygon->bounding_box_center;
|
$rotate[1] = Slic3r::Geometry::bounding_box_center($self->bounding_box);
|
||||||
@shift = @{$rotate[1]};
|
@shift = @{$rotate[1]};
|
||||||
|
|
||||||
if (defined $self->layer_id) {
|
if (defined $self->layer_id) {
|
||||||
|
@ -38,26 +39,20 @@ sub infill_direction {
|
||||||
sub rotate_points {
|
sub rotate_points {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($expolygon, $rotate_vector) = @_;
|
my ($expolygon, $rotate_vector) = @_;
|
||||||
my @rotate = @{$rotate_vector->[0]};
|
|
||||||
my @shift = @{$rotate_vector->[1]};
|
|
||||||
|
|
||||||
# rotate points as needed
|
# rotate points
|
||||||
if ($rotate[0]) {
|
$expolygon->rotate(@{$rotate_vector->[0]});
|
||||||
$expolygon->rotate(@rotate);
|
$expolygon->translate(@{$rotate_vector->[1]});
|
||||||
$expolygon->translate(@shift);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rotate_points_back {
|
sub rotate_points_back {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($paths, $rotate_vector) = @_;
|
my ($paths, $rotate_vector) = @_;
|
||||||
my @rotate = @{$rotate_vector->[0]};
|
my @rotate = (-$rotate_vector->[0][0], $rotate_vector->[0][1]);
|
||||||
my @shift = @{$rotate_vector->[1]};
|
my $shift = [ map -$_, @{$rotate_vector->[1]} ];
|
||||||
|
|
||||||
if ($rotate[0]) {
|
@$paths = map [ Slic3r::Geometry::rotate_points(@rotate, @$_) ],
|
||||||
@$paths = map [ Slic3r::Geometry::rotate_points(-$rotate[0], $rotate[1], @$_) ],
|
map [ Slic3r::Geometry::move_points($shift, @$_) ], @$paths;
|
||||||
map [ Slic3r::Geometry::move_points([map -$_, @shift], @$_) ], @$paths;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub adjust_solid_spacing {
|
sub adjust_solid_spacing {
|
||||||
|
|
|
@ -3,7 +3,6 @@ use Moo;
|
||||||
|
|
||||||
extends 'Slic3r::Fill::Base';
|
extends 'Slic3r::Fill::Base';
|
||||||
|
|
||||||
has 'bounding_box' => (is => 'rw');
|
|
||||||
has 'cache' => (is => 'rw', default => sub {{}});
|
has 'cache' => (is => 'rw', default => sub {{}});
|
||||||
|
|
||||||
use Slic3r::Geometry qw(PI X1 Y1 X2 Y2 X Y scale);
|
use Slic3r::Geometry qw(PI X1 Y1 X2 Y2 X Y scale);
|
||||||
|
@ -25,7 +24,7 @@ sub fill_surface {
|
||||||
|
|
||||||
my $cache_id = sprintf "d%s_s%s_a%s",
|
my $cache_id = sprintf "d%s_s%s_a%s",
|
||||||
$params{density}, $params{flow_spacing}, $rotate_vector->[0][0];
|
$params{density}, $params{flow_spacing}, $rotate_vector->[0][0];
|
||||||
if (!$self->cache->{$cache_id} || !defined $self->bounding_box) {
|
if (!$self->cache->{$cache_id}) {
|
||||||
|
|
||||||
# hexagons math
|
# hexagons math
|
||||||
my $hex_side = $distance / (sqrt(3)/2);
|
my $hex_side = $distance / (sqrt(3)/2);
|
||||||
|
@ -39,7 +38,7 @@ sub fill_surface {
|
||||||
|
|
||||||
# adjust actual bounding box to the nearest multiple of our hex pattern
|
# adjust actual bounding box to the nearest multiple of our hex pattern
|
||||||
# and align it so that it matches across layers
|
# and align it so that it matches across layers
|
||||||
my $bounding_box = [ $self->bounding_box ? @{$self->bounding_box} : $expolygon->bounding_box ];
|
my $bounding_box = [ @{$self->bounding_box} ]; # clone
|
||||||
$bounding_box->[$_] = 0 for X1, Y1;
|
$bounding_box->[$_] = 0 for X1, Y1;
|
||||||
{
|
{
|
||||||
my $bb_polygon = Slic3r::Polygon->new_from_bounding_box($bounding_box);
|
my $bb_polygon = Slic3r::Polygon->new_from_bounding_box($bounding_box);
|
||||||
|
|
|
@ -3,6 +3,8 @@ use Moo;
|
||||||
|
|
||||||
extends 'Slic3r::Fill::Base';
|
extends 'Slic3r::Fill::Base';
|
||||||
|
|
||||||
|
has 'cache' => (is => 'rw', default => sub {{}});
|
||||||
|
|
||||||
use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon);
|
use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon);
|
||||||
|
|
||||||
sub fill_surface {
|
sub fill_surface {
|
||||||
|
@ -16,39 +18,55 @@ sub fill_surface {
|
||||||
|
|
||||||
my ($expolygon_off) = $expolygon->offset_ex(scale $params{flow_spacing}/2);
|
my ($expolygon_off) = $expolygon->offset_ex(scale $params{flow_spacing}/2);
|
||||||
return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here)
|
return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here)
|
||||||
my $bounding_box = [ $expolygon->bounding_box ];
|
|
||||||
|
|
||||||
|
my $flow_spacing = $params{flow_spacing};
|
||||||
my $min_spacing = scale $params{flow_spacing};
|
my $min_spacing = scale $params{flow_spacing};
|
||||||
my $distance_between_lines = $min_spacing / $params{density};
|
my $distance_between_lines = $min_spacing / $params{density};
|
||||||
my $line_oscillation = $distance_between_lines - $min_spacing;
|
my $line_oscillation = $distance_between_lines - $min_spacing;
|
||||||
|
|
||||||
my $flow_spacing = $params{flow_spacing};
|
|
||||||
if ($params{density} == 1 && !$params{dont_adjust}) {
|
|
||||||
$distance_between_lines = $self->adjust_solid_spacing(
|
|
||||||
width => $bounding_box->[X2] - $bounding_box->[X1],
|
|
||||||
distance => $distance_between_lines,
|
|
||||||
);
|
|
||||||
$flow_spacing = unscale $distance_between_lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $x = $bounding_box->[X1];
|
|
||||||
my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
|
my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
|
||||||
my @vertical_lines = ();
|
|
||||||
for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) {
|
my $cache_id = sprintf "d%s_s%s_a%s",
|
||||||
my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]);
|
$params{density}, $params{flow_spacing}, $rotate_vector->[0][0];
|
||||||
if ($is_line_pattern && $i % 2) {
|
|
||||||
$vertical_line->[A][X] += $line_oscillation;
|
if (!$self->cache->{$cache_id}) {
|
||||||
$vertical_line->[B][X] -= $line_oscillation;
|
# compute bounding box
|
||||||
|
my $bounding_box = $self->bounding_box;
|
||||||
|
{
|
||||||
|
my $bb_expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_from_bounding_box($bounding_box));
|
||||||
|
$self->rotate_points($bb_expolygon, $rotate_vector);
|
||||||
|
$bounding_box = [ $bb_expolygon->bounding_box ];
|
||||||
}
|
}
|
||||||
push @vertical_lines, $vertical_line;
|
|
||||||
$x += $distance_between_lines;
|
# define flow spacing according to requested density
|
||||||
|
if ($params{density} == 1 && !$params{dont_adjust}) {
|
||||||
|
$distance_between_lines = $self->adjust_solid_spacing(
|
||||||
|
width => $bounding_box->[X2] - $bounding_box->[X1],
|
||||||
|
distance => $distance_between_lines,
|
||||||
|
);
|
||||||
|
$flow_spacing = unscale $distance_between_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate the basic pattern
|
||||||
|
my $x = $bounding_box->[X1];
|
||||||
|
my @vertical_lines = ();
|
||||||
|
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;
|
||||||
|
$vertical_line->[B][X] -= $line_oscillation;
|
||||||
|
}
|
||||||
|
push @vertical_lines, $vertical_line;
|
||||||
|
$x += $distance_between_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->cache->{$cache_id} = [@vertical_lines];
|
||||||
}
|
}
|
||||||
|
|
||||||
# clip paths against a slightly offsetted expolygon, so that the first and last paths
|
# clip paths against a slightly offsetted expolygon, so that the first and last paths
|
||||||
# are kept even if the expolygon has vertical sides
|
# are kept even if the expolygon has vertical sides
|
||||||
my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection(
|
my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection(
|
||||||
+($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
|
+($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
|
||||||
[ @vertical_lines ],
|
[ @{ $self->cache->{$cache_id} } ],
|
||||||
) };
|
) };
|
||||||
|
|
||||||
# connect lines
|
# connect lines
|
||||||
|
|
|
@ -248,7 +248,7 @@ sub extrude_path {
|
||||||
$gcode .= $self->G1($line->[B], undef, $e * $line_length, $description);
|
$gcode .= $self->G1($line->[B], undef, $e * $line_length, $description);
|
||||||
}
|
}
|
||||||
$self->wipe_path(Slic3r::Polyline->new([ reverse @{$path->points} ]))
|
$self->wipe_path(Slic3r::Polyline->new([ reverse @{$path->points} ]))
|
||||||
if $Slic3r::Config->wipe;
|
if $self->extruder->wipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Slic3r::Config->cooling) {
|
if ($Slic3r::Config->cooling) {
|
||||||
|
@ -358,19 +358,19 @@ sub retract {
|
||||||
|
|
||||||
# wipe
|
# wipe
|
||||||
my $wipe_path;
|
my $wipe_path;
|
||||||
if ($Slic3r::Config->wipe && $self->wipe_path) {
|
if ($self->extruder->wipe && $self->wipe_path) {
|
||||||
$wipe_path = Slic3r::Polyline->new([ $self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}] ])
|
$wipe_path = Slic3r::Polyline->new([ $self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}] ])
|
||||||
->clip_start($self->extruder->scaled_wipe_distance);
|
->clip_start($self->extruder->scaled_wipe_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
# prepare moves
|
# prepare moves
|
||||||
$self->speed('retract');
|
|
||||||
my $retract = [undef, undef, -$length, $comment];
|
my $retract = [undef, undef, -$length, $comment];
|
||||||
my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted
|
my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted
|
||||||
? undef
|
? undef
|
||||||
: [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel'];
|
: [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel'];
|
||||||
|
|
||||||
if (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && $params{travel_to}) {
|
if (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && $params{travel_to}) {
|
||||||
|
$self->speed('travel');
|
||||||
if ($lift) {
|
if ($lift) {
|
||||||
# combine lift and retract
|
# combine lift and retract
|
||||||
$lift->[2] = $retract->[2];
|
$lift->[2] = $retract->[2];
|
||||||
|
@ -382,22 +382,25 @@ sub retract {
|
||||||
}
|
}
|
||||||
} elsif (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && defined $params{move_z}) {
|
} elsif (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && defined $params{move_z}) {
|
||||||
# combine Z change and retraction
|
# combine Z change and retraction
|
||||||
|
$self->speed('travel');
|
||||||
my $travel = [undef, $params{move_z}, $retract->[2], "change layer and $comment"];
|
my $travel = [undef, $params{move_z}, $retract->[2], "change layer and $comment"];
|
||||||
$gcode .= $self->G0(@$travel);
|
$gcode .= $self->G0(@$travel);
|
||||||
} else {
|
} else {
|
||||||
if ($wipe_path) {
|
# check that we have a positive wipe length
|
||||||
|
if ($wipe_path && (my $total_wipe_length = $wipe_path->length)) {
|
||||||
$self->speed('travel');
|
$self->speed('travel');
|
||||||
# subdivide the retraction
|
|
||||||
my $total_wipe_length = $wipe_path->length;
|
|
||||||
|
|
||||||
|
# subdivide the retraction
|
||||||
for (1 .. $#$wipe_path) {
|
for (1 .. $#$wipe_path) {
|
||||||
my $segment_length = $wipe_path->[$_-1]->distance_to($wipe_path->[$_]);
|
my $segment_length = $wipe_path->[$_-1]->distance_to($wipe_path->[$_]);
|
||||||
$gcode .= $self->G1($wipe_path->[$_], undef, $retract->[2] * ($segment_length / $total_wipe_length), $retract->[3] . ";_WIPE");
|
$gcode .= $self->G1($wipe_path->[$_], undef, $retract->[2] * ($segment_length / $total_wipe_length), $retract->[3] . ";_WIPE");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$self->speed('retract');
|
||||||
$gcode .= $self->G1(@$retract);
|
$gcode .= $self->G1(@$retract);
|
||||||
}
|
}
|
||||||
if (!$self->lifted) {
|
if (!$self->lifted) {
|
||||||
|
$self->speed('travel');
|
||||||
if (defined $params{move_z} && $self->extruder->retract_lift > 0) {
|
if (defined $params{move_z} && $self->extruder->retract_lift > 0) {
|
||||||
my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
|
my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
|
||||||
$gcode .= $self->G0(@$travel);
|
$gcode .= $self->G0(@$travel);
|
||||||
|
|
77
lib/Slic3r/GCode/CoolingBuffer.pm
Normal file
77
lib/Slic3r/GCode/CoolingBuffer.pm
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package Slic3r::GCode::CoolingBuffer;
|
||||||
|
use Moo;
|
||||||
|
|
||||||
|
has 'config' => (is => 'ro', required => 1);
|
||||||
|
has 'gcodegen' => (is => 'ro', required => 1);
|
||||||
|
has 'gcode' => (is => 'rw', default => sub {""});
|
||||||
|
has 'layer_id' => (is => 'rw');
|
||||||
|
has 'last_z' => (is => 'rw');
|
||||||
|
has 'min_print_speed' => (is => 'lazy');
|
||||||
|
|
||||||
|
sub _build_min_print_speed {
|
||||||
|
my $self = shift;
|
||||||
|
return 60 * $self->config->min_print_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub append {
|
||||||
|
my $self = shift;
|
||||||
|
my ($gcode, $layer) = @_;
|
||||||
|
|
||||||
|
my $return = "";
|
||||||
|
if (defined $self->last_z && $self->last_z != $layer->print_z) {
|
||||||
|
$return = $self->flush;
|
||||||
|
$self->gcodegen->elapsed_time(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->layer_id($layer->id);
|
||||||
|
$self->last_z($layer->print_z);
|
||||||
|
$self->gcode($self->gcode . $gcode);
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub flush {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $gcode = $self->gcode;
|
||||||
|
$self->gcode("");
|
||||||
|
|
||||||
|
my $fan_speed = $self->config->fan_always_on ? $self->config->min_fan_speed : 0;
|
||||||
|
my $speed_factor = 1;
|
||||||
|
if ($self->config->cooling) {
|
||||||
|
my $layer_time = $self->gcodegen->elapsed_time;
|
||||||
|
Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $layer_time;
|
||||||
|
if ($layer_time < $self->config->slowdown_below_layer_time) {
|
||||||
|
$fan_speed = $self->config->max_fan_speed;
|
||||||
|
$speed_factor = $layer_time / $self->config->slowdown_below_layer_time;
|
||||||
|
} elsif ($layer_time < $self->config->fan_below_layer_time) {
|
||||||
|
$fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed)
|
||||||
|
* ($layer_time - $self->config->slowdown_below_layer_time)
|
||||||
|
/ ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/
|
||||||
|
}
|
||||||
|
Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
|
||||||
|
|
||||||
|
if ($speed_factor < 1) {
|
||||||
|
my $dec = $self->gcodegen->dec;
|
||||||
|
$gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?<!;_BRIDGE_FAN_START\n)(G1 .*?F)(\d+(?:\.\d+)?)/
|
||||||
|
my $new_speed = $2 * $speed_factor;
|
||||||
|
$1 . sprintf("%.${dec}f", $new_speed < $self->min_print_speed ? $self->min_print_speed : $new_speed)
|
||||||
|
/gexm;
|
||||||
|
}
|
||||||
|
$fan_speed = 0 if $self->layer_id < $self->config->disable_fan_first_layers;
|
||||||
|
}
|
||||||
|
$gcode = $self->gcodegen->set_fan($fan_speed) . $gcode;
|
||||||
|
|
||||||
|
# bridge fan speed
|
||||||
|
if (!$self->config->cooling || $self->config->bridge_fan_speed == 0 || $self->layer_id < $self->config->disable_fan_first_layers) {
|
||||||
|
$gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm;
|
||||||
|
} else {
|
||||||
|
$gcode =~ s/^;_BRIDGE_FAN_START\n/ $self->gcodegen->set_fan($self->config->bridge_fan_speed, 1) /gmex;
|
||||||
|
$gcode =~ s/^;_BRIDGE_FAN_END\n/ $self->gcodegen->set_fan($fan_speed, 1) /gmex;
|
||||||
|
}
|
||||||
|
$gcode =~ s/;_WIPE//g;
|
||||||
|
|
||||||
|
return $gcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -243,7 +243,9 @@ sub config_wizard {
|
||||||
|
|
||||||
return unless $self->check_unsaved_changes;
|
return unless $self->check_unsaved_changes;
|
||||||
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
|
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
|
||||||
$_->select_default_preset for values %{$self->{options_tabs}};
|
if ($self->{mode} eq 'expert') {
|
||||||
|
$_->select_default_preset for values %{$self->{options_tabs}};
|
||||||
|
}
|
||||||
$self->load_config($config);
|
$self->load_config($config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ our @ISA = qw(Exporter);
|
||||||
our @EXPORT_OK = qw(
|
our @EXPORT_OK = qw(
|
||||||
PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel
|
PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel
|
||||||
line_point_belongs_to_segment points_coincide distance_between_points
|
line_point_belongs_to_segment points_coincide distance_between_points
|
||||||
comparable_distance_between_points chained_path_items chained_path_points
|
chained_path_items chained_path_points
|
||||||
line_length midpoint point_in_polygon point_in_segment segment_in_segment
|
line_length midpoint point_in_polygon point_in_segment segment_in_segment
|
||||||
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
|
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
|
||||||
point_along_segment polygon_segment_having_point polygon_has_subsegment
|
point_along_segment polygon_segment_having_point polygon_has_subsegment
|
||||||
|
@ -115,11 +115,6 @@ sub distance_between_points {
|
||||||
return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2);
|
return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub comparable_distance_between_points {
|
|
||||||
my ($p1, $p2) = @_;
|
|
||||||
return (($p1->[X] - $p2->[X])**2) + (($p1->[Y] - $p2->[Y])**2);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub point_line_distance {
|
sub point_line_distance {
|
||||||
my ($point, $line) = @_;
|
my ($point, $line) = @_;
|
||||||
return distance_between_points($point, $line->[A])
|
return distance_between_points($point, $line->[A])
|
||||||
|
@ -248,14 +243,27 @@ sub nearest_point_index {
|
||||||
my ($point, $points) = @_;
|
my ($point, $points) = @_;
|
||||||
|
|
||||||
my ($nearest_point_index, $distance) = ();
|
my ($nearest_point_index, $distance) = ();
|
||||||
|
|
||||||
|
my $point_x = $point->[X];
|
||||||
|
my $point_y = $point->[Y];
|
||||||
|
|
||||||
for my $i (0..$#$points) {
|
for my $i (0..$#$points) {
|
||||||
my $d = comparable_distance_between_points($point, $points->[$i]);
|
my $d = ($point_x - $points->[$i]->[X])**2;
|
||||||
if (!defined $distance || $d < $distance) {
|
# If the X distance of the candidate is > than the total distance of the
|
||||||
$nearest_point_index = $i;
|
# best previous candidate, we know we don't want it
|
||||||
$distance = $d;
|
next if (defined $distance && $d > $distance);
|
||||||
return $i if $distance < epsilon;
|
|
||||||
}
|
# If the total distance of the candidate is > than the total distance of the
|
||||||
|
# best previous candidate, we know we don't want it
|
||||||
|
$d += ($point_y - $points->[$i]->[Y])**2;
|
||||||
|
next if (defined $distance && $d > $distance);
|
||||||
|
|
||||||
|
$nearest_point_index = $i;
|
||||||
|
$distance = $d;
|
||||||
|
|
||||||
|
last if $distance < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $nearest_point_index;
|
return $nearest_point_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,10 +679,10 @@ sub bounding_box {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub bounding_box_center {
|
sub bounding_box_center {
|
||||||
my @bounding_box = bounding_box(@_);
|
my ($bounding_box) = @_;
|
||||||
return Slic3r::Point->new(
|
return Slic3r::Point->new(
|
||||||
($bounding_box[X2] + $bounding_box[X1]) / 2,
|
($bounding_box->[X2] + $bounding_box->[X1]) / 2,
|
||||||
($bounding_box[Y2] + $bounding_box[Y1]) / 2,
|
($bounding_box->[Y2] + $bounding_box->[Y1]) / 2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ sub _merge_loops {
|
||||||
# winding order.
|
# winding order.
|
||||||
# TODO: find a faster algorithm for this.
|
# TODO: find a faster algorithm for this.
|
||||||
my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first
|
my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first
|
||||||
$safety_offset //= scale 0.1;
|
$safety_offset //= scale 0.0499;
|
||||||
@loops = @{ safety_offset(\@loops, $safety_offset) };
|
@loops = @{ safety_offset(\@loops, $safety_offset) };
|
||||||
my $expolygons = [];
|
my $expolygons = [];
|
||||||
while (my $loop = shift @loops) {
|
while (my $loop = shift @loops) {
|
||||||
|
|
|
@ -151,7 +151,9 @@ sub validate {
|
||||||
{
|
{
|
||||||
my @points = map [ @$_[X,Y] ], map @{$_->vertices}, @{$self->objects->[$obj_idx]->meshes};
|
my @points = map [ @$_[X,Y] ], map @{$_->vertices}, @{$self->objects->[$obj_idx]->meshes};
|
||||||
my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
|
my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points));
|
||||||
($clearance) = offset([$convex_hull], scale $Slic3r::Config->extruder_clearance_radius / 2, 1, JT_ROUND);
|
($clearance) = map Slic3r::Polygon->new($_),
|
||||||
|
Slic3r::Geometry::Clipper::offset(
|
||||||
|
[$convex_hull], scale $Slic3r::Config->extruder_clearance_radius / 2, 1, JT_ROUND);
|
||||||
}
|
}
|
||||||
for my $copy (@{$self->objects->[$obj_idx]->copies}) {
|
for my $copy (@{$self->objects->[$obj_idx]->copies}) {
|
||||||
my $copy_clearance = $clearance->clone;
|
my $copy_clearance = $clearance->clone;
|
||||||
|
@ -714,8 +716,6 @@ sub write_gcode {
|
||||||
multiple_extruders => (@{$self->extruders} > 1),
|
multiple_extruders => (@{$self->extruders} > 1),
|
||||||
layer_count => $self->layer_count,
|
layer_count => $self->layer_count,
|
||||||
);
|
);
|
||||||
my $min_print_speed = 60 * $Slic3r::Config->min_print_speed;
|
|
||||||
my $dec = $gcodegen->dec;
|
|
||||||
print $fh "G21 ; set units to millimeters\n";
|
print $fh "G21 ; set units to millimeters\n";
|
||||||
print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
|
print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
|
||||||
|
|
||||||
|
@ -794,11 +794,11 @@ sub write_gcode {
|
||||||
}
|
}
|
||||||
$gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature)
|
$gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature)
|
||||||
if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature;
|
if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature;
|
||||||
|
$second_layer_things_done = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# set new layer, but don't move Z as support material contact areas may need an intermediate one
|
# set new layer, but don't move Z as support material contact areas may need an intermediate one
|
||||||
$gcode .= $gcodegen->change_layer($layer);
|
$gcode .= $gcodegen->change_layer($layer);
|
||||||
$gcodegen->elapsed_time(0);
|
|
||||||
|
|
||||||
# prepare callback to call as soon as a Z command is generated
|
# prepare callback to call as soon as a Z command is generated
|
||||||
$gcodegen->move_z_callback(sub {
|
$gcodegen->move_z_callback(sub {
|
||||||
|
@ -940,42 +940,6 @@ sub write_gcode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if !$gcode;
|
|
||||||
|
|
||||||
my $fan_speed = $Slic3r::Config->fan_always_on ? $Slic3r::Config->min_fan_speed : 0;
|
|
||||||
my $speed_factor = 1;
|
|
||||||
if ($Slic3r::Config->cooling) {
|
|
||||||
my $layer_time = $gcodegen->elapsed_time;
|
|
||||||
Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $layer->id, $layer_time;
|
|
||||||
if ($layer_time < $Slic3r::Config->slowdown_below_layer_time) {
|
|
||||||
$fan_speed = $Slic3r::Config->max_fan_speed;
|
|
||||||
$speed_factor = $layer_time / $Slic3r::Config->slowdown_below_layer_time;
|
|
||||||
} elsif ($layer_time < $Slic3r::Config->fan_below_layer_time) {
|
|
||||||
$fan_speed = $Slic3r::Config->max_fan_speed - ($Slic3r::Config->max_fan_speed - $Slic3r::Config->min_fan_speed)
|
|
||||||
* ($layer_time - $Slic3r::Config->slowdown_below_layer_time)
|
|
||||||
/ ($Slic3r::Config->fan_below_layer_time - $Slic3r::Config->slowdown_below_layer_time); #/
|
|
||||||
}
|
|
||||||
Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
|
|
||||||
|
|
||||||
if ($speed_factor < 1) {
|
|
||||||
$gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?<!;_BRIDGE_FAN_START\n)(G1 .*?F)(\d+(?:\.\d+)?)/
|
|
||||||
my $new_speed = $2 * $speed_factor;
|
|
||||||
$1 . sprintf("%.${dec}f", $new_speed < $min_print_speed ? $min_print_speed : $new_speed)
|
|
||||||
/gexm;
|
|
||||||
}
|
|
||||||
$fan_speed = 0 if $layer->id < $Slic3r::Config->disable_fan_first_layers;
|
|
||||||
}
|
|
||||||
$gcode = $gcodegen->set_fan($fan_speed) . $gcode;
|
|
||||||
|
|
||||||
# bridge fan speed
|
|
||||||
if (!$Slic3r::Config->cooling || $Slic3r::Config->bridge_fan_speed == 0 || $layer->id < $Slic3r::Config->disable_fan_first_layers) {
|
|
||||||
$gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm;
|
|
||||||
} else {
|
|
||||||
$gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::Config->bridge_fan_speed, 1) /gmex;
|
|
||||||
$gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex;
|
|
||||||
}
|
|
||||||
$gcode =~ s/;_WIPE//g;
|
|
||||||
|
|
||||||
return $gcode;
|
return $gcode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -998,6 +962,11 @@ sub write_gcode {
|
||||||
print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
|
print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $buffer = Slic3r::GCode::CoolingBuffer->new(
|
||||||
|
config => $Slic3r::Config,
|
||||||
|
gcodegen => $gcodegen,
|
||||||
|
);
|
||||||
|
|
||||||
for my $layer (@{$self->objects->[$obj_idx]->layers}) {
|
for my $layer (@{$self->objects->[$obj_idx]->layers}) {
|
||||||
# if we are printing the bottom layer of an object, and we have already finished
|
# if we are printing the bottom layer of an object, and we have already finished
|
||||||
# another one, set first layer temperatures. this happens before the Z move
|
# another one, set first layer temperatures. this happens before the Z move
|
||||||
|
@ -1007,15 +976,20 @@ sub write_gcode {
|
||||||
if $Slic3r::Config->first_layer_bed_temperature;
|
if $Slic3r::Config->first_layer_bed_temperature;
|
||||||
$print_first_layer_temperature->();
|
$print_first_layer_temperature->();
|
||||||
}
|
}
|
||||||
print $fh $extrude_layer->($layer, [$copy]);
|
print $fh $buffer->append($extrude_layer->($layer, [$copy]), $layer);
|
||||||
}
|
}
|
||||||
|
print $fh $buffer->flush;
|
||||||
$finished_objects++;
|
$finished_objects++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print $fh $extrude_layer->($_, $_->object->copies)
|
my $buffer = Slic3r::GCode::CoolingBuffer->new(
|
||||||
for sort { $a->print_z <=> $b->print_z }
|
config => $Slic3r::Config,
|
||||||
map @{$_->layers}, @{$self->objects};
|
gcodegen => $gcodegen,
|
||||||
|
);
|
||||||
|
print $fh $buffer->append($extrude_layer->($_, $_->object->copies), $_)
|
||||||
|
for sort { $a->print_z <=> $b->print_z } map @{$_->layers}, @{$self->objects};
|
||||||
|
print $fh $buffer->flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
# save statistic data
|
# save statistic data
|
||||||
|
|
|
@ -894,6 +894,7 @@ sub generate_support_material {
|
||||||
|
|
||||||
# generate paths for the pattern that we're going to use
|
# generate paths for the pattern that we're going to use
|
||||||
Slic3r::debugf "Generating patterns\n";
|
Slic3r::debugf "Generating patterns\n";
|
||||||
|
my $fill = Slic3r::Fill->new(print => $self->print);
|
||||||
my $support_patterns = [];
|
my $support_patterns = [];
|
||||||
my $support_interface_patterns = [];
|
my $support_interface_patterns = [];
|
||||||
{
|
{
|
||||||
|
@ -908,10 +909,7 @@ sub generate_support_material {
|
||||||
push @angles, $angles[0] + 90;
|
push @angles, $angles[0] + 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $filler = Slic3r::Fill->filler($pattern);
|
my $filler = $fill->filler($pattern);
|
||||||
$filler->bounding_box([ Slic3r::Geometry::bounding_box([ map @$_, map @$_, @areas ]) ])
|
|
||||||
if $filler->can('bounding_box');
|
|
||||||
|
|
||||||
my $make_pattern = sub {
|
my $make_pattern = sub {
|
||||||
my ($expolygon, $density) = @_;
|
my ($expolygon, $density) = @_;
|
||||||
|
|
||||||
|
@ -996,7 +994,7 @@ sub generate_support_material {
|
||||||
|
|
||||||
# make a solid base on bottom layer
|
# make a solid base on bottom layer
|
||||||
if ($layer_id == 0) {
|
if ($layer_id == 0) {
|
||||||
my $filler = Slic3r::Fill->filler('rectilinear');
|
my $filler = $fill->filler('rectilinear');
|
||||||
$filler->angle($Slic3r::Config->support_material_angle + 90);
|
$filler->angle($Slic3r::Config->support_material_angle + 90);
|
||||||
foreach my $expolygon (@$islands) {
|
foreach my $expolygon (@$islands) {
|
||||||
my @paths = $filler->fill_surface(
|
my @paths = $filler->fill_surface(
|
||||||
|
|
|
@ -144,8 +144,8 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv
|
||||||
|
|
||||||
my $intersections = $expolygon->clip_line($line);
|
my $intersections = $expolygon->clip_line($line);
|
||||||
is_deeply $intersections, [
|
is_deeply $intersections, [
|
||||||
[ [152, 287], [152, 214], ],
|
[ [152.742, 288.086660915295], [152.742, 215.178843238354], ],
|
||||||
[ [152, 107], [152, 35] ],
|
[ [152.742, 108.087506777797], [152.742, 35.1664774739315] ],
|
||||||
], 'line is clipped to square with hole';
|
], 'line is clipped to square with hole';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ my $test = sub {
|
||||||
if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift);
|
if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift);
|
||||||
$lifted = 0;
|
$lifted = 0;
|
||||||
}
|
}
|
||||||
|
fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60;
|
||||||
}
|
}
|
||||||
if ($info->{retracting}) {
|
if ($info->{retracting}) {
|
||||||
$retracted[$tool] = 1;
|
$retracted[$tool] = 1;
|
||||||
|
@ -89,6 +90,7 @@ my $test = sub {
|
||||||
};
|
};
|
||||||
|
|
||||||
$config->set('first_layer_height', $config->layer_height);
|
$config->set('first_layer_height', $config->layer_height);
|
||||||
|
$config->set('first_layer_speed', '100%');
|
||||||
$config->set('start_gcode', ''); # to avoid dealing with the nozzle lift in start G-code
|
$config->set('start_gcode', ''); # to avoid dealing with the nozzle lift in start G-code
|
||||||
$config->set('retract_length', [1.5]);
|
$config->set('retract_length', [1.5]);
|
||||||
$config->set('retract_before_travel', [3]);
|
$config->set('retract_before_travel', [3]);
|
||||||
|
|
Loading…
Add table
Reference in a new issue