Assign extruders and flows to materials

This commit is contained in:
Alessandro Ranellucci 2012-09-23 02:40:25 +02:00
parent e2ab340edb
commit e696764af8
10 changed files with 161 additions and 120 deletions

View File

@ -41,6 +41,7 @@ lib/Slic3r/Point.pm
lib/Slic3r/Polygon.pm
lib/Slic3r/Polyline.pm
lib/Slic3r/Print.pm
lib/Slic3r/Print/Material.pm
lib/Slic3r/Print/Object.pm
lib/Slic3r/Surface.pm
lib/Slic3r/SVG.pm

View File

@ -50,6 +50,7 @@ use Slic3r::Point;
use Slic3r::Polygon;
use Slic3r::Polyline;
use Slic3r::Print;
use Slic3r::Print::Material;
use Slic3r::Print::Object;
use Slic3r::Surface;
use Slic3r::TriangleMesh;
@ -64,8 +65,7 @@ use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
# process. They should belong to the Print object, but we are keeping
# them here because it makes accessing them slightly faster.
our $Config;
our $extruders;
our ($flow, $first_layer_flow, $perimeter_flow, $infill_flow, $support_material_flow);
our $flow;
sub parallelize {
my %params = @_;

View File

@ -9,7 +9,9 @@ use constant OPTIONS => [qw(
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel
retract_length_toolchange retract_restart_extra_toolchange
)];
has $_ => (is => 'ro', required => 1) for @{&OPTIONS};
has 'id' => (is => 'rw', required => 1);
has $_ => (is => 'ro', required => 1) for @{&OPTIONS};
has 'retracted' => (is => 'rw', default => sub {0} );
has 'e_per_mm3' => (is => 'lazy');

View File

@ -5,13 +5,14 @@ use List::Util qw(first);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y);
has 'multiple_extruders' => (is => 'ro', default => sub {0} );
has 'layer' => (is => 'rw');
has 'shift_x' => (is => 'rw', default => sub {0} );
has 'shift_y' => (is => 'rw', default => sub {0} );
has 'z' => (is => 'rw', default => sub {0} );
has 'speed' => (is => 'rw');
has 'extruder_idx' => (is => 'rw');
has 'extruder' => (is => 'rw');
has 'extrusion_distance' => (is => 'rw', default => sub {0} );
has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds
has 'total_extrusion_length' => (is => 'rw', default => sub {0} );
@ -49,11 +50,6 @@ my %role_speeds = (
&EXTR_ROLE_SUPPORTMATERIAL => 'perimeter',
);
sub extruder {
my $self = shift;
return $Slic3r::extruders->[$self->extruder_idx];
}
sub change_layer {
my $self = shift;
my ($layer) = @_;
@ -374,26 +370,26 @@ sub _Gx {
return "$gcode\n";
}
sub set_tool {
sub set_extruder {
my $self = shift;
my ($tool) = @_;
my ($extruder) = @_;
# return nothing if this tool was already selected
return "" if (defined $self->extruder_idx) && ($self->extruder_idx == $tool);
# return nothing if this extruder was already selected
return "" if (defined $self->extruder) && ($self->extruder->id == $extruder);
# if we are running a single-extruder setup, just set the extruder and return nothing
if (@{$Slic3r::extruders} == 1) {
$self->extruder_idx($tool);
if (!$self->multiple_extruders) {
$self->extruder($extruder);
return "";
}
# trigger retraction on the current tool (if any)
# trigger retraction on the current extruder (if any)
my $gcode = "";
$gcode .= $self->retract(toolchange => 1) if defined $self->extruder_idx;
$gcode .= $self->retract(toolchange => 1) if defined $self->extruder;
# set the new tool
$self->extruder_idx($tool);
$gcode .= sprintf "T%d%s\n", $tool, ($Slic3r::Config->gcode_comments ? ' ; change tool' : '');
# set the new extruder
$self->extruder($extruder);
$gcode .= sprintf "T%d%s\n", $extruder, ($Slic3r::Config->gcode_comments ? ' ; change extruder' : '');
$gcode .= $self->reset_e;
return $gcode;
@ -426,7 +422,7 @@ sub set_temperature {
: ('M104', 'set temperature');
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 " : "";
(defined $tool && $tool != $self->extruder->id) ? "T$tool " : "";
$gcode .= "M116 ; wait for temperature to be reached\n"
if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait;

View File

@ -601,7 +601,6 @@ sub export_gcode2 {
}
$message .= ".";
$params{on_completed}->($message);
$print->cleanup;
};
$params{catch_error}->();
}

View File

@ -4,15 +4,14 @@ use Moo;
use Slic3r::Geometry::Clipper qw(union_ex);
has 'id' => (is => 'rw', required => 1); # sequential number of layer, 0-based
has 'object' => (is => 'ro', weak_ref => 1, required => 1);
has 'materials' => (is => 'ro', default => sub { [] });
has 'slicing_errors' => (is => 'rw');
has 'slice_z' => (is => 'lazy');
has 'print_z' => (is => 'lazy');
has 'height' => (is => 'lazy');
has 'flow' => (is => 'lazy');
has 'perimeter_flow' => (is => 'lazy');
has 'infill_flow' => (is => 'lazy');
has 'flow' => (is => 'ro', default => sub { $Slic3r::flow });
# collection of expolygons generated by slicing the original geometry;
# also known as 'islands' (all materials are merged here)
@ -43,34 +42,14 @@ sub _build_height {
return $self->id == 0 ? $Slic3r::Config->get_value('first_layer_height') : $Slic3r::Config->layer_height;
}
sub _build_flow {
my $self = shift;
return $self->id == 0 && $Slic3r::first_layer_flow
? $Slic3r::first_layer_flow
: $Slic3r::flow;
}
sub _build_perimeter_flow {
my $self = shift;
return $self->id == 0 && $Slic3r::first_layer_flow
? $Slic3r::first_layer_flow
: $Slic3r::perimeter_flow;
}
sub _build_infill_flow {
my $self = shift;
return $self->id == 0 && $Slic3r::first_layer_flow
? $Slic3r::first_layer_flow
: $Slic3r::infill_flow;
}
sub material {
my $self = shift;
my ($material_idx) = @_;
if (!defined $self->materials->[$material_idx]) {
$self->materials->[$material_idx] = Slic3r::Layer::Material->new(
layer => $self,
layer => $self,
material => $self->object->print->materials->[$material_idx],
);
}
return $self->materials->[$material_idx];

View File

@ -11,8 +11,11 @@ has 'layer' => (
is => 'ro',
weak_ref => 1,
required => 1,
handles => [qw(id slice_z print_z height flow perimeter_flow infill_flow)],
handles => [qw(id slice_z print_z height flow)],
);
has 'material' => (is => 'ro', required => 1);
has 'perimeter_flow' => (is => 'lazy');
has 'infill_flow' => (is => 'lazy');
# collection of spare segments generated by slicing the original geometry;
# these need to be merged in continuos (closed) polylines
@ -44,6 +47,20 @@ has 'perimeters' => (is => 'rw', default => sub { [] });
# ordered collection of extrusion paths to fill surfaces
has 'fills' => (is => 'rw', default => sub { [] });
sub _build_perimeter_flow {
my $self = shift;
return $self->id == 0
? $self->material->first_layer_flows->{perimeter}
: $self->material->flows->{perimeter}
}
sub _build_infill_flow {
my $self = shift;
return $self->id == 0
? $self->material->first_layer_flows->{infill}
: $self->material->flows->{infill}
}
# build polylines from lines
sub make_surfaces {
my $self = shift;

View File

@ -14,9 +14,11 @@ has 'config' => (is => 'rw', default => sub { Slic3r::Config->ne
has 'extra_variables' => (is => 'rw', default => sub {{}});
has 'objects' => (is => 'rw', default => sub {[]});
has 'total_extrusion_length' => (is => 'rw');
has 'processing_time' => (is => 'rw', required => 0);
has 'materials_count' => (is => 'rw', default => sub {1});
has 'processing_time' => (is => 'rw');
has 'extruders' => (is => 'rw', default => sub {[]});
has 'materials' => (is => 'rw', default => sub {[]});
has 'support_material_flow' => (is => 'rw');
has 'first_layer_support_material_flow' => (is => 'rw');
# ordered collection of extrusion paths to build skirt loops
has 'skirt' => (
@ -53,32 +55,6 @@ sub _trigger_config {
$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)) {
$Slic3r::extruders->[$t] ||= Slic3r::Extruder->new(
map { $_ => $self->config->get($_)->[$t] // $self->config->get($_)->[0] } #/
@{&Slic3r::Extruder::OPTIONS}
);
}
# calculate flow
$Slic3r::flow = $Slic3r::extruders->[0]->make_flow(width => $self->config->extrusion_width);
if ($self->config->first_layer_extrusion_width) {
$Slic3r::first_layer_flow = $Slic3r::extruders->[0]->make_flow(
layer_height => $self->config->get_value('first_layer_height'),
width => $self->config->first_layer_extrusion_width,
);
}
for (qw(perimeter infill support_material)) {
no strict 'refs';
${"Slic3r::${_}_flow"} = $Slic3r::extruders->[ $self->config->get("${_}_extruder")-1 ]
->make_flow(width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width);
}
Slic3r::debugf "Default flow width = %s (spacing = %s)\n",
$Slic3r::flow->width, $Slic3r::flow->spacing;
# 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';
@ -88,15 +64,24 @@ sub add_model {
my $self = shift;
my ($model) = @_;
# update materials count
$self->materials_count(max($self->materials_count, scalar keys %{$model->materials}));
# append/merge materials and preserve a mapping between the original material ID
# and our numeric material index
my %materials = ();
{
my @material_ids = sort keys %{$model->materials};
@material_ids = (0) if !@material_ids;
for (my $i = $self->materials_count; $i < @material_ids; $i++) {
push @{$self->materials}, Slic3r::Print::Material->new;
$materials{$material_ids[$i]} = $#{$self->materials};
}
}
foreach my $object (@{ $model->objects }) {
my @meshes = (); # by material_id
foreach my $volume (@{$object->volumes}) {
# should the object contain multiple volumes of the same material, merge them
my $material_id = $volume->material_id // 0; #/
my $material_id = defined $volume->material_id ? $materials{$volume->material_id} : 0;
my $mesh = $volume->mesh->clone;
$meshes[$material_id] = $meshes[$material_id]
? Slic3r::TriangleMesh->merge($meshes[$material_id], $mesh)
@ -180,6 +165,62 @@ sub validate {
}
}
sub init_extruders {
my $self = shift;
# map materials to extruders (ghetto mapping for now)
my %extruder_mapping = map { $_ => $_ } 0..$#{$self->materials};
# initialize all extruder(s) we need
my @used_extruders = (
0,
(map $self->config->get("${_}_extruder")-1, qw(perimeter infill support_material)),
(values %extruder_mapping),
);
for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
$self->extruders->[$extruder_id] = Slic3r::Extruder->new(
id => $extruder_id,
map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/
@{&Slic3r::Extruder::OPTIONS}
);
}
# calculate materials' flows
$Slic3r::flow = $self->extruders->[0]->make_flow(width => $self->config->extrusion_width);
for my $material_id (0 .. $#{$self->materials}) {
my $material = $self->materials->[$material_id];
# per-role extruders and flows
for (qw(perimeter infill)) {
$material->extruders->{$_} = ($self->materials_count > 1)
? $self->extruders->[$extruder_mapping{$material_id}]
: $self->extruders->[$self->config->get("${_}_extruder")-1];
$material->flows->{$_} = $material->extruders->{$_}->make_flow(
width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width,
);
$material->first_layer_flows->{$_} = $material->extruders->{$_}->make_flow(
layer_height => $self->config->get_value('first_layer_height'),
width => $self->config->first_layer_extrusion_width,
);
}
}
# calculate support material flow
if ($self->config->support_material) {
my $extruder = $self->extruders->[$self->config->support_material_extruder-1];
$self->support_material_flow($extruder->make_flow(
width => $self->config->support_material_extrusion_width || $self->config->extrusion_width,
));
$self->first_layer_support_material_flow($extruder->make_flow(
layer_height => $self->config->get_value('first_layer_height'),
width => $self->config->first_layer_extrusion_width,
));
}
Slic3r::debugf "Default flow width = %s (spacing = %s)\n",
$Slic3r::flow->width, $Slic3r::flow->spacing;
}
sub object_copies {
my $self = shift;
my @oc = ();
@ -189,14 +230,6 @@ sub object_copies {
return @oc;
}
sub cleanup {
my $self = shift;
$_->cleanup for @{$self->objects};
@{$self->skirt} = ();
$self->total_extrusion_length(0);
$self->processing_time(0);
}
sub layer_count {
my $self = shift;
my $count = 0;
@ -206,6 +239,11 @@ sub layer_count {
return $count;
}
sub materials_count {
my $self = shift;
return scalar @{$self->materials};
}
sub duplicate {
my $self = shift;
@ -275,12 +313,12 @@ sub export_gcode {
my $self = shift;
my %params = @_;
$self->init_extruders;
my $status_cb = $params{status_cb} || sub {};
my $t0 = [gettimeofday];
# skein the STL into layers
# each layer has surfaces with holes
$status_cb->(5, "Processing input file");
$status_cb->(10, "Processing triangulated mesh");
$_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects};
@ -587,26 +625,28 @@ sub write_gcode {
print $fh "\n";
# set up our extruder object
my $gcodegen = Slic3r::GCode->new;
my $gcodegen = Slic3r::GCode->new(
multiple_extruders => (@{$self->extruders} > 1),
);
my $min_print_speed = 60 * $Slic3r::Config->min_print_speed;
my $dec = $gcodegen->dec;
print $fh $gcodegen->set_tool(0);
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
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;
my $print_first_layer_temperature = sub {
for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) {
printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 0, $t)
if $Slic3r::extruders->[$t]->first_layer_temperature;
for my $t (grep $self->extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) {
printf $fh $gcodegen->set_temperature($self->extruders->[$t]->first_layer_temperature, 0, $t)
if $self->extruders->[$t]->first_layer_temperature;
}
};
$print_first_layer_temperature->();
printf $fh "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->start_gcode);
for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) {
printf $fh $gcodegen->set_temperature($Slic3r::extruders->[$t]->first_layer_temperature, 1, $t)
if $Slic3r::extruders->[$t]->first_layer_temperature && $Slic3r::Config->start_gcode !~ /M109/i;
for my $t (grep $self->extruders->[$_], 0 .. $#{$Slic3r::Config->first_layer_temperature}) {
printf $fh $gcodegen->set_temperature($self->extruders->[$t]->first_layer_temperature, 1, $t)
if $self->extruders->[$t]->first_layer_temperature && $Slic3r::Config->start_gcode !~ /M109/i;
}
print $fh "G90 ; use absolute coordinates\n";
print $fh "G21 ; set units to millimeters\n";
@ -636,9 +676,9 @@ sub write_gcode {
my $gcode = "";
if ($layer_id == 1) {
for my $t (grep $Slic3r::extruders->[$_], 0 .. $#{$Slic3r::Config->temperature}) {
$gcode .= $gcodegen->set_temperature($Slic3r::extruders->[$t]->temperature, 0, $t)
if $Slic3r::extruders->[$t]->temperature && $Slic3r::extruders->[$t]->temperature != $Slic3r::extruders->[$t]->first_layer_temperature;
for my $t (grep $self->extruders->[$_], 0 .. $#{$Slic3r::Config->temperature}) {
$gcode .= $gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t)
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;
@ -662,7 +702,7 @@ sub write_gcode {
# extrude brim
if ($layer_id == 0 && !$brim_done) {
$gcode .= $gcodegen->set_tool($Slic3r::Config->support_material_extruder-1);
$gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]);
$gcodegen->shift_x($shift[X]);
$gcodegen->shift_y($shift[Y]);
$gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim};
@ -682,13 +722,14 @@ sub write_gcode {
foreach my $material_id (0 .. ($self->materials_count-1)) {
my $layerm = $layer->materials->[$material_id];
my $material = $self->materials->[$material_id];
# extrude perimeters
$gcode .= $gcodegen->set_tool($Slic3r::Config->perimeter_extruder-1);
$gcode .= $gcodegen->set_extruder($material->extruders->{perimeter});
$gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters };
# extrude fills
$gcode .= $gcodegen->set_tool($Slic3r::Config->infill_extruder-1);
$gcode .= $gcodegen->set_extruder($material->extruders->{infill});
$gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration);
for my $fill (@{ $layerm->fills }) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
@ -702,7 +743,7 @@ sub write_gcode {
# extrude support material
if ($layer->support_fills) {
$gcode .= $gcodegen->set_tool($Slic3r::Config->support_material_extruder-1);
$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);
}
@ -807,7 +848,7 @@ sub write_gcode {
sub total_extrusion_volume {
my $self = shift;
return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000;
return $self->total_extrusion_length * ($self->extruders->[0]->filament_diameter**2) * PI/4 / 1000;
}
# this method will return the supplied input file path after expanding its

View File

@ -0,0 +1,8 @@
package Slic3r::Print::Material;
use Moo;
has 'extruders' => (is => 'rw', default => sub { {} }); # by role
has 'flows' => (is => 'rw', default => sub { {} }); # by role
has 'first_layer_flows' => (is => 'rw', default => sub { {} }); # by role
1;

View File

@ -23,11 +23,8 @@ sub layer {
my ($layer_id) = @_;
# 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);
}
for (my $i = $self->layer_count; $i <= $layer_id; $i++) {
push @{ $self->layers }, Slic3r::Layer->new(id => $i, object => $self);
}
return $self->layers->[$layer_id];
@ -172,11 +169,6 @@ sub slice {
if !@{$self->layers};
}
sub cleanup {
my $self = shift;
@{$self->layers} = ();
}
sub make_perimeters {
my $self = shift;
@ -510,9 +502,10 @@ sub combine_infill {
sub generate_support_material {
my $self = shift;
my $flow = $self->print->support_material_flow;
my $threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive
my $overhang_width = $threshold_rad == 0 ? undef : scale $Slic3r::Config->layer_height * ((cos $threshold_rad) / (sin $threshold_rad));
my $distance_from_object = 1.5 * scale $Slic3r::support_material_flow->width;
my $distance_from_object = 1.5 * scale $flow->width;
# determine support regions in each layer (for upper layers)
Slic3r::debugf "Detecting regions\n";
@ -536,7 +529,7 @@ sub generate_support_material {
[ map @$_, @current_support_regions ],
[ map @$_, map $_->offset_ex($distance_from_object), @{$layer->slices} ],
);
$_->simplify(scale $Slic3r::support_material_flow->spacing * 2) for @{$layers{$i}};
$_->simplify(scale $flow->spacing * 2) for @{$layers{$i}};
# step 2: get layer overhangs and put them into queue for adding support inside lower layers
# we need an angle threshold for this
@ -558,7 +551,7 @@ sub generate_support_material {
my $support_patterns = []; # in case we want cross-hatching
{
# 0.5 makes sure the paths don't get clipped externally when applying them to layers
my @support_material_areas = map $_->offset_ex(- 0.5 * scale $Slic3r::support_material_flow->width),
my @support_material_areas = map $_->offset_ex(- 0.5 * scale $flow->width),
@{union_ex([ map $_->contour, map @$_, values %layers ])};
my $fill = Slic3r::Fill->new(print => $self->print);
@ -569,8 +562,8 @@ sub generate_support_material {
foreach my $expolygon (@support_material_areas) {
my @paths = $filler->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon),
density => $Slic3r::support_material_flow->spacing / $Slic3r::Config->support_material_spacing,
flow_spacing => $Slic3r::support_material_flow->spacing,
density => $flow->spacing / $Slic3r::Config->support_material_spacing,
flow_spacing => $flow->spacing,
);
my $params = shift @paths;
@ -603,6 +596,11 @@ sub generate_support_material {
foreach my $expolygon (@$expolygons) {
push @paths,
map $_->pack,
map {
$_->flow_spacing($self->print->first_layer_support_material_flow->spacing)
if $layer_id == 0;
$_;
}
map $_->clip_with_expolygon($expolygon),
map $_->clip_with_polygon($expolygon->bounding_box_polygon),
@{$support_patterns->[ $layer_id % @$support_patterns ]};