Support material
This commit is contained in:
parent
e0e2023a5a
commit
7b67fae686
12 changed files with 135 additions and 17 deletions
|
@ -37,6 +37,7 @@ Slic3r current key features are:
|
||||||
* multiple solid layers near horizontal external surfaces;
|
* multiple solid layers near horizontal external surfaces;
|
||||||
* ability to scale, rotate and duplicate input object;
|
* ability to scale, rotate and duplicate input object;
|
||||||
* customizable initial and final GCODE;
|
* customizable initial and final GCODE;
|
||||||
|
* support material;
|
||||||
* use different speed for bottom layer and perimeters.
|
* use different speed for bottom layer and perimeters.
|
||||||
|
|
||||||
Experimental features include:
|
Experimental features include:
|
||||||
|
@ -150,6 +151,7 @@ The author is Alessandro Ranellucci (me).
|
||||||
--end-gcode Load final gcode from the supplied file. This will overwrite
|
--end-gcode Load final gcode from the supplied file. This will overwrite
|
||||||
the default commands (turn off temperature [M104 S0],
|
the default commands (turn off temperature [M104 S0],
|
||||||
home X axis [G28 X], disable motors [M84]).
|
home X axis [G28 X], disable motors [M84]).
|
||||||
|
--support-material Generate support material for overhangs
|
||||||
|
|
||||||
Retraction options:
|
Retraction options:
|
||||||
--retract-length Length of retraction in mm when pausing extrusion
|
--retract-length Length of retraction in mm when pausing extrusion
|
||||||
|
|
|
@ -95,6 +95,7 @@ our $fill_pattern = 'rectilinear';
|
||||||
our $solid_fill_pattern = 'rectilinear';
|
our $solid_fill_pattern = 'rectilinear';
|
||||||
our $fill_density = 0.4; # 1 = 100%
|
our $fill_density = 0.4; # 1 = 100%
|
||||||
our $fill_angle = 45;
|
our $fill_angle = 45;
|
||||||
|
our $support_material = 0;
|
||||||
our $start_gcode = "G28 ; home all axes";
|
our $start_gcode = "G28 ; home all axes";
|
||||||
our $end_gcode = <<"END";
|
our $end_gcode = <<"END";
|
||||||
M104 S0 ; turn off temperature
|
M104 S0 ; turn off temperature
|
||||||
|
|
|
@ -215,6 +215,11 @@ our $Options = {
|
||||||
cli => 'fill-angle=i',
|
cli => 'fill-angle=i',
|
||||||
type => 'i',
|
type => 'i',
|
||||||
},
|
},
|
||||||
|
'support_material' => {
|
||||||
|
label => 'Generate support material',
|
||||||
|
cli => 'support-material',
|
||||||
|
type => 'bool',
|
||||||
|
},
|
||||||
'start_gcode' => {
|
'start_gcode' => {
|
||||||
label => 'Start GCODE',
|
label => 'Start GCODE',
|
||||||
cli => 'start-gcode=s',
|
cli => 'start-gcode=s',
|
||||||
|
|
|
@ -149,7 +149,7 @@ sub extrude_path {
|
||||||
|
|
||||||
# extrude arc or line
|
# extrude arc or line
|
||||||
$self->print_feed_rate(
|
$self->print_feed_rate(
|
||||||
$path->role =~ /^(perimeter|skirt)$/o ? $self->perimeter_speed
|
$path->role =~ /^(perimeter|skirt|support-material)$/o ? $self->perimeter_speed
|
||||||
: $path->role eq 'small-perimeter' ? $self->small_perimeter_speed
|
: $path->role eq 'small-perimeter' ? $self->small_perimeter_speed
|
||||||
: $path->role eq 'fill' ? $self->infill_speed
|
: $path->role eq 'fill' ? $self->infill_speed
|
||||||
: $path->role eq 'solid-fill' ? $self->solid_infill_speed
|
: $path->role eq 'solid-fill' ? $self->solid_infill_speed
|
||||||
|
|
|
@ -44,4 +44,9 @@ sub split_at {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub split_at_first_point {
|
||||||
|
my $self = shift;
|
||||||
|
return $self->split_at($self->polygon->[0]);
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -17,7 +17,7 @@ has 'depth_layers' => (is => 'ro', default => sub {1});
|
||||||
|
|
||||||
has 'flow_spacing' => (is => 'rw');
|
has 'flow_spacing' => (is => 'rw');
|
||||||
|
|
||||||
# perimeter/fill/solid-fill/bridge/skirt
|
# perimeter/fill/solid-fill/bridge/skirt/support-material
|
||||||
has 'role' => (is => 'rw', required => 1);
|
has 'role' => (is => 'rw', required => 1);
|
||||||
|
|
||||||
sub BUILD {
|
sub BUILD {
|
||||||
|
@ -45,6 +45,22 @@ sub clip_end {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub clip_with_expolygon {
|
||||||
|
my $self = shift;
|
||||||
|
my ($expolygon) = @_;
|
||||||
|
|
||||||
|
my @paths = ();
|
||||||
|
foreach my $polyline ($self->polyline->clip_with_expolygon($expolygon)) {
|
||||||
|
push @paths, (ref $self)->new(
|
||||||
|
polyline => $polyline,
|
||||||
|
depth_layers => $self->depth_layers,
|
||||||
|
flow_spacing => $self->flow_spacing,
|
||||||
|
role => $self->role,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return @paths;
|
||||||
|
}
|
||||||
|
|
||||||
sub points {
|
sub points {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->polyline;
|
return $self->polyline;
|
||||||
|
|
|
@ -34,17 +34,16 @@ sub BUILD {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
$self->fillers->{$_} ||= $FillTypes{$_}->new(print => $self->print)
|
$self->fillers->{$_} ||= $FillTypes{$_}->new(print => $self->print)
|
||||||
for ('rectilinear', $Slic3r::fill_pattern, $Slic3r::solid_fill_pattern);
|
for ('rectilinear', $Slic3r::fill_pattern, $Slic3r::solid_fill_pattern);
|
||||||
|
|
||||||
|
my $max_print_dimension = $self->print->max_length * sqrt(2);
|
||||||
|
$_->max_print_dimension($max_print_dimension) for values %{$self->fillers};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub make_fill {
|
sub make_fill {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($layer) = @_;
|
my ($layer) = @_;
|
||||||
|
|
||||||
my $max_print_dimension = $self->print->max_length * sqrt(2);
|
$_->layer($layer) for values %{$self->fillers};
|
||||||
for (values %{$self->fillers}) {
|
|
||||||
$_->layer($layer);
|
|
||||||
$_->max_print_dimension($max_print_dimension);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf "Filling layer %d:\n", $layer->id;
|
printf "Filling layer %d:\n", $layer->id;
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,15 @@ sub infill_direction {
|
||||||
$rotate[1] = [ $self->max_print_dimension * sqrt(2) / 2, $self->max_print_dimension * sqrt(2) / 2 ];
|
$rotate[1] = [ $self->max_print_dimension * sqrt(2) / 2, $self->max_print_dimension * sqrt(2) / 2 ];
|
||||||
@shift = @{$rotate[1]};
|
@shift = @{$rotate[1]};
|
||||||
|
|
||||||
# alternate fill direction
|
if ($self->layer) {
|
||||||
if (($self->layer->id / $surface->depth_layers) % 2) {
|
# alternate fill direction
|
||||||
$rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2;
|
if (($self->layer->id / $surface->depth_layers) % 2) {
|
||||||
|
$rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# use bridge angle
|
# use bridge angle
|
||||||
if ($surface->surface_type eq 'bottom' && $self->layer->id > 0 && defined $surface->bridge_angle) {
|
if (defined $surface->bridge_angle) {
|
||||||
Slic3r::debugf "Filling bridge with angle %d\n", $surface->bridge_angle;
|
Slic3r::debugf "Filling bridge with angle %d\n", $surface->bridge_angle;
|
||||||
$rotate[0] = Slic3r::Geometry::deg2rad($surface->bridge_angle);
|
$rotate[0] = Slic3r::Geometry::deg2rad($surface->bridge_angle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,10 +66,16 @@ has 'skirts' => (
|
||||||
default => sub { [] },
|
default => sub { [] },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# ordered collection of extrusion paths to fill surfaces for support material
|
||||||
|
has 'support_fills' => (
|
||||||
|
is => 'rw',
|
||||||
|
#isa => 'Slic3r::ExtrusionPath::Collection',
|
||||||
|
);
|
||||||
|
|
||||||
# ordered collection of extrusion paths to fill surfaces
|
# ordered collection of extrusion paths to fill surfaces
|
||||||
has 'fills' => (
|
has 'fills' => (
|
||||||
is => 'rw',
|
is => 'rw',
|
||||||
#isa => 'ArrayRef[Slic3r::ExtrusionPath]',
|
#isa => 'ArrayRef[Slic3r::ExtrusionPath::Collection]',
|
||||||
default => sub { [] },
|
default => sub { [] },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -342,6 +342,7 @@ sub extrude_skirt {
|
||||||
my @points = (
|
my @points = (
|
||||||
(map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers),
|
(map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers),
|
||||||
(map @$_, map @{$_->thin_walls}, @layers),
|
(map @$_, map @{$_->thin_walls}, @layers),
|
||||||
|
(map @{$_->polyline}, map @{$_->support_fills->paths}, grep $_->support_fills, @layers),
|
||||||
);
|
);
|
||||||
return if !@points;
|
return if !@points;
|
||||||
|
|
||||||
|
@ -451,6 +452,74 @@ sub infill_every_layers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub generate_support_material {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
# generate paths for the pattern that we're going to use
|
||||||
|
my $support_pattern = [];
|
||||||
|
{
|
||||||
|
# get all surfaces needing support material
|
||||||
|
my @surfaces = grep $_->surface_type eq 'bottom' && !defined $_->bridge_angle,
|
||||||
|
map @{$_->slices}, grep $_->id > 0, @{$self->layers} or return;
|
||||||
|
|
||||||
|
my @support_material_areas = @{union_ex([ map $_->p, @surfaces ])};
|
||||||
|
|
||||||
|
for (1..$Slic3r::perimeters+1) {
|
||||||
|
foreach my $expolygon (@support_material_areas) {
|
||||||
|
push @$support_pattern,
|
||||||
|
map Slic3r::ExtrusionLoop->new(
|
||||||
|
polygon => $_,
|
||||||
|
role => 'support-material',
|
||||||
|
)->split_at_first_point, @$expolygon;
|
||||||
|
}
|
||||||
|
@support_material_areas = map $_->offset_ex(- scale $Slic3r::flow_spacing),
|
||||||
|
@support_material_areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $fill = Slic3r::Fill->new(print => $self);
|
||||||
|
foreach my $expolygon (@support_material_areas) {
|
||||||
|
my @paths = $fill->fillers->{rectilinear}->fill_surface(
|
||||||
|
Slic3r::Surface->new(
|
||||||
|
expolygon => $expolygon,
|
||||||
|
bridge_angle => $Slic3r::fill_angle + 45,
|
||||||
|
),
|
||||||
|
density => 0.15,
|
||||||
|
flow_spacing => $Slic3r::flow_spacing,
|
||||||
|
);
|
||||||
|
my $params = shift @paths;
|
||||||
|
|
||||||
|
push @$support_pattern,
|
||||||
|
map Slic3r::ExtrusionPath->new(
|
||||||
|
polyline => Slic3r::Polyline->new(@$_),
|
||||||
|
role => 'support-material',
|
||||||
|
depth_layers => 1,
|
||||||
|
flow_spacing => $params->{flow_spacing},
|
||||||
|
), @paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# now apply the pattern to layers below unsupported surfaces
|
||||||
|
my (@a, @b) = ();
|
||||||
|
for (my $i = $#{$self->layers}; $i >=0; $i--) {
|
||||||
|
my $layer = $self->layers->[$i];
|
||||||
|
my @c = ();
|
||||||
|
if (@b) {
|
||||||
|
@c = @{diff_ex(
|
||||||
|
[ map @$_, @b ],
|
||||||
|
[ map @$_, map $_->expolygon->offset_ex(scale $Slic3r::flow_width), @{$layer->slices} ],
|
||||||
|
)};
|
||||||
|
$layer->support_fills(Slic3r::ExtrusionPath::Collection->new);
|
||||||
|
foreach my $expolygon (@c) {
|
||||||
|
push @{$layer->support_fills->paths}, map $_->clip_with_expolygon($expolygon), @$support_pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@b = @{union_ex([ map @$_, @c, @a ])};
|
||||||
|
@a = map $_->expolygon->offset_ex(scale 2),
|
||||||
|
grep $_->surface_type eq 'bottom' && !defined $_->bridge_angle,
|
||||||
|
@{$layer->slices};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub export_gcode {
|
sub export_gcode {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
|
@ -522,6 +591,12 @@ sub export_gcode {
|
||||||
print $fh $extruder->extrude_path($_, 'fill')
|
print $fh $extruder->extrude_path($_, 'fill')
|
||||||
for $fill->shortest_path($extruder->last_pos);
|
for $fill->shortest_path($extruder->last_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# extrude support material
|
||||||
|
if ($layer->support_fills) {
|
||||||
|
print $fh $extruder->extrude_path($_, 'support material')
|
||||||
|
for $layer->support_fills->shortest_path($extruder->last_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,6 @@ sub go {
|
||||||
$print = Slic3r::Print->new_from_mesh($mesh);
|
$print = Slic3r::Print->new_from_mesh($mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
# make skirt
|
|
||||||
$self->status_cb->(15, "Generating skirt...");
|
|
||||||
$print->extrude_skirt;
|
|
||||||
|
|
||||||
# make perimeters
|
# make perimeters
|
||||||
# this will add a set of extrusion loops to each layer
|
# this will add a set of extrusion loops to each layer
|
||||||
# as well as generate infill boundaries
|
# as well as generate infill boundaries
|
||||||
|
@ -118,6 +114,16 @@ sub go {
|
||||||
# free memory
|
# free memory
|
||||||
@{$_->fill_surfaces} = () for @{$print->layers};
|
@{$_->fill_surfaces} = () for @{$print->layers};
|
||||||
|
|
||||||
|
# generate support material
|
||||||
|
if ($Slic3r::support_material) {
|
||||||
|
$self->status_cb->(85, "Generating support material...");
|
||||||
|
$print->generate_support_material;
|
||||||
|
}
|
||||||
|
|
||||||
|
# make skirt
|
||||||
|
$self->status_cb->(88, "Generating skirt...");
|
||||||
|
$print->extrude_skirt;
|
||||||
|
|
||||||
# output everything to a GCODE file
|
# output everything to a GCODE file
|
||||||
$self->status_cb->(90, "Exporting GCODE...");
|
$self->status_cb->(90, "Exporting GCODE...");
|
||||||
$print->export_gcode($self->expanded_output_filepath);
|
$print->export_gcode($self->expanded_output_filepath);
|
||||||
|
|
|
@ -167,6 +167,7 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
|
||||||
--end-gcode Load final gcode from the supplied file. This will overwrite
|
--end-gcode Load final gcode from the supplied file. This will overwrite
|
||||||
the default commands (turn off temperature [M104 S0],
|
the default commands (turn off temperature [M104 S0],
|
||||||
home X axis [G28 X], disable motors [M84]).
|
home X axis [G28 X], disable motors [M84]).
|
||||||
|
--support-material Generate support material for overhangs
|
||||||
|
|
||||||
Retraction options:
|
Retraction options:
|
||||||
--retract-length Length of retraction in mm when pausing extrusion
|
--retract-length Length of retraction in mm when pausing extrusion
|
||||||
|
|
Loading…
Reference in a new issue