Merge branch 'master' into xsdata

Conflicts:
	lib/Slic3r/Layer/Region.pm
	lib/Slic3r/Print.pm
	lib/Slic3r/Print/Object.pm
This commit is contained in:
Alessandro Ranellucci 2013-08-16 09:09:56 +02:00
commit fa07c512b4
18 changed files with 262 additions and 191 deletions

View file

@ -172,6 +172,9 @@ The author of the Silk icon set is Mark James.
--bridge-acceleration
Overrides firmware's default acceleration for bridges. (mm/s^2, set zero
to disable; default: 0)
--first-layer-acceleration
Overrides firmware's default acceleration for first layer. (mm/s^2, set zero
to disable; default: 0)
--default-acceleration
Acceleration will be reset to this value after the specific settings above
have been applied. (mm/s^2, set zero to disable; default: 130)

View file

@ -79,6 +79,7 @@ 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 INFILL_OVERLAP_OVER_SPACING => 0.45;
use constant EXTERNAL_INFILL_MARGIN => 3;
our $Config;

View file

@ -369,6 +369,14 @@ our $Options = {
type => 'f',
default => 0,
},
'first_layer_acceleration' => {
label => 'First layer',
tooltip => 'This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer.',
sidetext => 'mm/s²',
cli => 'first-layer-acceleration=f',
type => 'f',
default => 0,
},
# accuracy options
'layer_height' => {
@ -1377,6 +1385,11 @@ sub validate {
die "Invalid value for --extrusion-multiplier\n"
if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
# --default-acceleration
die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
&& !$self->default_acceleration;
# general validation, quick and dirty
foreach my $opt_key (keys %$Options) {
my $opt = $Options->{$opt_key};

View file

@ -127,6 +127,14 @@ sub make_fill {
# add spacing between surfaces
@surfaces = map @{$_->offset(-$distance_between_surfaces / 2 * &Slic3r::INFILL_OVERLAP_OVER_SPACING)}, @surfaces;
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
);
}
my @fills = ();
my @fills_ordering_points = ();
SURFACE: foreach my $surface (@surfaces) {

View file

@ -32,9 +32,8 @@ sub fill_surface {
$flow_spacing = unscale $line_spacing;
} else {
# extend bounding box so that our pattern will be aligned with other layers
# $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
$bounding_box->extents->[X][MIN] -= $bounding_box->x_min;
$bounding_box->extents->[Y][MIN] -= $bounding_box->y_min;
$bounding_box->extents->[X][MIN] -= $bounding_box->x_min % $line_spacing;
$bounding_box->extents->[Y][MIN] -= $bounding_box->y_min % $line_spacing;
}
# generate the basic pattern

View file

@ -14,8 +14,9 @@ has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled
has 'layer_count' => (is => 'ro', required => 1 );
has 'layer' => (is => 'rw');
has '_layer_islands' => (is => 'rw');
has '_upper_layer_islands' => (is => 'rw');
has '_layer_overhangs' => (is => 'rw');
has 'move_z_callback' => (is => 'rw');
has 'shift_x' => (is => 'rw', default => sub {0} );
has 'shift_y' => (is => 'rw', default => sub {0} );
has 'z' => (is => 'rw');
@ -98,7 +99,14 @@ sub change_layer {
$self->layer($layer);
# avoid computing overhangs if they're not needed
# avoid computing islands and overhangs if they're not needed
if ($self->config->only_retract_when_crossing_perimeters) {
$self->_layer_islands([ $layer->islands ]);
$self->_upper_layer_islands([ $layer->upper_layer_islands ]);
} else {
$self->_layer_islands([]);
$self->_upper_layer_islands([]);
}
$self->_layer_overhangs(
$layer->id > 0 && ($Slic3r::Config->overhangs || $Slic3r::Config->start_perimeters_at_non_overhang)
? [ map $_->expolygon, grep $_->surface_type == S_TYPE_BOTTOM, map @{$_->slices}, @{$layer->regions} ]
@ -116,6 +124,15 @@ sub change_layer {
int(99 * ($layer->id / ($self->layer_count - 1))),
($self->config->gcode_comments ? ' ; update progress' : '');
}
if ($self->config->first_layer_acceleration) {
if ($layer->id == 0) {
$gcode .= $self->set_acceleration($self->config->first_layer_acceleration);
} elsif ($layer->id == 1) {
$gcode .= $self->set_acceleration($self->config->default_acceleration);
}
}
$gcode .= $self->move_z($layer->print_z);
return $gcode;
}
@ -137,7 +154,6 @@ sub move_z {
$self->speed('travel');
$gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')'))
if !defined $self->z || abs($z - ($self->z - $self->lifted)) > epsilon;
$gcode .= $self->move_z_callback->() if defined $self->move_z_callback;
} elsif ($z < $self->z && $z > ($self->z - $self->lifted + epsilon)) {
# we're moving to a layer height which is greater than the nominal current one
# (nominal = actual - lifted) and less than the actual one. we're basically
@ -286,14 +302,16 @@ sub extrude_path {
# adjust acceleration
my $acceleration;
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->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;
}
$gcode .= $self->set_acceleration($acceleration) if $acceleration;
}
$gcode .= $self->set_acceleration($acceleration) if $acceleration;
my $area; # mm^3 of extrudate per mm of tool movement
if ($path->is_bridge) {
@ -361,9 +379,9 @@ sub travel_to {
# *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
&& (first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->upper_layer_slices})
&& (first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}))
|| ($role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands_enclose_line($travel))
&& (first { $_->encloses_line($travel, scaled_epsilon) } @{$self->_upper_layer_islands})
&& (first { $_->encloses_line($travel, scaled_epsilon) } @{$self->_layer_islands}))
|| ($role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->support_islands}))
) {
$self->straight_once(0);
$self->speed('travel');
@ -405,9 +423,9 @@ sub _plan {
my $need_retract = !$self->config->only_retract_when_crossing_perimeters;
if (!$need_retract) {
$need_retract = 1;
foreach my $slice (@{$self->layer->upper_layer_slices}) {
foreach my $island ($self->layer->upper_layer_islands) {
# discard the island if at any line is not enclosed in it
next if first { !$slice->encloses_line($_, scaled_epsilon) } @travel;
next if first { !$island->encloses_line($_, scaled_epsilon) } @travel;
# okay, this island encloses the full travel path
$need_retract = 0;
last;

View file

@ -46,23 +46,16 @@ sub process_layer {
$self->second_layer_things_done(1);
}
# set new layer, but don't move Z as support material contact areas may need an intermediate one
# set new layer - this will change Z and force a retraction if retract_layer_change is enabled
$gcode .= $self->gcodegen->change_layer($layer);
# prepare callback to call as soon as a Z command is generated
$self->gcodegen->move_z_callback(sub {
$self->gcodegen->move_z_callback(undef); # circular ref or not?
return "" if !$Slic3r::Config->layer_gcode;
return $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode, {
layer_num => $self->gcodegen->layer->id,
}) . "\n";
});
$gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode, {
layer_num => $self->gcodegen->layer->id,
}) . "\n" if $Slic3r::Config->layer_gcode;
# extrude skirt
if ((values %{$self->skirt_done}) < $Slic3r::Config->skirt_height && !$self->skirt_done->{$layer->print_z}) {
$self->gcodegen->set_shift(@{$self->shift});
$gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); # move_z requires extruder
$gcode .= $self->gcodegen->move_z($layer->print_z);
$gcode .= $self->gcodegen->set_extruder($self->extruders->[0]);
# skip skirt if we have a large brim
if ($layer->id < $Slic3r::Config->skirt_height) {
# distribute skirt loops across all extruders
@ -81,8 +74,7 @@ sub process_layer {
# extrude brim
if (!$self->brim_done) {
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); # move_z requires extruder
$gcode .= $self->gcodegen->move_z($layer->print_z);
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$Slic3r::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);
@ -98,7 +90,6 @@ 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')) {
$gcode .= $self->gcodegen->move_z($layer->print_z);
if ($layer->support_interface_fills) {
$gcode .= $self->gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_interface_extruder-1]);
$gcode .= $self->gcodegen->extrude_path($_, 'support material interface')
@ -111,9 +102,6 @@ sub process_layer {
}
}
# set actual Z - this will force a retraction
$gcode .= $self->gcodegen->move_z($layer->print_z);
# tweak region ordering to save toolchanges
my @region_ids = 0 .. ($self->print->regions_count-1);
if ($self->gcodegen->multiple_extruders) {

View file

@ -461,7 +461,7 @@ sub build {
},
{
title => 'Acceleration control (advanced)',
options => [qw(perimeter_acceleration infill_acceleration bridge_acceleration default_acceleration)],
options => [qw(perimeter_acceleration infill_acceleration bridge_acceleration first_layer_acceleration default_acceleration)],
},
]);

View file

@ -29,7 +29,7 @@ sub union {
my ($polygons, $jointype, $safety_offset) = @_;
$jointype = PFT_NONZERO unless defined $jointype;
$clipper->clear;
$clipper->add_subject_polygons($safety_offset ? safety_offset($polygons) : $polygons);
$clipper->add_subject_polygons(_convert($safety_offset ? safety_offset($polygons) : $polygons));
return [
map Slic3r::Polygon->new(@$_),
@{ $clipper->execute(CT_UNION, $jointype, $jointype) },

View file

@ -23,11 +23,16 @@ sub _trigger_id {
$_->_trigger_layer for @{$self->regions || []};
}
sub upper_layer_slices {
sub islands {
my $self = shift;
return @{$self->slices};
}
sub upper_layer_islands {
my $self = shift;
my $upper_layer = $self->object->layers->[ $self->id + 1 ] or return [];
return $upper_layer->slices;
my $upper_layer = $self->object->layers->[ $self->id + 1 ] or return ();
return $upper_layer->islands;
}
sub region {
@ -59,20 +64,18 @@ sub make_perimeters {
$_->make_perimeters for @{$self->regions};
}
sub support_islands_enclose_line {
my $self = shift;
my ($line) = @_;
return 0 if !$self->support_islands; # why can we arrive here if there are no support islands?
return (first { $_->encloses_line($line) } @{$self->support_islands}) ? 1 : 0;
}
package Slic3r::Layer::Support;
use Moo;
extends 'Slic3r::Layer';
# ordered collection of extrusion paths to fill surfaces for support material
has 'support_islands' => (is => 'rw');
has 'support_islands' => (is => 'rw', default => sub { [] });
has 'support_fills' => (is => 'rw');
has 'support_interface_fills' => (is => 'rw');
sub islands {
my $self = shift;
return @{$self->slices}, @{$self->support_islands};
}
1;

View file

@ -5,7 +5,8 @@ use List::Util qw(sum first);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(PI A B scale chained_path_items points_coincide);
use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex
offset offset2 offset_ex offset2_ex PFT_EVENODD union_pt traverse_pt diff intersection);
offset offset2 offset2_ex PFT_EVENODD union_pt traverse_pt diff intersection
union diff);
use Slic3r::Surface ':types';
has 'layer' => (
@ -131,22 +132,22 @@ sub _merge_loops {
# winding order.
# TODO: find a faster algorithm for this.
my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first
$safety_offset //= scale 0.0499;
@loops = @{ safety_offset(\@loops, $safety_offset) };
my $expolygons = [];
while (defined (my $loop = shift @loops)) {
if ($loop->is_counter_clockwise) {
$expolygons = union_ex([ $loop, map @$_, @$expolygons ]);
} else {
$expolygons = diff_ex([ map @$_, @$expolygons ], [$loop]);
}
# we don't perform a safety offset now because it might reverse cw loops
my $slices = [];
foreach my $loop (@loops) {
$slices = $loop->is_counter_clockwise
? union([ $loop, @$slices ])
: diff($slices, [$loop]);
}
$expolygons = offset_ex([ map @$_, @$expolygons ], -$safety_offset);
# perform a safety offset to merge very close facets (TODO: find test case for this)
$safety_offset //= scale 0.0499;
$slices = offset2_ex($slices, +$safety_offset, -$safety_offset);
Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
scalar(@$slices), scalar(map $_->holes, @$slices), scalar(@$loops) if $Slic3r::debug;
return map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), @$expolygons;
return map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), @$slices;
}
sub make_perimeters {
@ -174,6 +175,7 @@ sub make_perimeters {
# (one more than necessary so that we can detect gaps even after the desired
# number of perimeters has been generated)
my @last = @{$surface->expolygon};
my @this_gaps = ();
for my $i (0 .. $loop_number) {
# external loop only needs half inset distance
my $spacing = ($i == 0)
@ -194,7 +196,7 @@ sub make_perimeters {
# won't shrink the clip polygon to be smaller than intended.
offset(\@offsets, +0.5*$spacing + 2),
);
push @gaps, grep $_->area >= $gap_area_threshold, @$diff;
push @gaps, (@this_gaps = grep $_->area >= $gap_area_threshold, @$diff);
}
last if !@offsets || $i == $loop_number;
@ -203,16 +205,20 @@ sub make_perimeters {
@last = @offsets;
}
# make sure we don't infill narrow parts that are already gap-filled
# (we only consider this surface's gaps to reduce the diff() complexity)
@last = @{diff(\@last, [ map @$_, @this_gaps ])};
# create one more offset to be used as boundary for fill
# we offset by half the perimeter spacing (to get to the actual infill boundary)
# and then we offset back and forth by the infill spacing to only consider the
# and then we offset back and forth by half the infill spacing to only consider the
# non-collapsing regions
$self->fill_surfaces->append(
@{offset2_ex(
[ map $_->simplify_as_polygons(&Slic3r::SCALED_RESOLUTION), @{union_ex(\@last)} ],
-($perimeter_spacing/2 + $infill_spacing),
+$infill_spacing,
)},
-($perimeter_spacing/2 + $infill_spacing/2),
+$infill_spacing/2,
)}
);
}
@ -420,7 +426,7 @@ sub prepare_fill_surfaces {
sub process_external_surfaces {
my $self = shift;
my $margin = scale 3; # TODO: ensure this is greater than the total thickness of the perimeters
my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN;
my @bottom = ();
foreach my $surface (grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}) {

View file

@ -669,8 +669,9 @@ sub make_brim {
for my $i (reverse 1 .. $num_loops) {
# JT_SQUARE ensures no vertex is outside the given offset distance
# -0.5 because islands are not represented by their centerlines
# TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions
push @loops, @{offset2(\@islands, ($i - 1.5) * $flow->scaled_spacing, +1.0 * $flow->scaled_spacing, undef, JT_SQUARE)};
# (first offset more, then step back - reverse order than the one used for
# perimeters because here we're offsetting outwards)
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, undef, JT_SQUARE)};
}
@{$self->brim} = map Slic3r::ExtrusionLoop->new(
@ -720,7 +721,7 @@ sub write_gcode {
# set up our extruder object
my $gcodegen = Slic3r::GCode->new(
config => $self->config,
extruders => $self->extruders,
extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!)
layer_count => $self->layer_count,
);
print $fh "G21 ; set units to millimeters\n" if $Slic3r::Config->gcode_flavor ne 'makerware';
@ -756,6 +757,10 @@ 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]);
# calculate X,Y shift to center print around specified origin
my $print_bb = $self->bounding_box;
my $print_size = $print_bb->size;

View file

@ -562,6 +562,8 @@ sub discover_horizontal_shells {
Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n";
my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN;
for my $region_id (0 .. ($self->print->regions_count-1)) {
for (my $i = 0; $i < $self->layer_count; $i++) {
my $layerm = $self->layers->[$i]->regions->[$region_id];
@ -577,9 +579,13 @@ sub discover_horizontal_shells {
EXTERNAL: foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) {
# find slices of current type for current layer
# get both slices and fill_surfaces before the former contains the perimeters area
# and the latter contains the enlarged external surfaces
my $solid = [ map $_->expolygon, grep $_->surface_type == $type, @{$layerm->slices}, @{$layerm->fill_surfaces} ];
# use slices instead of fill_surfaces because they also include the perimeter area
# which needs to be propagated in shells; we need to grow slices like we did for
# fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
# not work in some situations, as there won't be any grown region in the perimeter
# area (this was seen in a model where the top layer had one extra perimeter, thus
# its fill_surfaces was thinner than the lower layer's infill)
my $solid = [ map $_->expolygon->offset_ex($margin), grep $_->surface_type == $type, @{$layerm->slices} ];
next if !@$solid;
Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom';
@ -817,14 +823,10 @@ sub generate_support_material {
}
# shape of contact area
my $contact_loops = 1;
my $circle_distance = 3 * $flow->scaled_width;
my $circle;
{
# TODO: make sure teeth between circles are compatible with support material flow
my $r = 1.5 * $flow->scaled_width;
$circle = Slic3r::Polygon->new(map [ $r * cos $_, $r * sin $_ ], (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
}
my $contact_loops = 1;
my $circle_radius = 1.5 * $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));
# determine contact areas
my %contact = (); # contact_z => [ polygons ]
@ -901,10 +903,18 @@ sub generate_support_material {
###$contact_z = $layer->print_z - $layer->height;
# ignore this contact area if it's too low
next if $contact_z < $Slic3r::Config->first_layer_height;
next if $contact_z < $Slic3r::Config->get_value('first_layer_height');
$contact{$contact_z} = [ @contact ];
$overhang{$contact_z} = [ @overhang ];
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("contact_" . $contact_z . ".svg",
expolygons => union_ex(\@contact),
red_expolygons => \@overhang,
);
}
}
}
my @contact_z = sort keys %contact;
@ -921,12 +931,17 @@ sub generate_support_material {
# ('new' means all the areas that are lower than the last top layer
# we considered)
my $min_top = min(keys %top) // max(keys %contact);
push @$projection, map @{$contact{$_}}, grep { $_ > $layer->print_z && $_ < $min_top } keys %contact;
# use <= instead of just < because otherwise we'd ignore any contact regions
# having the same Z of top layers
push @$projection, map @{$contact{$_}}, grep { $_ > $layer->print_z && $_ <= $min_top } keys %contact;
# now find whether any projection falls onto this top surface
my $touching = intersection($projection, [ map $_->p, @top ]);
if (@$touching) {
$top{ $layer->print_z } = $touching;
# grow top surfaces so that interface and support generation are generated
# with some spacing from object - it looks we don't need the actual
# top shapes so this can be done here
$top{ $layer->print_z } = [ offset($touching, $flow->scaled_spacing) ];
}
# remove the areas that touched from the projection that will continue on
@ -944,17 +959,15 @@ sub generate_support_material {
# if we wanted to apply some special logic to the first support layers lying on
# object's top surfaces this is the place to detect them
# Let's now determine shells (interface layers) and normal support below them.
# Let's now fill each support layer by generating shells (interface layers) and
# clipping support area to the actual object boundaries.
# let's now generate interface layers below contact areas
my %interface = (); # layer_id => [ polygons ]
my %support = (); # layer_id => [ polygons ]
my $interface_layers = $Slic3r::Config->support_material_interface_layers;
for my $layer_id (0 .. $#support_layers) {
my $z = $support_layers[$layer_id];
my $this = $contact{$z} // next;
# count contact layer as interface layer
for (my $i = $layer_id; $i >= 0 && $i > $layer_id-$interface_layers; $i--) {
for (my $i = $layer_id-1; $i >= 0 && $i > $layer_id-$interface_layers; $i--) {
$z = $support_layers[$i];
# Compute interface area on this layer as diff of upper contact area
# (or upper interface area) and layer slices.
@ -963,29 +976,32 @@ sub generate_support_material {
# surfaces before performing the diff, but this needs investigation.
$this = $interface{$i} = diff(
[
@$this,
@{ $interface{$i} || [] },
@$this, # clipped projection of the current contact regions
@{ $interface{$i} || [] }, # interface regions already applied to this layer
],
[
@{ $top{$z} || [] },
@{ $top{$z} || [] }, # top slices on this layer
@{ $contact{$z} || [] }, # contact regions on this layer
],
1,
);
}
# determine what layers does our support belong to
for (my $i = $layer_id-$interface_layers; $i >= 0; $i--) {
$z = $support_layers[$i];
# Compute support area on this layer as diff of upper support area
# and layer slices.
$this = $support{$i} = diff(
}
# let's now generate support layers under interface layers
my %support = (); # layer_id => [ polygons ]
{
for my $i (reverse 0 .. $#support_layers-1) {
my $z = $support_layers[$i];
$support{$i} = diff(
[
@$this,
@{ $support{$i} || [] },
@{ $support{$i+1} || [] }, # support regions on upper layer
@{ $interface{$i+1} || [] }, # interface regions on upper layer
],
[
@{ $top{$z} || [] },
@{ $interface{$i} || [] },
@{ $top{$z} || [] }, # top slices on this layer
@{ $interface{$i} || [] }, # interface regions on this layer
@{ $contact{$z} || [] }, # contact regions on this layer
],
1,
);
@ -1024,13 +1040,30 @@ sub generate_support_material {
my $process_layer = sub {
my ($layer_id) = @_;
my $result = { contact => [], interface => [], support => [] };
$contact{$layer_id} ||= [];
$interface{$layer_id} ||= [];
$support{$layer_id} ||= [];
$contact{$support_layers[$layer_id]} ||= [];
$interface{$layer_id} ||= [];
$support{$layer_id} ||= [];
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("layer_" . $support_layers[$layer_id] . ".svg",
red_expolygons => union_ex($contact{$support_layers[$layer_id]}),
green_expolygons => union_ex($interface{$layer_id}),
);
}
# islands
my $result = { contact => [], interface => [], support => [] };
$result->{islands} = union_ex([
map @$_,
$interface{$layer_id},
$support{$layer_id},
$contact{$support_layers[$layer_id]},
]);
# contact
my $contact_infill = [];
if ((my $contact = $contact{$support_layers[$layer_id]}) && $contact_loops > 0) {
my $overhang = $overhang{$support_layers[$layer_id]};
$contact = [ grep $_->is_counter_clockwise, @$contact ];
@ -1063,11 +1096,8 @@ sub generate_support_material {
[ map Slic3r::Polygon->new(@$_)->split_at_first_point, @loops ],
) };
# subtract loops from the contact area to detect the remaining part
$interface{$layer_id} = intersection(
$interface{$layer_id},
[ offset2(\@loops0, -($contact_loops) * $flow->scaled_spacing, +0.5*$flow->scaled_spacing) ],
);
# add the contact infill area to the interface area
$contact_infill = [ offset2(\@loops0, -($contact_loops + 0.5) * $flow->scaled_spacing, +0.5*$flow->scaled_spacing) ];
# transform loops into ExtrusionPath objects
@loops = map Slic3r::ExtrusionPath->pack(
@ -1079,14 +1109,15 @@ sub generate_support_material {
$result->{contact} = [ @loops ];
}
# interface
if (@{$interface{$layer_id}}) {
# interface and contact infill
if (@{$interface{$layer_id}} || @$contact_infill) {
$fillers{interface}->angle($interface_angle);
# steal some space from support
$interface{$layer_id} = intersection(
[ offset($interface{$layer_id}, scale 3) ],
[ @{$interface{$layer_id}}, @{$support{$layer_id}} ],
[ offset([ map @$_, $interface{$layer_id}, $contact_infill ], scale 3) ],
[ map @$_, $interface{$layer_id}, $support{$layer_id}, $contact_infill ],
undef, 1,
);
$support{$layer_id} = diff(
$support{$layer_id},
@ -1094,7 +1125,7 @@ sub generate_support_material {
);
my @paths = ();
foreach my $expolygon (offset_ex($interface{$layer_id}, -$flow->scaled_width/2)) {
foreach my $expolygon (@{union_ex($interface{$layer_id})}) {
my @p = $fillers{interface}->fill_surface(
Slic3r::Surface->new(expolygon => $expolygon),
density => $interface_density,
@ -1121,7 +1152,7 @@ sub generate_support_material {
my $flow_spacing = $flow->spacing;
# TODO: use offset2_ex()
my $to_infill = offset_ex(union($support{$layer_id}), -$flow->scaled_width/2);
my $to_infill = union_ex($support{$layer_id}, undef, 1);
my @paths = ();
# base flange
@ -1161,14 +1192,19 @@ sub generate_support_material {
), @p;
}
$result->{support} = [ @paths ];
push @{$result->{support}}, @paths;
}
# islands
$result->{islands} = union_ex([
@{$interface{$layer_id} || []},
@{$support{$layer_id} || []},
]);
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("islands_" . $support_layers[$layer_id] . ".svg",
red_expolygons => union_ex($contact{$support_layers[$layer_id]} || []),
green_expolygons => union_ex($interface{$layer_id} || []),
red_polylines => [ map $_->unpack->polyline, @{$result->{contact}} ],
green_polylines => [ map $_->unpack->polyline, @{$result->{interface}} ],
polylines => [ map $_->unpack->polyline, @{$result->{support}} ],
);
}
return $result;
};
@ -1183,6 +1219,7 @@ sub generate_support_material {
my $support_collection = Slic3r::ExtrusionPath::Collection->new(paths => $result->{support});
$layer->support_fills($support_collection) if @{$support_collection->paths} > 0;
# TODO: use a Slic3r::ExPolygon::Collection
$layer->support_islands($result->{islands});
};
Slic3r::parallelize(

View file

@ -34,33 +34,36 @@ sub svg {
}
sub output {
my ($filename, %things) = @_;
my ($filename, @things) = @_;
my $svg = svg();
my $arrows = 1;
foreach my $type (qw(expolygons red_expolygons green_expolygons)) {
next if !$things{$type};
my ($colour) = $type =~ /^(red|green)_/;
my $g = $svg->group(
style => {
'stroke-width' => 2,
'stroke' => $colour || 'black',
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
'fill-type' => $filltype,
},
);
foreach my $expolygon (@{$things{$type}}) {
my $points = join ' ', map "M $_ z", map join(" ", reverse map $_->[0]*factor() . " " . $_->[1]*factor(), @$_), @$expolygon;
$g->path(
d => $points,
while (my $type = shift @things) {
my $value = shift @things;
if ($type eq 'no_arrows') {
$arrows = 0;
} elsif ($type =~ /^(?:(.+?)_)?expolygons$/) {
my $colour = $1;
my $g = $svg->group(
style => {
'stroke-width' => 2,
'stroke' => $colour || 'black',
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
'fill-type' => $filltype,
},
);
}
}
foreach my $type (qw(polygons polylines white_polygons green_polygons red_polygons red_polylines)) {
if ($things{$type}) {
my $method = $type =~ /polygons/ ? 'polygon' : 'polyline';
my ($colour) = $type =~ /^(red|green)_/;
foreach my $expolygon (@$value) {
my $points = join ' ', map "M $_ z", map join(" ", reverse map $_->[0]*factor() . " " . $_->[1]*factor(), @$_), @$expolygon;
$g->path(
d => $points,
);
}
} elsif ($type =~ /^(?:(.+?)_)?(polygon|polyline)s$/) {
my ($colour, $method) = ($1, $2);
my $g = $svg->group(
style => {
'stroke-width' => 2,
@ -68,7 +71,7 @@ sub output {
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
},
);
foreach my $polygon (@{$things{$type}}) {
foreach my $polygon (@$value) {
my $path = $svg->get_path(
'x' => [ map($_->[X] * factor(), @$polygon) ],
'y' => [ map($_->[Y] * factor(), @$polygon) ],
@ -76,15 +79,13 @@ sub output {
);
$g->$method(
%$path,
'marker-end' => $things{no_arrows} ? "" : "url(#endArrow)",
'marker-end' => $arrows ? "" : "url(#endArrow)",
);
}
}
}
foreach my $type (qw(points red_points)) {
if ($things{$type}) {
my ($colour, $r) = $type eq 'points' ? ('black', 5) : ('red', 3);
} elsif ($type =~ /^(?:(.+?)_)?points$/) {
my $colour = $1;
my $r = $colour eq 'black' ? 5 : 3;
my $g = $svg->group(
style => {
'stroke-width' => 2,
@ -92,25 +93,22 @@ sub output {
'fill' => $colour,
},
);
foreach my $point (@{$things{$type}}) {
foreach my $point (@$value) {
$g->circle(
cx => $point->[X] * factor(),
cy => $point->[Y] * factor(),
r => $r,
);
}
}
}
foreach my $type (qw(lines red_lines green_lines)) {
if ($things{$type}) {
my ($colour) = $type =~ /^(red|green)_/;
} elsif ($type =~ /^(?:(.+?)_)?lines$/) {
my $colour = $1;
my $g = $svg->group(
style => {
'stroke-width' => 2,
},
);
foreach my $line (@{$things{$type}}) {
foreach my $line (@$value) {
$g->line(
x1 => $line->[0][X] * factor(),
y1 => $line->[0][Y] * factor(),
@ -119,7 +117,7 @@ sub output {
style => {
'stroke' => $colour || 'black',
},
'marker-end' => $things{no_arrows} ? "" : "url(#endArrow)",
'marker-end' => $arrows ? "" : "url(#endArrow)",
);
}
}
@ -128,26 +126,6 @@ sub output {
write_svg($svg, $filename);
}
sub output_points {
my ($print, $filename, $points, $red_points) = @_;
return output($print, $filename, points => $points, red_points => $red_points);
}
sub output_polygons {
my ($print, $filename, $polygons) = @_;
return output($print, $filename, polygons => $polygons);
}
sub output_polylines {
my ($print, $filename, $polylines) = @_;
return output($print, $filename, polylines => $polylines);
}
sub output_lines {
my ($print, $filename, $lines) = @_;
return output($print, $filename, lines => $lines);
}
sub write_svg {
my ($svg, $filename) = @_;

View file

@ -563,7 +563,9 @@ sub horizontal_projection {
}
$_->make_counter_clockwise for @f; # do this after scaling, as winding order might change while doing that
return union_ex(\@f, 1);
# the offset factor was tuned using groovemount.stl
return union_ex([ offset(\@f, Slic3r::Geometry::scale 0.01) ], 1);
}
1;

View file

@ -255,6 +255,9 @@ $j
--bridge-acceleration
Overrides firmware's default acceleration for bridges. (mm/s^2, set zero
to disable; default: $config->{bridge_acceleration})
--first-layer-acceleration
Overrides firmware's default acceleration for first layer. (mm/s^2, set zero
to disable; default: $config->{first_layer_acceleration})
--default-acceleration
Acceleration will be reset to this value after the specific settings above
have been applied. (mm/s^2, set zero to disable; default: $config->{travel_speed})

View file

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 32;
plan tests => 33;
BEGIN {
use FindBin;
@ -51,17 +51,21 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
{
my $test = sub {
my ($expolygon, $flow_spacing) = @_;
my ($expolygon, $flow_spacing, $angle, $density) = @_;
my $filler = Slic3r::Fill::Rectilinear->new(
bounding_box => $expolygon->bounding_box,
angle => 0,
angle => $angle // 0,
);
my $surface = Slic3r::Surface->new(
surface_type => S_TYPE_BOTTOM,
expolygon => $expolygon,
);
my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => $flow_spacing, density => 1);
my ($params, @paths) = $filler->fill_surface(
$surface,
flow_spacing => $flow_spacing,
density => $density // 1,
);
# check whether any part was left uncovered
my @grown_paths = map Slic3r::Polyline->new(@$_)->grow(scale $params->{flow_spacing}/2), @paths;
@ -100,6 +104,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
[[59515297,5422499],[59531249,5578697],[59695801,6123186],[59965713,6630228],[60328214,7070685],[60773285,7434379],[61274561,7702115],[61819378,7866770],[62390306,7924789],[62958700,7866744],[63503012,7702244],[64007365,7434357],[64449960,7070398],[64809327,6634999],[65082143,6123325],[65245005,5584454],[65266967,5422499],[66267307,5422499],[66269190,8310081],[66275379,17810072],[66277259,20697500],[65267237,20697500],[65245004,20533538],[65082082,19994444],[64811462,19488579],[64450624,19048208],[64012101,18686514],[63503122,18415781],[62959151,18251378],[62453416,18198442],[62390147,18197355],[62200087,18200576],[61813519,18252990],[61274433,18415918],[60768598,18686517],[60327567,19047892],[59963609,19493297],[59695865,19994587],[59531222,20539379],[59515153,20697500],[58502480,20697500],[58502480,5422499]]
);
$test->($expolygon, 0.524341649025257);
$expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [98,0], [98,10], [0,10] ]);
$test->($expolygon, 0.5, 45, 0.99); # non-solid infill
}
{

View file

@ -99,7 +99,7 @@ use Slic3r::Test;
$config->set('first_layer_speed', '100%'); # prevent speed alteration
$config->set('layer_height', 0.4);
$config->set('first_layer_height', '100%');
$config->set('extrusion_width', 0.5);
$config->set('extrusion_width', 0.55);
$config->set('bottom_solid_layers', 3);
$config->set('top_solid_layers', 0);
$config->set('solid_infill_speed', 99);