Merge branch 'master' into avoid-crossing-perimeters

Conflicts:
	lib/Slic3r/GCode.pm
	lib/Slic3r/GUI/Plater.pm
	lib/Slic3r/Print.pm
	lib/Slic3r/SVG.pm
This commit is contained in:
Alessandro Ranellucci 2013-01-12 19:00:18 +01:00
commit 48e00a4c40
52 changed files with 2388 additions and 821 deletions

View file

@ -3,7 +3,7 @@ use Moo;
use File::Basename qw(basename fileparse);
use File::Spec;
use List::Util qw(max);
use List::Util qw(max first);
use Math::ConvexHull::MonotoneChain qw(convex_hull);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN PI scale unscale move_points nearest_point);
@ -54,6 +54,8 @@ sub _trigger_config {
$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);
$self->config->set_ifndef('top_solid_layers', $self->config->solid_layers);
$self->config->set_ifndef('bottom_solid_layers', $self->config->solid_layers);
# G-code flavors
$self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3';
@ -339,12 +341,12 @@ sub export_gcode {
# simplify slices (both layer and region slices),
# we only need the max resolution for perimeters
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
$_->simplify(scale &Slic3r::RESOLUTION)
$_->simplify(&Slic3r::SCALED_RESOLUTION)
for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions});
}
# this will clip $layer->surfaces to the infill boundaries
# and split them in top/bottom/internal surfaces;
# this will transform $layer->fill_surfaces from expolygon
# to typed top/bottom/internal surfaces;
$status_cb->(30, "Detecting solid surfaces");
$_->detect_surfaces_type for @{$self->objects};
@ -362,9 +364,6 @@ sub export_gcode {
$status_cb->(60, "Generating horizontal shells");
$_->discover_horizontal_shells for @{$self->objects};
# free memory
$_->surfaces(undef) for map @{$_->regions}, map @{$_->layers}, @{$self->objects};
# combine fill surfaces to honor the "infill every N layers" option
$status_cb->(70, "Combining infill");
$_->combine_infill for @{$self->objects};
@ -433,8 +432,8 @@ sub export_gcode {
# output everything to a G-code file
my $output_file = $self->expanded_output_filepath($params{output_file});
$status_cb->(90, "Exporting G-code to $output_file");
$self->write_gcode($output_file);
$status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
$self->write_gcode($params{output_fh} || $output_file);
# run post-processing scripts
if (@{$Slic3r::Config->post_process}) {
@ -447,14 +446,16 @@ sub export_gcode {
}
# output some statistics
$self->processing_time(tv_interval($t0));
printf "Done. Process took %d minutes and %.3f seconds\n",
int($self->processing_time/60),
$self->processing_time - int($self->processing_time/60)*60;
# TODO: more statistics!
printf "Filament required: %.1fmm (%.1fcm3)\n",
$self->total_extrusion_length, $self->total_extrusion_volume;
unless ($params{quiet}) {
$self->processing_time(tv_interval($t0));
printf "Done. Process took %d minutes and %.3f seconds\n",
int($self->processing_time/60),
$self->processing_time - int($self->processing_time/60)*60;
# TODO: more statistics!
printf "Filament required: %.1fmm (%.1fcm3)\n",
$self->total_extrusion_length, $self->total_extrusion_volume;
}
}
sub export_svg {
@ -553,7 +554,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 @$_, map @{$_->slices}, @layers),
(map @$_, map @{$_->thin_walls}, map @{$_->regions}, @layers),
@ -566,16 +567,40 @@ sub make_skirt {
# find out convex hull
my $convex_hull = convex_hull(\@points);
my @extruded_length = (); # for each extruder
my $spacing = $Slic3r::first_layer_flow->spacing;
my $first_layer_height = $Slic3r::Config->get_value('first_layer_height');
my @extruders_e_per_mm = ();
my $extruder_idx = 0;
# draw outlines from outside to inside
# loop while we have less skirts than required or any extruder hasn't reached the min length if any
my $distance = scale $Slic3r::Config->skirt_distance;
for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) {
my $distance = scale ($Slic3r::Config->skirt_distance + ($Slic3r::first_layer_flow->spacing * $i));
my $outline = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND);
$distance += scale $spacing;
my ($loop) = Slic3r::Geometry::Clipper::offset([$convex_hull], $distance, 0.0001, JT_ROUND);
push @{$self->skirt}, Slic3r::ExtrusionLoop->pack(
polygon => Slic3r::Polygon->new(@{$outline->[0]}),
polygon => Slic3r::Polygon->new(@$loop),
role => EXTR_ROLE_SKIRT,
flow_spacing => $Slic3r::first_layer_flow->spacing,
flow_spacing => $spacing,
);
if ($Slic3r::Config->min_skirt_length > 0) {
bless $loop, 'Slic3r::Polygon';
$extruded_length[$extruder_idx] ||= 0;
$extruders_e_per_mm[$extruder_idx] ||= $self->extruders->[$extruder_idx]->e_per_mm($spacing, $first_layer_height);
$extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
$i++ if defined first { ($extruded_length[$_] // 0) < $Slic3r::Config->min_skirt_length } 0 .. $#{$self->extruders};
if ($extruded_length[$extruder_idx] >= $Slic3r::Config->min_skirt_length) {
if ($extruder_idx < $#{$self->extruders}) {
$extruder_idx++;
next;
}
}
}
}
@{$self->skirt} = reverse @{$self->skirt};
}
sub make_brim {
@ -608,7 +633,7 @@ sub make_brim {
polygon => Slic3r::Polygon->new($_),
role => EXTR_ROLE_SKIRT,
flow_spacing => $Slic3r::first_layer_flow->spacing,
) for @{Math::Clipper::offset(\@islands, $i * $Slic3r::first_layer_flow->scaled_spacing, 100, JT_SQUARE)};
) for Slic3r::Geometry::Clipper::offset(\@islands, $i * $Slic3r::first_layer_flow->scaled_spacing, undef, JT_SQUARE);
# TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions
}
}
@ -617,9 +642,14 @@ sub write_gcode {
my $self = shift;
my ($file) = @_;
# open output gcode file
open my $fh, ">", $file
or die "Failed to open $file for writing\n";
# open output gcode file if we weren't supplied a file-handle
my $fh;
if (ref $file eq 'IO::Scalar') {
$fh = $file;
} else {
open $fh, ">", $file
or die "Failed to open $file for writing\n";
}
# write some information
my @lt = localtime;
@ -629,14 +659,17 @@ sub write_gcode {
print $fh "; $_\n" foreach split /\R/, $Slic3r::Config->notes;
print $fh "\n" if $Slic3r::Config->notes;
for (qw(layer_height perimeters solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) {
for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) {
printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_;
}
for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) {
printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_->[0];
}
printf $fh "; single wall width = %.2fmm\n", $Slic3r::flow->width;
printf $fh "; first layer single wall width = %.2fmm\n", $Slic3r::first_layer_flow->width
printf $fh "; perimeters extrusion width = %.2fmm\n", $self->regions->[0]->flows->{perimeter}->width;
printf $fh "; infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{infill}->width;
printf $fh "; support material extrusion width = %.2fmm\n", $self->support_material_flow->width
if $self->support_material_flow;
printf $fh "; first layer extrusion width = %.2fmm\n", $Slic3r::first_layer_flow->width
if $Slic3r::first_layer_flow;
print $fh "\n";
@ -646,7 +679,6 @@ sub write_gcode {
);
my $min_print_speed = 60 * $Slic3r::Config->min_print_speed;
my $dec = $gcodegen->dec;
print $fh $gcodegen->set_extruder($self->extruders->[0]);
print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
# write start commands to file
@ -717,20 +749,36 @@ sub write_gcode {
if $self->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature;
}
$gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature)
if $Slic3r::Config->first_layer_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;
}
# go to layer (just use the first one, we only need Z from it)
$gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]);
# set new layer, but don't move Z as support material interfaces may need an intermediate one
$gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]);
$gcodegen->elapsed_time(0);
# prepare callback to call as soon as a Z command is generated
$gcodegen->move_z_callback(sub {
$gcodegen->move_z_callback(undef); # circular ref or not?
return "" if !$Slic3r::Config->layer_gcode;
return $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n";
});
# extrude skirt
if ($skirt_done < $Slic3r::Config->skirt_height) {
$gcodegen->set_shift(@shift);
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration);
$gcode .= $gcodegen->set_extruder($self->extruders->[0]); # move_z requires extruder
$gcode .= $gcodegen->move_z($gcodegen->layer->print_z);
# skip skirt if we have a large brim
if ($layer_id < $Slic3r::Config->skirt_height) {
$gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt};
# distribute skirt loops across all extruders
for my $i (0 .. $#{$self->skirt}) {
# when printing layers > 0 ignore 'min_skirt_length' and
# just use the 'skirts' setting; also just use the current extruder
last if ($layer_id > 0) && ($i >= $Slic3r::Config->skirts);
$gcode .= $gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ])
if $layer_id == 0;
$gcode .= $gcodegen->extrude_loop($self->skirt->[$i], 'skirt');
}
}
$skirt_done++;
$gcodegen->straight_once(1);
@ -738,9 +786,9 @@ sub write_gcode {
# extrude brim
if ($layer_id == 0 && !$brim_done) {
$gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]);
$gcodegen->shift_x($shift[X]);
$gcodegen->shift_y($shift[Y]);
$gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); # move_z requires extruder
$gcode .= $gcodegen->move_z($gcodegen->layer->print_z);
$gcodegen->set_shift(@shift);
$gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim};
$brim_done = 1;
$gcodegen->straight_once(1);
@ -751,44 +799,58 @@ sub write_gcode {
$gcodegen->new_object(1) if $last_obj_copy && $last_obj_copy ne "${obj_idx}_${copy}";
my $layer = $self->objects->[$obj_idx]->layers->[$layer_id];
# retract explicitely because changing the shift_[xy] properties below
# won't always trigger the automatic retraction
$gcode .= $gcodegen->retract;
$gcodegen->set_shift(map $shift[$_] + unscale $copy->[$_], X,Y);
$gcodegen->set_shift(
$shift[X] + unscale $copy->[X],
$shift[Y] + unscale $copy->[Y],
);
# extrude support material before other things because it might use a lower Z
# and also because we avoid travelling on other things when printing it
if ($Slic3r::Config->support_material) {
$gcode .= $gcodegen->move_z($layer->support_material_interface_z)
if ($layer->support_interface_fills && @{ $layer->support_interface_fills->paths });
$gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]);
if ($layer->support_interface_fills) {
$gcode .= $gcodegen->extrude_path($_, 'support material interface')
for $layer->support_interface_fills->shortest_path($gcodegen->last_pos);
}
$gcode .= $gcodegen->move_z($layer->print_z);
if ($layer->support_fills) {
$gcode .= $gcodegen->extrude_path($_, 'support material')
for $layer->support_fills->shortest_path($gcodegen->last_pos);
}
}
# set actual Z - this will force a retraction
$gcode .= $gcodegen->move_z($layer->print_z);
foreach my $region_id (0 .. ($self->regions_count-1)) {
my $layerm = $layer->regions->[$region_id];
my $region = $self->regions->[$region_id];
# extrude perimeters
$gcode .= $gcodegen->set_extruder($region->extruders->{perimeter});
$gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters };
if (@{ $layerm->perimeters }) {
$gcode .= $gcodegen->set_extruder($region->extruders->{perimeter});
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration);
$gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters };
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->default_acceleration)
if $Slic3r::Config->perimeter_acceleration;
}
# extrude fills
$gcode .= $gcodegen->set_extruder($region->extruders->{infill});
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration);
for my $fill (@{ $layerm->fills }) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $gcodegen->extrude($_, 'fill')
for $fill->shortest_path($gcodegen->last_pos);
} else {
$gcode .= $gcodegen->extrude($fill, 'fill') ;
if (@{ $layerm->fills }) {
$gcode .= $gcodegen->set_extruder($region->extruders->{infill});
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration);
for my $fill (@{ $layerm->fills }) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $gcodegen->extrude($_, 'fill')
for $fill->shortest_path($gcodegen->last_pos);
} else {
$gcode .= $gcodegen->extrude($fill, 'fill') ;
}
}
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->default_acceleration)
if $Slic3r::Config->infill_acceleration;
}
}
# extrude support material
if ($layer->support_fills) {
$gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]);
$gcode .= $gcodegen->extrude_path($_, 'support material')
for $layer->support_fills->shortest_path($gcodegen->last_pos);
}
$last_obj_copy = "${obj_idx}_${copy}";
}
return if !$gcode;
@ -808,7 +870,7 @@ sub write_gcode {
Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100;
if ($speed_factor < 1) {
$gcode =~ s/^(?=.*? [XY])(?=.*? E)(G1 .*?F)(\d+(?:\.\d+)?)/
$gcode =~ s/^(?=.*? [XY])(?=.*? E)(?<!;_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;
@ -842,10 +904,7 @@ sub write_gcode {
# this happens before Z goes down to layer 0 again, so that
# no collision happens hopefully.
if ($finished_objects > 0) {
$gcodegen->set_shift(
$shift[X] + unscale $copy->[X],
$shift[Y] + unscale $copy->[Y],
);
$gcodegen->set_shift(map $shift[$_] + unscale $copy->[$_], X,Y);
print $fh $gcodegen->retract;
print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
}
@ -880,12 +939,21 @@ sub write_gcode {
# write end commands to file
print $fh $gcodegen->retract;
print $fh $gcodegen->set_fan(0);
print $fh "M501 ; reset acceleration\n" if $Slic3r::Config->acceleration;
printf $fh "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->end_gcode);
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
$self->total_extrusion_length, $self->total_extrusion_volume;
if ($Slic3r::Config->gcode_comments) {
# append full config
print $fh "\n";
foreach my $opt_key (sort keys %{$Slic3r::Config}) {
next if $Slic3r::Config::Options->{$opt_key}{shortcut};
next if $Slic3r::Config::Options->{$opt_key}{gui_only};
printf $fh "; %s = %s\n", $opt_key, $Slic3r::Config->serialize($opt_key);
}
}
# close our gcode file
close $fh;
}
@ -903,6 +971,7 @@ sub expanded_output_filepath {
# if no input file was supplied, take the first one from our objects
$input_file ||= $self->objects->[0]->input_file;
return undef if !defined $input_file;
# if output path is an existing directory, we take that and append
# the specified filename format