2012-04-29 12:51:20 +02:00
package Slic3r::Print::Object ;
2016-09-13 11:24:55 +02:00
# extends c++ class Slic3r::PrintObject (Print.xsp)
2014-06-10 16:01:57 +02:00
use strict ;
use warnings ;
2012-04-29 12:51:20 +02:00
2013-07-29 20:49:54 +02:00
use List::Util qw(min max sum first) ;
2013-12-31 14:33:03 +01:00
use Slic3r::Flow ':roles' ;
2016-03-13 12:24:03 +01:00
use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon) ;
2013-07-29 20:49:54 +02:00
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
2016-12-13 19:22:23 +01:00
offset offset_ex offset2 offset2_ex JT_MITER) ;
2013-12-19 18:54:24 +01:00
use Slic3r::Print::State ':steps' ;
2012-05-19 16:04:33 +02:00
use Slic3r::Surface ':types' ;
2012-04-29 12:51:20 +02:00
2016-09-26 13:56:24 +02:00
# If enabled, phases of prepare_infill will be written into SVG files to an "out" directory.
our $SLIC3R_DEBUG_SLICE_PROCESSING = 0 ;
2013-12-15 16:17:12 +01:00
2014-05-06 11:07:18 +03:00
sub region_volumes {
my $self = shift ;
return [ map $self -> get_region_volumes ( $_ ), 0 .. ( $self -> region_count - 1 ) ];
}
sub layers {
my $self = shift ;
return [ map $self -> get_layer ( $_ ), 0 .. ( $self -> layer_count - 1 ) ];
}
sub support_layers {
my $self = shift ;
return [ map $self -> get_support_layer ( $_ ), 0 .. ( $self -> support_layer_count - 1 ) ];
}
2016-09-13 11:24:55 +02:00
# 1) Decides Z positions of the layers,
# 2) Initializes layers and their regions
# 3) Slices the object meshes
# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
# 5) Applies size compensation (offsets the slices in XY plane)
# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
# Resulting expolygons of layer regions are marked as Internal.
#
2013-12-15 16:17:12 +01:00
# this should be idempotent
2012-04-30 14:56:01 +02:00
sub slice {
my $self = shift ;
2014-06-13 20:05:18 +02:00
return if $self -> step_done ( STEP_SLICE );
$self -> set_step_started ( STEP_SLICE );
$self -> print -> status_cb -> ( 10 , "Processing triangulated mesh" );
2012-04-30 14:56:01 +02:00
2016-12-12 17:56:37 +01:00
$self -> _slice ;
2017-03-08 11:56:42 +01:00
my $warning = $self -> _fix_slicing_errors ;
warn $warning if ( defined ( $warning ) && $warning ne '' );
2013-12-19 15:23:10 +01:00
# simplify slices if required
2017-03-08 15:08:40 +01:00
$self -> _simplify_slices ( scale ( $self -> print -> config -> resolution ))
2017-03-08 13:43:49 +01:00
if ( $self -> print -> config -> resolution );
2014-06-13 20:05:18 +02:00
2015-11-04 19:27:58 +01:00
die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"
2014-06-13 20:05:18 +02:00
if ! @ { $self -> layers };
2014-07-15 19:07:38 +02:00
$self -> set_typed_slices ( 0 );
2014-06-13 20:05:18 +02:00
$self -> set_step_done ( STEP_SLICE );
2012-04-30 14:56:01 +02:00
}
2016-11-17 23:22:59 +01:00
# 1) Merges typed region slices into stInternal type.
# 2) Increases an "extra perimeters" counter at region slices where needed.
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
2012-05-05 16:36:10 +02:00
sub make_perimeters {
2016-11-26 12:28:39 +01:00
my ( $self ) = @_ ;
2012-05-05 16:36:10 +02:00
2014-06-13 20:05:18 +02:00
# prerequisites
$self -> slice ;
2017-03-03 12:53:05 +01:00
$self -> print -> status_cb -> ( 20 , "Generating perimeters" );
2016-11-26 12:28:39 +01:00
$self -> _make_perimeters ;
2014-06-13 20:05:18 +02:00
}
sub prepare_infill {
my ( $self ) = @_ ;
# prerequisites
$self -> make_perimeters ;
return if $self -> step_done ( STEP_PREPARE_INFILL );
$self -> set_step_started ( STEP_PREPARE_INFILL );
$self -> print -> status_cb -> ( 30 , "Preparing infill" );
2016-11-17 23:22:59 +01:00
# This will assign a type (top/bottom/internal) to $layerm->slices.
# Then the classifcation of $layerm->slices is transfered onto
# the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
# by the cummulative area of the previous $layerm->fill_surfaces.
2014-06-13 20:05:18 +02:00
$self -> detect_surfaces_type ;
2016-11-17 23:22:59 +01:00
# Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
2014-07-15 19:07:38 +02:00
$self -> set_typed_slices ( 1 );
2016-09-26 13:56:24 +02:00
# Decide what surfaces are to be filled.
# Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured.
# Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID.
2017-01-30 19:56:46 +01:00
# BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces...";
2014-06-13 20:05:18 +02:00
$_ -> prepare_fill_surfaces for map @ { $_ -> regions }, @ { $self -> layers };
# this will detect bridges and reverse bridges
# and rearrange top/bottom/internal surfaces
2016-09-26 13:56:24 +02:00
# It produces enlarged overlapping bridging areas.
#
# 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
# 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
# 3) Clip the internal surfaces by the grown top/bottom surfaces.
# 4) Merge surfaces with the same style. This will mostly get rid of the overlaps.
#FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties.
2014-06-13 20:05:18 +02:00
$self -> process_external_surfaces ;
2016-09-26 13:56:24 +02:00
# Add solid fills to ensure the shell vertical thickness.
$self -> discover_vertical_shells ;
# Debugging output.
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
$layerm -> export_region_slices_to_svg_debug ( "6_discover_vertical_shells-final" );
$layerm -> export_region_fill_surfaces_to_svg_debug ( "6_discover_vertical_shells-final" );
} # for each layer
} # for each region
}
# Detect, which fill surfaces are near external layers.
# They will be split in internal and internal-solid surfaces.
# The purpose is to add a configurable number of solid layers to support the TOP surfaces
# and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
# to close these surfaces reliably.
#FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
2014-06-13 20:05:18 +02:00
$self -> discover_horizontal_shells ;
2016-09-26 13:56:24 +02:00
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
# Debugging output.
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
$layerm -> export_region_slices_to_svg_debug ( "7_discover_horizontal_shells-final" );
$layerm -> export_region_fill_surfaces_to_svg_debug ( "7_discover_horizontal_shells-final" );
} # for each layer
} # for each region
}
# Only active if config->infill_only_where_needed. This step trims the sparse infill,
# so it acts as an internal support. It maintains all other infill types intact.
# Here the internal surfaces and perimeters have to be supported by the sparse infill.
#FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
# Likely the sparse infill will not be anchored correctly, so it will not work as intended.
# Also one wishes the perimeters to be supported by a full infill.
2014-06-13 20:05:18 +02:00
$self -> clip_fill_surfaces ;
2016-09-26 13:56:24 +02:00
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
# Debugging output.
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
$layerm -> export_region_slices_to_svg_debug ( "8_clip_surfaces-final" );
$layerm -> export_region_fill_surfaces_to_svg_debug ( "8_clip_surfaces-final" );
} # for each layer
} # for each region
}
2014-06-13 20:05:18 +02:00
# the following step needs to be done before combination because it may need
# to remove only half of the combined infill
$self -> bridge_over_infill ;
# combine fill surfaces to honor the "infill every N layers" option
$self -> combine_infill ;
2016-09-26 13:56:24 +02:00
# Debugging output.
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
$layerm -> export_region_slices_to_svg_debug ( "9_prepare_infill-final" );
$layerm -> export_region_fill_surfaces_to_svg_debug ( "9_prepare_infill-final" );
} # for each layer
} # for each region
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layer = $self -> get_layer ( $i );
$layer -> export_region_slices_to_svg_debug ( "9_prepare_infill-final" );
$layer -> export_region_fill_surfaces_to_svg_debug ( "9_prepare_infill-final" );
} # for each layer
}
2014-06-13 20:05:18 +02:00
$self -> set_step_done ( STEP_PREPARE_INFILL );
}
sub infill {
my ( $self ) = @_ ;
# prerequisites
$self -> prepare_infill ;
2016-11-26 12:28:39 +01:00
$self -> _infill ;
2014-06-13 20:05:18 +02:00
}
sub generate_support_material {
my $self = shift ;
# prerequisites
$self -> slice ;
return if $self -> step_done ( STEP_SUPPORTMATERIAL );
$self -> set_step_started ( STEP_SUPPORTMATERIAL );
$self -> clear_support_layers ;
2016-12-20 12:19:13 +01:00
if (( $self -> config -> support_material || $self -> config -> raft_layers > 0 ) && scalar ( @ { $self -> layers }) > 1 ) {
$self -> print -> status_cb -> ( 85 , "Generating support material" );
2017-01-30 19:56:46 +01:00
# New supports, C++ implementation.
$self -> _generate_support_material ;
2014-06-13 20:18:34 +02:00
}
2015-01-19 09:52:24 +01:00
$self -> set_step_done ( STEP_SUPPORTMATERIAL );
2017-02-22 12:01:31 +01:00
my $stats = sprintf "Weight: %.1fg, Cost: %.1f" , $self -> print -> total_weight , $self -> print -> total_cost ;
2017-01-16 01:00:05 -06:00
$self -> print -> status_cb -> ( 85 , $stats );
2015-01-19 09:52:24 +01:00
}
2014-12-24 01:29:36 +01:00
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
2013-02-09 23:36:32 +01:00
sub clip_fill_surfaces {
my $self = shift ;
2013-08-25 14:37:50 +02:00
return unless $self -> config -> infill_only_where_needed ;
2013-02-09 23:36:32 +01:00
# We only want infill under ceilings; this is almost like an
# internal support material.
2015-02-23 00:44:51 +01:00
# proceed top-down skipping bottom layer
my $upper_internal = [] ;
for my $layer_id ( reverse 1 .. ( $self -> layer_count - 1 )) {
my $layer = $self -> get_layer ( $layer_id );
my $lower_layer = $self -> get_layer ( $layer_id - 1 );
2013-02-09 23:36:32 +01:00
2015-02-23 00:44:51 +01:00
# detect things that we need to support
my $overhangs = [] ; # Polygons
# we need to support any solid surface
push @$overhangs , map $_ -> p ,
grep $_ -> is_solid , map @ { $_ -> fill_surfaces }, @ { $layer -> regions };
# we also need to support perimeters when there's at least one full
# unsupported loop
{
# get perimeters area as the difference between slices and fill_surfaces
my $perimeters = diff (
[ map @$_ , @ { $layer -> slices } ],
[ map $_ -> p , map @ { $_ -> fill_surfaces }, @ { $layer -> regions } ],
);
# only consider the area that is not supported by lower perimeters
$perimeters = intersection (
$perimeters ,
[ map $_ -> p , map @ { $_ -> fill_surfaces }, @ { $lower_layer -> regions } ],
1 ,
);
# only consider perimeter areas that are at least one extrusion width thick
2016-09-26 13:56:24 +02:00
#FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
#Should the $pw not be half of the current value?
2015-02-23 00:44:51 +01:00
my $pw = min ( map $_ -> flow ( FLOW_ROLE_PERIMETER ) -> scaled_width , @ { $layer -> regions });
$perimeters = offset2 ( $perimeters , - $pw , + $pw );
# append such thick perimeters to the areas that need support
push @$overhangs , @$perimeters ;
}
# find new internal infill
$upper_internal = my $new_internal = intersection (
[
@$overhangs ,
@$upper_internal ,
],
[
# our current internal fill boundaries
map $_ -> p ,
grep $_ -> surface_type == S_TYPE_INTERNAL || $_ -> surface_type == S_TYPE_INTERNALVOID ,
map @ { $_ -> fill_surfaces }, @ { $lower_layer -> regions }
],
);
# apply new internal infill to regions
foreach my $layerm ( @ { $lower_layer -> regions }) {
2013-09-07 00:02:58 +02:00
my ( @internal , @other ) = ();
foreach my $surface ( map $_ -> clone , @ { $layerm -> fill_surfaces }) {
2015-02-23 00:44:51 +01:00
if ( $surface -> surface_type == S_TYPE_INTERNAL || $surface -> surface_type == S_TYPE_INTERNALVOID ) {
2014-12-24 01:29:36 +01:00
push @internal , $surface ;
} else {
push @other , $surface ;
}
2013-09-07 00:02:58 +02:00
}
2015-02-23 00:44:51 +01:00
my @new = map Slic3r::Surface -> new (
2013-09-07 00:02:58 +02:00
expolygon => $_ ,
surface_type => S_TYPE_INTERNAL ,
),
2014-12-24 01:29:36 +01:00
@ { intersection_ex (
[ map $_ -> p , @internal ],
2015-02-23 00:44:51 +01:00
$new_internal ,
1 ,
2014-12-24 01:29:36 +01:00
)};
2015-02-23 00:44:51 +01:00
push @other , map Slic3r::Surface -> new (
2014-12-24 01:29:36 +01:00
expolygon => $_ ,
surface_type => S_TYPE_INTERNALVOID ,
),
@ { diff_ex (
[ map $_ -> p , @internal ],
2015-02-23 00:44:51 +01:00
$new_internal ,
1 ,
2014-12-24 01:29:36 +01:00
)};
2015-02-23 00:55:00 +01:00
# If there are voids it means that our internal infill is not adjacent to
# perimeters. In this case it would be nice to add a loop around infill to
# make it more robust and nicer. TODO.
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
2014-11-09 16:23:50 +01:00
$layerm -> fill_surfaces -> append ( $_ ) for ( @new , @other );
2016-09-26 13:56:24 +02:00
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
$layerm -> export_region_fill_surfaces_to_svg_debug ( "6_clip_fill_surfaces" );
}
2013-02-09 23:36:32 +01:00
}
}
}
2012-04-29 12:51:20 +02:00
sub discover_horizontal_shells {
my $self = shift ;
Slic3r:: debugf "==> DISCOVERING HORIZONTAL SHELLS\n" ;
2014-08-03 18:41:09 +02:00
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
2014-05-06 11:07:18 +03:00
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
2014-06-13 18:45:44 +03:00
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
2012-09-28 15:46:29 +02:00
2015-10-26 23:23:03 +01:00
if ( $layerm -> region -> config -> solid_infill_every_layers && $layerm -> region -> config -> fill_density > 0
&& ( $i % $ layerm -> region -> config -> solid_infill_every_layers ) == 0 ) {
2016-09-26 13:56:24 +02:00
# This is the layer to put the sparse infill in. Mark S_TYPE_INTERNAL surfaces as S_TYPE_INTERNALSOLID or S_TYPE_INTERNALBRIDGE.
# If the sparse infill is not active, the internal surfaces are of type S_TYPE_INTERNAL.
2015-12-19 12:48:48 +01:00
my $type = $layerm -> region -> config -> fill_density == 100 ? S_TYPE_INTERNALSOLID : S_TYPE_INTERNALBRIDGE ;
$_ -> surface_type ( $type ) for @ { $layerm -> fill_surfaces -> filter_by_type ( S_TYPE_INTERNAL )};
2012-09-28 15:46:29 +02:00
}
2016-11-10 19:23:01 +01:00
# If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
next if ( $layerm -> region -> config -> ensure_vertical_shell_thickness );
2012-09-28 15:46:29 +02:00
2014-03-25 01:11:28 +01:00
EXTERNAL: foreach my $type ( S_TYPE_TOP , S_TYPE_BOTTOM , S_TYPE_BOTTOMBRIDGE ) {
2012-12-21 20:25:48 +01:00
# find slices of current type for current layer
2013-08-13 09:45:33 +02:00
# 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
2013-09-17 19:24:16 +02:00
# its fill_surfaces were thinner than the lower layer's infill), however it's the best
# solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
# too much solid infill inside nearly-vertical slopes.
my $solid = [
2016-11-09 10:24:45 +01:00
# Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
# (not covered by a layer above / below).
# This does not contain the areas covered by perimeters!
2013-09-17 19:24:16 +02:00
( map $_ -> p , @ { $layerm -> slices -> filter_by_type ( $type )}),
2016-11-09 10:24:45 +01:00
# Infill areas (slices without the perimeters).
2013-09-17 19:24:16 +02:00
( map $_ -> p , @ { $layerm -> fill_surfaces -> filter_by_type ( $type )}),
];
2013-03-11 18:37:01 +01:00
next if ! @$solid ;
2013-07-15 12:14:22 +02:00
Slic3r:: debugf "Layer %d has %s surfaces\n" , $i , ( $type == S_TYPE_TOP ) ? 'top' : 'bottom' ;
2012-04-29 12:51:20 +02:00
2012-10-28 12:52:44 +01:00
my $solid_layers = ( $type == S_TYPE_TOP )
2015-10-26 23:23:03 +01:00
? $layerm -> region -> config -> top_solid_layers
: $layerm -> region -> config -> bottom_solid_layers ;
2013-08-08 02:10:34 +02:00
NEIGHBOR: for ( my $n = ( $type == S_TYPE_TOP ) ? $i - 1 : $i + 1 ;
2016-09-26 13:56:24 +02:00
abs ( $n - $i ) < $solid_layers ;
( $type == S_TYPE_TOP ) ? $n -- : $n ++ ) {
2012-09-22 19:04:36 +02:00
2014-05-06 11:07:18 +03:00
next if $n < 0 || $n >= $self -> layer_count ;
2012-09-22 19:04:36 +02:00
Slic3r:: debugf " looking for neighbors on layer %d...\n" , $n ;
2016-09-26 13:56:24 +02:00
# Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
2014-06-13 18:45:44 +03:00
my $neighbor_layerm = $self -> get_layer ( $n ) -> regions -> [ $region_id ];
2016-09-26 13:56:24 +02:00
# Reference to the neighbour fill surfaces.
2014-02-07 01:48:47 +01:00
my $neighbor_fill_surfaces = $neighbor_layerm -> fill_surfaces ;
2016-09-26 13:56:24 +02:00
# Clone because we will use these surfaces even after clearing the collection.
my @neighbor_fill_surfaces = map $_ -> clone , @$neighbor_fill_surfaces ;
2012-09-22 19:04:36 +02:00
# find intersection between neighbor and current layer's surfaces
# intersections have contours and holes
2013-07-27 19:41:36 +02:00
# we update $solid so that we limit the next neighbor layer to the areas that were
# found on this one - in other words, solid shells on one layer (for a given external surface)
# are always a subset of the shells found on the previous shell layer
# this approach allows for DWIM in hollow sloping vases, where we want bottom
# shells to be generated in the base but not in the walls (where there are many
# narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
# upper perimeter as an obstacle and shell will not be propagated to more upper layers
2016-09-26 13:56:24 +02:00
#FIXME How does it work for S_TYPE_INTERNALBRIDGE? This is set for sparse infill. Likely this does not work.
2013-09-06 19:21:38 +02:00
my $new_internal_solid = $solid = intersection (
$solid ,
2013-07-15 12:14:22 +02:00
[ map $_ -> p , grep { ( $_ -> surface_type == S_TYPE_INTERNAL ) || ( $_ -> surface_type == S_TYPE_INTERNALSOLID ) } @neighbor_fill_surfaces ],
2013-07-17 00:29:09 +02:00
1 ,
2012-09-22 19:04:36 +02:00
);
2013-07-28 10:56:41 +02:00
next EXTERNAL if ! @$new_internal_solid ;
2012-09-22 19:04:36 +02:00
2015-10-26 23:23:03 +01:00
if ( $layerm -> region -> config -> fill_density == 0 ) {
2014-02-07 01:48:47 +01:00
# if we're printing a hollow object we discard any solid shell thinner
# than a perimeter width, since it's probably just crossing a sloping wall
# and it's not wanted in a hollow print even if it would make sense when
# obeying the solid shell count option strictly (DWIM!)
2014-06-09 21:14:48 +02:00
my $margin = $neighbor_layerm -> flow ( FLOW_ROLE_EXTERNAL_PERIMETER ) -> scaled_width ;
2016-11-28 17:33:17 +01:00
my $regularized = offset2 ( $new_internal_solid , - $margin , + $margin , JT_MITER , 5 );
2014-02-07 01:48:47 +01:00
my $too_narrow = diff (
$new_internal_solid ,
2016-11-09 10:24:45 +01:00
$regularized ,
2014-02-07 01:48:47 +01:00
1 ,
);
2016-11-09 10:24:45 +01:00
# Trim the regularized region by the original region.
$new_internal_solid = $solid = intersection (
2014-02-07 01:48:47 +01:00
$new_internal_solid ,
2016-11-09 10:24:45 +01:00
$regularized ,
2014-02-07 01:48:47 +01:00
) if @$too_narrow ;
}
# make sure the new internal solid is wide enough, as it might get collapsed
# when spacing is added in Fill.pm
2016-11-09 10:24:45 +01:00
if ( $layerm -> region -> config -> ensure_vertical_shell_thickness ) {
# The possible thin sickles of top / bottom surfaces on steeply sloping surfaces touch
# the projections of top / bottom perimeters, therefore they will be sufficiently inflated by
# merging them with the projections of the top / bottom perimeters.
} else {
#FIXME Vojtech: Disable this and you will be sorry.
# https://github.com/prusa3d/Slic3r/issues/26 bottom
2014-02-07 01:48:47 +01:00
my $margin = 3 * $layerm -> flow ( FLOW_ROLE_SOLID_INFILL ) -> scaled_width ; # require at least this size
2013-09-06 17:43:40 +02:00
# we use a higher miterLimit here to handle areas with acute angles
# in those cases, the default miterLimit would cut the corner and we'd
# 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.
2013-09-06 19:21:38 +02:00
my $too_narrow = diff (
$new_internal_solid ,
2016-11-28 17:33:17 +01:00
offset2 ( $new_internal_solid , - $margin , + $margin , JT_MITER , 5 ),
2013-03-18 17:55:16 +01:00
1 ,
2013-03-11 18:37:01 +01:00
);
if ( @$too_narrow ) {
2014-02-07 01:48:47 +01:00
# 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
# make sure our grown surfaces don't exceed the fill area
my @grown = @ { intersection (
offset ( $too_narrow , + $margin ),
2015-07-03 23:38:41 +02:00
# Discard bridges as they are grown for anchoring and we can't
# remove such anchors. (This may happen when a bridge is being
# anchored onto a wall where little space remains after the bridge
# is grown, and that little space is an internal solid shell so
# it triggers this too_narrow logic.)
[ map $_ -> p , grep { $_ -> is_internal && ! $_ -> is_bridge } @neighbor_fill_surfaces ],
2014-02-07 01:48:47 +01:00
)};
$new_internal_solid = $solid = [ @grown , @$new_internal_solid ];
2013-03-11 18:37:01 +01:00
}
}
2012-09-22 19:04:36 +02:00
# internal-solid are the union of the existing internal-solid surfaces
# and new ones
my $internal_solid = union_ex ([
( map $_ -> p , grep $_ -> surface_type == S_TYPE_INTERNALSOLID , @neighbor_fill_surfaces ),
2013-09-06 19:21:38 +02:00
@$new_internal_solid ,
2012-09-22 19:04:36 +02:00
]);
2013-03-07 15:47:32 +01:00
# subtract intersections from layer surfaces to get resulting internal surfaces
2012-09-22 19:04:36 +02:00
my $internal = diff_ex (
[ map $_ -> p , grep $_ -> surface_type == S_TYPE_INTERNAL , @neighbor_fill_surfaces ],
[ map @$_ , @$internal_solid ],
2012-05-01 18:51:47 +02:00
1 ,
2012-04-29 12:51:20 +02:00
);
2012-09-22 19:04:36 +02:00
Slic3r:: debugf " %d internal-solid and %d internal surfaces found\n" ,
scalar ( @$internal_solid ), scalar ( @$internal );
2013-03-07 15:47:32 +01:00
# assign resulting internal surfaces to layer
2013-07-15 12:14:22 +02:00
$neighbor_fill_surfaces -> clear ;
2014-11-09 16:23:50 +01:00
$neighbor_fill_surfaces -> append ( $_ )
for map Slic3r::Surface -> new ( expolygon => $_ , surface_type => S_TYPE_INTERNAL ),
@$internal ;
2012-09-22 19:04:36 +02:00
# assign new internal-solid surfaces to layer
2014-11-09 16:23:50 +01:00
$neighbor_fill_surfaces -> append ( $_ )
for map Slic3r::Surface -> new ( expolygon => $_ , surface_type => S_TYPE_INTERNALSOLID ),
@$internal_solid ;
2012-09-22 19:04:36 +02:00
# assign top and bottom surfaces to layer
2014-03-25 01:11:28 +01:00
foreach my $s ( @ { Slic3r::Surface::Collection -> new ( grep { ( $_ -> surface_type == S_TYPE_TOP ) || $_ -> is_bottom } @neighbor_fill_surfaces ) -> group }) {
2012-09-22 19:04:36 +02:00
my $solid_surfaces = diff_ex (
[ map $_ -> p , @$s ],
[ map @$_ , @$internal_solid , @$internal ],
1 ,
);
2014-11-09 16:23:50 +01:00
$neighbor_fill_surfaces -> append ( $_ )
for map $s -> [ 0 ] -> clone ( expolygon => $_ ), @$solid_surfaces ;
2012-09-22 19:04:36 +02:00
}
2012-04-29 12:51:20 +02:00
}
2016-09-26 13:56:24 +02:00
} # foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_BOTTOMBRIDGE)
} # for each layer
} # for each region
# Debugging output.
if ( $SLIC3R_DEBUG_SLICE_PROCESSING ) {
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
for ( my $i = 0 ; $i < $self -> layer_count ; $i ++ ) {
my $layerm = $self -> get_layer ( $i ) -> regions -> [ $region_id ];
$layerm -> export_region_slices_to_svg_debug ( "5_discover_horizontal_shells" );
$layerm -> export_region_fill_surfaces_to_svg_debug ( "5_discover_horizontal_shells" );
} # for each layer
} # for each region
2012-04-29 12:51:20 +02:00
}
}
2016-09-26 13:56:24 +02:00
# combine fill surfaces across layers to honor the "infill every N layers" option
2014-12-08 21:23:42 +01:00
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
2012-07-22 20:48:38 +02:00
sub combine_infill {
2012-04-29 12:51:20 +02:00
my $self = shift ;
2014-01-02 10:44:54 +01:00
2014-12-08 21:23:42 +01:00
# define the type used for voids
my %voidtype = (
& S_TYPE_INTERNAL () => S_TYPE_INTERNALVOID ,
);
2014-12-08 20:14:04 +01:00
# work on each region separately
2014-08-03 18:41:09 +02:00
for my $region_id ( 0 .. ( $self -> print -> region_count - 1 )) {
2014-12-08 21:23:42 +01:00
my $region = $self -> print -> get_region ( $region_id );
2014-01-02 10:44:54 +01:00
my $every = $region -> config -> infill_every_layers ;
2014-12-08 20:14:04 +01:00
next unless $every > 1 && $region -> config -> fill_density > 0 ;
2014-01-02 10:44:54 +01:00
2013-02-22 16:24:24 +01:00
# limit the number of combined layers to the maximum height allowed by this regions' nozzle
2014-12-17 00:34:00 +01:00
my $nozzle_diameter = min (
$self -> print -> config -> get_at ( 'nozzle_diameter' , $region -> config -> infill_extruder - 1 ),
$self -> print -> config -> get_at ( 'nozzle_diameter' , $region -> config -> solid_infill_extruder - 1 ),
);
2013-03-10 12:08:18 +01:00
# define the combinations
2014-12-08 20:14:04 +01:00
my %combine = (); # layer_idx => number of additional combined lower layers
2013-03-10 12:08:18 +01:00
{
my $current_height = my $layers = 0 ;
2014-12-08 20:14:04 +01:00
for my $layer_idx ( 0 .. ( $self -> layer_count - 1 )) {
my $layer = $self -> get_layer ( $layer_idx );
next if $layer -> id == 0 ; # skip first print layer (which may not be first layer in array because of raft)
my $height = $layer -> height ;
2013-03-10 12:08:18 +01:00
2014-12-08 20:14:04 +01:00
# check whether the combination of this layer with the lower layers' buffer
# would exceed max layer height or max combined layer count
2016-03-13 12:24:03 +01:00
if ( $current_height + $height >= $nozzle_diameter + epsilon || $layers >= $every ) {
2014-12-08 20:14:04 +01:00
# append combination to lower layer
$combine { $layer_idx - 1 } = $layers ;
2013-03-10 12:08:18 +01:00
$current_height = $layers = 0 ;
}
$current_height += $height ;
$layers ++ ;
}
2014-12-08 20:14:04 +01:00
# append lower layers (if any) to uppermost layer
$combine { $self -> layer_count - 1 } = $layers ;
2013-03-10 12:08:18 +01:00
}
2013-02-22 16:24:24 +01:00
2014-12-08 20:14:04 +01:00
# loop through layers to which we have assigned layers to combine
for my $layer_idx ( sort keys %combine ) {
next unless $combine { $layer_idx } > 1 ;
# get all the LayerRegion objects to be combined
2014-12-08 21:23:42 +01:00
my @layerms = map $self -> get_layer ( $_ ) -> get_region ( $region_id ),
2014-12-08 20:14:04 +01:00
( $layer_idx - ( $combine { $layer_idx } - 1 ) .. $layer_idx );
2012-04-29 12:51:20 +02:00
2013-06-23 18:21:47 +02:00
# only combine internal infill
for my $type ( S_TYPE_INTERNAL ) {
2013-02-10 12:40:43 +01:00
# we need to perform a multi-layer intersection, so let's split it in pairs
2012-04-29 12:51:20 +02:00
2013-02-10 12:40:43 +01:00
# initialize the intersection with the candidates of the lowest layer
2013-09-06 18:36:38 +02:00
my $intersection = [ map $_ -> expolygon , @ { $layerms [ 0 ] -> fill_surfaces -> filter_by_type ( $type )} ];
2012-09-22 19:04:36 +02:00
2013-02-10 12:40:43 +01:00
# start looping from the second layer and intersect the current intersection with it
for my $layerm ( @layerms [ 1 .. $#layerms ]) {
$intersection = intersection_ex (
[ map @$_ , @$intersection ],
2013-09-06 18:36:38 +02:00
[ map @ { $_ -> expolygon }, @ { $layerm -> fill_surfaces -> filter_by_type ( $type )} ],
2013-02-10 12:40:43 +01:00
);
2012-04-29 12:51:20 +02:00
}
2012-09-22 19:04:36 +02:00
2013-02-18 11:52:47 +01:00
my $area_threshold = $layerms [ 0 ] -> infill_area_threshold ;
2013-02-10 12:40:43 +01:00
@$intersection = grep $_ -> area > $area_threshold , @$intersection ;
next if ! @$intersection ;
Slic3r:: debugf " combining %d %s regions from layers %d-%d\n" ,
scalar ( @$intersection ),
( $type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid' ),
2014-12-08 20:14:04 +01:00
$layer_idx - ( $every - 1 ), $layer_idx ;
2013-02-10 12:40:43 +01:00
# $intersection now contains the regions that can be combined across the full amount of layers
# so let's remove those areas from all layers
2013-02-16 07:53:47 -08:00
2013-07-17 00:48:29 +02:00
my @intersection_with_clearance = map @ { $_ -> offset (
2014-01-05 00:36:33 +01:00
$layerms [ - 1 ] -> flow ( FLOW_ROLE_SOLID_INFILL ) -> scaled_width / 2
+ $layerms [ - 1 ] -> flow ( FLOW_ROLE_PERIMETER ) -> scaled_width / 2
2013-03-17 00:57:58 +01:00
# Because fill areas for rectilinear and honeycomb are grown
# later to overlap perimeters, we need to counteract that too.
2015-11-01 19:03:11 +01:00
+ (( $type == S_TYPE_INTERNALSOLID || $region -> config -> fill_pattern =~ /(rectilinear|grid|line|honeycomb)/ )
2015-01-13 20:51:31 +01:00
? $layerms [ - 1 ] -> flow ( FLOW_ROLE_SOLID_INFILL ) -> scaled_width
2013-03-17 00:57:58 +01:00
: 0 )
2013-07-17 00:48:29 +02:00
)}, @$intersection ;
2013-03-17 00:57:58 +01:00
2013-02-16 07:53:47 -08:00
2013-02-10 12:40:43 +01:00
foreach my $layerm ( @layerms ) {
2013-09-06 18:36:38 +02:00
my @this_type = @ { $layerm -> fill_surfaces -> filter_by_type ( $type )};
2013-07-15 15:26:56 +02:00
my @other_types = map $_ -> clone , grep $_ -> surface_type != $type , @ { $layerm -> fill_surfaces };
2013-02-10 12:40:43 +01:00
2013-03-13 01:03:54 +01:00
my @new_this_type = map Slic3r::Surface -> new ( expolygon => $_ , surface_type => $type ),
2013-02-10 12:40:43 +01:00
@ { diff_ex (
2013-09-06 18:36:38 +02:00
[ map $_ -> p , @this_type ],
2013-02-16 07:53:47 -08:00
[ @intersection_with_clearance ],
2013-02-10 12:40:43 +01:00
)};
# apply surfaces back with adjusted depth to the uppermost layer
2015-10-26 23:23:03 +01:00
if ( $layerm -> layer -> id == $self -> get_layer ( $layer_idx ) -> id ) {
2013-03-13 01:03:54 +01:00
push @new_this_type ,
2013-03-17 01:10:40 +01:00
map Slic3r::Surface -> new (
expolygon => $_ ,
surface_type => $type ,
2015-10-26 23:23:03 +01:00
thickness => sum ( map $_ -> layer -> height , @layerms ),
2013-03-17 01:10:40 +01:00
thickness_layers => scalar ( @layerms ),
),
2013-02-10 12:40:43 +01:00
@$intersection ;
2013-03-13 01:03:54 +01:00
} else {
# save void surfaces
2014-12-08 21:23:42 +01:00
push @new_this_type ,
map Slic3r::Surface -> new ( expolygon => $_ , surface_type => $voidtype { $type }),
2013-03-13 01:03:54 +01:00
@ { intersection_ex (
[ map @ { $_ -> expolygon }, @this_type ],
[ @intersection_with_clearance ],
)};
2012-09-22 19:04:36 +02:00
}
2013-02-10 12:40:43 +01:00
2013-07-14 14:56:43 +02:00
$layerm -> fill_surfaces -> clear ;
2014-11-09 16:23:50 +01:00
$layerm -> fill_surfaces -> append ( $_ ) for ( @new_this_type , @other_types );
2012-04-29 12:51:20 +02:00
}
}
}
}
}
2017-01-30 19:56:46 +01:00
# Used by t/support.t and by GCode.pm to export support line width as a comment.
# To be removed.
2013-12-31 14:33:03 +01:00
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
2014-01-03 18:27:46 +01:00
return Slic3r::Flow -> new_from_width (
2014-04-25 19:39:27 +02:00
width => $self -> config -> support_material_extrusion_width || $self -> config -> extrusion_width ,
2013-12-31 14:33:03 +01:00
role => $role ,
2014-01-05 00:36:33 +01:00
nozzle_diameter => $self -> print -> config -> nozzle_diameter -> [ $extruder - 1 ] // $self -> print -> config -> nozzle_diameter -> [ 0 ],
2013-12-31 14:33:03 +01:00
layer_height => $self -> config -> layer_height ,
bridge_flow_ratio => 0 ,
);
}
2012-04-29 12:51:20 +02:00
1 ;