Satisfy test suite and CLI

This commit is contained in:
Alessandro Ranellucci 2014-01-02 17:24:23 +01:00
parent e2f1040a76
commit 5bf0942f45
20 changed files with 300 additions and 272 deletions

View File

@ -15,18 +15,18 @@ our $Options = print_config_def();
{ {
no strict 'refs'; no strict 'refs';
for my $opt_key (keys %$Options) { for my $opt_key (keys %$Options) {
*{$opt_key} = sub { $_[0]->_get($opt_key) }; *{$opt_key} = sub { $_[0]->get($opt_key) };
} }
} }
sub _get { # sub _get {
my ($self, $opt_key) = @_; # my ($self, $opt_key) = @_;
use XXX; # use XXX;
if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { # if (!defined first { $_ eq $opt_key } @{$self->get_keys}) {
ZZZ $opt_key; # ZZZ $opt_key;
} # }
return $self->get($opt_key); # return $self->get($opt_key);
} # }
sub new_from_defaults { sub new_from_defaults {
my $class = shift; my $class = shift;
@ -200,7 +200,8 @@ sub diff {
return [@diff]; return [@diff];
} }
# this method is idempotent by design # this method is idempotent by design and only applies to ::DynamicConfig or ::Full
# objects because it performs cross checks
sub validate { sub validate {
my $self = shift; my $self = shift;
@ -317,6 +318,31 @@ sub validate {
if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration) if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
&& !$self->default_acceleration; && !$self->default_acceleration;
# --spiral-vase
if ($self->spiral_vase) {
# Note that we might want to have more than one perimeter on the bottom
# solid layers.
die "Can't make more than one perimeter when spiral vase mode is enabled\n"
if $self->perimeters > 1;
die "Can't make less than one perimeter when spiral vase mode is enabled\n"
if $self->perimeters < 1;
die "Spiral vase mode is not compatible with non-zero fill density\n"
if $self->fill_density > 0;
die "Spiral vase mode is not compatible with top solid layers\n"
if $self->top_solid_layers > 0;
die "Spiral vase mode is not compatible with support material\n"
if $self->support_material || $self->support_material_enforce_layers > 0;
# This should be enforce automatically only on spiral layers and
# done on the others
die "Spiral vase mode is not compatible with retraction on layer change\n"
if defined first { $_ } @{ $self->retract_layer_change };
}
# general validation, quick and dirty # general validation, quick and dirty
foreach my $opt_key (@{$self->get_keys}) { foreach my $opt_key (@{$self->get_keys}) {
my $opt = $Options->{$opt_key}; my $opt = $Options->{$opt_key};

View File

@ -18,7 +18,7 @@ use constant OPTIONS => [qw(
has 'id' => (is => 'rw', required => 1); has 'id' => (is => 'rw', required => 1);
has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; has $_ => (is => 'ro', required => 1) for @{&OPTIONS};
has 'config'=> (is => 'ro', required => 1); has 'use_relative_e_distances' => (is => 'ro', default => sub {0});
has 'E' => (is => 'rw', default => sub {0} ); has 'E' => (is => 'rw', default => sub {0} );
has 'absolute_E' => (is => 'rw', default => sub {0} ); has 'absolute_E' => (is => 'rw', default => sub {0} );
@ -56,7 +56,7 @@ sub scaled_wipe_distance {
sub extrude { sub extrude {
my ($self, $E) = @_; my ($self, $E) = @_;
$self->E(0) if $self->config->use_relative_e_distances; $self->E(0) if $self->use_relative_e_distances;
$self->absolute_E($self->absolute_E + $E); $self->absolute_E($self->absolute_E + $E);
return $self->E($self->E + $E); return $self->E($self->E + $E);
} }

View File

@ -1,6 +1,7 @@
package Slic3r::Fill; package Slic3r::Fill;
use Moo; use Moo;
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Fill::ArchimedeanChords; use Slic3r::Fill::ArchimedeanChords;
use Slic3r::Fill::Base; use Slic3r::Fill::Base;
use Slic3r::Fill::Concentric; use Slic3r::Fill::Concentric;
@ -11,7 +12,7 @@ use Slic3r::Fill::Line;
use Slic3r::Fill::OctagramSpiral; use Slic3r::Fill::OctagramSpiral;
use Slic3r::Fill::PlanePath; use Slic3r::Fill::PlanePath;
use Slic3r::Fill::Rectilinear; use Slic3r::Fill::Rectilinear;
use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(X Y PI scale chained_path); use Slic3r::Geometry qw(X Y PI scale chained_path);
use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2); use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
@ -50,7 +51,10 @@ sub make_fill {
my ($layerm) = @_; my ($layerm) = @_;
Slic3r::debugf "Filling layer %d:\n", $layerm->id; Slic3r::debugf "Filling layer %d:\n", $layerm->id;
my $fill_density = $layerm->config->fill_density;
my $fill_density = $layerm->config->fill_density;
my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL);
my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
my @surfaces = (); my @surfaces = ();
@ -95,7 +99,7 @@ sub make_fill {
# we are going to grow such regions by overlapping them with the void (if any) # we are going to grow such regions by overlapping them with the void (if any)
# TODO: detect and investigate whether there could be narrow regions without # TODO: detect and investigate whether there could be narrow regions without
# any void neighbors # any void neighbors
my $distance_between_surfaces = $layerm->solid_infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
{ {
my $collapsed = diff( my $collapsed = diff(
[ map @{$_->expolygon}, @surfaces ], [ map @{$_->expolygon}, @surfaces ],
@ -133,11 +137,10 @@ sub make_fill {
my $filler = $layerm->config->fill_pattern; my $filler = $layerm->config->fill_pattern;
my $density = $fill_density; my $density = $fill_density;
my $flow = ($surface->surface_type == S_TYPE_TOP) my $flow = ($surface->surface_type == S_TYPE_TOP)
? $layerm->top_infill_flow ? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)
: $surface->is_solid : $surface->is_solid
? $layerm->solid_infill_flow ? $solid_infill_flow
: $layerm->infill_flow; : $infill_flow;
my $flow_spacing = $flow->spacing;
my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_bridge = $layerm->id > 0 && $surface->is_bridge;
my $is_solid = $surface->is_solid; my $is_solid = $surface->is_solid;
@ -147,7 +150,7 @@ sub make_fill {
$filler = $layerm->config->solid_fill_pattern; $filler = $layerm->config->solid_fill_pattern;
if ($is_bridge) { if ($is_bridge) {
$filler = 'rectilinear'; $filler = 'rectilinear';
$flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing; $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 1);
} elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) {
$filler = 'rectilinear'; $filler = 'rectilinear';
} }
@ -155,6 +158,8 @@ sub make_fill {
next SURFACE unless $density > 0; next SURFACE unless $density > 0;
} }
my $flow_spacing = $flow->spacing;
my $f = $self->filler($filler); my $f = $self->filler($filler);
$f->layer_id($layerm->id); $f->layer_id($layerm->id);
$f->angle($layerm->config->fill_angle); $f->angle($layerm->config->fill_angle);
@ -166,7 +171,7 @@ sub make_fill {
next unless @polylines; next unless @polylines;
# ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed)
$params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge; $params->{flow_spacing} = $flow->width if $is_bridge;
# save into layer # save into layer
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;

View File

@ -36,7 +36,10 @@ sub BUILDARGS {
$args{width} = $args{layer_height} * $1 / 100; $args{width} = $args{layer_height} * $1 / 100;
} }
$args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)});
%args = @args{qw(width spacing)}; %args = (
width => $args{width},
spacing => $args{spacing},
);
} }
return {%args}; return {%args};

View File

@ -3,11 +3,12 @@ use Moo;
use List::Util qw(min first); use List::Util qw(min first);
use Slic3r::ExtrusionPath ':roles'; use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B); use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B);
use Slic3r::Geometry::Clipper qw(union_ex); use Slic3r::Geometry::Clipper qw(union_ex);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
has 'config' => (is => 'ro', required => 1); has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'extra_variables' => (is => 'rw', default => sub {{}});
has 'extruders' => (is => 'ro', required => 1); has 'extruders' => (is => 'ro', required => 1);
has 'multiple_extruders' => (is => 'lazy'); has 'multiple_extruders' => (is => 'lazy');
@ -16,6 +17,7 @@ has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled
has 'layer_count' => (is => 'ro', required => 1 ); has 'layer_count' => (is => 'ro', required => 1 );
has 'layer' => (is => 'rw'); has 'layer' => (is => 'rw');
has 'region' => (is => 'rw');
has '_layer_islands' => (is => 'rw'); has '_layer_islands' => (is => 'rw');
has '_upper_layer_islands' => (is => 'rw'); has '_upper_layer_islands' => (is => 'rw');
has '_layer_overhangs' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new }); has '_layer_overhangs' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
@ -23,6 +25,8 @@ has 'shift_x' => (is => 'rw', default => sub {0} );
has 'shift_y' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} );
has 'z' => (is => 'rw'); has 'z' => (is => 'rw');
has 'speed' => (is => 'rw'); has 'speed' => (is => 'rw');
has '_extrusion_axis' => (is => 'rw');
has '_retract_lift' => (is => 'rw');
has 'speeds' => (is => 'lazy'); # mm/min has 'speeds' => (is => 'lazy'); # mm/min
has 'external_mp' => (is => 'rw'); has 'external_mp' => (is => 'rw');
@ -38,12 +42,19 @@ has 'last_f' => (is => 'rw', default => sub {""});
has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'last_fan_speed' => (is => 'rw', default => sub {0});
has 'wipe_path' => (is => 'rw'); has 'wipe_path' => (is => 'rw');
sub BUILD {
my ($self) = @_;
$self->_extrusion_axis($self->print_config->get_extrusion_axis);
$self->_retract_lift($self->print_config->retract_lift->[0]);
}
sub _build_speeds { sub _build_speeds {
my $self = shift; my $self = shift;
return { return {
map { $_ => 60 * $self->config->get_value("${_}_speed") } map { $_ => 60 * $self->print_config->get_value("${_}_speed") }
qw(travel perimeter small_perimeter external_perimeter infill qw(travel perimeter small_perimeter external_perimeter infill
solid_infill top_solid_infill support_material bridge gap_fill retract), solid_infill top_solid_infill bridge gap_fill retract),
}; };
} }
@ -59,7 +70,6 @@ my %role_speeds = (
&EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_BRIDGE => 'bridge',
&EXTR_ROLE_INTERNALBRIDGE => 'solid_infill', &EXTR_ROLE_INTERNALBRIDGE => 'solid_infill',
&EXTR_ROLE_SKIRT => 'perimeter', &EXTR_ROLE_SKIRT => 'perimeter',
&EXTR_ROLE_SUPPORTMATERIAL => 'support_material',
&EXTR_ROLE_GAPFILL => 'gap_fill', &EXTR_ROLE_GAPFILL => 'gap_fill',
); );
@ -97,29 +107,29 @@ sub change_layer {
$self->_layer_islands($layer->islands); $self->_layer_islands($layer->islands);
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []); $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
$self->_layer_overhangs->clear; $self->_layer_overhangs->clear;
if ($layer->id > 0 && ($layer->config->overhangs || $self->config->start_perimeters_at_non_overhang)) { if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) {
$self->_layer_overhangs->append( $self->_layer_overhangs->append(
# clone ExPolygons because they come from Surface objects but will be used outside here # clone ExPolygons because they come from Surface objects but will be used outside here
map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions} map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions}
); );
} }
if ($self->config->avoid_crossing_perimeters) { if ($self->print_config->avoid_crossing_perimeters) {
$self->layer_mp(Slic3r::GCode::MotionPlanner->new( $self->layer_mp(Slic3r::GCode::MotionPlanner->new(
islands => union_ex([ map @$_, @{$layer->slices} ], 1), islands => union_ex([ map @$_, @{$layer->slices} ], 1),
)); ));
} }
my $gcode = ""; my $gcode = "";
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
$gcode .= sprintf "M73 P%s%s\n", $gcode .= sprintf "M73 P%s%s\n",
int(99 * ($layer->id / ($self->layer_count - 1))), int(99 * ($layer->id / ($self->layer_count - 1))),
($self->config->gcode_comments ? ' ; update progress' : ''); ($self->print_config->gcode_comments ? ' ; update progress' : '');
} }
if ($self->config->first_layer_acceleration) { if ($self->print_config->first_layer_acceleration) {
if ($layer->id == 0) { if ($layer->id == 0) {
$gcode .= $self->set_acceleration($self->config->first_layer_acceleration); $gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration);
} elsif ($layer->id == 1) { } elsif ($layer->id == 1) {
$gcode .= $self->set_acceleration($self->config->default_acceleration); $gcode .= $self->set_acceleration($self->print_config->default_acceleration);
} }
} }
@ -133,7 +143,7 @@ sub move_z {
my $gcode = ""; my $gcode = "";
$z += $self->config->z_offset; $z += $self->print_config->z_offset;
my $current_z = $self->z; my $current_z = $self->z;
my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef; my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef;
@ -181,11 +191,11 @@ sub extrude_loop {
# find candidate starting points # find candidate starting points
# start looking for concave vertices not being overhangs # start looking for concave vertices not being overhangs
my @concave = (); my @concave = ();
if ($self->config->start_perimeters_at_concave_points) { if ($self->print_config->start_perimeters_at_concave_points) {
@concave = $polygon->concave_points; @concave = $polygon->concave_points;
} }
my @candidates = (); my @candidates = ();
if ($self->config->start_perimeters_at_non_overhang) { if ($self->print_config->start_perimeters_at_non_overhang) {
@candidates = grep !$self->_layer_overhangs->contains_point($_), @concave; @candidates = grep !$self->_layer_overhangs->contains_point($_), @concave;
} }
if (!@candidates) { if (!@candidates) {
@ -193,7 +203,7 @@ sub extrude_loop {
@candidates = @concave; @candidates = @concave;
if (!@candidates) { if (!@candidates) {
# if none, look for any non-overhang vertex # if none, look for any non-overhang vertex
if ($self->config->start_perimeters_at_non_overhang) { if ($self->print_config->start_perimeters_at_non_overhang) {
@candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon; @candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon;
} }
if (!@candidates) { if (!@candidates) {
@ -206,9 +216,9 @@ sub extrude_loop {
# find the point of the loop that is closest to the current extruder position # find the point of the loop that is closest to the current extruder position
# or randomize if requested # or randomize if requested
my $last_pos = $self->last_pos; my $last_pos = $self->last_pos;
if ($self->config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) {
$last_pos = Slic3r::Point->new(scale $self->config->print_center->[X], scale $self->config->bed_size->[Y]); $last_pos = Slic3r::Point->new(scale $self->print_config->print_center->[X], scale $self->print_config->bed_size->[Y]);
$last_pos->rotate(rand(2*PI), $self->config->print_center); $last_pos->rotate(rand(2*PI), $self->print_config->print_center);
} }
# split the loop at the starting point and make a path # split the loop at the starting point and make a path
@ -224,7 +234,7 @@ sub extrude_loop {
my @paths = (); my @paths = ();
# detect overhanging/bridging perimeters # detect overhanging/bridging perimeters
if ($self->layer->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) { if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) {
# get non-overhang paths by subtracting overhangs from the loop # get non-overhang paths by subtracting overhangs from the loop
push @paths, push @paths,
map $_->clone, map $_->clone,
@ -234,7 +244,7 @@ sub extrude_loop {
push @paths, push @paths,
map { map {
$_->role(EXTR_ROLE_OVERHANG_PERIMETER); $_->role(EXTR_ROLE_OVERHANG_PERIMETER);
$_->flow_spacing($self->extruder->bridge_flow->width); $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width);
$_ $_
} }
map $_->clone, map $_->clone,
@ -259,7 +269,7 @@ sub extrude_loop {
$self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe; $self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe;
# make a little move inwards before leaving loop # make a little move inwards before leaving loop
if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->layer->object->config->perimeters > 1) { if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) {
# detect angle between last and first segment # detect angle between last and first segment
# the side depends on the original winding order of the polygon (left for contours, right for holes) # the side depends on the original winding order of the polygon (left for contours, right for holes)
my @points = $was_clockwise ? (-2, 1) : (1, -2); my @points = $was_clockwise ? (-2, 1) : (1, -2);
@ -299,13 +309,13 @@ sub extrude_path {
# adjust acceleration # adjust acceleration
my $acceleration; my $acceleration;
if (!$self->config->first_layer_acceleration || $self->layer->id != 0) { if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) {
if ($self->config->perimeter_acceleration && $path->is_perimeter) { if ($self->print_config->perimeter_acceleration && $path->is_perimeter) {
$acceleration = $self->config->perimeter_acceleration; $acceleration = $self->print_config->perimeter_acceleration;
} elsif ($self->config->infill_acceleration && $path->is_fill) { } elsif ($self->print_config->infill_acceleration && $path->is_fill) {
$acceleration = $self->config->infill_acceleration; $acceleration = $self->print_config->infill_acceleration;
} elsif ($self->config->infill_acceleration && $path->is_bridge) { } elsif ($self->print_config->infill_acceleration && $path->is_bridge) {
$acceleration = $self->config->bridge_acceleration; $acceleration = $self->print_config->bridge_acceleration;
} }
$gcode .= $self->set_acceleration($acceleration) if $acceleration; $gcode .= $self->set_acceleration($acceleration) if $acceleration;
} }
@ -322,15 +332,13 @@ sub extrude_path {
# calculate extrusion length per distance unit # calculate extrusion length per distance unit
my $e = $self->extruder->e_per_mm3 * $area; my $e = $self->extruder->e_per_mm3 * $area;
$e = 0 if !$self->config->extrusion_axis; $e = 0 if !$self->_extrusion_axis;
# set speed # set speed
$self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role ); $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role );
my $F = $self->speeds->{$self->speed} // $self->speed; my $F = $self->speeds->{$self->speed} // $self->speed;
if ($self->layer->id == 0) { if ($self->layer->id == 0) {
$F = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ $F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60;
? sprintf("%.3f", $F * $1/100)
: $self->config->first_layer_speed * 60;
} }
# extrude arc or line # extrude arc or line
@ -350,12 +358,12 @@ sub extrude_path {
$gcode .= sprintf "G1 X%.3f Y%.3f", $gcode .= sprintf "G1 X%.3f Y%.3f",
($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X],
($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #**
$gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) $gcode .= sprintf(" %s%.5f", $self->_extrusion_axis, $E)
if $E; if $E;
$gcode .= " F$local_F" $gcode .= " F$local_F"
if $local_F; if $local_F;
$gcode .= " ; $description" $gcode .= " ; $description"
if $self->config->gcode_comments; if $self->print_config->gcode_comments;
$gcode .= "\n"; $gcode .= "\n";
# only include F in the first line # only include F in the first line
@ -369,19 +377,14 @@ sub extrude_path {
$gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge; $gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge;
$self->last_pos($path->last_point); $self->last_pos($path->last_point);
if ($self->config->cooling) { if ($self->print_config->cooling) {
my $path_time = $path_length / $F * 60; my $path_time = $path_length / $F * 60;
if ($self->layer->id == 0) {
$path_time = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/
? $path_time / ($1/100)
: $path_length / $self->config->first_layer_speed * 60;
}
$self->elapsed_time($self->elapsed_time + $path_time); $self->elapsed_time($self->elapsed_time + $path_time);
} }
# reset acceleration # reset acceleration
$gcode .= $self->set_acceleration($self->config->default_acceleration) $gcode .= $self->set_acceleration($self->print_config->default_acceleration)
if $acceleration && $self->config->default_acceleration; if $acceleration && $self->print_config->default_acceleration;
return $gcode; return $gcode;
} }
@ -400,7 +403,7 @@ sub travel_to {
# skip retraction if the travel move is contained in an island in the current layer # skip retraction if the travel move is contained in an island in the current layer
# *and* in an island in the upper layer (so that the ooze will not be visible) # *and* in an island in the upper layer (so that the ooze will not be visible)
if ($travel->length < scale $self->extruder->retract_before_travel if ($travel->length < scale $self->extruder->retract_before_travel
|| ($self->config->only_retract_when_crossing_perimeters || ($self->print_config->only_retract_when_crossing_perimeters
&& (first { $_->contains_line($travel) } @{$self->_upper_layer_islands}) && (first { $_->contains_line($travel) } @{$self->_upper_layer_islands})
&& (first { $_->contains_line($travel) } @{$self->_layer_islands})) && (first { $_->contains_line($travel) } @{$self->_layer_islands}))
|| (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands})) || (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands}))
@ -408,7 +411,7 @@ sub travel_to {
$self->straight_once(0); $self->straight_once(0);
$self->speed('travel'); $self->speed('travel');
$gcode .= $self->G0($point, undef, 0, $comment || ""); $gcode .= $self->G0($point, undef, 0, $comment || "");
} elsif (!$self->config->avoid_crossing_perimeters || $self->straight_once) { } elsif (!$self->print_config->avoid_crossing_perimeters || $self->straight_once) {
$self->straight_once(0); $self->straight_once(0);
$gcode .= $self->retract; $gcode .= $self->retract;
$self->speed('travel'); $self->speed('travel');
@ -441,7 +444,7 @@ sub _plan {
my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines}; my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines};
# if the path is not contained in a single island we need to retract # if the path is not contained in a single island we need to retract
my $need_retract = !$self->config->only_retract_when_crossing_perimeters; my $need_retract = !$self->print_config->only_retract_when_crossing_perimeters;
if (!$need_retract) { if (!$need_retract) {
$need_retract = 1; $need_retract = 1;
foreach my $island (@{$self->_upper_layer_islands}) { foreach my $island (@{$self->_upper_layer_islands}) {
@ -481,14 +484,14 @@ sub retract {
if ($self->extruder->wipe && $self->wipe_path) { if ($self->extruder->wipe && $self->wipe_path) {
my @points = @{$self->wipe_path}; my @points = @{$self->wipe_path};
$wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]); $wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]);
$wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->config->travel_speed)); $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->print_config->travel_speed));
} }
# prepare moves # prepare moves
my $retract = [undef, undef, -$length, $comment]; my $retract = [undef, undef, -$length, $comment];
my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted my $lift = ($self->_retract_lift == 0 || defined $params{move_z}) && !$self->lifted
? undef ? undef
: [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel']; : [undef, $self->z + $self->_retract_lift, 0, 'lift plate during travel'];
# check that we have a positive wipe length # check that we have a positive wipe length
if ($wipe_path) { if ($wipe_path) {
@ -500,7 +503,7 @@ sub retract {
my $segment_length = $line->length; my $segment_length = $line->length;
# reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one # reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
# due to rounding # due to rounding
my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->config->travel_speed)) * 0.95; my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)) * 0.95;
$retracted += $e; $retracted += $e;
$gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE"); $gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE");
} }
@ -510,7 +513,7 @@ sub retract {
$self->speed('retract'); $self->speed('retract');
$gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment); $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment);
} }
} elsif ($self->config->use_firmware_retraction) { } elsif ($self->print_config->use_firmware_retraction) {
$gcode .= "G10 ; retract\n"; $gcode .= "G10 ; retract\n";
} else { } else {
$self->speed('retract'); $self->speed('retract');
@ -518,23 +521,23 @@ sub retract {
} }
if (!$self->lifted) { if (!$self->lifted) {
$self->speed('travel'); $self->speed('travel');
if (defined $params{move_z} && $self->extruder->retract_lift > 0) { if (defined $params{move_z} && $self->_retract_lift > 0) {
my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
$gcode .= $self->G0(@$travel); $gcode .= $self->G0(@$travel);
$self->lifted($self->extruder->retract_lift); $self->lifted($self->_retract_lift);
} elsif ($lift) { } elsif ($lift) {
$gcode .= $self->G1(@$lift); $gcode .= $self->G1(@$lift);
} }
} }
$self->extruder->retracted($self->extruder->retracted + $length); $self->extruder->retracted($self->extruder->retracted + $length);
$self->extruder->restart_extra($restart_extra); $self->extruder->restart_extra($restart_extra);
$self->lifted($self->extruder->retract_lift) if $lift; $self->lifted($self->_retract_lift) if $lift;
# reset extrusion distance during retracts # reset extrusion distance during retracts
# this makes sure we leave sufficient precision in the firmware # this makes sure we leave sufficient precision in the firmware
$gcode .= $self->reset_e; $gcode .= $self->reset_e;
$gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware'; $gcode .= "M103 ; extruder off\n" if $self->print_config->gcode_flavor eq 'makerware';
return $gcode; return $gcode;
} }
@ -543,7 +546,7 @@ sub unretract {
my ($self) = @_; my ($self) = @_;
my $gcode = ""; my $gcode = "";
$gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware'; $gcode .= "M101 ; extruder on\n" if $self->print_config->gcode_flavor eq 'makerware';
if ($self->lifted) { if ($self->lifted) {
$self->speed('travel'); $self->speed('travel');
@ -554,15 +557,15 @@ sub unretract {
my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra; my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra;
if ($to_unretract) { if ($to_unretract) {
$self->speed('retract'); $self->speed('retract');
if ($self->config->use_firmware_retraction) { if ($self->print_config->use_firmware_retraction) {
$gcode .= "G11 ; unretract\n"; $gcode .= "G11 ; unretract\n";
} elsif ($self->config->extrusion_axis) { } elsif ($self->_extrusion_axis) {
# use G1 instead of G0 because G0 will blend the restart with the previous travel move # use G1 instead of G0 because G0 will blend the restart with the previous travel move
$gcode .= sprintf "G1 %s%.5f F%.3f", $gcode .= sprintf "G1 %s%.5f F%.3f",
$self->config->extrusion_axis, $self->_extrusion_axis,
$self->extruder->extrude($to_unretract), $self->extruder->extrude($to_unretract),
$self->extruder->retract_speed_mm_min; $self->extruder->retract_speed_mm_min;
$gcode .= " ; compensate retraction" if $self->config->gcode_comments; $gcode .= " ; compensate retraction" if $self->print_config->gcode_comments;
$gcode .= "\n"; $gcode .= "\n";
} }
$self->extruder->retracted(0); $self->extruder->retracted(0);
@ -574,11 +577,11 @@ sub unretract {
sub reset_e { sub reset_e {
my ($self) = @_; my ($self) = @_;
return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/; return "" if $self->print_config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/;
$self->extruder->E(0) if $self->extruder; $self->extruder->E(0) if $self->extruder;
return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '') return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '')
if $self->config->extrusion_axis && !$self->config->use_relative_e_distances; if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances;
} }
sub set_acceleration { sub set_acceleration {
@ -586,12 +589,12 @@ sub set_acceleration {
return "" if !$acceleration; return "" if !$acceleration;
return sprintf "M204 S%s%s\n", return sprintf "M204 S%s%s\n",
$acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : ''); $acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : '');
} }
sub G0 { sub G0 {
my $self = shift; my $self = shift;
return $self->G1(@_) if !($self->config->g0 || $self->config->gcode_flavor eq 'mach3'); return $self->G1(@_) if !($self->print_config->g0 || $self->print_config->gcode_flavor eq 'mach3');
return $self->_G0_G1("G0", @_); return $self->_G0_G1("G0", @_);
} }
@ -628,11 +631,11 @@ sub _Gx {
$gcode .= sprintf " F%.3f", $F; $gcode .= sprintf " F%.3f", $F;
# output extrusion distance # output extrusion distance
if ($e && $self->config->extrusion_axis) { if ($e && $self->_extrusion_axis) {
$gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->extrude($e); $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $self->extruder->extrude($e);
} }
$gcode .= " ; $comment" if $comment && $self->config->gcode_comments; $gcode .= " ; $comment" if $comment && $self->print_config->gcode_comments;
return "$gcode\n"; return "$gcode\n";
} }
@ -653,8 +656,8 @@ sub set_extruder {
$gcode .= $self->retract(toolchange => 1) if defined $self->extruder; $gcode .= $self->retract(toolchange => 1) if defined $self->extruder;
# append custom toolchange G-code # append custom toolchange G-code
if (defined $self->extruder && $self->config->toolchange_gcode) { if (defined $self->extruder && $self->print_config->toolchange_gcode) {
$gcode .= sprintf "%s\n", $self->replace_variables($self->config->toolchange_gcode, { $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, {
previous_extruder => $self->extruder->id, previous_extruder => $self->extruder->id,
next_extruder => $extruder->id, next_extruder => $extruder->id,
}); });
@ -669,24 +672,24 @@ sub set_extruder {
? $self->extruder->first_layer_temperature ? $self->extruder->first_layer_temperature
: $self->extruder->temperature; : $self->extruder->temperature;
# we assume that heating is always slower than cooling, so no need to block # we assume that heating is always slower than cooling, so no need to block
$gcode .= $self->set_temperature($temp + $self->config->standby_temperature_delta, 0); $gcode .= $self->set_temperature($temp + $self->print_config->standby_temperature_delta, 0);
} }
# set the new extruder # set the new extruder
$self->extruder($extruder); $self->extruder($extruder);
$gcode .= sprintf "%s%d%s\n", $gcode .= sprintf "%s%d%s\n",
($self->config->gcode_flavor eq 'makerware' ($self->print_config->gcode_flavor eq 'makerware'
? 'M135 T' ? 'M135 T'
: $self->config->gcode_flavor eq 'sailfish' : $self->print_config->gcode_flavor eq 'sailfish'
? 'M108 T' ? 'M108 T'
: 'T'), : 'T'),
$extruder->id, $extruder->id,
($self->config->gcode_comments ? ' ; change extruder' : ''); ($self->print_config->gcode_comments ? ' ; change extruder' : '');
$gcode .= $self->reset_e; $gcode .= $self->reset_e;
# set the new extruder to the operating temperature # set the new extruder to the operating temperature
if ($self->config->ooze_prevention) { if ($self->print_config->ooze_prevention) {
my $temp = defined $self->layer && $self->layer->id == 0 my $temp = defined $self->layer && $self->layer->id == 0
? $self->extruder->first_layer_temperature ? $self->extruder->first_layer_temperature
: $self->extruder->temperature; : $self->extruder->temperature;
@ -702,18 +705,18 @@ sub set_fan {
if ($self->last_fan_speed != $speed || $dont_save) { if ($self->last_fan_speed != $speed || $dont_save) {
$self->last_fan_speed($speed) if !$dont_save; $self->last_fan_speed($speed) if !$dont_save;
if ($speed == 0) { if ($speed == 0) {
my $code = $self->config->gcode_flavor eq 'teacup' my $code = $self->print_config->gcode_flavor eq 'teacup'
? 'M106 S0' ? 'M106 S0'
: $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ : $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/
? 'M127' ? 'M127'
: 'M107'; : 'M107';
return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : ''); return sprintf "$code%s\n", ($self->print_config->gcode_comments ? ' ; disable fan' : '');
} else { } else {
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : ''); return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : '');
} else { } else {
return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
(255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : ''); (255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : '');
} }
} }
} }
@ -723,17 +726,17 @@ sub set_fan {
sub set_temperature { sub set_temperature {
my ($self, $temperature, $wait, $tool) = @_; my ($self, $temperature, $wait, $tool) = @_;
return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/; return "" if $wait && $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
? ('M109', 'wait for temperature to be reached') ? ('M109', 'wait for temperature to be reached')
: ('M104', 'set temperature'); : ('M104', 'set temperature');
my $gcode = sprintf "$code %s%d %s; $comment\n", my $gcode = sprintf "$code %s%d %s; $comment\n",
($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
(defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : ""; (defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
$gcode .= "M116 ; wait for temperature to be reached\n" $gcode .= "M116 ; wait for temperature to be reached\n"
if $self->config->gcode_flavor eq 'teacup' && $wait; if $self->print_config->gcode_flavor eq 'teacup' && $wait;
return $gcode; return $gcode;
} }
@ -741,21 +744,21 @@ sub set_temperature {
sub set_bed_temperature { sub set_bed_temperature {
my ($self, $temperature, $wait) = @_; my ($self, $temperature, $wait) = @_;
my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') ? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
: ('M140', 'set bed temperature'); : ('M140', 'set bed temperature');
my $gcode = sprintf "$code %s%d ; $comment\n", my $gcode = sprintf "$code %s%d ; $comment\n",
($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
$gcode .= "M116 ; wait for bed temperature to be reached\n" $gcode .= "M116 ; wait for bed temperature to be reached\n"
if $self->config->gcode_flavor eq 'teacup' && $wait; if $self->print_config->gcode_flavor eq 'teacup' && $wait;
return $gcode; return $gcode;
} }
sub replace_variables { sub replace_variables {
my ($self, $string, $extra) = @_; my ($self, $string, $extra) = @_;
return $self->config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } }); return $self->print_config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } });
} }
1; 1;

View File

@ -104,7 +104,7 @@ sub flush_path {
$gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y); $gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y);
my $E = 0; # TODO: compute E using $length my $E = 0; # TODO: compute E using $length
$gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) $gcode .= sprintf(" %s%.5f", $self->config->get_extrusion_axis, $E)
if $E; if $E;
my $F = 0; # TODO: extract F from original moves my $F = 0; # TODO: extract F from original moves

View File

@ -1,7 +1,7 @@
package Slic3r::GCode::CoolingBuffer; package Slic3r::GCode::CoolingBuffer;
use Moo; use Moo;
has 'config' => (is => 'ro', required => 1); has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print
has 'gcodegen' => (is => 'ro', required => 1); has 'gcodegen' => (is => 'ro', required => 1);
has 'gcode' => (is => 'rw', default => sub {""}); has 'gcode' => (is => 'rw', default => sub {""});
has 'elapsed_time' => (is => 'rw', default => sub {0}); has 'elapsed_time' => (is => 'rw', default => sub {0});

View File

@ -19,24 +19,24 @@ has '_last_obj_copy' => (is => 'rw');
sub _build_spiralvase { sub _build_spiralvase {
my $self = shift; my $self = shift;
return $self->gcodegen->config->spiral_vase return $self->print->config->spiral_vase
? Slic3r::GCode::SpiralVase->new(config => $self->gcodegen->config) ? Slic3r::GCode::SpiralVase->new(config => $self->print->config)
: undef; : undef;
} }
sub _build_vibration_limit { sub _build_vibration_limit {
my $self = shift; my $self = shift;
return $self->gcodegen->config->vibration_limit return $self->print->config->vibration_limit
? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config) ? Slic3r::GCode::VibrationLimit->new(config => $self->print->config)
: undef; : undef;
} }
sub _build_arc_fitting { sub _build_arc_fitting {
my $self = shift; my $self = shift;
return $self->gcodegen->config->gcode_arcs return $self->print->config->gcode_arcs
? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config) ? Slic3r::GCode::ArcFitting->new(config => $self->print->config)
: undef; : undef;
} }
@ -45,44 +45,46 @@ sub process_layer {
my ($layer, $object_copies) = @_; my ($layer, $object_copies) = @_;
my $gcode = ""; my $gcode = "";
my $object = $layer->object;
# check whether we're going to apply spiralvase logic # check whether we're going to apply spiralvase logic
my $spiralvase = defined $self->spiralvase my $spiralvase = defined $self->spiralvase
&& ($layer->id > 0 || $self->gcodegen->config->brim_width == 0) && ($layer->id > 0 || $self->print->config->brim_width == 0)
&& ($layer->id >= $self->gcodegen->config->skirt_height && $self->gcodegen->config->skirt_height != -1) && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
&& ($layer->id >= $self->gcodegen->config->bottom_solid_layers); && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions});
# if we're going to apply spiralvase to this layer, disable loop clipping # if we're going to apply spiralvase to this layer, disable loop clipping
$self->gcodegen->enable_loop_clipping(!$spiralvase); $self->gcodegen->enable_loop_clipping(!$spiralvase);
if (!$self->second_layer_things_done && $layer->id == 1) { if (!$self->second_layer_things_done && $layer->id == 1) {
for my $t (grep $self->extruders->[$_], 0 .. $#{$self->gcodegen->config->temperature}) { for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) {
$gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t) $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t)
if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature;
} }
$gcode .= $self->gcodegen->set_bed_temperature($self->gcodegen->config->bed_temperature) $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature)
if $self->gcodegen->config->bed_temperature && $self->gcodegen->config->bed_temperature != $self->gcodegen->config->first_layer_bed_temperature; if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature;
$self->second_layer_things_done(1); $self->second_layer_things_done(1);
} }
# set new layer - this will change Z and force a retraction if retract_layer_change is enabled # set new layer - this will change Z and force a retraction if retract_layer_change is enabled
$gcode .= $self->gcodegen->change_layer($layer); $gcode .= $self->gcodegen->change_layer($layer);
$gcode .= $self->gcodegen->replace_variables($self->gcodegen->config->layer_gcode, { $gcode .= $self->gcodegen->replace_variables($self->print->config->layer_gcode, {
layer_num => $self->gcodegen->layer->id, layer_num => $self->gcodegen->layer->id,
}) . "\n" if $self->gcodegen->config->layer_gcode; }) . "\n" if $self->print->config->layer_gcode;
# extrude skirt # extrude skirt
if (((values %{$self->skirt_done}) < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1)
&& !$self->skirt_done->{$layer->print_z}) { && !$self->skirt_done->{$layer->print_z}) {
$self->gcodegen->set_shift(@{$self->shift}); $self->gcodegen->set_shift(@{$self->shift});
$gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]);
# skip skirt if we have a large brim # skip skirt if we have a large brim
if ($layer->id < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) { if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) {
# distribute skirt loops across all extruders # distribute skirt loops across all extruders
my @skirt_loops = @{$self->print->skirt}; my @skirt_loops = @{$self->print->skirt};
for my $i (0 .. $#skirt_loops) { for my $i (0 .. $#skirt_loops) {
# when printing layers > 0 ignore 'min_skirt_length' and # when printing layers > 0 ignore 'min_skirt_length' and
# just use the 'skirts' setting; also just use the current extruder # just use the 'skirts' setting; also just use the current extruder
last if ($layer->id > 0) && ($i >= $self->gcodegen->config->skirts); last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
$gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]) $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ])
if $layer->id == 0; if $layer->id == 0;
$gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
@ -94,7 +96,7 @@ sub process_layer {
# extrude brim # extrude brim
if (!$self->brim_done) { if (!$self->brim_done) {
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]);
$self->gcodegen->set_shift(@{$self->shift}); $self->gcodegen->set_shift(@{$self->shift});
$gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim}; $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim};
$self->brim_done(1); $self->brim_done(1);
@ -109,15 +111,17 @@ sub process_layer {
# extrude support material before other things because it might use a lower Z # 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 # and also because we avoid travelling on other things when printing it
if ($self->print->has_support_material && $layer->isa('Slic3r::Layer::Support')) { if ($layer->isa('Slic3r::Layer::Support')) {
if ($layer->support_interface_fills->count > 0) { if ($layer->support_interface_fills->count > 0) {
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_interface_extruder-1]); $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]);
$gcode .= $self->gcodegen->extrude_path($_, 'support material interface') my %params = (speed => $object->config->support_material_speed*60);
$gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params)
for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)};
} }
if ($layer->support_fills->count > 0) { if ($layer->support_fills->count > 0) {
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]);
$gcode .= $self->gcodegen->extrude_path($_, 'support material') my %params = (speed => $object->config->support_material_speed*60);
$gcode .= $self->gcodegen->extrude_path($_, 'support material', %params)
for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)}; for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)};
} }
} }
@ -126,16 +130,17 @@ sub process_layer {
my @region_ids = 0 .. ($self->print->regions_count-1); my @region_ids = 0 .. ($self->print->regions_count-1);
if ($self->gcodegen->multiple_extruders) { if ($self->gcodegen->multiple_extruders) {
my $last_extruder = $self->gcodegen->extruder; my $last_extruder = $self->gcodegen->extruder;
my $best_region_id = first { $self->print->regions->[$_]->extruders->{perimeter} eq $last_extruder } @region_ids; my $best_region_id = first { $self->print->regions->[$_]->config->perimeter_extruder-1 eq $last_extruder } @region_ids;
@region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id; @region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id;
} }
foreach my $region_id (@region_ids) { foreach my $region_id (@region_ids) {
my $layerm = $layer->regions->[$region_id] or next; my $layerm = $layer->regions->[$region_id] or next;
my $region = $self->print->regions->[$region_id]; my $region = $self->print->regions->[$region_id];
$self->gcodegen->region($region);
my @islands = (); my @islands = ();
if ($self->gcodegen->config->avoid_crossing_perimeters) { if ($self->print->config->avoid_crossing_perimeters) {
push @islands, { perimeters => [], fills => [] } push @islands, { perimeters => [], fills => [] }
for 1 .. (@{$layer->slices} || 1); # make sure we have at least one island hash to avoid failure of the -1 subscript below for 1 .. (@{$layer->slices} || 1); # make sure we have at least one island hash to avoid failure of the -1 subscript below
PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) { PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) {
@ -166,8 +171,8 @@ sub process_layer {
foreach my $island (@islands) { foreach my $island (@islands) {
# give priority to infill if we were already using its extruder and it wouldn't # give priority to infill if we were already using its extruder and it wouldn't
# be good for perimeters # be good for perimeters
if ($self->gcodegen->config->infill_first if ($self->print->config->infill_first
|| ($self->gcodegen->multiple_extruders && $region->extruders->{infill} eq $self->gcodegen->extruder) && $region->extruders->{infill} ne $region->extruders->{perimeter}) { || ($self->gcodegen->multiple_extruders && $region->config->infill_extruder-1 == $self->gcodegen->extruder->id && $region->config->infill_extruder != $region->config->perimeter_extruder)) {
$gcode .= $self->_extrude_infill($island, $region); $gcode .= $self->_extrude_infill($island, $region);
$gcode .= $self->_extrude_perimeters($island, $region); $gcode .= $self->_extrude_perimeters($island, $region);
} else { } else {
@ -184,11 +189,11 @@ sub process_layer {
# apply vibration limit if enabled # apply vibration limit if enabled
$gcode = $self->vibration_limit->process($gcode) $gcode = $self->vibration_limit->process($gcode)
if $self->gcodegen->config->vibration_limit != 0; if $self->print->config->vibration_limit != 0;
# apply arc fitting if enabled # apply arc fitting if enabled
$gcode = $self->arc_fitting->process($gcode) $gcode = $self->arc_fitting->process($gcode)
if $self->gcodegen->config->gcode_arcs; if $self->print->config->gcode_arcs;
return $gcode; return $gcode;
} }
@ -200,7 +205,7 @@ sub _extrude_perimeters {
return "" if !@{ $island->{perimeters} }; return "" if !@{ $island->{perimeters} };
my $gcode = ""; my $gcode = "";
$gcode .= $self->gcodegen->set_extruder($region->extruders->{perimeter}); $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]);
$gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} }; $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} };
return $gcode; return $gcode;
} }
@ -212,7 +217,7 @@ sub _extrude_infill {
return "" if !@{ $island->{fills} }; return "" if !@{ $island->{fills} };
my $gcode = ""; my $gcode = "";
$gcode .= $self->gcodegen->set_extruder($region->extruders->{infill}); $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]);
for my $fill (@{ $island->{fills} }) { for my $fill (@{ $island->{fills} }) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $self->gcodegen->extrude($_, 'fill') $gcode .= $self->gcodegen->extrude($_, 'fill')

View File

@ -12,9 +12,9 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter
offset2 union union_pt_chained JT_ROUND JT_SQUARE); offset2 union union_pt_chained JT_ROUND JT_SQUARE);
use Slic3r::Print::State ':steps'; use Slic3r::Print::State ':steps';
has 'config' => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config); has 'config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
has 'default_object_config' => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new }); has 'default_object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
has 'default_region_config' => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new }); has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new });
has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'extra_variables' => (is => 'rw', default => sub {{}});
has 'objects' => (is => 'rw', default => sub {[]}); has 'objects' => (is => 'rw', default => sub {[]});
has 'status_cb' => (is => 'rw'); has 'status_cb' => (is => 'rw');
@ -28,52 +28,12 @@ has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->
# ordered collection of extrusion paths to build a brim # ordered collection of extrusion paths to build a brim
has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new });
sub BUILD {
my $self = shift;
# call this manually because the 'default' coderef doesn't trigger the trigger
$self->init_config;
}
# this method needs to be idempotent
sub init_config {
my $self = shift;
# legacy with existing config files
$self->config->set('first_layer_height', $self->config->layer_height)
if !$self->config->first_layer_height;
$self->config->set_ifndef('small_perimeter_speed', $self->config->perimeter_speed);
$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';
$self->config->set('extrusion_axis', '') if $self->config->gcode_flavor eq 'no-extrusion';
# enforce some settings when spiral_vase is set
if ($self->config->spiral_vase) {
$self->config->set('perimeters', 1);
$self->config->set('fill_density', 0);
$self->config->set('top_solid_layers', 0);
$self->config->set('support_material', 0);
$self->config->set('support_material_enforce_layers', 0);
$self->config->set('retract_layer_change', [0]); # TODO: only apply this to the spiral layers
}
# force all retraction lift values to be the same
$self->config->set('retract_lift', [ map $self->config->retract_lift->[0], @{$self->config->retract_lift} ]);
}
sub apply_config { sub apply_config {
my ($self, $config) = @_; my ($self, $config) = @_;
$self->config->apply_dynamic($config); $self->config->apply_dynamic($config);
$self->default_object_config->apply_dynamic($config); $self->default_object_config->apply_dynamic($config);
$self->default_region_config->apply_dynamic($config); $self->default_region_config->apply_dynamic($config);
$self->init_config;
} }
sub has_support_material { sub has_support_material {
@ -261,11 +221,21 @@ sub init_extruders {
} }
for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) { for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
# make sure print config contains a value for all extruders
my %extruder_config = ();
foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) {
my $value = $self->config->get($opt_key);
if (!defined $value->[$extruder_id]) {
$value->[$extruder_id] = $value->[0];
$self->config->set($opt_key, $value);
}
$extruder_config{$opt_key} = $value->[$extruder_id];
}
$self->extruders->[$extruder_id] = Slic3r::Extruder->new( $self->extruders->[$extruder_id] = Slic3r::Extruder->new(
config => $self->config,
id => $extruder_id, id => $extruder_id,
map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/ use_relative_e_distances => $self->config->use_relative_e_distances,
@{&Slic3r::Extruder::OPTIONS} %extruder_config,
); );
} }
@ -614,16 +584,17 @@ sub make_skirt {
# skirt may be printed on several layers, having distinct layer heights, # skirt may be printed on several layers, having distinct layer heights,
# but loops must be aligned so can't vary width/spacing # but loops must be aligned so can't vary width/spacing
# TODO: use each extruder's own flow # TODO: use each extruder's own flow
my $region0_config = $self->regions->[0]->config;
my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height');
my $flow = Slic3r::Flow->new( my $flow = Slic3r::Flow->new(
width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), width => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width),
role => FLOW_ROLE_PERIMETER, role => FLOW_ROLE_PERIMETER,
nozzle_diameter => $self->config->nozzle_diameter->[0], nozzle_diameter => $self->config->nozzle_diameter->[0],
layer_height => $self->config->get_abs_value('first_layer_height'), layer_height => $first_layer_height,
bridge_flow_ratio => 0, bridge_flow_ratio => 0,
); );
my $spacing = $flow->spacing; my $spacing = $flow->spacing;
my $first_layer_height = $self->config->get_value('first_layer_height');
my @extruders_e_per_mm = (); my @extruders_e_per_mm = ();
my $extruder_idx = 0; my $extruder_idx = 0;
@ -664,10 +635,10 @@ sub make_brim {
# brim is only printed on first layer and uses support material extruder # brim is only printed on first layer and uses support material extruder
my $flow = Slic3r::Flow->new( my $flow = Slic3r::Flow->new(
width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), width => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
role => FLOW_ROLE_PERIMETER, role => FLOW_ROLE_PERIMETER,
nozzle_diameter => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ],
layer_height => $self->config->get_abs_value('first_layer_height'), layer_height => $self->objects->[0]->config->get_abs_value('first_layer_height'),
bridge_flow_ratio => 0, bridge_flow_ratio => 0,
); );
@ -737,34 +708,28 @@ sub write_gcode {
print $fh "; $_\n" foreach split /\R/, $self->config->notes; print $fh "; $_\n" foreach split /\R/, $self->config->notes;
print $fh "\n" if $self->config->notes; print $fh "\n" if $self->config->notes;
for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed)) { my $layer_height = $self->objects->[0]->config->layer_height;
printf $fh "; %s = %s\n", $_, $self->config->$_;
}
for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) {
printf $fh "; %s = %s\n", $_, $self->config->$_->[0];
}
for my $region_id (0..$#{$self->regions}) { for my $region_id (0..$#{$self->regions}) {
printf $fh "; perimeters extrusion width = %.2fmm\n", printf $fh "; perimeters extrusion width = %.2fmm\n",
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width; $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height)->width;
printf $fh "; infill extrusion width = %.2fmm\n", printf $fh "; infill extrusion width = %.2fmm\n",
$self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width; $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height)->width;
printf $fh "; solid infill extrusion width = %.2fmm\n", printf $fh "; solid infill extrusion width = %.2fmm\n",
$self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width; $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height)->width;
printf $fh "; top infill extrusion width = %.2fmm\n", printf $fh "; top infill extrusion width = %.2fmm\n",
$self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height)->width;
printf $fh "; support material extrusion width = %.2fmm\n", printf $fh "; support material extrusion width = %.2fmm\n",
$self->objects->[0]->support_material_flow->width $self->objects->[0]->support_material_flow->width
if $self->has_support_material; if $self->has_support_material;
printf $fh "; first layer extrusion width = %.2fmm\n", printf $fh "; first layer extrusion width = %.2fmm\n",
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width
if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0); if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0');
print $fh "\n"; print $fh "\n";
} }
# set up our extruder object # set up our extruder object
my $gcodegen = Slic3r::GCode->new( my $gcodegen = Slic3r::GCode->new(
config => $self->config, print_config => $self->config,
extra_variables => $self->extra_variables, extra_variables => $self->extra_variables,
extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!) extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!)
layer_count => $self->layer_count, layer_count => $self->layer_count,
@ -941,13 +906,11 @@ sub write_gcode {
$extruder->absolute_E, $extruder->extruded_volume/1000; $extruder->absolute_E, $extruder->extruded_volume/1000;
} }
if ($self->config->gcode_comments) { # append full config
# append full config print $fh "\n";
print $fh "\n"; foreach my $opt_key (sort @{$self->config->get_keys}) {
foreach my $opt_key (sort @{$self->config->get_keys}) { next if $Slic3r::Config::Options->{$opt_key}{shortcut};
next if $Slic3r::Config::Options->{$opt_key}{shortcut}; printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
}
} }
# close our gcode file # close our gcode file

View File

@ -521,13 +521,16 @@ sub clip_fill_surfaces {
sub bridge_over_infill { sub bridge_over_infill {
my $self = shift; my $self = shift;
return if $self->config->fill_density == 1;
for my $layer_id (1..$#{$self->layers}) { for my $region_id (0..$#{$self->print->regions}) {
my $layer = $self->layers->[$layer_id]; my $fill_density = $self->print->regions->[$region_id]->config->fill_density;
my $lower_layer = $self->layers->[$layer_id-1]; next if $fill_density == 1 || $fill_density == 0;
for my $layer_id (1..$#{$self->layers}) {
my $layer = $self->layers->[$layer_id];
my $layerm = $layer->regions->[$region_id];
my $lower_layer = $self->layers->[$layer_id-1];
foreach my $layerm (@{$layer->regions}) {
# compute the areas needing bridge math # compute the areas needing bridge math
my @internal_solid = @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNALSOLID)}; my @internal_solid = @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNALSOLID)};
my @lower_internal = map @{$_->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}, @{$lower_layer->regions}; my @lower_internal = map @{$_->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}, @{$lower_layer->regions};
@ -861,7 +864,8 @@ sub generate_support_material {
&& $self->layer_count >= 2; && $self->layer_count >= 2;
my $s = Slic3r::Print::SupportMaterial->new( my $s = Slic3r::Print::SupportMaterial->new(
config => $self->config, print_config => $self->print->config,
object_config => $self->config,
flow => $self->support_material_flow, flow => $self->support_material_flow,
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
); );

View File

@ -53,7 +53,7 @@ sub flow {
role => $role, role => $role,
nozzle_diameter => $nozzle_diameter, nozzle_diameter => $nozzle_diameter,
layer_height => $layer_height, layer_height => $layer_height,
bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), bridge_flow_ratio => ($bridge ? $self->print->config->bridge_flow_ratio : 0),
); );
} }

View File

@ -3,13 +3,15 @@ use Moo;
use List::Util qw(sum min max); use List::Util qw(sum min max);
use Slic3r::ExtrusionPath ':roles'; use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad); use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad);
use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
intersection_pl); intersection_pl);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
has 'config' => (is => 'rw', required => 1); has 'print_config' => (is => 'rw', required => 1);
has 'flow' => (is => 'rw', required => 1); has 'object_config' => (is => 'rw', required => 1);
has 'flow' => (is => 'rw', required => 1);
use constant DEBUG_CONTACT_ONLY => 0; use constant DEBUG_CONTACT_ONLY => 0;
@ -73,8 +75,8 @@ sub contact_area {
# if user specified a custom angle threshold, convert it to radians # if user specified a custom angle threshold, convert it to radians
my $threshold_rad; my $threshold_rad;
if ($self->config->support_material_threshold) { if ($self->object_config->support_material_threshold) {
$threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive $threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
} }
@ -86,9 +88,9 @@ sub contact_area {
# so $layer_id == 0 means first object layer # so $layer_id == 0 means first object layer
# and $layer->id == 0 means first print layer (including raft) # and $layer->id == 0 means first print layer (including raft)
if ($self->config->raft_layers == 0) { if ($self->object_config->raft_layers == 0) {
next if $layer_id == 0; next if $layer_id == 0;
} elsif (!$self->config->support_material) { } elsif (!$self->object_config->support_material) {
# if we are only going to generate raft just check # if we are only going to generate raft just check
# the 'overhangs' of the first object layer # the 'overhangs' of the first object layer
last if $layer_id > 0; last if $layer_id > 0;
@ -110,8 +112,8 @@ sub contact_area {
# If a threshold angle was specified, use a different logic for detecting overhangs. # If a threshold angle was specified, use a different logic for detecting overhangs.
if (defined $threshold_rad if (defined $threshold_rad
|| $layer_id < $self->config->support_material_enforce_layers || $layer_id < $self->object_config->support_material_enforce_layers
|| $self->config->raft_layers > 0) { || $self->object_config->raft_layers > 0) {
my $d = defined $threshold_rad my $d = defined $threshold_rad
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
: 0; : 0;
@ -170,8 +172,8 @@ sub contact_area {
# now apply the contact areas to the layer were they need to be made # now apply the contact areas to the layer were they need to be made
{ {
# get the average nozzle diameter used on this layer # get the average nozzle diameter used on this layer
my @nozzle_diameters = map $_->nozzle_diameter, my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_],
map { $_->perimeter_flow, $_->solid_infill_flow } map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 }
@{$layer->regions}; @{$layer->regions};
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
@ -179,7 +181,7 @@ sub contact_area {
###$contact_z = $layer->print_z - $layer->height; ###$contact_z = $layer->print_z - $layer->height;
# ignore this contact area if it's too low # ignore this contact area if it's too low
next if $contact_z < $self->config->get_value('first_layer_height'); next if $contact_z < $self->object_config->get_value('first_layer_height');
$contact{$contact_z} = [ @contact ]; $contact{$contact_z} = [ @contact ];
$overhang{$contact_z} = [ @overhang ]; $overhang{$contact_z} = [ @overhang ];
@ -242,25 +244,25 @@ sub support_layers_z {
# determine layer height for any non-contact layer # determine layer height for any non-contact layer
# we use max() to prevent many ultra-thin layers to be inserted in case # we use max() to prevent many ultra-thin layers to be inserted in case
# layer_height > nozzle_diameter * 0.75 # layer_height > nozzle_diameter * 0.75
my $nozzle_diameter = $self->flow->nozzle_diameter; my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1];
my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z); my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z);
# enforce first layer height # enforce first layer height
my $first_layer_height = $self->config->get_value('first_layer_height'); my $first_layer_height = $self->object_config->get_value('first_layer_height');
shift @z while @z && $z[0] <= $first_layer_height; shift @z while @z && $z[0] <= $first_layer_height;
unshift @z, $first_layer_height; unshift @z, $first_layer_height;
# add raft layers by dividing the space between first layer and # add raft layers by dividing the space between first layer and
# first contact layer evenly # first contact layer evenly
if ($self->config->raft_layers > 1 && @z >= 2) { if ($self->object_config->raft_layers > 1 && @z >= 2) {
# $z[1] is last raft layer (contact layer for the first layer object) # $z[1] is last raft layer (contact layer for the first layer object)
my $height = ($z[1] - $z[0]) / ($self->config->raft_layers - 1); my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1);
splice @z, 1, 0, splice @z, 1, 0,
map { int($_*100)/100 } map { int($_*100)/100 }
map { $z[0] + $height * $_ } map { $z[0] + $height * $_ }
0..($self->config->raft_layers - 1); 0..($self->object_config->raft_layers - 1);
} }
for (my $i = $#z; $i >= 0; $i--) { for (my $i = $#z; $i >= 0; $i--) {
@ -291,7 +293,7 @@ sub generate_interface_layers {
# let's now generate interface layers below contact areas # let's now generate interface layers below contact areas
my %interface = (); # layer_id => [ polygons ] my %interface = (); # layer_id => [ polygons ]
my $interface_layers = $self->config->support_material_interface_layers; my $interface_layers = $self->object_config->support_material_interface_layers;
for my $layer_id (0 .. $#$support_z) { for my $layer_id (0 .. $#$support_z) {
my $z = $support_z->[$layer_id]; my $z = $support_z->[$layer_id];
my $this = $contact->{$z} // next; my $this = $contact->{$z} // next;
@ -339,7 +341,7 @@ sub generate_base_layers {
# in case we have no interface layers, look at upper contact # in case we have no interface layers, look at upper contact
# (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty) # (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty)
my @upper_contact = (); my @upper_contact = ();
if ($self->config->support_material_interface_layers <= 1) { if ($self->object_config->support_material_interface_layers <= 1) {
@upper_contact = @{ $contact->{$support_z->[$i+1]} || [] }; @upper_contact = @{ $contact->{$support_z->[$i+1]} || [] };
} }
@ -395,8 +397,8 @@ sub generate_toolpaths {
Slic3r::debugf "Generating patterns\n"; Slic3r::debugf "Generating patterns\n";
# prepare fillers # prepare fillers
my $pattern = $self->config->support_material_pattern; my $pattern = $self->object_config->support_material_pattern;
my @angles = ($self->config->support_material_angle); my @angles = ($self->object_config->support_material_angle);
if ($pattern eq 'rectilinear-grid') { if ($pattern eq 'rectilinear-grid') {
$pattern = 'rectilinear'; $pattern = 'rectilinear';
push @angles, $angles[0] + 90; push @angles, $angles[0] + 90;
@ -407,10 +409,10 @@ sub generate_toolpaths {
support => $object->fill_maker->filler($pattern), support => $object->fill_maker->filler($pattern),
); );
my $interface_angle = $self->config->support_material_angle + 90; my $interface_angle = $self->object_config->support_material_angle + 90;
my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing; my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing;
my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing; my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
my $support_spacing = $self->config->support_material_spacing + $flow->spacing; my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing;
my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing; my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing;
my $process_layer = sub { my $process_layer = sub {
@ -441,7 +443,7 @@ sub generate_toolpaths {
# contact # contact
my $contact_infill = []; my $contact_infill = [];
if ($self->config->support_material_interface_layers == 0) { if ($self->object_config->support_material_interface_layers == 0) {
# if no interface layers were requested we treat the contact layer # if no interface layers were requested we treat the contact layer
# exactly as a generic base layer # exactly as a generic base layer
push @$base, @$contact; push @$base, @$contact;
@ -561,7 +563,7 @@ sub generate_toolpaths {
# base flange # base flange
if ($layer_id == 0) { if ($layer_id == 0) {
$filler = $fillers{interface}; $filler = $fillers{interface};
$filler->angle($self->config->support_material_angle + 90); $filler->angle($self->object_config->support_material_angle + 90);
$density = 0.5; $density = 0.5;
$flow_spacing = $object->print->first_layer_support_material_flow->spacing; $flow_spacing = $object->print->first_layer_support_material_flow->spacing;
} else { } else {
@ -609,7 +611,7 @@ sub generate_toolpaths {
}; };
Slic3r::parallelize( Slic3r::parallelize(
threads => $self->config->threads, threads => $self->print_config->threads,
items => [ 0 .. $#{$object->support_layers} ], items => [ 0 .. $#{$object->support_layers} ],
thread_cb => sub { thread_cb => sub {
my $q = shift; my $q = shift;

View File

@ -99,20 +99,21 @@ sub model {
sub init_print { sub init_print {
my ($model_name, %params) = @_; my ($model_name, %params) = @_;
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new;
$config->apply($params{config}) if $params{config}; $config->apply($params{config}) if $params{config};
$config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE}; $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
my $print = Slic3r::Print->new(config => $config); my $print = Slic3r::Print->new;
$print->apply_config($config);
$model_name = [$model_name] if ref($model_name) ne 'ARRAY'; $model_name = [$model_name] if ref($model_name) ne 'ARRAY';
for my $model (map model($_, %params), @$model_name) { for my $model (map model($_, %params), @$model_name) {
die "Unknown model in test" if !defined $model; die "Unknown model in test" if !defined $model;
if (defined $params{duplicate} && $params{duplicate} > 1) { if (defined $params{duplicate} && $params{duplicate} > 1) {
$model->duplicate($params{duplicate} // 1, $config->min_object_distance); $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
} }
$model->arrange_objects($config->min_object_distance); $model->arrange_objects($print->config->min_object_distance);
$model->center_instances_around_point($config->print_center); $model->center_instances_around_point($print->config->print_center);
$print->add_model_object($_) for @{$model->objects}; $print->add_model_object($_) for @{$model->objects};
} }
$print->validate; $print->validate;

View File

@ -153,12 +153,12 @@ if (@ARGV) { # slicing from command line
} }
my $print = Slic3r::Print->new( my $print = Slic3r::Print->new(
config => $config,
status_cb => sub { status_cb => sub {
my ($percent, $message) = @_; my ($percent, $message) = @_;
printf "=> %s\n", $message; printf "=> %s\n", $message;
}, },
); );
$print->apply_config($config);
foreach my $model_object (@{$model->objects}) { foreach my $model_object (@{$model->objects}) {
$print->auto_assign_extruders($model_object); $print->auto_assign_extruders($model_object);
$print->add_model_object($model_object); $print->add_model_object($model_object);

View File

@ -13,10 +13,14 @@ use Slic3r;
use Slic3r::Test; use Slic3r::Test;
sub buffer { sub buffer {
my $config = shift || Slic3r::Config->new_from_defaults; my $config = shift || Slic3r::Config->new;
my $print_config = Slic3r::Config::Print->new;
$print_config->apply_dynamic($config);
my $buffer = Slic3r::GCode::CoolingBuffer->new( my $buffer = Slic3r::GCode::CoolingBuffer->new(
config => $config, config => $print_config,
gcodegen => Slic3r::GCode->new(config => $config, layer_count => 10, extruders => []), gcodegen => Slic3r::GCode->new(print_config => $print_config, layer_count => 10, extruders => []),
); );
return $buffer; return $buffer;
} }

View File

@ -14,9 +14,8 @@ use Slic3r::Test;
{ {
my $gcodegen = Slic3r::GCode->new( my $gcodegen = Slic3r::GCode->new(
config => Slic3r::Config->new_from_defaults, layer_count => 1,
layer_count => 1, extruders => [],
extruders => [],
); );
$gcodegen->set_shift(10, 10); $gcodegen->set_shift(10, 10);
is_deeply $gcodegen->last_pos->arrayref, [scale -10, scale -10], 'last_pos is shifted correctly'; is_deeply $gcodegen->last_pos->arrayref, [scale -10, scale -10], 'last_pos is shifted correctly';

View File

@ -140,6 +140,9 @@ use Slic3r::Test;
{ {
my $config = Slic3r::Config->new_from_defaults; my $config = Slic3r::Config->new_from_defaults;
$config->set('perimeters', 1);
$config->set('fill_density', 0);
$config->set('top_solid_layers', 0);
$config->set('spiral_vase', 1); $config->set('spiral_vase', 1);
$config->set('bottom_solid_layers', 0); $config->set('bottom_solid_layers', 0);
$config->set('skirts', 0); $config->set('skirts', 0);

View File

@ -21,16 +21,16 @@ use Slic3r::Test;
my $test = sub { my $test = sub {
my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
$print->init_extruders; $print->init_extruders;
my $flow = $print->support_material_flow; my $flow = $print->objects->[0]->support_material_flow;
my $support_z = Slic3r::Print::SupportMaterial my $support_z = Slic3r::Print::SupportMaterial
->new(config => $config, flow => $flow) ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow)
->support_layers_z(\@contact_z, \@top_z, $config->layer_height); ->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
is $support_z->[0], $config->first_layer_height, is $support_z->[0], $config->first_layer_height,
'first layer height is honored'; 'first layer height is honored';
is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0,
'no null or negative support layers'; 'no null or negative support layers';
is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#$support_z), 0, is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $config->nozzle_diameter->[0] + epsilon } 1..$#$support_z), 0,
'no layers thicker than nozzle diameter'; 'no layers thicker than nozzle diameter';
my $wrong_top_spacing = 0; my $wrong_top_spacing = 0;
@ -40,7 +40,7 @@ use Slic3r::Test;
# check that first support layer above this top surface is spaced with nozzle diameter # check that first support layer above this top surface is spaced with nozzle diameter
$wrong_top_spacing = 1 $wrong_top_spacing = 1
if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $flow->nozzle_diameter; if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $config->nozzle_diameter->[0];
} }
ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly'; ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly';
}; };

View File

@ -899,6 +899,7 @@ class PrintObjectConfig : public virtual StaticConfig
public: public:
ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height; ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
ConfigOptionFloat layer_height; ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers; ConfigOptionInt raft_layers;
ConfigOptionBool support_material; ConfigOptionBool support_material;
@ -921,6 +922,7 @@ class PrintObjectConfig : public virtual StaticConfig
this->extrusion_width.percent = false; this->extrusion_width.percent = false;
this->first_layer_height.value = 0.35; this->first_layer_height.value = 0.35;
this->first_layer_height.percent = false; this->first_layer_height.percent = false;
this->infill_only_where_needed.value = false;
this->layer_height.value = 0.4; this->layer_height.value = 0.4;
this->raft_layers.value = 0; this->raft_layers.value = 0;
this->support_material.value = false; this->support_material.value = false;
@ -941,6 +943,7 @@ class PrintObjectConfig : public virtual StaticConfig
ConfigOption* option(const t_config_option_key opt_key, bool create = false) { ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
if (opt_key == "extrusion_width") return &this->extrusion_width; if (opt_key == "extrusion_width") return &this->extrusion_width;
if (opt_key == "first_layer_height") return &this->first_layer_height; if (opt_key == "first_layer_height") return &this->first_layer_height;
if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed;
if (opt_key == "layer_height") return &this->layer_height; if (opt_key == "layer_height") return &this->layer_height;
if (opt_key == "raft_layers") return &this->raft_layers; if (opt_key == "raft_layers") return &this->raft_layers;
if (opt_key == "support_material") return &this->support_material; if (opt_key == "support_material") return &this->support_material;
@ -972,7 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig
ConfigOptionInt infill_extruder; ConfigOptionInt infill_extruder;
ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionFloatOrPercent infill_extrusion_width;
ConfigOptionInt infill_every_layers; ConfigOptionInt infill_every_layers;
ConfigOptionBool infill_only_where_needed;
ConfigOptionInt perimeter_extruder; ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionInt perimeters; ConfigOptionInt perimeters;
@ -999,7 +1001,6 @@ class PrintRegionConfig : public virtual StaticConfig
this->infill_extrusion_width.value = 0; this->infill_extrusion_width.value = 0;
this->infill_extrusion_width.percent = false; this->infill_extrusion_width.percent = false;
this->infill_every_layers.value = 1; this->infill_every_layers.value = 1;
this->infill_only_where_needed.value = false;
this->perimeter_extruder.value = 1; this->perimeter_extruder.value = 1;
this->perimeter_extrusion_width.value = 0; this->perimeter_extrusion_width.value = 0;
this->perimeter_extrusion_width.percent = false; this->perimeter_extrusion_width.percent = false;
@ -1025,7 +1026,6 @@ class PrintRegionConfig : public virtual StaticConfig
if (opt_key == "infill_extruder") return &this->infill_extruder; if (opt_key == "infill_extruder") return &this->infill_extruder;
if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width;
if (opt_key == "infill_every_layers") return &this->infill_every_layers; if (opt_key == "infill_every_layers") return &this->infill_every_layers;
if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed;
if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; if (opt_key == "perimeter_extruder") return &this->perimeter_extruder;
if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width;
if (opt_key == "perimeters") return &this->perimeters; if (opt_key == "perimeters") return &this->perimeters;
@ -1332,6 +1332,15 @@ class PrintConfig : public virtual StaticConfig
return NULL; return NULL;
}; };
std::string get_extrusion_axis() {
if (this->gcode_flavor == gcfMach3) {
return std::string("A");
} else if (this->gcode_flavor == gcfNoExtrusion) {
return std::string("");
}
return this->extrusion_axis;
}
}; };
class DynamicPrintConfig : public DynamicConfig class DynamicPrintConfig : public DynamicConfig

View File

@ -41,6 +41,7 @@
%code{% THIS->apply(*other, true); %}; %code{% THIS->apply(*other, true); %};
std::vector<std::string> get_keys() std::vector<std::string> get_keys()
%code{% THIS->keys(&RETVAL); %}; %code{% THIS->keys(&RETVAL); %};
std::string get_extrusion_axis();
}; };
%name{Slic3r::Config::PrintRegion} class PrintRegionConfig { %name{Slic3r::Config::PrintRegion} class PrintRegionConfig {