mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-30 12:11:15 -06:00
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:
commit
48e00a4c40
52 changed files with 2388 additions and 821 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue