mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-23 22:54:08 -06:00
Refactoring: moved G-code export logic into new Slic3r::Print::GCode class. Removed Slic3r::GCode::Layer class. Fixes the order of post-processing filters so that cooling buffer is applied before any other filter whose logic is affected by speeds
This commit is contained in:
parent
7a7d00c8d6
commit
98cb9f0e18
4 changed files with 497 additions and 490 deletions
|
@ -7,8 +7,7 @@ use File::Spec;
|
|||
use List::Util qw(min max first sum);
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale chained_path
|
||||
convex_hull);
|
||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex intersection offset
|
||||
offset2 union union_pt_chained JT_ROUND JT_SQUARE);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
@ -456,243 +455,11 @@ sub write_gcode {
|
|||
binmode $fh, ':utf8';
|
||||
}
|
||||
|
||||
|
||||
# write some information
|
||||
my @lt = localtime;
|
||||
printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
|
||||
$lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0];
|
||||
|
||||
print $fh "; $_\n" foreach split /\R/, $self->config->notes;
|
||||
print $fh "\n" if $self->config->notes;
|
||||
|
||||
my $first_object = $self->objects->[0];
|
||||
my $layer_height = $first_object->config->layer_height;
|
||||
for my $region_id (0..$#{$self->regions}) {
|
||||
printf $fh "; external perimeters extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
|
||||
printf $fh "; perimeters extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
|
||||
printf $fh "; infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
|
||||
printf $fh "; solid infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
|
||||
printf $fh "; top infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
|
||||
printf $fh "; support material extrusion width = %.2fmm\n",
|
||||
$self->objects->[0]->support_material_flow->width
|
||||
if $self->has_support_material;
|
||||
printf $fh "; first layer extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1, -1, $self->objects->[0])->width
|
||||
if $self->regions->[$region_id]->config->first_layer_extrusion_width;
|
||||
print $fh "\n";
|
||||
}
|
||||
|
||||
# prepare the helper object for replacing placeholders in custom G-code and output filename
|
||||
$self->placeholder_parser->update_timestamp;
|
||||
|
||||
# estimate the total number of layer changes
|
||||
# TODO: only do this when M73 is enabled
|
||||
my $layer_count;
|
||||
if ($self->config->complete_objects) {
|
||||
$layer_count = sum(map { $_->total_layer_count * @{$_->copies} } @{$self->objects});
|
||||
} else {
|
||||
# if sequential printing is not enable, all copies of the same object share the same layer change command(s)
|
||||
$layer_count = sum(map { $_->total_layer_count } @{$self->objects});
|
||||
}
|
||||
|
||||
# set up our helper object
|
||||
my $gcodegen = Slic3r::GCode->new(
|
||||
placeholder_parser => $self->placeholder_parser,
|
||||
layer_count => $layer_count,
|
||||
enable_cooling_markers => 1,
|
||||
my $exporter = Slic3r::Print::GCode->new(
|
||||
print => $self,
|
||||
fh => $fh,
|
||||
);
|
||||
$gcodegen->apply_print_config($self->config);
|
||||
$gcodegen->set_extruders($self->extruders);
|
||||
|
||||
print $fh $gcodegen->writer->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers;
|
||||
|
||||
# set bed temperature
|
||||
if ((my $temp = $self->config->first_layer_bed_temperature) && $self->config->start_gcode !~ /M(?:190|140)/i) {
|
||||
printf $fh $gcodegen->writer->set_bed_temperature($temp, 1);
|
||||
}
|
||||
|
||||
# set extruder(s) temperature before and after start G-code
|
||||
my $print_first_layer_temperature = sub {
|
||||
my ($wait) = @_;
|
||||
|
||||
return if $self->config->start_gcode =~ /M(?:109|104)/i;
|
||||
for my $t (@{$self->extruders}) {
|
||||
my $temp = $self->config->get_at('first_layer_temperature', $t);
|
||||
$temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;
|
||||
printf $fh $gcodegen->writer->set_temperature($temp, $wait, $t) if $temp > 0;
|
||||
}
|
||||
};
|
||||
$print_first_layer_temperature->(0);
|
||||
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->start_gcode);
|
||||
$print_first_layer_temperature->(1);
|
||||
|
||||
# set other general things
|
||||
print $fh $gcodegen->preamble;
|
||||
|
||||
# initialize a motion planner for object-to-object travel moves
|
||||
if ($self->config->avoid_crossing_perimeters) {
|
||||
my $distance_from_objects = 1;
|
||||
# compute the offsetted convex hull for each object and repeat it for each copy.
|
||||
my @islands = ();
|
||||
foreach my $obj_idx (0 .. ($self->object_count - 1)) {
|
||||
my $convex_hull = convex_hull([
|
||||
map @{$_->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers},
|
||||
]);
|
||||
# discard layers only containing thin walls (offset would fail on an empty polygon)
|
||||
if (@$convex_hull) {
|
||||
my $expolygon = Slic3r::ExPolygon->new($convex_hull);
|
||||
my @island = @{$expolygon->offset_ex(scale $distance_from_objects, 1, JT_SQUARE)};
|
||||
foreach my $copy (@{ $self->objects->[$obj_idx]->_shifted_copies }) {
|
||||
push @islands, map { my $c = $_->clone; $c->translate(@$copy); $c } @island;
|
||||
}
|
||||
}
|
||||
}
|
||||
$gcodegen->avoid_crossing_perimeters->init_external_mp(union_ex([ map @$_, @islands ]));
|
||||
}
|
||||
|
||||
# calculate wiping points if needed
|
||||
if ($self->config->ooze_prevention) {
|
||||
my @skirt_points = map @$_, map @$_, @{$self->skirt};
|
||||
if (@skirt_points) {
|
||||
my $outer_skirt = convex_hull(\@skirt_points);
|
||||
my @skirts = ();
|
||||
foreach my $extruder_id (@{$self->extruders}) {
|
||||
push @skirts, my $s = $outer_skirt->clone;
|
||||
$s->translate(map scale($_), @{$self->config->get_at('extruder_offset', $extruder_id)});
|
||||
}
|
||||
my $convex_hull = convex_hull([ map @$_, @skirts ]);
|
||||
|
||||
$gcodegen->ooze_prevention->enable(1);
|
||||
$gcodegen->ooze_prevention->standby_points(
|
||||
[ map $_->clone, map @$_, map $_->subdivide(scale 10), @{offset([$convex_hull], scale 3)} ]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# prepare the layer processor
|
||||
my $layer_gcode = Slic3r::GCode::Layer->new(
|
||||
print => $self,
|
||||
gcodegen => $gcodegen,
|
||||
);
|
||||
|
||||
# set initial extruder only after custom start G-code
|
||||
print $fh $gcodegen->set_extruder($self->extruders->[0]);
|
||||
|
||||
# do all objects for each layer
|
||||
if ($self->config->complete_objects) {
|
||||
# print objects from the smallest to the tallest to avoid collisions
|
||||
# when moving onto next object starting point
|
||||
my @obj_idx = sort { $self->objects->[$a]->size->[Z] <=> $self->objects->[$b]->size->[Z] } 0..($self->object_count - 1);
|
||||
|
||||
my $finished_objects = 0;
|
||||
for my $obj_idx (@obj_idx) {
|
||||
my $object = $self->objects->[$obj_idx];
|
||||
for my $copy (@{ $self->objects->[$obj_idx]->_shifted_copies }) {
|
||||
# move to the origin position for the copy we're going to print.
|
||||
# this happens before Z goes down to layer 0 again, so that
|
||||
# no collision happens hopefully.
|
||||
if ($finished_objects > 0) {
|
||||
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
|
||||
print $fh $gcodegen->retract;
|
||||
print $fh $gcodegen->travel_to(
|
||||
$object->_copies_shift->negative,
|
||||
undef,
|
||||
'move to origin position for next object',
|
||||
);
|
||||
}
|
||||
|
||||
my $buffer = Slic3r::GCode::CoolingBuffer->new(
|
||||
config => $self->config,
|
||||
gcodegen => $gcodegen,
|
||||
);
|
||||
|
||||
my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers};
|
||||
for my $layer (@layers) {
|
||||
# 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
|
||||
# is triggered, so machine has more time to reach such temperatures
|
||||
if ($layer->id == 0 && $finished_objects > 0) {
|
||||
printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature),
|
||||
if $self->config->first_layer_bed_temperature;
|
||||
$print_first_layer_temperature->(0);
|
||||
}
|
||||
print $fh $buffer->append(
|
||||
$layer_gcode->process_layer($layer, [$copy]),
|
||||
$layer->object->ptr,
|
||||
$layer->id,
|
||||
$layer->print_z,
|
||||
);
|
||||
}
|
||||
print $fh $buffer->flush;
|
||||
$finished_objects++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# order objects using a nearest neighbor search
|
||||
my @obj_idx = @{chained_path([ map Slic3r::Point->new(@{$_->_shifted_copies->[0]}), @{$self->objects} ])};
|
||||
|
||||
# sort layers by Z
|
||||
my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx
|
||||
foreach my $obj_idx (0 .. ($self->object_count - 1)) {
|
||||
my $object = $self->objects->[$obj_idx];
|
||||
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||
$layers{ $layer->print_z } ||= [];
|
||||
$layers{ $layer->print_z }[$obj_idx] ||= [];
|
||||
push @{$layers{ $layer->print_z }[$obj_idx]}, $layer;
|
||||
}
|
||||
}
|
||||
|
||||
my $buffer = Slic3r::GCode::CoolingBuffer->new(
|
||||
config => $self->config,
|
||||
gcodegen => $gcodegen,
|
||||
);
|
||||
foreach my $print_z (sort { $a <=> $b } keys %layers) {
|
||||
foreach my $obj_idx (@obj_idx) {
|
||||
foreach my $layer (@{ $layers{$print_z}[$obj_idx] // [] }) {
|
||||
print $fh $buffer->append(
|
||||
$layer_gcode->process_layer($layer, $layer->object->_shifted_copies),
|
||||
$layer->object->ptr . ref($layer), # differentiate $obj_id between normal layers and support layers
|
||||
$layer->id,
|
||||
$layer->print_z,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
print $fh $buffer->flush;
|
||||
}
|
||||
|
||||
# write end commands to file
|
||||
print $fh $gcodegen->retract; # TODO: process this retract through PressureRegulator in order to discharge fully
|
||||
print $fh $gcodegen->writer->set_fan(0);
|
||||
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode);
|
||||
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
|
||||
|
||||
$self->total_used_filament(0);
|
||||
$self->total_extruded_volume(0);
|
||||
foreach my $extruder (@{$gcodegen->writer->extruders}) {
|
||||
my $used_filament = $extruder->used_filament;
|
||||
my $extruded_volume = $extruder->extruded_volume;
|
||||
|
||||
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
|
||||
$used_filament, $extruded_volume/1000;
|
||||
|
||||
$self->total_used_filament($self->total_used_filament + $used_filament);
|
||||
$self->total_extruded_volume($self->total_extruded_volume + $extruded_volume);
|
||||
}
|
||||
|
||||
# append full config
|
||||
print $fh "\n";
|
||||
foreach my $config ($self->config, $self->default_object_config, $self->default_region_config) {
|
||||
foreach my $opt_key (sort @{$config->get_keys}) {
|
||||
next if $Slic3r::Config::Options->{$opt_key}{shortcut};
|
||||
printf $fh "; %s = %s\n", $opt_key, $config->serialize($opt_key);
|
||||
}
|
||||
}
|
||||
$exporter->export;
|
||||
|
||||
# close our gcode file
|
||||
close $fh;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue