mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-08 06:24:01 -06:00
Merge branch 'region-config'
Conflicts: lib/Slic3r/Format/AMF/Parser.pm
This commit is contained in:
commit
0bdea60b53
62 changed files with 2164 additions and 1432 deletions
|
@ -68,6 +68,7 @@ use Slic3r::Polyline;
|
|||
use Slic3r::Print;
|
||||
use Slic3r::Print::Object;
|
||||
use Slic3r::Print::Region;
|
||||
use Slic3r::Print::Simple;
|
||||
use Slic3r::Print::SupportMaterial;
|
||||
use Slic3r::Surface;
|
||||
use Slic3r::TriangleMesh;
|
||||
|
@ -76,9 +77,8 @@ our $build = eval "use Slic3r::Build; 1";
|
|||
use constant SCALING_FACTOR => 0.000001;
|
||||
use constant RESOLUTION => 0.0125;
|
||||
use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR;
|
||||
use constant OVERLAP_FACTOR => 1;
|
||||
use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
|
||||
use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15;
|
||||
use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15;
|
||||
use constant INFILL_OVERLAP_OVER_SPACING => 0.45;
|
||||
use constant EXTERNAL_INFILL_MARGIN => 3;
|
||||
|
||||
|
@ -132,7 +132,10 @@ sub thread_cleanup {
|
|||
# prevent destruction of shared objects
|
||||
no warnings 'redefine';
|
||||
*Slic3r::Config::DESTROY = sub {};
|
||||
*Slic3r::Config::Full::DESTROY = sub {};
|
||||
*Slic3r::Config::Print::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintObject::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintRegion::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::Collection::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionLoop::DESTROY = sub {};
|
||||
|
|
|
@ -7,7 +7,7 @@ use List::Util qw(first);
|
|||
|
||||
# cemetery of old config settings
|
||||
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
|
||||
adjust_overhang_flow standby_temperature);
|
||||
adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid);
|
||||
|
||||
our $Options = print_config_def();
|
||||
|
||||
|
@ -24,7 +24,7 @@ sub new_from_defaults {
|
|||
my (@opt_keys) = @_;
|
||||
|
||||
my $self = $class->new;
|
||||
my $defaults = Slic3r::Config::Print->new;
|
||||
my $defaults = Slic3r::Config::Full->new;
|
||||
if (@opt_keys) {
|
||||
$self->set($_, $defaults->get($_)) for @opt_keys;
|
||||
} else {
|
||||
|
@ -76,7 +76,7 @@ sub load {
|
|||
my ($file) = @_;
|
||||
|
||||
my $ini = __PACKAGE__->read_ini($file);
|
||||
my $config = __PACKAGE__->new;
|
||||
my $config = $class->new;
|
||||
foreach my $opt_key (keys %{$ini->{_}}) {
|
||||
($opt_key, my $value) = _handle_legacy($opt_key, $ini->{_}{$opt_key});
|
||||
next if !defined $opt_key;
|
||||
|
@ -88,7 +88,7 @@ sub load {
|
|||
sub clone {
|
||||
my $self = shift;
|
||||
|
||||
my $new = __PACKAGE__->new;
|
||||
my $new = (ref $self)->new;
|
||||
$new->apply($self);
|
||||
return $new;
|
||||
}
|
||||
|
@ -175,7 +175,25 @@ sub setenv {
|
|||
}
|
||||
}
|
||||
|
||||
# this method is idempotent by design
|
||||
sub equals {
|
||||
my ($self, $other) = @_;
|
||||
return @{ $self->diff($other) } == 0;
|
||||
}
|
||||
|
||||
# this will *ignore* options not present in both configs
|
||||
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 and only applies to ::DynamicConfig or ::Full
|
||||
# objects because it performs cross checks
|
||||
sub validate {
|
||||
my $self = shift;
|
||||
|
||||
|
@ -248,27 +266,11 @@ sub validate {
|
|||
die "Invalid value for --infill-every-layers\n"
|
||||
if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
|
||||
|
||||
# --scale
|
||||
die "Invalid value for --scale\n"
|
||||
if $self->scale <= 0;
|
||||
|
||||
# --bed-size
|
||||
die "Invalid value for --bed-size\n"
|
||||
if !ref $self->bed_size
|
||||
&& (!$self->bed_size || $self->bed_size !~ /^\d+,\d+$/);
|
||||
|
||||
# --duplicate-grid
|
||||
die "Invalid value for --duplicate-grid\n"
|
||||
if !ref $self->duplicate_grid
|
||||
&& (!$self->duplicate_grid || $self->duplicate_grid !~ /^\d+,\d+$/);
|
||||
|
||||
# --duplicate
|
||||
die "Invalid value for --duplicate or --duplicate-grid\n"
|
||||
if !$self->duplicate || $self->duplicate < 1 || !$self->duplicate_grid
|
||||
|| (grep !$_, @{$self->duplicate_grid});
|
||||
die "Use either --duplicate or --duplicate-grid (using both doesn't make sense)\n"
|
||||
if $self->duplicate > 1 && $self->duplicate_grid && (grep $_ && $_ > 1, @{$self->duplicate_grid});
|
||||
|
||||
# --skirt-height
|
||||
die "Invalid value for --skirt-height\n"
|
||||
if $self->skirt_height < -1; # -1 means as tall as the object
|
||||
|
@ -292,8 +294,33 @@ sub validate {
|
|||
if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_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
|
||||
foreach my $opt_key (keys %$Options) {
|
||||
foreach my $opt_key (@{$self->get_keys}) {
|
||||
my $opt = $Options->{$opt_key};
|
||||
next unless defined $self->$opt_key;
|
||||
next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
|
||||
|
@ -415,4 +442,16 @@ sub read_ini {
|
|||
return $ini;
|
||||
}
|
||||
|
||||
package Slic3r::Config::Print;
|
||||
use parent 'Slic3r::Config';
|
||||
|
||||
package Slic3r::Config::PrintObject;
|
||||
use parent 'Slic3r::Config';
|
||||
|
||||
package Slic3r::Config::PrintRegion;
|
||||
use parent 'Slic3r::Config';
|
||||
|
||||
package Slic3r::Config::Full;
|
||||
use parent 'Slic3r::Config';
|
||||
|
||||
1;
|
||||
|
|
|
@ -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(
|
||||
|
@ -12,24 +18,31 @@ use constant OPTIONS => [qw(
|
|||
|
||||
has 'id' => (is => 'rw', required => 1);
|
||||
has $_ => (is => 'ro', required => 1) for @{&OPTIONS};
|
||||
has 'config'=> (is => 'ro', required => 1);
|
||||
has 'use_relative_e_distances' => (is => 'ro', default => sub {0});
|
||||
|
||||
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} );
|
||||
has 'restart_extra' => (is => 'rw', default => sub {0} );
|
||||
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;
|
||||
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 new_from_config {
|
||||
my ($class, $config, $extruder_id) = @_;
|
||||
|
||||
return Slic3r::Flow::Bridge->new(
|
||||
nozzle_diameter => $self->nozzle_diameter,
|
||||
bridge_flow_ratio => $self->config->bridge_flow_ratio,
|
||||
my %conf = (
|
||||
id => $extruder_id,
|
||||
use_relative_e_distances => $config->use_relative_e_distances,
|
||||
);
|
||||
foreach my $opt_key (@{&OPTIONS}) {
|
||||
$conf{$opt_key} = $config->get_at($opt_key, $extruder_id);
|
||||
}
|
||||
return $class->new(%conf);
|
||||
}
|
||||
|
||||
sub _build_e_per_mm3 {
|
||||
|
@ -42,6 +55,15 @@ sub _build_retract_speed_mm_min {
|
|||
return $self->retract_speed * 60;
|
||||
}
|
||||
|
||||
sub reset {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->E(0);
|
||||
$self->absolute_E(0);
|
||||
$self->retracted(0);
|
||||
$self->restart_extra(0);
|
||||
}
|
||||
|
||||
sub scaled_wipe_distance {
|
||||
my ($self, $travel_speed) = @_;
|
||||
|
||||
|
@ -55,7 +77,7 @@ sub scaled_wipe_distance {
|
|||
sub extrude {
|
||||
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);
|
||||
return $self->E($self->E + $E);
|
||||
}
|
||||
|
@ -65,37 +87,9 @@ sub extruded_volume {
|
|||
return $self->absolute_E * ($self->filament_diameter**2) * PI/4;
|
||||
}
|
||||
|
||||
sub make_flow {
|
||||
my $self = shift;
|
||||
return Slic3r::Flow->new(nozzle_diameter => $self->nozzle_diameter, @_);
|
||||
}
|
||||
|
||||
sub mm3_per_mm {
|
||||
my $self = shift;
|
||||
my ($s, $h) = @_;
|
||||
|
||||
my $cache_key = "${s}_${h}";
|
||||
if (!exists $self->_mm3_per_mm_cache->{$cache_key}) {
|
||||
my $w_threshold = $h + $self->nozzle_diameter;
|
||||
my $s_threshold = $w_threshold - &Slic3r::OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4)));
|
||||
|
||||
if ($s >= $s_threshold) {
|
||||
# rectangle with semicircles at the ends
|
||||
my $w = $s + &Slic3r::OVERLAP_FACTOR * $h * (1 - PI/4);
|
||||
$self->_mm3_per_mm_cache->{$cache_key} = $w * $h + ($h**2) / 4 * (PI - 4);
|
||||
} else {
|
||||
# rectangle with shrunk semicircles at the ends
|
||||
my $w = ($s + $self->nozzle_diameter * &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)) / (1 + &Slic3r::OVERLAP_FACTOR * (PI/4 - 1));
|
||||
$self->_mm3_per_mm_cache->{$cache_key} = $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4;
|
||||
}
|
||||
}
|
||||
return $self->_mm3_per_mm_cache->{$cache_key};
|
||||
}
|
||||
|
||||
sub e_per_mm {
|
||||
my $self = shift;
|
||||
my ($s, $h) = @_;
|
||||
return $self->mm3_per_mm($s, $h) * $self->e_per_mm3;
|
||||
my ($self, $mm3_per_mm) = @_;
|
||||
return $mm3_per_mm * $self->e_per_mm3;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -8,8 +8,7 @@ sub split_at {
|
|||
return Slic3r::ExtrusionPath->new(
|
||||
polyline => $self->polygon->split_at(@_),
|
||||
role => $self->role,
|
||||
flow_spacing => $self->flow_spacing,
|
||||
height => $self->height,
|
||||
mm3_per_mm => $self->mm3_per_mm,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package Slic3r::Fill;
|
||||
use Moo;
|
||||
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Fill::ArchimedeanChords;
|
||||
use Slic3r::Fill::Base;
|
||||
use Slic3r::Fill::Concentric;
|
||||
|
@ -11,7 +12,7 @@ use Slic3r::Fill::Line;
|
|||
use Slic3r::Fill::OctagramSpiral;
|
||||
use Slic3r::Fill::PlanePath;
|
||||
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::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2);
|
||||
use Slic3r::Surface ':types';
|
||||
|
@ -50,7 +51,10 @@ sub make_fill {
|
|||
my ($layerm) = @_;
|
||||
|
||||
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 = ();
|
||||
|
||||
|
@ -95,7 +99,7 @@ sub make_fill {
|
|||
# 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
|
||||
# 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(
|
||||
[ map @{$_->expolygon}, @surfaces ],
|
||||
|
@ -133,11 +137,10 @@ sub make_fill {
|
|||
my $filler = $layerm->config->fill_pattern;
|
||||
my $density = $fill_density;
|
||||
my $flow = ($surface->surface_type == S_TYPE_TOP)
|
||||
? $layerm->top_infill_flow
|
||||
? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)
|
||||
: $surface->is_solid
|
||||
? $layerm->solid_infill_flow
|
||||
: $layerm->infill_flow;
|
||||
my $flow_spacing = $flow->spacing;
|
||||
? $solid_infill_flow
|
||||
: $infill_flow;
|
||||
my $is_bridge = $layerm->id > 0 && $surface->is_bridge;
|
||||
my $is_solid = $surface->is_solid;
|
||||
|
||||
|
@ -147,7 +150,7 @@ sub make_fill {
|
|||
$filler = $layerm->config->solid_fill_pattern;
|
||||
if ($is_bridge) {
|
||||
$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) {
|
||||
$filler = 'rectilinear';
|
||||
}
|
||||
|
@ -160,13 +163,14 @@ sub make_fill {
|
|||
$f->angle($layerm->config->fill_angle);
|
||||
my ($params, @polylines) = $f->fill_surface(
|
||||
$surface,
|
||||
density => $density,
|
||||
flow_spacing => $flow_spacing,
|
||||
density => $density,
|
||||
flow => $flow,
|
||||
);
|
||||
next unless @polylines;
|
||||
|
||||
# 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;
|
||||
my $h = $surface->thickness;
|
||||
$h = $layerm->height if $h == -1;
|
||||
my $mm3_per_mm = $params->{flow}->mm3_per_mm($h);
|
||||
|
||||
# save into layer
|
||||
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
|
||||
|
@ -182,8 +186,7 @@ sub make_fill {
|
|||
: $is_solid
|
||||
? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
|
||||
: EXTR_ROLE_FILL),
|
||||
height => $surface->thickness,
|
||||
flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"),
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), @polylines,
|
||||
);
|
||||
push @fills_ordering_points, $polylines[0]->first_point;
|
||||
|
|
|
@ -15,16 +15,22 @@ sub fill_surface {
|
|||
my $expolygon = $surface->expolygon;
|
||||
my $bounding_box = $expolygon->bounding_box;
|
||||
|
||||
my $min_spacing = scale $params{flow_spacing};
|
||||
my $flow = $params{flow};
|
||||
my $min_spacing = $flow->scaled_spacing;
|
||||
my $distance = $min_spacing / $params{density};
|
||||
|
||||
my $flow_spacing = $params{flow_spacing};
|
||||
my $flow_spacing = $flow->spacing;
|
||||
if ($params{density} == 1 && !$params{dont_adjust}) {
|
||||
$distance = $self->adjust_solid_spacing(
|
||||
width => $bounding_box->size->[X],
|
||||
distance => $distance,
|
||||
);
|
||||
$flow_spacing = unscale $distance;
|
||||
$flow = Slic3r::Flow->new_from_spacing(
|
||||
spacing => unscale($distance),
|
||||
nozzle_diameter => $flow->nozzle_diameter,
|
||||
layer_height => $surface->thickness,
|
||||
bridge => $flow->bridge,
|
||||
);
|
||||
}
|
||||
|
||||
# compensate the overlap which is good for rectilinear but harmful for concentric
|
||||
|
@ -48,11 +54,11 @@ sub fill_surface {
|
|||
}
|
||||
|
||||
# clip the paths to avoid the extruder to get exactly on the first point of the loop
|
||||
my $clip_length = scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING;
|
||||
my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
|
||||
$_->clip_end($clip_length) for @paths;
|
||||
|
||||
# TODO: return ExtrusionLoop objects to get better chained paths
|
||||
return { flow_spacing => $flow_spacing, no_sort => 1 }, @paths;
|
||||
return { flow => $flow, no_sort => 1 }, @paths;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -17,11 +17,11 @@ sub fill_surface {
|
|||
my $rotate_vector = $self->infill_direction($surface);
|
||||
|
||||
# cache hexagons math
|
||||
my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow_spacing};
|
||||
my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow}->spacing;
|
||||
my $m;
|
||||
if (!($m = $self->cache->{$cache_id})) {
|
||||
$m = $self->cache->{$cache_id} = {};
|
||||
my $min_spacing = scale $params{flow_spacing};
|
||||
my $min_spacing = $params{flow}->scaled_spacing;
|
||||
$m->{distance} = $min_spacing / $params{density};
|
||||
$m->{hex_side} = $m->{distance} / (sqrt(3)/2);
|
||||
$m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
|
@ -120,7 +120,7 @@ sub fill_surface {
|
|||
)};
|
||||
}
|
||||
|
||||
return { flow_spacing => $params{flow_spacing} }, @paths;
|
||||
return { flow => $params{flow} }, @paths;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -27,7 +27,8 @@ sub fill_surface {
|
|||
my $rotate_vector = $self->infill_direction($surface);
|
||||
$self->rotate_points($expolygon, $rotate_vector);
|
||||
|
||||
my $distance_between_lines = scale $params{flow_spacing} / $params{density} * $self->multiplier;
|
||||
my $flow = $params{flow};
|
||||
my $distance_between_lines = $flow->scaled_spacing / $params{density} * $self->multiplier;
|
||||
my $bounding_box = $expolygon->bounding_box;
|
||||
|
||||
(ref $self) =~ /::([^:]+)$/;
|
||||
|
@ -54,7 +55,7 @@ sub fill_surface {
|
|||
# paths must be rotated back
|
||||
$self->rotate_points_back(\@paths, $rotate_vector);
|
||||
|
||||
return { flow_spacing => $params{flow_spacing} }, @paths;
|
||||
return { flow => $flow }, @paths;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -17,8 +17,8 @@ sub fill_surface {
|
|||
my $rotate_vector = $self->infill_direction($surface);
|
||||
$self->rotate_points($expolygon, $rotate_vector);
|
||||
|
||||
my $flow_spacing = $params{flow_spacing};
|
||||
my $min_spacing = scale $params{flow_spacing};
|
||||
my $flow = $params{flow} or die "No flow supplied to fill_surface()";
|
||||
my $min_spacing = $flow->scaled_spacing;
|
||||
my $line_spacing = $min_spacing / $params{density};
|
||||
my $line_oscillation = $line_spacing - $min_spacing;
|
||||
my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
|
||||
|
@ -30,7 +30,12 @@ sub fill_surface {
|
|||
width => $bounding_box->size->[X],
|
||||
distance => $line_spacing,
|
||||
);
|
||||
$flow_spacing = unscale $line_spacing;
|
||||
$flow = Slic3r::Flow->new_from_spacing(
|
||||
spacing => unscale($line_spacing),
|
||||
nozzle_diameter => $flow->nozzle_diameter,
|
||||
layer_height => $surface->thickness,
|
||||
bridge => $flow->bridge,
|
||||
);
|
||||
} else {
|
||||
# extend bounding box so that our pattern will be aligned with other layers
|
||||
$bounding_box->extents->[X][MIN] -= $bounding_box->x_min % $line_spacing;
|
||||
|
@ -62,7 +67,7 @@ sub fill_surface {
|
|||
|
||||
# connect lines
|
||||
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
|
||||
my ($expolygon_off) = @{$expolygon->offset_ex(scale $params{flow_spacing}/2)};
|
||||
my ($expolygon_off) = @{$expolygon->offset_ex($min_spacing/2)};
|
||||
my $collection = Slic3r::Polyline::Collection->new(@polylines);
|
||||
@polylines = ();
|
||||
|
||||
|
@ -97,7 +102,7 @@ sub fill_surface {
|
|||
# paths must be rotated back
|
||||
$self->rotate_points_back(\@polylines, $rotate_vector);
|
||||
|
||||
return { flow_spacing => $flow_spacing }, @polylines;
|
||||
return { flow => $flow }, @polylines;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,108 +1,12 @@
|
|||
package Slic3r::Flow;
|
||||
use Moo;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Slic3r::Geometry qw(PI scale);
|
||||
use parent qw(Exporter);
|
||||
|
||||
has 'nozzle_diameter' => (is => 'ro', required => 1);
|
||||
has 'layer_height' => (is => 'ro', required => 1);
|
||||
has 'role' => (is => 'ro', default => sub { '' });
|
||||
|
||||
has 'width' => (is => 'rwp', builder => 1);
|
||||
has 'spacing' => (is => 'lazy');
|
||||
has 'scaled_width' => (is => 'lazy');
|
||||
has 'scaled_spacing' => (is => 'lazy');
|
||||
|
||||
sub BUILD {
|
||||
my $self = shift;
|
||||
|
||||
if ($self->width =~ /^(\d+(?:\.\d+)?)%$/) {
|
||||
$self->_set_width($self->layer_height * $1 / 100);
|
||||
}
|
||||
$self->_set_width($self->_build_width) if $self->width == 0; # auto
|
||||
}
|
||||
|
||||
sub _build_width {
|
||||
my $self = shift;
|
||||
|
||||
# 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 $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);
|
||||
} else {
|
||||
# rectangle with squished semicircles at the ends
|
||||
$width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1);
|
||||
}
|
||||
|
||||
my $min = $self->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') {
|
||||
# do not limit width for sparse infill so that we use full native flow for it
|
||||
$max = $self->nozzle_diameter * 1.7;
|
||||
}
|
||||
$width = $max if defined($max) && $width > $max;
|
||||
$width = $min if $width < $min;
|
||||
|
||||
return $width;
|
||||
}
|
||||
|
||||
sub _build_spacing {
|
||||
my $self = shift;
|
||||
|
||||
my $min_flow_spacing;
|
||||
if ($self->width >= ($self->nozzle_diameter + $self->layer_height)) {
|
||||
# rectangle with semicircles at the ends
|
||||
$min_flow_spacing = $self->width - $self->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;
|
||||
}
|
||||
return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing);
|
||||
}
|
||||
|
||||
sub clone {
|
||||
my $self = shift;
|
||||
|
||||
return (ref $self)->new(
|
||||
nozzle_diameter => $self->nozzle_diameter,
|
||||
layer_height => $self->layer_height,
|
||||
@_,
|
||||
);
|
||||
}
|
||||
|
||||
sub _build_scaled_width {
|
||||
my $self = shift;
|
||||
return 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;
|
||||
}
|
||||
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);
|
||||
|
||||
1;
|
||||
|
|
|
@ -100,7 +100,7 @@ sub end_element {
|
|||
if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) {
|
||||
my $opt_key = $1;
|
||||
if (exists $Slic3r::Config::Options->{$opt_key}) {
|
||||
$self->{_material}->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key});
|
||||
$self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{"slic3r.$opt_key"});
|
||||
}
|
||||
}
|
||||
$self->{_material_metadata_type} = undef;
|
||||
|
|
|
@ -3,19 +3,19 @@ use Moo;
|
|||
|
||||
use List::Util qw(min first);
|
||||
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::Clipper qw(union_ex);
|
||||
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 'extruders' => (is => 'ro', required => 1);
|
||||
has 'multiple_extruders' => (is => 'lazy');
|
||||
has 'standby_points' => (is => 'rw');
|
||||
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 => 'rw', default => sub {0}); # at least one extruder has wipe enabled
|
||||
has 'layer_count' => (is => 'ro', required => 1 );
|
||||
has 'layer' => (is => 'rw');
|
||||
has 'region' => (is => 'rw');
|
||||
has '_layer_islands' => (is => 'rw');
|
||||
has '_upper_layer_islands' => (is => 'rw');
|
||||
has '_layer_overhangs' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new });
|
||||
|
@ -23,13 +23,15 @@ has 'shift_x' => (is => 'rw', default => sub {0} );
|
|||
has 'shift_y' => (is => 'rw', default => sub {0} );
|
||||
has 'z' => (is => 'rw');
|
||||
has 'speed' => (is => 'rw');
|
||||
|
||||
has '_extrusion_axis' => (is => 'rw');
|
||||
has '_retract_lift' => (is => 'rw');
|
||||
has 'extruders' => (is => 'ro', default => sub {{}});
|
||||
has 'extruder' => (is => 'rw');
|
||||
has 'speeds' => (is => 'lazy'); # mm/min
|
||||
has 'external_mp' => (is => 'rw');
|
||||
has 'layer_mp' => (is => 'rw');
|
||||
has 'new_object' => (is => 'rw', default => sub {0});
|
||||
has 'straight_once' => (is => 'rw', default => sub {1});
|
||||
has 'extruder' => (is => 'rw');
|
||||
has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds
|
||||
has 'lifted' => (is => 'rw', default => sub {0} );
|
||||
has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } );
|
||||
|
@ -38,12 +40,28 @@ has 'last_f' => (is => 'rw', default => sub {""});
|
|||
has 'last_fan_speed' => (is => 'rw', default => sub {0});
|
||||
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 set_extruders {
|
||||
my ($self, $extruder_ids) = @_;
|
||||
|
||||
foreach my $i (@$extruder_ids) {
|
||||
$self->extruders->{$i} = my $e = Slic3r::Extruder->new_from_config($self->print_config, $i);
|
||||
$self->enable_wipe(1) if $e->wipe;
|
||||
}
|
||||
}
|
||||
|
||||
sub _build_speeds {
|
||||
my $self = shift;
|
||||
return {
|
||||
map { $_ => 60 * $self->config->get_value("${_}_speed") }
|
||||
map { $_ => 60 * $self->print_config->get_value("${_}_speed") }
|
||||
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,18 +77,12 @@ my %role_speeds = (
|
|||
&EXTR_ROLE_BRIDGE => 'bridge',
|
||||
&EXTR_ROLE_INTERNALBRIDGE => 'solid_infill',
|
||||
&EXTR_ROLE_SKIRT => 'perimeter',
|
||||
&EXTR_ROLE_SUPPORTMATERIAL => 'support_material',
|
||||
&EXTR_ROLE_GAPFILL => 'gap_fill',
|
||||
);
|
||||
|
||||
sub _build_multiple_extruders {
|
||||
sub multiple_extruders {
|
||||
my $self = shift;
|
||||
return @{$self->extruders} > 1;
|
||||
}
|
||||
|
||||
sub _build_enable_wipe {
|
||||
my $self = shift;
|
||||
return (first { $_->wipe } @{$self->extruders}) ? 1 : 0;
|
||||
return (keys %{$self->extruders}) > 1;
|
||||
}
|
||||
|
||||
sub set_shift {
|
||||
|
@ -97,29 +109,29 @@ sub change_layer {
|
|||
$self->_layer_islands($layer->islands);
|
||||
$self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
|
||||
$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(
|
||||
# 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}
|
||||
);
|
||||
}
|
||||
if ($self->config->avoid_crossing_perimeters) {
|
||||
if ($self->print_config->avoid_crossing_perimeters) {
|
||||
$self->layer_mp(Slic3r::GCode::MotionPlanner->new(
|
||||
islands => union_ex([ map @$_, @{$layer->slices} ], 1),
|
||||
));
|
||||
}
|
||||
|
||||
my $gcode = "";
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
$gcode .= sprintf "M73 P%s%s\n",
|
||||
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) {
|
||||
$gcode .= $self->set_acceleration($self->config->first_layer_acceleration);
|
||||
$gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration);
|
||||
} elsif ($layer->id == 1) {
|
||||
$gcode .= $self->set_acceleration($self->config->default_acceleration);
|
||||
$gcode .= $self->set_acceleration($self->print_config->default_acceleration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +145,7 @@ sub move_z {
|
|||
|
||||
my $gcode = "";
|
||||
|
||||
$z += $self->config->z_offset;
|
||||
$z += $self->print_config->z_offset;
|
||||
my $current_z = $self->z;
|
||||
my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef;
|
||||
|
||||
|
@ -181,11 +193,11 @@ sub extrude_loop {
|
|||
# find candidate starting points
|
||||
# start looking for concave vertices not being overhangs
|
||||
my @concave = ();
|
||||
if ($self->config->start_perimeters_at_concave_points) {
|
||||
if ($self->print_config->start_perimeters_at_concave_points) {
|
||||
@concave = $polygon->concave_points;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!@candidates) {
|
||||
|
@ -193,7 +205,7 @@ sub extrude_loop {
|
|||
@candidates = @concave;
|
||||
if (!@candidates) {
|
||||
# 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;
|
||||
}
|
||||
if (!@candidates) {
|
||||
|
@ -206,9 +218,9 @@ sub extrude_loop {
|
|||
# find the point of the loop that is closest to the current extruder position
|
||||
# or randomize if requested
|
||||
my $last_pos = $self->last_pos;
|
||||
if ($self->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->rotate(rand(2*PI), $self->config->print_center);
|
||||
if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) {
|
||||
$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->print_config->print_center);
|
||||
}
|
||||
|
||||
# split the loop at the starting point and make a path
|
||||
|
@ -218,27 +230,25 @@ sub extrude_loop {
|
|||
# clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
# if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
# we discard it in that case
|
||||
$extrusion_path->clip_end(scale $extrusion_path->flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING)
|
||||
$extrusion_path->clip_end(scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER)
|
||||
if $self->enable_loop_clipping;
|
||||
return '' if !@{$extrusion_path->polyline};
|
||||
|
||||
my @paths = ();
|
||||
# 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
|
||||
push @paths,
|
||||
map $_->clone,
|
||||
@{$extrusion_path->subtract_expolygons($self->_layer_overhangs)};
|
||||
|
||||
# get overhang paths by intersecting overhangs with the loop
|
||||
push @paths,
|
||||
map {
|
||||
$_->role(EXTR_ROLE_OVERHANG_PERIMETER);
|
||||
$_->flow_spacing($self->extruder->bridge_flow->width);
|
||||
$_
|
||||
}
|
||||
map $_->clone,
|
||||
@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)};
|
||||
foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) {
|
||||
$path = $path->clone;
|
||||
$path->role(EXTR_ROLE_OVERHANG_PERIMETER);
|
||||
$path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1)->mm3_per_mm(-1));
|
||||
push @paths, $path;
|
||||
}
|
||||
|
||||
# reapply the nearest point search for starting point
|
||||
# (clone because the collection gets DESTROY'ed)
|
||||
|
@ -259,7 +269,7 @@ sub extrude_loop {
|
|||
$self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe;
|
||||
|
||||
# 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
|
||||
# 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);
|
||||
|
@ -270,7 +280,7 @@ sub extrude_loop {
|
|||
# we make sure we don't exceed the segment length because we don't know
|
||||
# the rotation of the second segment so we might cross the object boundary
|
||||
my $first_segment = Slic3r::Line->new(@{$extrusion_path->polyline}[0,1]);
|
||||
my $distance = min(scale $extrusion_path->flow_spacing, $first_segment->length);
|
||||
my $distance = min(scale($self->extruder->nozzle_diameter), $first_segment->length);
|
||||
my $point = $first_segment->point_at($distance);
|
||||
$point->rotate($angle, $extrusion_path->first_point);
|
||||
|
||||
|
@ -299,38 +309,26 @@ sub extrude_path {
|
|||
|
||||
# adjust acceleration
|
||||
my $acceleration;
|
||||
if (!$self->config->first_layer_acceleration || $self->layer->id != 0) {
|
||||
if ($self->config->perimeter_acceleration && $path->is_perimeter) {
|
||||
$acceleration = $self->config->perimeter_acceleration;
|
||||
} elsif ($self->config->infill_acceleration && $path->is_fill) {
|
||||
$acceleration = $self->config->infill_acceleration;
|
||||
} elsif ($self->config->infill_acceleration && $path->is_bridge) {
|
||||
$acceleration = $self->config->bridge_acceleration;
|
||||
if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) {
|
||||
if ($self->print_config->perimeter_acceleration && $path->is_perimeter) {
|
||||
$acceleration = $self->print_config->perimeter_acceleration;
|
||||
} elsif ($self->print_config->infill_acceleration && $path->is_fill) {
|
||||
$acceleration = $self->print_config->infill_acceleration;
|
||||
} elsif ($self->print_config->infill_acceleration && $path->is_bridge) {
|
||||
$acceleration = $self->print_config->bridge_acceleration;
|
||||
}
|
||||
$gcode .= $self->set_acceleration($acceleration) if $acceleration;
|
||||
}
|
||||
|
||||
my $area; # mm^3 of extrudate per mm of tool movement
|
||||
if ($path->is_bridge) {
|
||||
my $s = $path->flow_spacing;
|
||||
$area = ($s**2) * PI/4;
|
||||
} else {
|
||||
my $s = $path->flow_spacing;
|
||||
my $h = (defined $path->height && $path->height != -1) ? $path->height : $self->layer->height;
|
||||
$area = $self->extruder->mm3_per_mm($s, $h);
|
||||
}
|
||||
|
||||
# calculate extrusion length per distance unit
|
||||
my $e = $self->extruder->e_per_mm3 * $area;
|
||||
$e = 0 if !$self->config->extrusion_axis;
|
||||
my $e = $self->extruder->e_per_mm3 * $path->mm3_per_mm;
|
||||
$e = 0 if !$self->_extrusion_axis;
|
||||
|
||||
# set speed
|
||||
$self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role );
|
||||
my $F = $self->speeds->{$self->speed} // $self->speed;
|
||||
if ($self->layer->id == 0) {
|
||||
$F = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/
|
||||
? sprintf("%.3f", $F * $1/100)
|
||||
: $self->config->first_layer_speed * 60;
|
||||
$F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60;
|
||||
}
|
||||
|
||||
# extrude arc or line
|
||||
|
@ -350,12 +348,12 @@ sub extrude_path {
|
|||
$gcode .= sprintf "G1 X%.3f Y%.3f",
|
||||
($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]; #**
|
||||
$gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E)
|
||||
$gcode .= sprintf(" %s%.5f", $self->_extrusion_axis, $E)
|
||||
if $E;
|
||||
$gcode .= " F$local_F"
|
||||
if $local_F;
|
||||
$gcode .= " ; $description"
|
||||
if $self->config->gcode_comments;
|
||||
if $self->print_config->gcode_comments;
|
||||
$gcode .= "\n";
|
||||
|
||||
# only include F in the first line
|
||||
|
@ -369,19 +367,14 @@ sub extrude_path {
|
|||
$gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge;
|
||||
$self->last_pos($path->last_point);
|
||||
|
||||
if ($self->config->cooling) {
|
||||
if ($self->print_config->cooling) {
|
||||
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);
|
||||
}
|
||||
|
||||
# reset acceleration
|
||||
$gcode .= $self->set_acceleration($self->config->default_acceleration)
|
||||
if $acceleration && $self->config->default_acceleration;
|
||||
$gcode .= $self->set_acceleration($self->print_config->default_acceleration)
|
||||
if $acceleration && $self->print_config->default_acceleration;
|
||||
|
||||
return $gcode;
|
||||
}
|
||||
|
@ -400,7 +393,7 @@ sub travel_to {
|
|||
# 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)
|
||||
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->_layer_islands}))
|
||||
|| (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands}))
|
||||
|
@ -408,7 +401,7 @@ sub travel_to {
|
|||
$self->straight_once(0);
|
||||
$self->speed('travel');
|
||||
$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);
|
||||
$gcode .= $self->retract;
|
||||
$self->speed('travel');
|
||||
|
@ -441,7 +434,7 @@ sub _plan {
|
|||
my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines};
|
||||
|
||||
# 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) {
|
||||
$need_retract = 1;
|
||||
foreach my $island (@{$self->_upper_layer_islands}) {
|
||||
|
@ -481,14 +474,14 @@ sub retract {
|
|||
if ($self->extruder->wipe && $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->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
|
||||
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, $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
|
||||
if ($wipe_path) {
|
||||
|
@ -500,7 +493,7 @@ sub retract {
|
|||
my $segment_length = $line->length;
|
||||
# reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
# 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;
|
||||
$gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE");
|
||||
}
|
||||
|
@ -510,7 +503,7 @@ sub retract {
|
|||
$self->speed('retract');
|
||||
$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";
|
||||
} else {
|
||||
$self->speed('retract');
|
||||
|
@ -518,23 +511,23 @@ sub retract {
|
|||
}
|
||||
if (!$self->lifted) {
|
||||
$self->speed('travel');
|
||||
if (defined $params{move_z} && $self->extruder->retract_lift > 0) {
|
||||
my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
|
||||
if (defined $params{move_z} && $self->_retract_lift > 0) {
|
||||
my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
|
||||
$gcode .= $self->G0(@$travel);
|
||||
$self->lifted($self->extruder->retract_lift);
|
||||
$self->lifted($self->_retract_lift);
|
||||
} elsif ($lift) {
|
||||
$gcode .= $self->G1(@$lift);
|
||||
}
|
||||
}
|
||||
$self->extruder->retracted($self->extruder->retracted + $length);
|
||||
$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
|
||||
# this makes sure we leave sufficient precision in the firmware
|
||||
$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;
|
||||
}
|
||||
|
@ -543,7 +536,7 @@ sub unretract {
|
|||
my ($self) = @_;
|
||||
|
||||
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) {
|
||||
$self->speed('travel');
|
||||
|
@ -554,15 +547,15 @@ sub unretract {
|
|||
my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra;
|
||||
if ($to_unretract) {
|
||||
$self->speed('retract');
|
||||
if ($self->config->use_firmware_retraction) {
|
||||
if ($self->print_config->use_firmware_retraction) {
|
||||
$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
|
||||
$gcode .= sprintf "G1 %s%.5f F%.3f",
|
||||
$self->config->extrusion_axis,
|
||||
$self->_extrusion_axis,
|
||||
$self->extruder->extrude($to_unretract),
|
||||
$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";
|
||||
}
|
||||
$self->extruder->retracted(0);
|
||||
|
@ -574,11 +567,11 @@ sub unretract {
|
|||
|
||||
sub reset_e {
|
||||
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;
|
||||
return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '')
|
||||
if $self->config->extrusion_axis && !$self->config->use_relative_e_distances;
|
||||
return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '')
|
||||
if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances;
|
||||
}
|
||||
|
||||
sub set_acceleration {
|
||||
|
@ -586,12 +579,12 @@ sub set_acceleration {
|
|||
return "" if !$acceleration;
|
||||
|
||||
return sprintf "M204 S%s%s\n",
|
||||
$acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : '');
|
||||
$acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : '');
|
||||
}
|
||||
|
||||
sub G0 {
|
||||
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", @_);
|
||||
}
|
||||
|
||||
|
@ -628,23 +621,23 @@ sub _Gx {
|
|||
$gcode .= sprintf " F%.3f", $F;
|
||||
|
||||
# output extrusion distance
|
||||
if ($e && $self->config->extrusion_axis) {
|
||||
$gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->extrude($e);
|
||||
if ($e && $self->_extrusion_axis) {
|
||||
$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";
|
||||
}
|
||||
|
||||
sub set_extruder {
|
||||
my ($self, $extruder) = @_;
|
||||
my ($self, $extruder_id) = @_;
|
||||
|
||||
# return nothing if this extruder was already selected
|
||||
return "" if (defined $self->extruder) && ($self->extruder->id == $extruder->id);
|
||||
return "" if (defined $self->extruder) && ($self->extruder->id == $extruder_id);
|
||||
|
||||
# if we are running a single-extruder setup, just set the extruder and return nothing
|
||||
if (!$self->multiple_extruders) {
|
||||
$self->extruder($extruder);
|
||||
$self->extruder($self->extruders->{$extruder_id});
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -653,10 +646,10 @@ sub set_extruder {
|
|||
$gcode .= $self->retract(toolchange => 1) if defined $self->extruder;
|
||||
|
||||
# append custom toolchange G-code
|
||||
if (defined $self->extruder && $self->config->toolchange_gcode) {
|
||||
$gcode .= sprintf "%s\n", $self->replace_variables($self->config->toolchange_gcode, {
|
||||
if (defined $self->extruder && $self->print_config->toolchange_gcode) {
|
||||
$gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, {
|
||||
previous_extruder => $self->extruder->id,
|
||||
next_extruder => $extruder->id,
|
||||
next_extruder => $extruder_id,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -669,24 +662,24 @@ sub set_extruder {
|
|||
? $self->extruder->first_layer_temperature
|
||||
: $self->extruder->temperature;
|
||||
# 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
|
||||
$self->extruder($extruder);
|
||||
$self->extruder($self->extruders->{$extruder_id});
|
||||
$gcode .= sprintf "%s%d%s\n",
|
||||
($self->config->gcode_flavor eq 'makerware'
|
||||
($self->print_config->gcode_flavor eq 'makerware'
|
||||
? 'M135 T'
|
||||
: $self->config->gcode_flavor eq 'sailfish'
|
||||
: $self->print_config->gcode_flavor eq 'sailfish'
|
||||
? 'M108 T'
|
||||
: 'T'),
|
||||
$extruder->id,
|
||||
($self->config->gcode_comments ? ' ; change extruder' : '');
|
||||
$extruder_id,
|
||||
($self->print_config->gcode_comments ? ' ; change extruder' : '');
|
||||
|
||||
$gcode .= $self->reset_e;
|
||||
|
||||
# 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
|
||||
? $self->extruder->first_layer_temperature
|
||||
: $self->extruder->temperature;
|
||||
|
@ -702,18 +695,18 @@ sub set_fan {
|
|||
if ($self->last_fan_speed != $speed || $dont_save) {
|
||||
$self->last_fan_speed($speed) if !$dont_save;
|
||||
if ($speed == 0) {
|
||||
my $code = $self->config->gcode_flavor eq 'teacup'
|
||||
my $code = $self->print_config->gcode_flavor eq 'teacup'
|
||||
? 'M106 S0'
|
||||
: $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/
|
||||
: $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/
|
||||
? 'M127'
|
||||
: '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 {
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : '');
|
||||
if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : '');
|
||||
} else {
|
||||
return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
|
||||
(255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : '');
|
||||
return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
|
||||
(255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -723,17 +716,17 @@ sub set_fan {
|
|||
sub set_temperature {
|
||||
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')
|
||||
: ('M104', 'set temperature');
|
||||
my $gcode = sprintf "$code %s%d %s; $comment\n",
|
||||
($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
|
||||
(defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
|
||||
($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
|
||||
(defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
|
||||
|
||||
$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;
|
||||
}
|
||||
|
@ -741,21 +734,21 @@ sub set_temperature {
|
|||
sub set_bed_temperature {
|
||||
my ($self, $temperature, $wait) = @_;
|
||||
|
||||
my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
|
||||
? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
|
||||
my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
|
||||
? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
|
||||
: ('M140', 'set bed temperature');
|
||||
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"
|
||||
if $self->config->gcode_flavor eq 'teacup' && $wait;
|
||||
if $self->print_config->gcode_flavor eq 'teacup' && $wait;
|
||||
|
||||
return $gcode;
|
||||
}
|
||||
|
||||
sub replace_variables {
|
||||
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;
|
||||
|
|
|
@ -104,7 +104,7 @@ sub flush_path {
|
|||
$gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y);
|
||||
|
||||
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;
|
||||
|
||||
my $F = 0; # TODO: extract F from original moves
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package Slic3r::GCode::CoolingBuffer;
|
||||
use Moo;
|
||||
|
||||
has 'config' => (is => 'ro', required => 1);
|
||||
has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print
|
||||
has 'gcodegen' => (is => 'ro', required => 1);
|
||||
has 'gcode' => (is => 'rw', default => sub {""});
|
||||
has 'elapsed_time' => (is => 'rw', default => sub {0});
|
||||
|
|
|
@ -4,8 +4,8 @@ use Moo;
|
|||
use List::Util qw(first);
|
||||
use Slic3r::Geometry qw(X Y unscale);
|
||||
|
||||
has 'print' => (is => 'ro', required => 1, handles => [qw(extruders)]);
|
||||
has 'gcodegen' => (is => 'ro', required => 1);
|
||||
has 'print' => (is => 'ro', required => 1);
|
||||
has 'gcodegen' => (is => 'ro', required => 1, handles => [qw(extruders)]);
|
||||
has 'shift' => (is => 'ro', default => sub { [0,0] });
|
||||
|
||||
has 'spiralvase' => (is => 'lazy');
|
||||
|
@ -19,24 +19,24 @@ has '_last_obj_copy' => (is => 'rw');
|
|||
sub _build_spiralvase {
|
||||
my $self = shift;
|
||||
|
||||
return $self->gcodegen->config->spiral_vase
|
||||
? Slic3r::GCode::SpiralVase->new(config => $self->gcodegen->config)
|
||||
return $self->print->config->spiral_vase
|
||||
? Slic3r::GCode::SpiralVase->new(config => $self->print->config)
|
||||
: undef;
|
||||
}
|
||||
|
||||
sub _build_vibration_limit {
|
||||
my $self = shift;
|
||||
|
||||
return $self->gcodegen->config->vibration_limit
|
||||
? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config)
|
||||
return $self->print->config->vibration_limit
|
||||
? Slic3r::GCode::VibrationLimit->new(config => $self->print->config)
|
||||
: undef;
|
||||
}
|
||||
|
||||
sub _build_arc_fitting {
|
||||
my $self = shift;
|
||||
|
||||
return $self->gcodegen->config->gcode_arcs
|
||||
? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config)
|
||||
return $self->print->config->gcode_arcs
|
||||
? Slic3r::GCode::ArcFitting->new(config => $self->print->config)
|
||||
: undef;
|
||||
}
|
||||
|
||||
|
@ -45,45 +45,49 @@ sub process_layer {
|
|||
my ($layer, $object_copies) = @_;
|
||||
my $gcode = "";
|
||||
|
||||
my $object = $layer->object;
|
||||
|
||||
# check whether we're going to apply spiralvase logic
|
||||
my $spiralvase = defined $self->spiralvase
|
||||
&& ($layer->id > 0 || $self->gcodegen->config->brim_width == 0)
|
||||
&& ($layer->id >= $self->gcodegen->config->skirt_height && $self->gcodegen->config->skirt_height != -1)
|
||||
&& ($layer->id >= $self->gcodegen->config->bottom_solid_layers);
|
||||
&& ($layer->id > 0 || $self->print->config->brim_width == 0)
|
||||
&& ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
|
||||
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions});
|
||||
|
||||
# if we're going to apply spiralvase to this layer, disable loop clipping
|
||||
$self->gcodegen->enable_loop_clipping(!$spiralvase);
|
||||
|
||||
if (!$self->second_layer_things_done && $layer->id == 1) {
|
||||
for my $t (grep $self->extruders->[$_], 0 .. $#{$self->gcodegen->config->temperature}) {
|
||||
$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;
|
||||
for my $extruder_id (sort keys %{$self->extruders}) {
|
||||
my $extruder = $self->extruders->{$extruder_id};
|
||||
$gcode .= $self->gcodegen->set_temperature($extruder->temperature, 0, $extruder->id)
|
||||
if $extruder->temperature && $extruder->temperature != $extruder->first_layer_temperature;
|
||||
}
|
||||
$gcode .= $self->gcodegen->set_bed_temperature($self->gcodegen->config->bed_temperature)
|
||||
if $self->gcodegen->config->bed_temperature && $self->gcodegen->config->bed_temperature != $self->gcodegen->config->first_layer_bed_temperature;
|
||||
$gcode .= $self->gcodegen->set_bed_temperature($self->print->config->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);
|
||||
}
|
||||
|
||||
# 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->replace_variables($self->gcodegen->config->layer_gcode, {
|
||||
$gcode .= $self->gcodegen->replace_variables($self->print->config->layer_gcode, {
|
||||
layer_num => $self->gcodegen->layer->id,
|
||||
}) . "\n" if $self->gcodegen->config->layer_gcode;
|
||||
}) . "\n" if $self->print->config->layer_gcode;
|
||||
|
||||
# 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->gcodegen->set_shift(@{$self->shift});
|
||||
$gcode .= $self->gcodegen->set_extruder($self->extruders->[0]);
|
||||
my @extruder_ids = sort keys %{$self->extruders};
|
||||
$gcode .= $self->gcodegen->set_extruder($extruder_ids[0]);
|
||||
# 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
|
||||
my @skirt_loops = @{$self->print->skirt};
|
||||
for my $i (0 .. $#skirt_loops) {
|
||||
# when printing layers > 0 ignore 'min_skirt_length' and
|
||||
# just use the 'skirts' setting; also just use the current extruder
|
||||
last if ($layer->id > 0) && ($i >= $self->gcodegen->config->skirts);
|
||||
$gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ])
|
||||
last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
|
||||
$gcode .= $self->gcodegen->set_extruder(($i/@extruder_ids) % @extruder_ids)
|
||||
if $layer->id == 0;
|
||||
$gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
|
||||
}
|
||||
|
@ -94,7 +98,7 @@ sub process_layer {
|
|||
|
||||
# extrude brim
|
||||
if (!$self->brim_done) {
|
||||
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]);
|
||||
$gcode .= $self->gcodegen->set_extruder($self->print->objects->[0]->config->support_material_extruder-1);
|
||||
$self->gcodegen->set_shift(@{$self->shift});
|
||||
$gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim};
|
||||
$self->brim_done(1);
|
||||
|
@ -109,15 +113,17 @@ sub process_layer {
|
|||
|
||||
# extrude support material before other things because it might use a lower Z
|
||||
# and also because we avoid travelling on other things when printing it
|
||||
if ($self->print->has_support_material && $layer->isa('Slic3r::Layer::Support')) {
|
||||
if ($layer->isa('Slic3r::Layer::Support')) {
|
||||
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->extrude_path($_, 'support material interface')
|
||||
$gcode .= $self->gcodegen->set_extruder($object->config->support_material_interface_extruder-1);
|
||||
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)};
|
||||
}
|
||||
if ($layer->support_fills->count > 0) {
|
||||
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]);
|
||||
$gcode .= $self->gcodegen->extrude_path($_, 'support material')
|
||||
$gcode .= $self->gcodegen->set_extruder($object->config->support_material_extruder-1);
|
||||
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)};
|
||||
}
|
||||
}
|
||||
|
@ -126,16 +132,17 @@ sub process_layer {
|
|||
my @region_ids = 0 .. ($self->print->regions_count-1);
|
||||
if ($self->gcodegen->multiple_extruders) {
|
||||
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;
|
||||
}
|
||||
|
||||
foreach my $region_id (@region_ids) {
|
||||
my $layerm = $layer->regions->[$region_id] or next;
|
||||
my $region = $self->print->regions->[$region_id];
|
||||
$self->gcodegen->region($region);
|
||||
|
||||
my @islands = ();
|
||||
if ($self->gcodegen->config->avoid_crossing_perimeters) {
|
||||
if ($self->print->config->avoid_crossing_perimeters) {
|
||||
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
|
||||
PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) {
|
||||
|
@ -166,8 +173,8 @@ sub process_layer {
|
|||
foreach my $island (@islands) {
|
||||
# give priority to infill if we were already using its extruder and it wouldn't
|
||||
# be good for perimeters
|
||||
if ($self->gcodegen->config->infill_first
|
||||
|| ($self->gcodegen->multiple_extruders && $region->extruders->{infill} eq $self->gcodegen->extruder) && $region->extruders->{infill} ne $region->extruders->{perimeter}) {
|
||||
if ($self->print->config->infill_first
|
||||
|| ($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_perimeters($island, $region);
|
||||
} else {
|
||||
|
@ -184,11 +191,11 @@ sub process_layer {
|
|||
|
||||
# apply vibration limit if enabled
|
||||
$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
|
||||
$gcode = $self->arc_fitting->process($gcode)
|
||||
if $self->gcodegen->config->gcode_arcs;
|
||||
if $self->print->config->gcode_arcs;
|
||||
|
||||
return $gcode;
|
||||
}
|
||||
|
@ -200,7 +207,7 @@ sub _extrude_perimeters {
|
|||
return "" if !@{ $island->{perimeters} };
|
||||
|
||||
my $gcode = "";
|
||||
$gcode .= $self->gcodegen->set_extruder($region->extruders->{perimeter});
|
||||
$gcode .= $self->gcodegen->set_extruder($region->config->perimeter_extruder-1);
|
||||
$gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} };
|
||||
return $gcode;
|
||||
}
|
||||
|
@ -212,7 +219,7 @@ sub _extrude_infill {
|
|||
return "" if !@{ $island->{fills} };
|
||||
|
||||
my $gcode = "";
|
||||
$gcode .= $self->gcodegen->set_extruder($region->extruders->{infill});
|
||||
$gcode .= $self->gcodegen->set_extruder($region->config->infill_extruder-1);
|
||||
for my $fill (@{ $island->{fills} }) {
|
||||
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
|
||||
$gcode .= $self->gcodegen->extrude($_, 'fill')
|
||||
|
|
|
@ -387,59 +387,65 @@ sub load_file {
|
|||
my $model = eval { Slic3r::Model->read_from_file($input_file) };
|
||||
Slic3r::GUI::show_error($self, $@) if $@;
|
||||
|
||||
$self->load_model_object($_) for @{$model->objects};
|
||||
$self->load_model_objects(@{$model->objects});
|
||||
|
||||
$process_dialog->Destroy;
|
||||
$self->statusbar->SetStatusText("Loaded " . basename($input_file));
|
||||
}
|
||||
|
||||
sub load_model_object {
|
||||
my ($self, $model_object) = @_;
|
||||
|
||||
my $o = $self->{model}->add_object($model_object);
|
||||
|
||||
push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(
|
||||
name => basename($model_object->input_file),
|
||||
);
|
||||
sub load_model_objects {
|
||||
my ($self, @model_objects) = @_;
|
||||
|
||||
my $need_arrange = 0;
|
||||
if (!defined $model_object->instances) {
|
||||
# if object has no defined position(s) we need to rearrange everything after loading
|
||||
$need_arrange = 1;
|
||||
my @obj_idx = ();
|
||||
foreach my $model_object (@model_objects) {
|
||||
my $o = $self->{model}->add_object($model_object);
|
||||
|
||||
# add a default instance and center object around origin
|
||||
$o->center_around_origin;
|
||||
$o->add_instance(offset => [ @{$self->{config}->print_center} ]);
|
||||
}
|
||||
push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(
|
||||
name => basename($model_object->input_file),
|
||||
);
|
||||
push @obj_idx, $#{ $self->{objects} };
|
||||
|
||||
$self->{print}->add_model_object($o);
|
||||
if (!defined $model_object->instances) {
|
||||
# if object has no defined position(s) we need to rearrange everything after loading
|
||||
$need_arrange = 1;
|
||||
|
||||
# add a default instance and center object around origin
|
||||
$o->center_around_origin;
|
||||
$o->add_instance(offset => [ @{$self->{config}->print_center} ]);
|
||||
}
|
||||
|
||||
$self->{print}->add_model_object($o);
|
||||
}
|
||||
|
||||
# if user turned autocentering off, automatic arranging would disappoint them
|
||||
if (!$Slic3r::GUI::Settings->{_}{autocenter}) {
|
||||
$need_arrange = 0;
|
||||
}
|
||||
|
||||
$self->object_loaded($#{ $self->{objects} }, no_arrange => !$need_arrange);
|
||||
$self->objects_loaded(\@obj_idx, no_arrange => !$need_arrange);
|
||||
}
|
||||
|
||||
sub object_loaded {
|
||||
sub objects_loaded {
|
||||
my $self = shift;
|
||||
my ($obj_idx, %params) = @_;
|
||||
my ($obj_idxs, %params) = @_;
|
||||
|
||||
my $object = $self->{objects}[$obj_idx];
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$self->{list}->InsertStringItem($obj_idx, $object->name);
|
||||
$self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
|
||||
if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
|
||||
foreach my $obj_idx (@$obj_idxs) {
|
||||
my $object = $self->{objects}[$obj_idx];
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$self->{list}->InsertStringItem($obj_idx, $object->name);
|
||||
$self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
|
||||
if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
|
||||
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
$self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
$self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
|
||||
|
||||
$self->make_thumbnail($obj_idx);
|
||||
$self->make_thumbnail($obj_idx);
|
||||
}
|
||||
$self->arrange unless $params{no_arrange};
|
||||
$self->update;
|
||||
$self->{list}->Update;
|
||||
$self->{list}->Select($obj_idx, 1);
|
||||
$self->{list}->Select($obj_idxs->[-1], 1);
|
||||
$self->object_list_changed;
|
||||
}
|
||||
|
||||
|
@ -638,7 +644,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,
|
||||
|
@ -658,7 +663,7 @@ sub split_object {
|
|||
}
|
||||
# we need to center this single object around origin
|
||||
$model_object->center_around_origin;
|
||||
$self->load_model_object($model_object);
|
||||
$self->load_model_objects($model_object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -755,11 +760,15 @@ sub export_gcode2 {
|
|||
} if $Slic3r::have_threads;
|
||||
|
||||
my $print = $self->{print};
|
||||
$print->apply_config($config);
|
||||
$print->apply_extra_variables($extra_variables);
|
||||
|
||||
|
||||
eval {
|
||||
$print->config->validate;
|
||||
# this will throw errors if config is not valid
|
||||
$config->validate;
|
||||
|
||||
$print->apply_config($config);
|
||||
$print->apply_extra_variables($extra_variables);
|
||||
|
||||
$print->validate;
|
||||
|
||||
{
|
||||
|
|
|
@ -114,7 +114,7 @@ sub update_optgroup {
|
|||
|
||||
my $config = $self->model_object->config;
|
||||
my %categories = ();
|
||||
foreach my $opt_key (keys %$config) {
|
||||
foreach my $opt_key (@{$config->get_keys}) {
|
||||
my $category = $Slic3r::Config::Options->{$opt_key}{category};
|
||||
$categories{$category} ||= [];
|
||||
push @{$categories{$category}}, $opt_key;
|
||||
|
@ -288,31 +288,41 @@ sub new {
|
|||
# get unique materials used in this object
|
||||
$self->{materials} = [ $self->model_object->unique_materials ];
|
||||
|
||||
# build an OptionsGroup
|
||||
$self->{mapping} = {
|
||||
(map { $self->{materials}[$_] => $_+1 } 0..$#{ $self->{materials} }), # defaults
|
||||
%{$self->model_object->material_mapping},
|
||||
};
|
||||
my $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Extruders',
|
||||
label_width => 300,
|
||||
options => [
|
||||
map {
|
||||
my $i = $_;
|
||||
my $material_id = $self->{materials}[$i];
|
||||
{
|
||||
opt_key => "material_extruder_$_",
|
||||
type => 'i',
|
||||
label => $self->model_object->model->get_material_name($material_id),
|
||||
min => 1,
|
||||
default => $self->{mapping}{$material_id},
|
||||
on_change => sub { $self->{mapping}{$material_id} = $_[0] },
|
||||
}
|
||||
} 0..$#{ $self->{materials} }
|
||||
],
|
||||
);
|
||||
$self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
||||
# get the current mapping
|
||||
$self->{mapping} = {};
|
||||
foreach my $material_id (@{ $self->{materials}}) {
|
||||
my $config = $self->model_object->model->materials->{ $material_id }->config;
|
||||
$self->{mapping}{$material_id} = ($config->perimeter_extruder // 0) + 1;
|
||||
}
|
||||
|
||||
if (@{$self->{materials}} > 0) {
|
||||
# build an OptionsGroup
|
||||
my $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Extruders',
|
||||
label_width => 300,
|
||||
options => [
|
||||
map {
|
||||
my $i = $_;
|
||||
my $material_id = $self->{materials}[$i];
|
||||
{
|
||||
opt_key => "material_extruder_$_",
|
||||
type => 'i',
|
||||
label => $self->model_object->model->get_material_name($material_id),
|
||||
min => 1,
|
||||
default => $self->{mapping}{$material_id} // 1,
|
||||
on_change => sub { $self->{mapping}{$material_id} = $_[0] },
|
||||
}
|
||||
} 0..$#{ $self->{materials} }
|
||||
],
|
||||
);
|
||||
$self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
||||
} else {
|
||||
my $label = Wx::StaticText->new($self, -1, "This object does not contain named materials.",
|
||||
wxDefaultPosition, [-1, 25]);
|
||||
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
}
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
@ -324,7 +334,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->volumes}) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -88,25 +88,15 @@ sub quick_slice {
|
|||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
||||
my $process_dialog;
|
||||
my $progress_dialog;
|
||||
eval {
|
||||
# validate configuration
|
||||
my $config = $self->config;
|
||||
$config->validate;
|
||||
|
||||
# confirm slicing of more than one copies
|
||||
my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y];
|
||||
$copies = $config->duplicate if $config->duplicate > 1;
|
||||
if ($copies > 1) {
|
||||
my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?",
|
||||
'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
return unless $confirmation->ShowModal == wxID_OK;
|
||||
}
|
||||
|
||||
# select input file
|
||||
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
|
||||
|
||||
my $input_file;
|
||||
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
|
||||
if (!$params{reslice}) {
|
||||
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if ($dialog->ShowModal != wxID_OK) {
|
||||
|
@ -133,28 +123,23 @@ sub quick_slice {
|
|||
$Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
|
||||
Slic3r::GUI->save_settings;
|
||||
|
||||
my $print = $self->init_print;
|
||||
my $model = eval { Slic3r::Model->read_from_file($input_file) };
|
||||
Slic3r::GUI::show_error($self, $@) if $@;
|
||||
my $sprint = Slic3r::Print::Simple->new(
|
||||
status_cb => sub {
|
||||
my ($percent, $message) = @_;
|
||||
return if &Wx::wxVERSION_STRING !~ / 2\.(8\.|9\.[2-9])/;
|
||||
$progress_dialog->Update($percent, "$message…");
|
||||
},
|
||||
);
|
||||
|
||||
if ($model->has_objects_with_no_instances) {
|
||||
# apply a default position to all objects not having one
|
||||
foreach my $object (@{$model->objects}) {
|
||||
$object->add_instance(offset => [0,0]) if !defined $object->instances;
|
||||
}
|
||||
$model->arrange_objects($config->min_object_distance);
|
||||
}
|
||||
$model->center_instances_around_point($config->print_center);
|
||||
$sprint->apply_config($config);
|
||||
$sprint->set_model(Slic3r::Model->read_from_file($input_file));
|
||||
|
||||
$print->add_model_object($_) for @{ $model->objects };
|
||||
$print->validate;
|
||||
|
||||
# select output file
|
||||
my $output_file = $main::opt{output};
|
||||
my $output_file;
|
||||
if ($params{reslice}) {
|
||||
$output_file = $last_output_file if defined $last_output_file;
|
||||
} elsif ($params{save_as}) {
|
||||
$output_file = $print->expanded_output_filepath($output_file);
|
||||
$output_file = $sprint->expanded_output_filepath;
|
||||
$output_file =~ s/\.gcode$/.svg/i if $params{export_svg};
|
||||
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
|
||||
Slic3r::GUI->output_path(dirname($output_file)),
|
||||
|
@ -171,40 +156,32 @@ sub quick_slice {
|
|||
}
|
||||
|
||||
# show processbar dialog
|
||||
$process_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…",
|
||||
$progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…",
|
||||
100, $self, 0);
|
||||
$process_dialog->Pulse;
|
||||
$progress_dialog->Pulse;
|
||||
|
||||
{
|
||||
my @warnings = ();
|
||||
local $SIG{__WARN__} = sub { push @warnings, $_[0] };
|
||||
my %export_params = (
|
||||
output_file => $output_file,
|
||||
);
|
||||
$print->status_cb(sub {
|
||||
my ($percent, $message) = @_;
|
||||
if (&Wx::wxVERSION_STRING =~ / 2\.(8\.|9\.[2-9])/) {
|
||||
$process_dialog->Update($percent, "$message…");
|
||||
}
|
||||
});
|
||||
|
||||
$sprint->output_file($output_file);
|
||||
if ($params{export_svg}) {
|
||||
$print->export_svg(%export_params);
|
||||
$sprint->export_svg;
|
||||
} else {
|
||||
$print->process;
|
||||
$print->export_gcode(%export_params);
|
||||
$sprint->export_gcode;
|
||||
}
|
||||
$print->status_cb(undef);
|
||||
$sprint->status_cb(undef);
|
||||
Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
|
||||
}
|
||||
$process_dialog->Destroy;
|
||||
undef $process_dialog;
|
||||
$progress_dialog->Destroy;
|
||||
undef $progress_dialog;
|
||||
|
||||
my $message = "$input_file_basename was successfully sliced.";
|
||||
&Wx::wxTheApp->notify($message);
|
||||
Wx::MessageDialog->new($self, $message, 'Slicing Done!',
|
||||
wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
};
|
||||
Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog });
|
||||
Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
|
||||
}
|
||||
|
||||
sub repair_stl {
|
||||
|
|
|
@ -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
|
||||
|
@ -13,14 +14,9 @@ has 'layer' => (
|
|||
is => 'ro',
|
||||
weak_ref => 1,
|
||||
required => 1,
|
||||
trigger => 1,
|
||||
handles => [qw(id slice_z print_z height flow config)],
|
||||
handles => [qw(id slice_z print_z height object print)],
|
||||
);
|
||||
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 'region' => (is => 'ro', required => 1, handles => [qw(config)]);
|
||||
has 'infill_area_threshold' => (is => 'lazy');
|
||||
has 'overhang_width' => (is => 'lazy');
|
||||
|
||||
|
@ -40,43 +36,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 +124,12 @@ 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 $mm3_per_mm = $perimeter_flow->mm3_per_mm($self->height);
|
||||
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;
|
||||
|
@ -188,7 +169,7 @@ sub make_perimeters {
|
|||
@offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))};
|
||||
|
||||
# look for gaps
|
||||
if ($self->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
|
||||
if ($self->print->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
|
||||
my $diff = diff_ex(
|
||||
offset(\@last, -0.5*$pspacing),
|
||||
offset(\@offsets, +0.5*$pspacing),
|
||||
|
@ -284,7 +265,7 @@ sub make_perimeters {
|
|||
push @loops, Slic3r::ExtrusionLoop->new(
|
||||
polygon => $polygon,
|
||||
role => $role,
|
||||
flow_spacing => $self->perimeter_flow->spacing,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
);
|
||||
}
|
||||
return @loops;
|
||||
|
@ -297,8 +278,8 @@ sub make_perimeters {
|
|||
# we continue inwards after having finished the brim
|
||||
# TODO: add test for perimeter order
|
||||
@loops = reverse @loops
|
||||
if $self->config->external_perimeters_first
|
||||
|| ($self->layer->id == 0 && $self->config->brim_width > 0);
|
||||
if $self->print->config->external_perimeters_first
|
||||
|| ($self->layer->id == 0 && $self->print->config->brim_width > 0);
|
||||
|
||||
# append perimeters
|
||||
$self->perimeters->append(@loops);
|
||||
|
@ -310,8 +291,8 @@ sub make_perimeters {
|
|||
for my $p (@p) {
|
||||
next if $p->length <= $pspacing * 2;
|
||||
my %params = (
|
||||
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
flow_spacing => $self->perimeter_flow->spacing,
|
||||
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
);
|
||||
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)},
|
||||
|
@ -359,8 +340,8 @@ sub _fill_gaps {
|
|||
# fill gaps using dynamic extrusion width, by treating them like thin polygons,
|
||||
# thus generating the skeleton and using it to fill them
|
||||
my %path_args = (
|
||||
role => EXTR_ROLE_SOLIDFILL,
|
||||
flow_spacing => $flow->spacing,
|
||||
role => EXTR_ROLE_SOLIDFILL,
|
||||
mm3_per_mm => $flow->mm3_per_mm($self->height),
|
||||
);
|
||||
$self->thin_fills->append(map {
|
||||
$_->isa('Slic3r::Polygon')
|
||||
|
@ -380,9 +361,10 @@ sub _fill_gaps {
|
|||
foreach my $expolygon (@infill) {
|
||||
my ($params, @paths) = $filler->fill_surface(
|
||||
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNALSOLID),
|
||||
density => 1,
|
||||
flow_spacing => $flow->spacing,
|
||||
density => 1,
|
||||
flow => $flow,
|
||||
);
|
||||
my $mm3_per_mm = $params->{flow}->mm3_per_mm($self->height);
|
||||
|
||||
# Split polylines into lines so that the chained_path() search
|
||||
# at the final stage has more freedom and will choose starting
|
||||
|
@ -399,8 +381,7 @@ sub _fill_gaps {
|
|||
@paths = map Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polyline->new(@$_),
|
||||
role => EXTR_ROLE_GAPFILL,
|
||||
height => $self->height,
|
||||
flow_spacing => $params->{flow_spacing},
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), @lines;
|
||||
$_->simplify($flow->scaled_width/3) for @paths;
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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}) {
|
||||
|
@ -55,10 +54,11 @@ sub add_object {
|
|||
|
||||
if (defined $volume->material_id) {
|
||||
# merge material attributes (should we rename materials in case of duplicates?)
|
||||
$self->set_material($volume->material_id, {
|
||||
%{ $object->model->materials->{$volume->material_id} },
|
||||
%{ $self->materials->{$volume->material_id} || {} },
|
||||
});
|
||||
my %attributes = %{ $object->model->materials->{$volume->material_id}->attributes };
|
||||
if (exists $self->materials->{$volume->material_id}) {
|
||||
%attributes = (%attributes, %{ $self->materials->{$volume->material_id}->attributes });
|
||||
}
|
||||
$self->set_material($volume->material_id, {%attributes});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,6 +324,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 +339,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 {
|
||||
|
@ -479,7 +479,8 @@ sub unique_materials {
|
|||
my $self = shift;
|
||||
|
||||
my %materials = ();
|
||||
$materials{ $_->material_id // '_' } = 1 for @{$self->volumes};
|
||||
$materials{ $_->material_id } = 1
|
||||
for grep { defined $_->material_id } @{$self->volumes};
|
||||
return sort keys %materials;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,21 +5,22 @@ use File::Basename qw(basename fileparse);
|
|||
use File::Spec;
|
||||
use List::Util qw(min max first);
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale move_points chained_path
|
||||
convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex intersection offset
|
||||
offset2 union union_pt_chained JT_ROUND JT_SQUARE);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }, trigger => \&init_config);
|
||||
has 'config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
|
||||
has 'default_object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
|
||||
has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new });
|
||||
has 'extra_variables' => (is => 'rw', default => sub {{}});
|
||||
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 'total_used_filament' => (is => 'rw');
|
||||
has 'total_extruded_volume' => (is => 'rw');
|
||||
has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new });
|
||||
|
||||
# ordered collection of extrusion paths to build skirt loops
|
||||
|
@ -28,54 +29,93 @@ has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->
|
|||
# ordered collection of extrusion paths to build a brim
|
||||
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 {
|
||||
my ($self, $config) = @_;
|
||||
|
||||
$self->config->apply($config);
|
||||
$self->init_config;
|
||||
$_->init_config for @{$self->objects};
|
||||
# handle changes to print config
|
||||
my $print_diff = $self->config->diff($config);
|
||||
if (@$print_diff) {
|
||||
$self->config->apply_dynamic($config);
|
||||
|
||||
# TODO: only invalidate changed steps
|
||||
$self->_state->invalidate_all;
|
||||
}
|
||||
|
||||
# handle changes to object config defaults
|
||||
$self->default_object_config->apply_dynamic($config);
|
||||
foreach my $object (@{$self->objects}) {
|
||||
# we don't assume that $config contains a full ObjectConfig,
|
||||
# so we base it on the current print-wise default
|
||||
my $new = $self->default_object_config->clone;
|
||||
|
||||
# we override the new config with object-specific options
|
||||
$new->apply_dynamic($object->model_object->config);
|
||||
|
||||
# check whether the new config is different from the current one
|
||||
my $diff = $object->config->diff($new);
|
||||
if (@$diff) {
|
||||
$object->config->apply($new);
|
||||
# TODO: only invalidate changed steps
|
||||
$object->_state->invalidate_all;
|
||||
}
|
||||
}
|
||||
|
||||
# handle changes to regions config defaults
|
||||
$self->default_region_config->apply_dynamic($config);
|
||||
|
||||
# check whether after applying the new region config defaults to all existing regions
|
||||
# they still have distinct configs; if not we need to re-add objects in order to
|
||||
# merge the now-equal regions
|
||||
|
||||
# first compute the transformed region configs
|
||||
my @new_region_configs = ();
|
||||
foreach my $region_id (0..$#{$self->regions}) {
|
||||
my $new = $self->default_region_config->clone;
|
||||
foreach my $object (@{$self->objects}) {
|
||||
foreach my $volume_id (@{ $object->region_volumes->[$region_id] }) {
|
||||
my $volume = $object->model_object->volumes->[$volume_id];
|
||||
next if !defined $volume->material_id;
|
||||
my $material = $object->model_object->model->materials->{$volume->material_id};
|
||||
$new->apply_dynamic($material->config);
|
||||
}
|
||||
}
|
||||
push @new_region_configs, $new;
|
||||
}
|
||||
|
||||
# then find the first pair of identical configs
|
||||
my $have_identical_configs = 0;
|
||||
my $region_diff = [];
|
||||
for my $i (0..$#new_region_configs) {
|
||||
for my $j (($i+1)..$#new_region_configs) {
|
||||
if ($new_region_configs[$i]->equals($new_region_configs[$j])) {
|
||||
$have_identical_configs = 1;
|
||||
}
|
||||
}
|
||||
my $diff = $self->regions->[$i]->config->diff($new_region_configs[$i]);
|
||||
push @$region_diff, @$diff;
|
||||
}
|
||||
|
||||
if ($have_identical_configs) {
|
||||
# okay, the current subdivision of regions does not make sense anymore.
|
||||
# we need to remove all objects and re-add them
|
||||
my @model_objects = map $_->model_object, @{$self->object};
|
||||
$self->delete_all_objects;
|
||||
$self->add_model_object($_) for @model_objects;
|
||||
} elsif (@$region_diff > 0) {
|
||||
# if there are no identical regions even after applying the change in
|
||||
# region config defaults, but at least one region config option changed,
|
||||
# store the new region configs and invalidate
|
||||
# the affected step(s)
|
||||
foreach my $region_id (0..$#{$self->regions}) {
|
||||
$self->regions->[$region_id]->config->apply($new_region_configs[$region_id]);
|
||||
}
|
||||
|
||||
# TODO: only invalidate changed steps
|
||||
$_->_state->invalidate_all for @{$self->objects};
|
||||
}
|
||||
}
|
||||
|
||||
sub _build_has_support_material {
|
||||
sub has_support_material {
|
||||
my $self = shift;
|
||||
return (first { $_->config->support_material } @{$self->objects})
|
||||
|| (first { $_->config->raft_layers > 0 } @{$self->objects})
|
||||
|
@ -88,30 +128,43 @@ 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: start from our global defaults
|
||||
my $config = Slic3r::Config::PrintRegion->new;
|
||||
$config->apply($self->default_region_config);
|
||||
|
||||
# override the defaults with per-object config and then with per-material config
|
||||
$config->apply_dynamic($object->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 };
|
||||
} else {
|
||||
$region_id = 0;
|
||||
my $material_config = $object->model->materials->{ $volume->material_id }->config;
|
||||
$config->apply_dynamic($material_config);
|
||||
}
|
||||
|
||||
# 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}, my $r = Slic3r::Print::Region->new(
|
||||
print => $self,
|
||||
);
|
||||
$r->config->apply($config);
|
||||
$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
|
||||
|
@ -120,9 +173,14 @@ sub add_model_object {
|
|||
model_object => $object,
|
||||
region_volumes => [ map $volumes{$_}, 0..$#{$self->regions} ],
|
||||
copies => [ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ],
|
||||
config_overrides => $object->config,
|
||||
layer_height_ranges => $object->layer_height_ranges,
|
||||
);
|
||||
|
||||
# apply config to print object
|
||||
$o->config->apply($self->default_object_config);
|
||||
$o->config->apply_dynamic($object->config);
|
||||
|
||||
# store print object at the given position
|
||||
if (defined $obj_idx) {
|
||||
splice @{$self->objects}, $obj_idx, 0, $o;
|
||||
} else {
|
||||
|
@ -225,72 +283,32 @@ sub validate {
|
|||
}
|
||||
}
|
||||
|
||||
# 0-based indices of used extruders
|
||||
sub extruders {
|
||||
my ($self) = @_;
|
||||
|
||||
# initialize all extruder(s) we need
|
||||
my @used_extruders = ();
|
||||
foreach my $region (@{$self->regions}) {
|
||||
push @used_extruders,
|
||||
map $region->config->get("${_}_extruder")-1,
|
||||
qw(perimeter infill);
|
||||
}
|
||||
foreach my $object (@{$self->objects}) {
|
||||
push @used_extruders,
|
||||
map $object->config->get("${_}_extruder")-1,
|
||||
qw(support_material support_material_interface);
|
||||
}
|
||||
|
||||
my %h = map { $_ => 1 } @used_extruders;
|
||||
return [ sort keys %h ];
|
||||
}
|
||||
|
||||
sub init_extruders {
|
||||
my $self = shift;
|
||||
|
||||
# map regions to extruders (ghetto mapping for now)
|
||||
my %extruder_mapping = map { $_ => $_ } 0..$#{$self->regions};
|
||||
|
||||
# initialize all extruder(s) we need
|
||||
my @used_extruders = (
|
||||
0,
|
||||
(map $self->config->get("${_}_extruder")-1, qw(perimeter infill support_material support_material_interface)),
|
||||
(values %extruder_mapping),
|
||||
);
|
||||
for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) {
|
||||
$self->extruders->[$extruder_id] = Slic3r::Extruder->new(
|
||||
config => $self->config,
|
||||
id => $extruder_id,
|
||||
map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/
|
||||
@{&Slic3r::Extruder::OPTIONS}
|
||||
);
|
||||
}
|
||||
|
||||
# calculate regions' flows
|
||||
for my $region_id (0 .. $#{$self->regions}) {
|
||||
my $region = $self->regions->[$region_id];
|
||||
|
||||
# per-role extruders and flows
|
||||
for (qw(perimeter infill solid_infill top_infill)) {
|
||||
my $extruder_name = $_;
|
||||
$extruder_name =~ s/^(?:solid|top)_//;
|
||||
$region->extruders->{$_} = ($self->regions_count > 1)
|
||||
? $self->extruders->[$extruder_mapping{$region_id}]
|
||||
: $self->extruders->[$self->config->get("${extruder_name}_extruder")-1];
|
||||
$region->flows->{$_} = $region->extruders->{$_}->make_flow(
|
||||
layer_height => $self->config->layer_height,
|
||||
width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width,
|
||||
role => $_,
|
||||
);
|
||||
$region->first_layer_flows->{$_} = $region->extruders->{$_}->make_flow(
|
||||
layer_height => $self->config->get_value('first_layer_height'),
|
||||
width => $self->config->first_layer_extrusion_width,
|
||||
role => $_,
|
||||
) if $self->config->first_layer_extrusion_width;
|
||||
}
|
||||
}
|
||||
|
||||
# calculate support material flow
|
||||
# Note: we should calculate a different flow for support material interface
|
||||
# TODO: support material layers have their own variable layer heights, so we
|
||||
# probably need a DynamicFlow object that calculates flow on the fly
|
||||
# (or the Flow object must support a mutable layer_height)
|
||||
if ($self->has_support_material) {
|
||||
my $extruder = $self->extruders->[$self->config->support_material_extruder-1];
|
||||
$self->support_material_flow($extruder->make_flow(
|
||||
layer_height => $self->config->layer_height, # WRONG!
|
||||
width => $self->config->support_material_extrusion_width || $self->config->extrusion_width,
|
||||
role => 'support_material',
|
||||
));
|
||||
$self->first_layer_support_material_flow($extruder->make_flow(
|
||||
layer_height => $self->config->get_value('first_layer_height'),
|
||||
width => $self->config->first_layer_extrusion_width,
|
||||
role => 'support_material',
|
||||
));
|
||||
}
|
||||
|
||||
# enforce tall skirt if using ooze_prevention
|
||||
# NOTE: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
|
||||
# FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
|
||||
if ($self->config->ooze_prevention && @{$self->extruders} > 1) {
|
||||
$self->config->set('skirt_height', -1);
|
||||
$self->config->set('skirts', 1) if $self->config->skirts == 0;
|
||||
|
@ -631,10 +649,20 @@ sub make_skirt {
|
|||
|
||||
my @extruded_length = (); # for each extruder
|
||||
|
||||
# skirt may be printed on several layers, having distinct layer heights,
|
||||
# but loops must be aligned so can't vary width/spacing
|
||||
# TODO: use each extruder's own flow
|
||||
my $spacing = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow->spacing;
|
||||
my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height');
|
||||
my $flow = Slic3r::Flow->new_from_width(
|
||||
width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
|
||||
role => FLOW_ROLE_PERIMETER,
|
||||
nozzle_diameter => $self->config->nozzle_diameter->[0],
|
||||
layer_height => $first_layer_height,
|
||||
bridge_flow_ratio => 0,
|
||||
);
|
||||
my $spacing = $flow->spacing;
|
||||
my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height);
|
||||
|
||||
my $first_layer_height = $self->config->get_value('first_layer_height');
|
||||
my @extruders_e_per_mm = ();
|
||||
my $extruder_idx = 0;
|
||||
|
||||
|
@ -647,13 +675,16 @@ sub make_skirt {
|
|||
$self->skirt->append(Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$loop),
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
flow_spacing => $spacing,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
));
|
||||
|
||||
if ($self->config->min_skirt_length > 0) {
|
||||
$extruded_length[$extruder_idx] ||= 0;
|
||||
$extruders_e_per_mm[$extruder_idx] ||= $self->extruders->[$extruder_idx]->e_per_mm($spacing, $first_layer_height);
|
||||
$extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
|
||||
$extruded_length[$extruder_idx] ||= 0;
|
||||
if (!$extruders_e_per_mm[$extruder_idx]) {
|
||||
my $extruder = Slic3r::Extruder->new_from_config($self->config, $extruder_idx);
|
||||
$extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm);
|
||||
}
|
||||
$extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
|
||||
$i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders};
|
||||
if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) {
|
||||
if ($extruder_idx < $#{$self->extruders}) {
|
||||
|
@ -673,7 +704,16 @@ sub make_brim {
|
|||
|
||||
$self->brim->clear; # method must be idempotent
|
||||
|
||||
my $flow = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow;
|
||||
# brim is only printed on first layer and uses support material extruder
|
||||
my $first_layer_height = $self->objects->[0]->config->get_abs_value('first_layer_height');
|
||||
my $flow = Slic3r::Flow->new_from_width(
|
||||
width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width),
|
||||
role => FLOW_ROLE_PERIMETER,
|
||||
nozzle_diameter => $self->config->get_at('nozzle_diameter', $self->objects->[0]->config->support_material_extruder-1),
|
||||
layer_height => $first_layer_height,
|
||||
bridge_flow_ratio => 0,
|
||||
);
|
||||
my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height);
|
||||
|
||||
my $grow_distance = $flow->scaled_width / 2;
|
||||
my @islands = (); # array of polygons
|
||||
|
@ -716,7 +756,7 @@ sub make_brim {
|
|||
$self->brim->append(map Slic3r::ExtrusionLoop->new(
|
||||
polygon => Slic3r::Polygon->new(@$_),
|
||||
role => EXTR_ROLE_SKIRT,
|
||||
flow_spacing => $flow->spacing,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), reverse @{union_pt_chained(\@loops)});
|
||||
}
|
||||
|
||||
|
@ -741,29 +781,33 @@ sub write_gcode {
|
|||
print $fh "; $_\n" foreach split /\R/, $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)) {
|
||||
printf $fh "; %s = %s\n", $_, $self->config->$_;
|
||||
my $layer_height = $self->objects->[0]->config->layer_height;
|
||||
for my $region_id (0..$#{$self->regions}) {
|
||||
printf $fh "; perimeters extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height)->width;
|
||||
printf $fh "; infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height)->width;
|
||||
printf $fh "; solid infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height)->width;
|
||||
printf $fh "; top infill extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height)->width;
|
||||
printf $fh "; support material extrusion width = %.2fmm\n",
|
||||
$self->objects->[0]->support_material_flow->width
|
||||
if $self->has_support_material;
|
||||
printf $fh "; first layer extrusion width = %.2fmm\n",
|
||||
$self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width
|
||||
if $self->regions->[$region_id]->config->first_layer_extrusion_width;
|
||||
print $fh "\n";
|
||||
}
|
||||
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";
|
||||
|
||||
# set up our extruder object
|
||||
my $gcodegen = Slic3r::GCode->new(
|
||||
config => $self->config,
|
||||
print_config => $self->config,
|
||||
extra_variables => $self->extra_variables,
|
||||
extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!)
|
||||
layer_count => $self->layer_count,
|
||||
);
|
||||
$gcodegen->set_extruders($self->extruders);
|
||||
|
||||
print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware';
|
||||
print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers;
|
||||
|
||||
|
@ -777,8 +821,8 @@ sub write_gcode {
|
|||
my ($wait) = @_;
|
||||
|
||||
return if $self->config->start_gcode =~ /M(?:109|104)/i;
|
||||
for my $t (0 .. $#{$self->extruders}) {
|
||||
my $temp = $self->extruders->[$t]->first_layer_temperature;
|
||||
for my $t (@{$self->extruders}) {
|
||||
my $temp = $self->config->get_at('first_layer_temperature', $t);
|
||||
$temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;
|
||||
printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0;
|
||||
}
|
||||
|
@ -798,10 +842,6 @@ sub write_gcode {
|
|||
}
|
||||
}
|
||||
|
||||
# always start with first extruder
|
||||
# TODO: make sure we select the first *used* extruder
|
||||
print $fh $gcodegen->set_extruder($self->extruders->[0]);
|
||||
|
||||
# initialize a motion planner for object-to-object travel moves
|
||||
if ($self->config->avoid_crossing_perimeters) {
|
||||
my $distance_from_objects = 1;
|
||||
|
@ -832,9 +872,9 @@ sub write_gcode {
|
|||
if (@skirt_points) {
|
||||
my $outer_skirt = convex_hull(\@skirt_points);
|
||||
my @skirts = ();
|
||||
foreach my $extruder (@{$self->extruders}) {
|
||||
foreach my $extruder_id (@{$self->extruders}) {
|
||||
push @skirts, my $s = $outer_skirt->clone;
|
||||
$s->translate(map scale($_), @{$extruder->extruder_offset});
|
||||
$s->translate(map scale($_), @{$self->config->get_at('extruder_offset', $extruder_id)});
|
||||
}
|
||||
my $convex_hull = convex_hull([ map @$_, @skirts ]);
|
||||
$gcodegen->standby_points([ map $_->clone, map @$_, map $_->subdivide(scale 10), @{offset([$convex_hull], scale 3)} ]);
|
||||
|
@ -847,6 +887,9 @@ sub write_gcode {
|
|||
gcodegen => $gcodegen,
|
||||
);
|
||||
|
||||
# set initial extruder only after custom start G-code
|
||||
print $fh $gcodegen->set_extruder($self->extruders->[0]);
|
||||
|
||||
# do all objects for each layer
|
||||
if ($self->config->complete_objects) {
|
||||
# print objects from the smallest to the tallest to avoid collisions
|
||||
|
@ -931,18 +974,22 @@ sub write_gcode {
|
|||
print $fh $gcodegen->set_fan(0);
|
||||
printf $fh "%s\n", $gcodegen->replace_variables($self->config->end_gcode);
|
||||
|
||||
foreach my $extruder (@{$self->extruders}) {
|
||||
$self->total_used_filament(0);
|
||||
$self->total_extruded_volume(0);
|
||||
foreach my $extruder_id (@{$self->extruders}) {
|
||||
my $extruder = $gcodegen->extruders->{$extruder_id};
|
||||
$self->total_used_filament($self->total_used_filament + $extruder->absolute_E);
|
||||
$self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume);
|
||||
|
||||
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
|
||||
$extruder->absolute_E, $extruder->extruded_volume/1000;
|
||||
}
|
||||
|
||||
if ($self->config->gcode_comments) {
|
||||
# append full config
|
||||
print $fh "\n";
|
||||
foreach my $opt_key (sort @{$self->config->get_keys}) {
|
||||
next if $Slic3r::Config::Options->{$opt_key}{shortcut};
|
||||
printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
|
||||
}
|
||||
# append full config
|
||||
print $fh "\n";
|
||||
foreach my $opt_key (sort @{$self->config->get_keys}) {
|
||||
next if $Slic3r::Config::Options->{$opt_key}{shortcut};
|
||||
printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
|
||||
}
|
||||
|
||||
# close our gcode file
|
||||
|
@ -1012,4 +1059,23 @@ 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('perimeter_extruder', $i);
|
||||
$config->set_ifndef('infill_extruder', $i);
|
||||
$config->set_ifndef('support_material_extruder', $i);
|
||||
$config->set_ifndef('support_material_interface_extruder', $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -2,6 +2,7 @@ package Slic3r::Print::Object;
|
|||
use Moo;
|
||||
|
||||
use List::Util qw(min max sum first);
|
||||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(X Y Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path);
|
||||
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
||||
offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER);
|
||||
|
@ -12,8 +13,7 @@ has 'print' => (is => 'ro', weak_ref => 1, required => 1);
|
|||
has 'model_object' => (is => 'ro', required => 1);
|
||||
has 'region_volumes' => (is => 'rw', default => sub { [] }); # by region_id
|
||||
has 'copies' => (is => 'ro'); # Slic3r::Point objects in scaled G-code coordinates
|
||||
has 'config_overrides' => (is => 'rw', default => sub { Slic3r::Config->new });
|
||||
has 'config' => (is => 'rw');
|
||||
has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
|
||||
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
|
||||
|
||||
has 'size' => (is => 'rw'); # XYZ in scaled coordinates
|
||||
|
@ -27,8 +27,6 @@ has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->n
|
|||
sub BUILD {
|
||||
my $self = shift;
|
||||
|
||||
$self->init_config;
|
||||
|
||||
# translate meshes so that we work with smaller coordinates
|
||||
{
|
||||
# compute the bounding box of the supplied meshes
|
||||
|
@ -96,11 +94,6 @@ sub delete_all_copies {
|
|||
$self->_trigger_copies;
|
||||
}
|
||||
|
||||
sub init_config {
|
||||
my $self = shift;
|
||||
$self->config(Slic3r::Config->merge($self->print->config, $self->config_overrides));
|
||||
}
|
||||
|
||||
sub layer_count {
|
||||
my $self = shift;
|
||||
return scalar @{ $self->layers };
|
||||
|
@ -280,8 +273,8 @@ sub slice {
|
|||
}
|
||||
|
||||
# simplify slices if required
|
||||
if ($self->config->resolution) {
|
||||
$self->_simplify_slices(scale($self->config->resolution));
|
||||
if ($self->print->config->resolution) {
|
||||
$self->_simplify_slices(scale($self->print->config->resolution));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,17 +288,20 @@ sub make_perimeters {
|
|||
# but we don't generate any extra perimeter if fill density is zero, as they would be floating
|
||||
# inside the object - infill_only_where_needed should be the method of choice for printing
|
||||
# hollow objects
|
||||
if ($self->config->extra_perimeters && $self->config->perimeters > 0 && $self->config->fill_density > 0) {
|
||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
||||
my $region = $self->print->regions->[$region_id];
|
||||
my $region_perimeters = $region->config->perimeters;
|
||||
|
||||
if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
|
||||
for my $layer_id (0 .. $self->layer_count-2) {
|
||||
my $layerm = $self->layers->[$layer_id]->regions->[$region_id];
|
||||
my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id];
|
||||
my $perimeter_spacing = $layerm->perimeter_flow->scaled_spacing;
|
||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
||||
|
||||
my $overlap = $perimeter_spacing; # one perimeter
|
||||
|
||||
my $diff = diff(
|
||||
offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($self->config->perimeters * $perimeter_spacing)),
|
||||
offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($region_perimeters * $perimeter_spacing)),
|
||||
offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap),
|
||||
);
|
||||
next if !@$diff;
|
||||
|
@ -329,8 +325,8 @@ sub make_perimeters {
|
|||
# of our slice
|
||||
$extra_perimeters++;
|
||||
my $hypothetical_perimeter = diff(
|
||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters-1))),
|
||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters))),
|
||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters-1))),
|
||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters))),
|
||||
);
|
||||
last CYCLE if !@$hypothetical_perimeter; # no extra perimeter is possible
|
||||
|
||||
|
@ -346,7 +342,7 @@ sub make_perimeters {
|
|||
}
|
||||
|
||||
Slic3r::parallelize(
|
||||
threads => $self->config->threads,
|
||||
threads => $self->print->config->threads,
|
||||
items => sub { 0 .. ($self->layer_count-1) },
|
||||
thread_cb => sub {
|
||||
my $q = shift;
|
||||
|
@ -383,7 +379,7 @@ sub detect_surfaces_type {
|
|||
);
|
||||
|
||||
# collapse very narrow parts (using the safety offset in the diff is not enough)
|
||||
my $offset = $layerm->perimeter_flow->scaled_width / 10;
|
||||
my $offset = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width / 10;
|
||||
return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type),
|
||||
@{ offset2_ex($diff, -$offset, +$offset) };
|
||||
};
|
||||
|
@ -525,13 +521,16 @@ sub clip_fill_surfaces {
|
|||
|
||||
sub bridge_over_infill {
|
||||
my $self = shift;
|
||||
return if $self->config->fill_density == 1;
|
||||
|
||||
for my $layer_id (1..$#{$self->layers}) {
|
||||
my $layer = $self->layers->[$layer_id];
|
||||
my $lower_layer = $self->layers->[$layer_id-1];
|
||||
for my $region_id (0..$#{$self->print->regions}) {
|
||||
my $fill_density = $self->print->regions->[$region_id]->config->fill_density;
|
||||
next if $fill_density == 1 || $fill_density == 0;
|
||||
|
||||
foreach my $layerm (@{$layer->regions}) {
|
||||
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];
|
||||
|
||||
# compute the areas needing bridge math
|
||||
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};
|
||||
|
@ -616,8 +615,8 @@ sub discover_horizontal_shells {
|
|||
for (my $i = 0; $i < $self->layer_count; $i++) {
|
||||
my $layerm = $self->layers->[$i]->regions->[$region_id];
|
||||
|
||||
if ($self->config->solid_infill_every_layers && $self->config->fill_density > 0
|
||||
&& ($i % $self->config->solid_infill_every_layers) == 0) {
|
||||
if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0
|
||||
&& ($i % $layerm->config->solid_infill_every_layers) == 0) {
|
||||
$_->surface_type(S_TYPE_INTERNALSOLID) for @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)};
|
||||
}
|
||||
|
||||
|
@ -639,8 +638,8 @@ sub discover_horizontal_shells {
|
|||
Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom';
|
||||
|
||||
my $solid_layers = ($type == S_TYPE_TOP)
|
||||
? $self->config->top_solid_layers
|
||||
: $self->config->bottom_solid_layers;
|
||||
? $layerm->config->top_solid_layers
|
||||
: $layerm->config->bottom_solid_layers;
|
||||
NEIGHBOR: for (my $n = ($type == S_TYPE_TOP) ? $i-1 : $i+1;
|
||||
abs($n - $i) <= $solid_layers-1;
|
||||
($type == S_TYPE_TOP) ? $n-- : $n++) {
|
||||
|
@ -675,7 +674,7 @@ sub discover_horizontal_shells {
|
|||
# get a triangle in $too_narrow; if we grow it below then the shell
|
||||
# would have a different shape from the external surface and we'd still
|
||||
# have the same angle, so the next shell would be grown even more and so on.
|
||||
my $margin = 3 * $layerm->solid_infill_flow->scaled_width; # require at least this size
|
||||
my $margin = 3 * $layerm->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width; # require at least this size
|
||||
my $too_narrow = diff(
|
||||
$new_internal_solid,
|
||||
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
|
||||
|
@ -684,7 +683,7 @@ sub discover_horizontal_shells {
|
|||
|
||||
# if some parts are going to collapse, use a different strategy according to fill density
|
||||
if (@$too_narrow) {
|
||||
if ($self->config->fill_density > 0) {
|
||||
if ($layerm->config->fill_density > 0) {
|
||||
# if we have internal infill, grow the collapsing parts and add the extra area to
|
||||
# the neighbor layer as well as to our original surfaces so that we support this
|
||||
# additional area in the next shell too
|
||||
|
@ -748,15 +747,18 @@ sub discover_horizontal_shells {
|
|||
# combine fill surfaces across layers
|
||||
sub combine_infill {
|
||||
my $self = shift;
|
||||
return unless $self->config->infill_every_layers > 1 && $self->config->fill_density > 0;
|
||||
my $every = $self->config->infill_every_layers;
|
||||
|
||||
return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions};
|
||||
|
||||
my $layer_count = $self->layer_count;
|
||||
my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1;
|
||||
|
||||
for my $region_id (0 .. ($self->print->regions_count-1)) {
|
||||
my $region = $self->print->regions->[$region_id];
|
||||
my $every = $region->config->infill_every_layers;
|
||||
|
||||
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
|
||||
my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter;
|
||||
my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1);
|
||||
|
||||
# define the combinations
|
||||
my @combine = (); # layer_id => thickness in layers
|
||||
|
@ -808,12 +810,12 @@ sub combine_infill {
|
|||
# so let's remove those areas from all layers
|
||||
|
||||
my @intersection_with_clearance = map @{$_->offset(
|
||||
$layerms[-1]->solid_infill_flow->scaled_width / 2
|
||||
+ $layerms[-1]->perimeter_flow->scaled_width / 2
|
||||
$layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width / 2
|
||||
+ $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2
|
||||
# Because fill areas for rectilinear and honeycomb are grown
|
||||
# later to overlap perimeters, we need to counteract that too.
|
||||
+ (($type == S_TYPE_INTERNALSOLID || $self->config->fill_pattern =~ /(rectilinear|honeycomb)/)
|
||||
? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
|
||||
+ (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/)
|
||||
? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
|
||||
: 0)
|
||||
)}, @$intersection;
|
||||
|
||||
|
@ -861,9 +863,23 @@ 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 $first_layer_flow = Slic3r::Flow->new_from_width(
|
||||
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
||||
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
||||
nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
|
||||
// $self->print->config->nozzle_diameter->[0],
|
||||
layer_height => $self->config->get_abs_value('first_layer_height'),
|
||||
bridge_flow_ratio => 0,
|
||||
);
|
||||
|
||||
my $s = Slic3r::Print::SupportMaterial->new(
|
||||
print_config => $self->print->config,
|
||||
object_config => $self->config,
|
||||
first_layer_flow => $first_layer_flow,
|
||||
flow => $self->support_material_flow,
|
||||
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
|
||||
);
|
||||
$s->generate($self);
|
||||
}
|
||||
|
||||
sub _simplify_slices {
|
||||
|
@ -875,4 +891,23 @@ sub _simplify_slices {
|
|||
}
|
||||
}
|
||||
|
||||
sub support_material_flow {
|
||||
my ($self, $role) = @_;
|
||||
|
||||
$role //= FLOW_ROLE_SUPPORT_MATERIAL;
|
||||
my $extruder = ($role == FLOW_ROLE_SUPPORT_MATERIAL)
|
||||
? $self->config->support_material_extruder
|
||||
: $self->config->support_material_interface_extruder;
|
||||
|
||||
# we use a bogus layer_height because we use the same flow for all
|
||||
# support material layers
|
||||
return Slic3r::Flow->new_from_width(
|
||||
width => $self->config->support_material_extrusion_width,
|
||||
role => $role,
|
||||
nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1] // $self->print->config->nozzle_diameter->[0],
|
||||
layer_height => $self->config->layer_height,
|
||||
bridge_flow_ratio => 0,
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -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', default => sub { Slic3r::Config::PrintRegion->new});
|
||||
|
||||
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 && $self->print->config->first_layer_extrusion_width) {
|
||||
$config_width = $self->print->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) {
|
||||
$extruder = $self->config->perimeter_extruder;
|
||||
} elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) {
|
||||
$extruder = $self->config->infill_extruder;
|
||||
} else {
|
||||
die "Unknown role $role";
|
||||
}
|
||||
my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $extruder-1);
|
||||
|
||||
return Slic3r::Flow->new_from_width(
|
||||
width => $config_width,
|
||||
role => $role,
|
||||
nozzle_diameter => $nozzle_diameter,
|
||||
layer_height => $layer_height,
|
||||
bridge_flow_ratio => ($bridge ? $self->print->config->bridge_flow_ratio : 0),
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
112
lib/Slic3r/Print/Simple.pm
Normal file
112
lib/Slic3r/Print/Simple.pm
Normal file
|
@ -0,0 +1,112 @@
|
|||
package Slic3r::Print::Simple;
|
||||
use Moo;
|
||||
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
|
||||
has '_print' => (
|
||||
is => 'ro',
|
||||
default => sub { Slic3r::Print->new },
|
||||
handles => [qw(apply_config extruders expanded_output_filepath
|
||||
total_used_filament total_extruded_volume)],
|
||||
);
|
||||
|
||||
has 'duplicate' => (
|
||||
is => 'rw',
|
||||
default => sub { 1 },
|
||||
);
|
||||
|
||||
has 'scale' => (
|
||||
is => 'rw',
|
||||
default => sub { 1 },
|
||||
);
|
||||
|
||||
has 'rotate' => (
|
||||
is => 'rw',
|
||||
default => sub { 0 },
|
||||
);
|
||||
|
||||
has 'duplicate_grid' => (
|
||||
is => 'rw',
|
||||
default => sub { [1,1] },
|
||||
);
|
||||
|
||||
has 'status_cb' => (
|
||||
is => 'rw',
|
||||
default => sub { sub {} },
|
||||
);
|
||||
|
||||
has 'output_file' => (
|
||||
is => 'rw',
|
||||
);
|
||||
|
||||
sub set_model {
|
||||
my ($self, $model) = @_;
|
||||
|
||||
# make method idempotent so that the object is reusable
|
||||
$self->_print->delete_all_objects;
|
||||
|
||||
my $need_arrange = $model->has_objects_with_no_instances;
|
||||
if ($need_arrange) {
|
||||
# apply a default position to all objects not having one
|
||||
foreach my $object (@{$model->objects}) {
|
||||
$object->add_instance(offset => [0,0]) if !defined $object->instances;
|
||||
}
|
||||
}
|
||||
|
||||
# apply scaling and rotation supplied from command line if any
|
||||
foreach my $instance (map @{$_->instances}, @{$model->objects}) {
|
||||
$instance->scaling_factor($instance->scaling_factor * $self->scale);
|
||||
$instance->rotation($instance->rotation + $self->rotate);
|
||||
}
|
||||
|
||||
if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) {
|
||||
$model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance);
|
||||
} elsif ($need_arrange) {
|
||||
$model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance);
|
||||
} elsif ($self->duplicate > 1) {
|
||||
# if all input objects have defined position(s) apply duplication to the whole model
|
||||
$model->duplicate($self->duplicate, $self->_print->config->min_object_distance);
|
||||
}
|
||||
$model->center_instances_around_point($self->_print->config->print_center);
|
||||
|
||||
foreach my $model_object (@{$model->objects}) {
|
||||
$self->_print->auto_assign_extruders($model_object);
|
||||
$self->_print->add_model_object($model_object);
|
||||
}
|
||||
}
|
||||
|
||||
sub _before_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->status_cb($self->status_cb);
|
||||
$self->_print->validate;
|
||||
}
|
||||
|
||||
sub _after_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->status_cb(undef);
|
||||
}
|
||||
|
||||
sub export_gcode {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
|
||||
$self->_print->process;
|
||||
$self->_print->export_gcode(output_file => $self->output_file);
|
||||
|
||||
$self->_after_export;
|
||||
}
|
||||
|
||||
sub export_svg {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
|
||||
$self->_print->export_svg(output_file => $self->output_file);
|
||||
|
||||
$self->_after_export;
|
||||
}
|
||||
|
||||
1;
|
|
@ -3,13 +3,17 @@ use Moo;
|
|||
|
||||
use List::Util qw(sum min max);
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad);
|
||||
use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
|
||||
intersection_pl);
|
||||
use Slic3r::Surface ':types';
|
||||
|
||||
has 'config' => (is => 'rw', required => 1);
|
||||
has 'flow' => (is => 'rw', required => 1);
|
||||
has 'print_config' => (is => 'rw', required => 1);
|
||||
has 'object_config' => (is => 'rw', required => 1);
|
||||
has 'flow' => (is => 'rw', required => 1);
|
||||
has 'first_layer_flow' => (is => 'rw', required => 1);
|
||||
has 'interface_flow' => (is => 'rw', required => 1);
|
||||
|
||||
use constant DEBUG_CONTACT_ONLY => 0;
|
||||
|
||||
|
@ -73,8 +77,8 @@ sub contact_area {
|
|||
|
||||
# if user specified a custom angle threshold, convert it to radians
|
||||
my $threshold_rad;
|
||||
if ($self->config->support_material_threshold) {
|
||||
$threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive
|
||||
if ($self->object_config->support_material_threshold) {
|
||||
$threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive
|
||||
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
|
||||
}
|
||||
|
||||
|
@ -86,9 +90,9 @@ sub contact_area {
|
|||
# so $layer_id == 0 means first object layer
|
||||
# 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;
|
||||
} elsif (!$self->config->support_material) {
|
||||
} elsif (!$self->object_config->support_material) {
|
||||
# if we are only going to generate raft just check
|
||||
# the 'overhangs' of the first object layer
|
||||
last if $layer_id > 0;
|
||||
|
@ -105,13 +109,13 @@ sub contact_area {
|
|||
} else {
|
||||
my $lower_layer = $object->layers->[$layer_id-1];
|
||||
foreach my $layerm (@{$layer->regions}) {
|
||||
my $fw = $layerm->perimeter_flow->scaled_width;
|
||||
my $fw = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width;
|
||||
my $diff;
|
||||
|
||||
# If a threshold angle was specified, use a different logic for detecting overhangs.
|
||||
if (defined $threshold_rad
|
||||
|| $layer_id < $self->config->support_material_enforce_layers
|
||||
|| $self->config->raft_layers > 0) {
|
||||
|| $layer_id < $self->object_config->support_material_enforce_layers
|
||||
|| $self->object_config->raft_layers > 0) {
|
||||
my $d = defined $threshold_rad
|
||||
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
|
||||
: 0;
|
||||
|
@ -170,8 +174,8 @@ sub contact_area {
|
|||
# now apply the contact areas to the layer were they need to be made
|
||||
{
|
||||
# get the average nozzle diameter used on this layer
|
||||
my @nozzle_diameters = map $_->nozzle_diameter,
|
||||
map { $_->perimeter_flow, $_->solid_infill_flow }
|
||||
my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_),
|
||||
map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 }
|
||||
@{$layer->regions};
|
||||
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
|
||||
|
||||
|
@ -179,7 +183,7 @@ sub contact_area {
|
|||
###$contact_z = $layer->print_z - $layer->height;
|
||||
|
||||
# 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 ];
|
||||
$overhang{$contact_z} = [ @overhang ];
|
||||
|
@ -242,25 +246,25 @@ sub support_layers_z {
|
|||
# determine layer height for any non-contact layer
|
||||
# we use max() to prevent many ultra-thin layers to be inserted in case
|
||||
# layer_height > nozzle_diameter * 0.75
|
||||
my $nozzle_diameter = $self->flow->nozzle_diameter;
|
||||
my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1);
|
||||
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);
|
||||
|
||||
# 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;
|
||||
unshift @z, $first_layer_height;
|
||||
|
||||
# add raft layers by dividing the space between first layer and
|
||||
# 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)
|
||||
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,
|
||||
map { int($_*100)/100 }
|
||||
map { $z[0] + $height * $_ }
|
||||
0..($self->config->raft_layers - 1);
|
||||
0..($self->object_config->raft_layers - 1);
|
||||
}
|
||||
|
||||
for (my $i = $#z; $i >= 0; $i--) {
|
||||
|
@ -291,7 +295,7 @@ sub generate_interface_layers {
|
|||
|
||||
# let's now generate interface layers below contact areas
|
||||
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) {
|
||||
my $z = $support_z->[$layer_id];
|
||||
my $this = $contact->{$z} // next;
|
||||
|
@ -339,7 +343,7 @@ sub generate_base_layers {
|
|||
# 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)
|
||||
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]} || [] };
|
||||
}
|
||||
|
||||
|
@ -383,11 +387,12 @@ sub clip_with_object {
|
|||
sub generate_toolpaths {
|
||||
my ($self, $object, $overhang, $contact, $interface, $base) = @_;
|
||||
|
||||
my $flow = $self->flow;
|
||||
my $flow = $self->flow;
|
||||
my $interface_flow = $self->interface_flow;
|
||||
|
||||
# shape of contact area
|
||||
my $contact_loops = 1;
|
||||
my $circle_radius = 1.5 * $flow->scaled_width;
|
||||
my $circle_radius = 1.5 * $interface_flow->scaled_width;
|
||||
my $circle_distance = 3 * $circle_radius;
|
||||
my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ],
|
||||
(5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
|
||||
|
@ -395,8 +400,8 @@ sub generate_toolpaths {
|
|||
Slic3r::debugf "Generating patterns\n";
|
||||
|
||||
# prepare fillers
|
||||
my $pattern = $self->config->support_material_pattern;
|
||||
my @angles = ($self->config->support_material_angle);
|
||||
my $pattern = $self->object_config->support_material_pattern;
|
||||
my @angles = ($self->object_config->support_material_angle);
|
||||
if ($pattern eq 'rectilinear-grid') {
|
||||
$pattern = 'rectilinear';
|
||||
push @angles, $angles[0] + 90;
|
||||
|
@ -407,10 +412,10 @@ sub generate_toolpaths {
|
|||
support => $object->fill_maker->filler($pattern),
|
||||
);
|
||||
|
||||
my $interface_angle = $self->config->support_material_angle + 90;
|
||||
my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing;
|
||||
my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing;
|
||||
my $support_spacing = $self->config->support_material_spacing + $flow->spacing;
|
||||
my $interface_angle = $self->object_config->support_material_angle + 90;
|
||||
my $interface_spacing = $self->object_config->support_material_interface_spacing + $interface_flow->spacing;
|
||||
my $interface_density = $interface_spacing == 0 ? 1 : $interface_flow->spacing / $interface_spacing;
|
||||
my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing;
|
||||
my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing;
|
||||
|
||||
my $process_layer = sub {
|
||||
|
@ -441,7 +446,7 @@ sub generate_toolpaths {
|
|||
|
||||
# contact
|
||||
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
|
||||
# exactly as a generic base layer
|
||||
push @$base, @$contact;
|
||||
|
@ -450,11 +455,11 @@ sub generate_toolpaths {
|
|||
my @loops0 = ();
|
||||
{
|
||||
# find centerline of the external loop of the contours
|
||||
my @external_loops = @{offset($contact, -$flow->scaled_width/2)};
|
||||
my @external_loops = @{offset($contact, -$interface_flow->scaled_width/2)};
|
||||
|
||||
# only consider the loops facing the overhang
|
||||
{
|
||||
my $overhang_with_margin = offset($overhang, +$flow->scaled_width/2);
|
||||
my $overhang_with_margin = offset($overhang, +$interface_flow->scaled_width/2);
|
||||
@external_loops = grep {
|
||||
@{intersection_pl(
|
||||
[ $_->split_at_first_point ],
|
||||
|
@ -474,8 +479,8 @@ sub generate_toolpaths {
|
|||
# make more loops
|
||||
my @loops = @loops0;
|
||||
for my $i (2..$contact_loops) {
|
||||
my $d = ($i-1) * $flow->scaled_spacing;
|
||||
push @loops, @{offset2(\@loops0, -$d -0.5*$flow->scaled_spacing, +0.5*$flow->scaled_spacing)};
|
||||
my $d = ($i-1) * $interface_flow->scaled_spacing;
|
||||
push @loops, @{offset2(\@loops0, -$d -0.5*$interface_flow->scaled_spacing, +0.5*$interface_flow->scaled_spacing)};
|
||||
}
|
||||
|
||||
# clip such loops to the side oriented towards the object
|
||||
|
@ -495,10 +500,11 @@ sub generate_toolpaths {
|
|||
);
|
||||
|
||||
# transform loops into ExtrusionPath objects
|
||||
my $mm3_per_mm = $interface_flow->mm3_per_mm($layer->height);
|
||||
@loops = map Slic3r::ExtrusionPath->new(
|
||||
polyline => $_,
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
flow_spacing => $flow->spacing,
|
||||
polyline => $_,
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), @loops;
|
||||
|
||||
$layer->support_interface_fills->append(@loops);
|
||||
|
@ -531,16 +537,16 @@ sub generate_toolpaths {
|
|||
foreach my $expolygon (@{union_ex($interface)}) {
|
||||
my ($params, @p) = $fillers{interface}->fill_surface(
|
||||
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
|
||||
density => $interface_density,
|
||||
flow_spacing => $flow->spacing,
|
||||
complete => 1,
|
||||
density => $interface_density,
|
||||
flow => $interface_flow,
|
||||
complete => 1,
|
||||
);
|
||||
my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height);
|
||||
|
||||
push @paths, map Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polyline->new(@$_),
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
height => undef,
|
||||
flow_spacing => $params->{flow_spacing},
|
||||
polyline => Slic3r::Polyline->new(@$_),
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), @p;
|
||||
}
|
||||
|
||||
|
@ -551,8 +557,8 @@ sub generate_toolpaths {
|
|||
if (@$base) {
|
||||
my $filler = $fillers{support};
|
||||
$filler->angle($angles[ ($layer_id) % @angles ]);
|
||||
my $density = $support_density;
|
||||
my $flow_spacing = $flow->spacing;
|
||||
my $density = $support_density;
|
||||
my $base_flow = $flow;
|
||||
|
||||
# TODO: use offset2_ex()
|
||||
my $to_infill = union_ex($base, 1);
|
||||
|
@ -561,17 +567,17 @@ sub generate_toolpaths {
|
|||
# base flange
|
||||
if ($layer_id == 0) {
|
||||
$filler = $fillers{interface};
|
||||
$filler->angle($self->config->support_material_angle + 90);
|
||||
$filler->angle($self->object_config->support_material_angle + 90);
|
||||
$density = 0.5;
|
||||
$flow_spacing = $object->print->first_layer_support_material_flow->spacing;
|
||||
$base_flow = $self->first_layer_flow;
|
||||
} else {
|
||||
# draw a perimeter all around support infill
|
||||
# TODO: use brim ordering algorithm
|
||||
my $mm3_per_mm = $flow->mm3_per_mm($layer->height);
|
||||
push @paths, map Slic3r::ExtrusionPath->new(
|
||||
polyline => $_->split_at_first_point,
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
height => undef,
|
||||
flow_spacing => $flow->spacing,
|
||||
polyline => $_->split_at_first_point,
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), map @$_, @$to_infill;
|
||||
|
||||
# TODO: use offset2_ex()
|
||||
|
@ -581,16 +587,16 @@ sub generate_toolpaths {
|
|||
foreach my $expolygon (@$to_infill) {
|
||||
my ($params, @p) = $filler->fill_surface(
|
||||
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
|
||||
density => $density,
|
||||
flow_spacing => $flow_spacing,
|
||||
complete => 1,
|
||||
density => $density,
|
||||
flow => $base_flow,
|
||||
complete => 1,
|
||||
);
|
||||
my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height);
|
||||
|
||||
push @paths, map Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polyline->new(@$_),
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
height => undef,
|
||||
flow_spacing => $params->{flow_spacing},
|
||||
polyline => Slic3r::Polyline->new(@$_),
|
||||
role => EXTR_ROLE_SUPPORTMATERIAL,
|
||||
mm3_per_mm => $mm3_per_mm,
|
||||
), @p;
|
||||
}
|
||||
|
||||
|
@ -609,7 +615,7 @@ sub generate_toolpaths {
|
|||
};
|
||||
|
||||
Slic3r::parallelize(
|
||||
threads => $self->config->threads,
|
||||
threads => $self->print_config->threads,
|
||||
items => [ 0 .. $#{$object->support_layers} ],
|
||||
thread_cb => sub {
|
||||
my $q = shift;
|
||||
|
|
|
@ -99,20 +99,21 @@ sub model {
|
|||
sub init_print {
|
||||
my ($model_name, %params) = @_;
|
||||
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
my $config = Slic3r::Config->new;
|
||||
$config->apply($params{config}) if $params{config};
|
||||
$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';
|
||||
for my $model (map model($_, %params), @$model_name) {
|
||||
die "Unknown model in test" if !defined $model;
|
||||
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->center_instances_around_point($config->print_center);
|
||||
$model->arrange_objects($print->config->min_object_distance);
|
||||
$model->center_instances_around_point($print->config->print_center);
|
||||
$print->add_model_object($_) for @{$model->objects};
|
||||
}
|
||||
$print->validate;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue