diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0db6e69e1..bd924448b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -175,6 +175,22 @@ sub setenv { } } +sub equals { + my ($self, $other) = @_; + return @{ $self->diff($other) } == 0; +} + +sub diff { + my ($self, $other) = @_; + + my @diff = (); + foreach my $opt_key (sort @{$self->get_keys}) { + push @diff, $opt_key + if !$other->has($opt_key) || $other->serialize($opt_key) ne $self->serialize($opt_key); + } + return [@diff]; +} + # this method is idempotent by design sub validate { my $self = shift; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 9444f6916..1025c3459 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -1,6 +1,12 @@ package Slic3r::Extruder; use Moo; +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(EXTRUDER_ROLE_PERIMETER EXTRUDER_ROLE_INFILL EXTRUDER_ROLE_SUPPORT_MATERIAL + EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE); +our %EXPORT_TAGS = (roles => \@EXPORT_OK); + use Slic3r::Geometry qw(PI scale); use constant OPTIONS => [qw( @@ -14,7 +20,6 @@ has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; has 'config'=> (is => 'ro', required => 1); -has 'bridge_flow' => (is => 'lazy'); has 'E' => (is => 'rw', default => sub {0} ); has 'absolute_E' => (is => 'rw', default => sub {0} ); has 'retracted' => (is => 'rw', default => sub {0} ); @@ -23,14 +28,10 @@ has 'e_per_mm3' => (is => 'lazy'); has 'retract_speed_mm_min' => (is => 'lazy'); has '_mm3_per_mm_cache' => (is => 'ro', default => sub {{}}); -sub _build_bridge_flow { - my $self = shift; - - return Slic3r::Flow::Bridge->new( - nozzle_diameter => $self->nozzle_diameter, - bridge_flow_ratio => $self->config->bridge_flow_ratio, - ); -} +use constant EXTRUDER_ROLE_PERIMETER => 1; +use constant EXTRUDER_ROLE_INFILL => 2; +use constant EXTRUDER_ROLE_SUPPORT_MATERIAL => 3; +use constant EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE => 4; sub _build_e_per_mm3 { my $self = shift; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index a02f7b34e..0a9747fa8 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -1,48 +1,73 @@ package Slic3r::Flow; use Moo; -use Slic3r::Geometry qw(PI scale); +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL + FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE); +our %EXPORT_TAGS = (roles => \@EXPORT_OK); -has 'nozzle_diameter' => (is => 'ro', required => 1); -has 'layer_height' => (is => 'ro', required => 1); -has 'role' => (is => 'ro', default => sub { '' }); +use Slic3r::Geometry qw(PI); -has 'width' => (is => 'rwp', builder => 1); -has 'spacing' => (is => 'lazy'); +has 'width' => (is => 'ro'); +has 'spacing' => (is => 'ro'); has 'scaled_width' => (is => 'lazy'); has 'scaled_spacing' => (is => 'lazy'); -sub BUILD { - my $self = shift; +use constant FLOW_ROLE_PERIMETER => 1; +use constant FLOW_ROLE_INFILL => 2; +use constant FLOW_ROLE_SOLID_INFILL => 3; +use constant FLOW_ROLE_TOP_SOLID_INFILL => 4; +use constant FLOW_ROLE_SUPPORT_MATERIAL => 5; +use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE => 6; + +sub BUILDARGS { + my ($self, %args) = @_; - if ($self->width =~ /^(\d+(?:\.\d+)?)%$/) { - $self->_set_width($self->layer_height * $1 / 100); + # the constructor can take two sets of arguments: + # - width (only absolute value), spacing + # - width (abs/%/0), role, nozzle_diameter, layer_height, bridge_flow_ratio + # (if bridge_flow_ratio == 0, we return a non-bridge flow) + + if (exists $args{role}) { + if ($args{width} eq '0') { + $args{width} = $self->_width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)}); + } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) { + $args{width} = $args{layer_height} * $1 / 100; + } + $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); + %args = @args{qw(width spacing)}; } - $self->_set_width($self->_build_width) if $self->width == 0; # auto + + return {%args}; } -sub _build_width { - my $self = shift; +sub _width { + my ($self, $role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + + if ($bridge_flow_ratio > 0) { + return sqrt($bridge_flow_ratio * ($nozzle_diameter**2)); + } # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate - my $volume = ($self->nozzle_diameter**2) * PI/4; - my $shape_threshold = $self->nozzle_diameter * $self->layer_height + ($self->layer_height**2) * PI/4; + my $volume = ($nozzle_diameter**2) * PI/4; + my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4; my $width; if ($volume >= $shape_threshold) { # rectangle with semicircles at the ends - $width = (($self->nozzle_diameter**2) * PI + ($self->layer_height**2) * (4 - PI)) / (4 * $self->layer_height); + $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height); } else { # rectangle with squished semicircles at the ends - $width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1); + $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1); } - my $min = $self->nozzle_diameter * 1.05; + my $min = $nozzle_diameter * 1.05; my $max; - if ($self->role eq 'perimeter' || $self->role eq 'support_material') { - $min = $max = $self->nozzle_diameter; - } elsif ($self->role ne 'infill') { + if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) { + $min = $max = $nozzle_diameter; + } elsif ($role != FLOW_ROLE_INFILL) { # do not limit width for sparse infill so that we use full native flow for it - $max = $self->nozzle_diameter * 1.7; + $max = $nozzle_diameter * 1.7; } $width = $max if defined($max) && $width > $max; $width = $min if $width < $min; @@ -50,59 +75,41 @@ sub _build_width { return $width; } -sub _build_spacing { - my $self = shift; +sub _spacing { + my ($self, $width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + + if ($bridge_flow_ratio > 0) { + return $width + 0.05; + } my $min_flow_spacing; - if ($self->width >= ($self->nozzle_diameter + $self->layer_height)) { + if ($width >= ($nozzle_diameter + $layer_height)) { # rectangle with semicircles at the ends - $min_flow_spacing = $self->width - $self->layer_height * (1 - PI/4); + $min_flow_spacing = $width - $layer_height * (1 - PI/4); } else { # rectangle with shrunk semicircles at the ends - $min_flow_spacing = $self->nozzle_diameter * (1 - PI/4) + $self->width * PI/4; + $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4; } - return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing); + return $width - &Slic3r::OVERLAP_FACTOR * ($width - $min_flow_spacing); } sub clone { my $self = shift; return (ref $self)->new( - nozzle_diameter => $self->nozzle_diameter, - layer_height => $self->layer_height, - @_, + width => $self->width, + spacing => $self->spacing, ); } sub _build_scaled_width { my $self = shift; - return scale $self->width; + return Slic3r::Geometry::scale($self->width); } sub _build_scaled_spacing { my $self = shift; - return scale $self->spacing; -} - - -package Slic3r::Flow::Bridge; -use Moo; -extends 'Slic3r::Flow'; - -# layer_height is not required in this case -has '+layer_height' => (is => 'ro', required => 0); -has 'bridge_flow_ratio' => (is => 'ro', required => 1); - -use Slic3r::Geometry qw(PI); - -sub _build_width { - my $self = shift; - return sqrt($self->bridge_flow_ratio * ($self->nozzle_diameter**2)); -} - -sub _build_spacing { - my $self = shift; - return $self->width + 0.05; + return Slic3r::Geometry::scale($self->spacing); } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ed85250a2..e0b0fcf4a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -638,7 +638,6 @@ sub split_object { input_file => $current_model_object->input_file, config => $current_model_object->config->clone, layer_height_ranges => $current_model_object->layer_height_ranges, # TODO: clone this - material_mapping => $current_model_object->material_mapping, # TODO: clone this ); $model_object->add_volume( mesh => $mesh, diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 30a5dadeb..e681d7238 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -324,7 +324,12 @@ sub Closing { my $self = shift; # save mappings into the plater object - $self->model_object->material_mapping($self->{mapping}); + foreach my $volume (@{$self->model_object}) { + if (defined $volume->material_id) { + my $config = $self->model_object->model->materials->{ $volume->material_id }->config; + $config->set('extruder', $self->{mapping}{ $volume->material_id }); + } + } } 1; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index ee11147bd..7750794c0 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -146,7 +146,10 @@ sub quick_slice { } $model->center_instances_around_point($config->print_center); - $print->add_model_object($_) for @{ $model->objects }; + foreach my $model_object (@{$model->objects}) { + $print->auto_assign_extruders($model_object); + $print->add_model_object($model_object); + } $print->validate; # select output file diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 53f40a659..e065f561e 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -3,6 +3,7 @@ use Moo; use List::Util qw(sum first); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(PI A B scale unscale chained_path points_coincide); use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex offset offset2 offset2_ex union_pt diff intersection @@ -17,10 +18,6 @@ has 'layer' => ( handles => [qw(id slice_z print_z height flow config)], ); has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); -has 'perimeter_flow' => (is => 'rw'); -has 'infill_flow' => (is => 'rw'); -has 'solid_infill_flow' => (is => 'rw'); -has 'top_infill_flow' => (is => 'rw'); has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); @@ -40,43 +37,26 @@ has 'perimeters' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collect # ordered collection of extrusion paths to fill surfaces has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); -sub BUILD { - my $self = shift; - $self->_update_flows; -} - -sub _trigger_layer { - my $self = shift; - $self->_update_flows; -} - -sub _update_flows { - my $self = shift; - return if !$self->region; - - if ($self->id == 0) { - for (qw(perimeter infill solid_infill top_infill)) { - my $method = "${_}_flow"; - $self->$method - ($self->region->first_layer_flows->{$_} || $self->region->flows->{$_}); - } - } else { - $self->perimeter_flow($self->region->flows->{perimeter}); - $self->infill_flow($self->region->flows->{infill}); - $self->solid_infill_flow($self->region->flows->{solid_infill}); - $self->top_infill_flow($self->region->flows->{top_infill}); - } -} - sub _build_overhang_width { my $self = shift; - my $threshold_rad = PI/2 - atan2($self->perimeter_flow->width / $self->height / 2, 1); + my $threshold_rad = PI/2 - atan2($self->flow(FLOW_ROLE_PERIMETER)->width / $self->height / 2, 1); return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad))); } sub _build_infill_area_threshold { my $self = shift; - return $self->solid_infill_flow->scaled_spacing ** 2; + return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2; +} + +sub flow { + my ($self, $role, $bridge, $width) = @_; + return $self->region->flow( + $role, + $self->layer->height, + $bridge // 0, + $self->layer->id == 0, + $width, + ); } # build polylines from lines @@ -145,10 +125,11 @@ sub _merge_loops { sub make_perimeters { my $self = shift; - my $pwidth = $self->perimeter_flow->scaled_width; - my $pspacing = $self->perimeter_flow->scaled_spacing; - my $ispacing = $self->solid_infill_flow->scaled_spacing; - my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2; + my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); + my $pwidth = $perimeter_flow->scaled_width; + my $pspacing = $perimeter_flow->scaled_spacing; + my $ispacing = $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing; + my $gap_area_threshold = $pwidth ** 2; $self->perimeters->clear; $self->fill_surfaces->clear; @@ -284,7 +265,7 @@ sub make_perimeters { push @loops, Slic3r::ExtrusionLoop->new( polygon => $polygon, role => $role, - flow_spacing => $self->perimeter_flow->spacing, + flow_spacing => $perimeter_flow->spacing, ); } return @loops; @@ -311,7 +292,7 @@ sub make_perimeters { next if $p->length <= $pspacing * 2; my %params = ( role => EXTR_ROLE_EXTERNAL_PERIMETER, - flow_spacing => $self->perimeter_flow->spacing, + flow_spacing => $perimeter_flow->spacing, ); push @paths, $p->isa('Slic3r::Polygon') ? Slic3r::ExtrusionLoop->new(polygon => $p, %params) @@ -345,10 +326,10 @@ sub _fill_gaps { # we could try with 1.5*$w for example, but that doesn't work well for zigzag fill # because it tends to create very sparse points along the gap when the infill direction # is not parallel to the gap (1.5*$w thus may only work well with a straight line) - my $w = $self->perimeter_flow->width; + my $w = $self->flow(FLOW_ROLE_PERIMETER)->width; my @widths = ($w, 0.4 * $w); # worth trying 0.2 too? foreach my $width (@widths) { - my $flow = $self->perimeter_flow->clone(width => $width); + my $flow = $self->flow(FLOW_ROLE_PERIMETER, 0, $width); # extract the gaps having this width my @this_width = map @{$_->offset_ex(+0.5*$flow->scaled_width)}, @@ -499,7 +480,10 @@ sub process_external_surfaces { sub _detect_bridge_direction { my ($self, $expolygon, $lower_layer) = @_; - my $grown = $expolygon->offset_ex(+$self->perimeter_flow->scaled_width); + my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); + my $infill_flow = $self->flow(FLOW_ROLE_INFILL); + + my $grown = $expolygon->offset_ex(+$perimeter_flow->scaled_width); my @lower = @{$lower_layer->slices}; # expolygons # detect what edges lie on lower slices @@ -554,7 +538,7 @@ sub _detect_bridge_direction { } } elsif (@edges) { # inset the bridge expolygon; we'll use this one to clip our test lines - my $inset = $expolygon->offset_ex($self->infill_flow->scaled_width); + my $inset = $expolygon->offset_ex($infill_flow->scaled_width); # detect anchors as intersection between our bridge expolygon and the lower slices my $anchors = intersection_ex( @@ -568,7 +552,7 @@ sub _detect_bridge_direction { # endpoints within anchors my %directions = (); # angle => score my $angle_increment = PI/36; # 5° - my $line_increment = $self->infill_flow->scaled_width; + my $line_increment = $infill_flow->scaled_width; for (my $angle = 0; $angle <= PI; $angle += $angle_increment) { # rotate everything - the center point doesn't matter $_->rotate($angle, [0,0]) for @$inset, @$anchors; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index d9b5ef463..401deb47e 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -44,7 +44,6 @@ sub add_object { input_file => $object->input_file, config => $object->config, layer_height_ranges => $object->layer_height_ranges, # TODO: clone! - material_mapping => $object->material_mapping, # TODO: clone! ); foreach my $volume (@{$object->volumes}) { @@ -324,6 +323,7 @@ use Moo; has 'model' => (is => 'ro', weak_ref => 1, required => 1); has 'attributes' => (is => 'rw', default => sub { {} }); +has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); package Slic3r::Model::Object; use Moo; @@ -338,7 +338,6 @@ has 'volumes' => (is => 'ro', default => sub { [] }); has 'instances' => (is => 'rw'); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] -has 'material_mapping' => (is => 'rw', default => sub { {} }); # { material_id => region_idx } has '_bounding_box' => (is => 'rw'); sub add_volume { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 59d90af7d..ef32677ea 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -17,8 +17,6 @@ has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); has 'extruders' => (is => 'rw', default => sub {[]}); has 'regions' => (is => 'rw', default => sub {[]}); -has 'support_material_flow' => (is => 'rw'); -has 'first_layer_support_material_flow' => (is => 'rw'); has 'has_support_material' => (is => 'lazy'); has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); @@ -88,30 +86,37 @@ sub add_model_object { my $self = shift; my ($object, $obj_idx) = @_; - # read the material mapping provided by the model object, if any - my %matmap = %{ $object->material_mapping || {} }; - $_-- for values %matmap; # extruders in the mapping are 1-indexed but we want 0-indexed - my %volumes = (); # region_id => [ volume_id, ... ] foreach my $volume_id (0..$#{$object->volumes}) { my $volume = $object->volumes->[$volume_id]; - # determine what region should this volume be mapped to - my $region_id; + # get the config applied to this volume + my $config; if (defined $volume->material_id) { - if (!exists $matmap{ $volume->material_id }) { - # there's no mapping between this material and a region - $matmap{ $volume->material_id } = scalar(@{ $self->regions }); - } - $region_id = $matmap{ $volume->material_id }; + my $config = $object->model->materials->{ $volume->material_id }->config; } else { - $region_id = 0; + $config = Slic3r::Config->new; } + + # find an existing print region with the same config + my $region_id; + foreach my $i (0..$#{$self->regions}) { + my $region = $self->regions->[$i]; + if ($config->equals($region->config)) { + $region_id = $i; + last; + } + } + + # if no region exists with the same config, create a new one + if (!defined $region_id) { + push @{$self->regions}, Slic3r::Print::Region->new(config => $config->clone); + $region_id = $#{$self->regions}; + } + + # assign volume to region $volumes{$region_id} //= []; push @{ $volumes{$region_id} }, $volume_id; - - # instantiate region if it does not exist - $self->regions->[$region_id] //= Slic3r::Print::Region->new; } # initialize print object @@ -632,7 +637,7 @@ sub make_skirt { my @extruded_length = (); # for each extruder # TODO: use each extruder's own flow - my $spacing = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow->spacing; + my $spacing = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->spacing; my $first_layer_height = $self->config->get_value('first_layer_height'); my @extruders_e_per_mm = (); @@ -673,7 +678,7 @@ sub make_brim { $self->brim->clear; # method must be idempotent - my $flow = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow; + my $flow = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL); my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons @@ -747,15 +752,24 @@ sub write_gcode { for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { printf $fh "; %s = %s\n", $_, $self->config->$_->[0]; } - 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 "; solid infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{solid_infill}->width; - printf $fh "; top infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{top_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", $self->regions->[0]->first_layer_flows->{perimeter}->width - if $self->regions->[0]->first_layer_flows->{perimeter}; - print $fh "\n"; + + for my $region_id (0..$#{$self->regions}) { + printf $fh "; perimeters extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width; + printf $fh "; infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width; + printf $fh "; solid infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width; + printf $fh "; top infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; + printf $fh "; support material extrusion width = %.2fmm\n", + $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->width + if $self->support_material_flow; + printf $fh "; first layer extrusion width = %.2fmm\n", + $self->flow(FLOW_ROLE_SUPPORT_MATERIAL, 0, 1)->width + if $self->regions->[0]->first_layer_flows->{perimeter}; + print $fh "\n"; + } # set up our extruder object my $gcodegen = Slic3r::GCode->new( @@ -1012,4 +1026,64 @@ sub invalidate_step { keys %Slic3r::Print::State::prereqs; } +# This method assigns extruders to the volumes having a material +# but not having extruders set in the material config. +sub auto_assign_extruders { + my ($self, $model_object) = @_; + + my $extruders = scalar @{ $self->config->nozzle_diameter }; + foreach my $i (0..$#{$model_object->volumes}) { + my $volume = $model_object->volumes->[$i]; + if (defined $volume->material_id) { + my $material = $model_object->model->materials->{ $volume->material_id }; + my $config = $material->config; + $config->set_ifndef('perimeters_extruder', $i); + $config->set_ifndef('infill_extruder', $i); + $config->set_ifndef('support_material_extruder', $i); + $config->set_ifndef('support_material_interface_extruder', $i); + } + } +} + +sub flow { + my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; + + $bridge //= 0; + $first_layer //= 0; + + # use the supplied custom width, if any + my $config_width = $width; + if (!defined $config_width) { + # get extrusion width from configuration + # (might be an absolute value, or a percent value, or zero for auto) + if ($first_layer) { + $config_width = $self->config->first_layer_extrusion_width; + } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL || $role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { + $config_width = $self->config->support_material_extrusion_width; + } else { + die "Unknown role $role"; + } + } + + # get the configured nozzle_diameter for the extruder associated + # to the flow role requested + my $extruder; # 1-based + if ($role == FLOW_ROLE_SUPPORT_MATERIAL) { + $config_width = $self->config->support_material_extruder; + } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { + $config_width = $self->config->support_material_interface_extruder; + } else { + die "Unknown role $role"; + } + my $nozzle_diameter = $self->config->nozzle_diameter->[$extruder-1]; + + return Slic3r::Flow->new( + width => $config_width, + role => $role, + nozzle_diameter => $nozzle_diameter, + layer_height => $layer_height, + bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + ); +} + 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index d7d8f00db..f1edc98f8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -861,9 +861,11 @@ sub generate_support_material { return unless ($self->config->support_material || $self->config->raft_layers > 0) && $self->layer_count >= 2; - Slic3r::Print::SupportMaterial - ->new(config => $self->config, flow => $self->print->support_material_flow) - ->generate($self); + my $s = Slic3r::Print::SupportMaterial->new( + config => $self->config, + flow => $self->print->support_material_flow, + ); + $s->generate($self); } sub _simplify_slices { diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 253fde2ba..0d3b844f4 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -1,8 +1,60 @@ package Slic3r::Print::Region; 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 +use Slic3r::Extruder ':roles'; +use Slic3r::Flow ':roles'; + +# A Print::Region object represents a group of volumes to print +# sharing the same config (including the same assigned extruder(s)) + +has 'print' => (is => 'ro', required => 1, weak_ref => 1); +has 'config' => (is => 'ro', required => 1); + +sub flow { + my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; + + $bridge //= 0; + $first_layer //= 0; + + # use the supplied custom width, if any + my $config_width = $width; + if (!defined $config_width) { + # get extrusion width from configuration + # (might be an absolute value, or a percent value, or zero for auto) + if ($first_layer) { + $config_width = $self->config->first_layer_extrusion_width; + } elsif ($role == FLOW_ROLE_PERIMETER) { + $config_width = $self->config->perimeter_extrusion_width; + } elsif ($role == FLOW_ROLE_INFILL) { + $config_width = $self->config->infill_extrusion_width; + } elsif ($role == FLOW_ROLE_SOLID_INFILL) { + $config_width = $self->config->solid_infill_extrusion_width; + } elsif ($role == FLOW_ROLE_TOP_SOLID_INFILL) { + $config_width = $self->config->top_infill_extrusion_width; + } else { + die "Unknown role $role"; + } + } + + # get the configured nozzle_diameter for the extruder associated + # to the flow role requested + my $extruder; # 1-based + if ($role == FLOW_ROLE_PERIMETER) { + $config_width = $self->config->perimeter_extruder; + } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) { + $config_width = $self->config->infill_extruder; + } else { + die "Unknown role $role"; + } + my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1]; + + return Slic3r::Flow->new( + width => $config_width, + role => $role, + nozzle_diameter => $nozzle_diameter, + layer_height => $layer_height, + bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + ); +} 1; diff --git a/slic3r.pl b/slic3r.pl index c55bd19a6..0be1219d0 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -159,7 +159,10 @@ if (@ARGV) { # slicing from command line printf "=> %s\n", $message; }, ); - $print->add_model_object($_) for @{$model->objects}; + foreach my $model_object (@{$model->objects}) { + $print->auto_assign_extruders($model_object); + $print->add_model_object($model_object); + } undef $model; # free memory $print->validate; if ($opt{export_svg}) { diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 4dd732150..a6bd71ec7 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -761,6 +761,7 @@ class PrintConfig : public StaticConfig Options["infill_extruder"].label = "Infill extruder"; Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; Options["infill_extruder"].cli = "infill-extruder=i"; + Options["infill_extruder"].shortcut.push_back("extruder"); Options["infill_extrusion_width"].type = coFloatOrPercent; Options["infill_extrusion_width"].label = "Infill"; @@ -878,6 +879,7 @@ class PrintConfig : public StaticConfig Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters."; Options["perimeter_extruder"].cli = "perimeter-extruder=i"; Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); + Options["perimeter_extruder"].shortcut.push_back("extruder"); Options["perimeter_extrusion_width"].type = coFloatOrPercent; Options["perimeter_extrusion_width"].label = "Perimeters"; @@ -1139,6 +1141,7 @@ class PrintConfig : public StaticConfig Options["support_material_extruder"].label = "Support material extruder"; Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; Options["support_material_extruder"].cli = "support-material-extruder=i"; + Options["support_material_extruder"].shortcut.push_back("extruder"); Options["support_material_extrusion_width"].type = coFloatOrPercent; Options["support_material_extrusion_width"].label = "Support material"; @@ -1150,6 +1153,7 @@ class PrintConfig : public StaticConfig Options["support_material_interface_extruder"].label = "Support material interface extruder"; Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; + Options["support_material_interface_extruder"].shortcut.push_back("extruder"); Options["support_material_interface_layers"].type = coInt; Options["support_material_interface_layers"].label = "Interface layers";