From 8598b66b0a9dfa19c49598e52d603657d3113fd7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2011 12:29:27 +0100 Subject: [PATCH] Large refactoring. Cleaner logic, smaller memory footprint. --- lib/Slic3r/Fill.pm | 95 ++++++++++++---------- lib/Slic3r/Layer.pm | 104 +++++++----------------- lib/Slic3r/Perimeter.pm | 12 +-- lib/Slic3r/Print.pm | 172 +++++++++++++++------------------------- lib/Slic3r/Skein.pm | 36 +++++---- lib/Slic3r/Surface.pm | 6 +- 6 files changed, 178 insertions(+), 247 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 1c9373fd..9d630c7d 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -11,6 +11,7 @@ use Slic3r::Fill::PlanePath; use Slic3r::Fill::Rectilinear; use Slic3r::Fill::Rectilinear2; use Slic3r::Geometry qw(shortest_path); +use Slic3r::Geometry::Clipper qw(union_ex diff_ex); use XXX; @@ -45,51 +46,65 @@ sub make_fill { printf "Filling layer %d:\n", $layer->id; - # organize $layer->fill_surfaces using a shortest path search - @{ $layer->fill_surfaces } = @{shortest_path([ - map [ $_->[0]->contour->points->[0], $_ ], grep @$_, @{ $layer->fill_surfaces }, - ])}; - - foreach my $surfaces (@{ $layer->fill_surfaces }) { - - # organize $surfaces using a shortest path search - @$surfaces = @{shortest_path([ - map [ $_->contour->points->[0], $_ ], @$surfaces, - ])}; - - SURFACE: foreach my $surface (@$surfaces) { - my $filler = $Slic3r::fill_pattern; - my $density = $Slic3r::fill_density; - my $flow_width = $Slic3r::flow_width; + # merge overlapping surfaces + my @surfaces = (); + { + my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layer->surfaces}; + foreach my $group (Slic3r::Surface->group({merge_solid => 1}, @{$layer->surfaces})) { + my $union = union_ex([ map $_->p, @$group ]); - # force 100% density and rectilinear fill for external surfaces - if ($surface->surface_type ne 'internal') { - my $is_bridge = $layer->id > 0 && $surface->surface_type eq 'bottom'; - $density = 1; - $filler = $is_bridge ? 'rectilinear' : $Slic3r::solid_fill_pattern; - $flow_width = $Slic3r::nozzle_diameter if $is_bridge; - } else { - next SURFACE unless $density > 0; + # subtract surfaces having a defined bridge_angle from any other + if (@surfaces_with_bridge_angle && !defined $group->[0]->bridge_angle) { + $union = diff_ex( + [ map @$_, @$union ], + [ map $_->p, @surfaces_with_bridge_angle ], + ); } - my @paths = $self->fillers->{$filler}->fill_surface( - $surface, - density => $density, - flow_width => $flow_width, - ); - - # save into layer - push @{ $layer->fills }, Slic3r::ExtrusionPath::Collection->new( - paths => [ - map Slic3r::ExtrusionPath->cast( - [ @$_ ], - depth_layers => $surface->depth_layers, - ), @paths, - ], - ); - $layer->fills->[-1]->cleanup; + push @surfaces, map Slic3r::Surface->cast_from_expolygon($_, + surface_type => $group->[0]->surface_type, + bridge_angle => $group->[0]->bridge_angle, + ), @$union; } } + + # organize infill surfaces using a shortest path search + @surfaces = @{shortest_path([ + map [ $_->contour->points->[0], $_ ], @surfaces, + ])}; + + SURFACE: foreach my $surface (@surfaces) { + my $filler = $Slic3r::fill_pattern; + my $density = $Slic3r::fill_density; + my $flow_width = $Slic3r::flow_width; + + # force 100% density and rectilinear fill for external surfaces + if ($surface->surface_type ne 'internal') { + my $is_bridge = $layer->id > 0 && $surface->surface_type eq 'bottom'; + $density = 1; + $filler = $is_bridge ? 'rectilinear' : $Slic3r::solid_fill_pattern; + $flow_width = $Slic3r::nozzle_diameter if $is_bridge; + } else { + next SURFACE unless $density > 0; + } + + my @paths = $self->fillers->{$filler}->fill_surface( + $surface, + density => $density, + flow_width => $flow_width, + ); + + # save into layer + push @{ $layer->fills }, Slic3r::ExtrusionPath::Collection->new( + paths => [ + map Slic3r::ExtrusionPath->cast( + [ @$_ ], + depth_layers => $surface->depth_layers, + ), @paths, + ], + ); + $layer->fills->[-1]->cleanup; + } } 1; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 5ae594ea..a4ad4738 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -29,20 +29,6 @@ has 'surfaces' => ( default => sub { [] }, ); -# collection of surfaces representing bridges -has 'bridges' => ( - is => 'rw', - #isa => 'ArrayRef[Slic3r::Surface]', - default => sub { [] }, -); - -# collection of surfaces to make perimeters for -has 'perimeter_surfaces' => ( - is => 'rw', - #isa => 'ArrayRef[Slic3r::Surface]', - default => sub { [] }, -); - # ordered collection of extrusion paths to build all perimeters has 'perimeters' => ( is => 'rw', @@ -58,10 +44,10 @@ has 'skirts' => ( ); # collection of surfaces generated by offsetting the innermost perimeter(s) -# they represent boundaries of areas to fill (grouped by original objects) -has 'fill_surfaces' => ( +# they represent boundaries of areas to fill +has 'fill_boundaries' => ( is => 'rw', - #isa => 'ArrayRef[ArrayRef[Slic3r::Surface]]', + #isa => 'ArrayRef[Slic3r::Surface]', default => sub { [] }, ); @@ -352,6 +338,8 @@ sub remove_small_perimeters { sub process_bridges { my $self = shift; + my @bridges = (); + # a bottom surface on a layer > 0 is either a bridge or a overhang # or a combination of both; any top surface is a candidate for # reverse bridge processing @@ -426,8 +414,8 @@ sub process_bridges { # now, extend our bridge by taking a portion of supporting surfaces { - # offset the bridge by the specified amount of mm - my $bridge_overlap = 2 * $Slic3r::perimeters * $Slic3r::flow_width / $Slic3r::resolution; + # offset the bridge by the specified amount of mm (minimum 3) + my $bridge_overlap = 3 / $Slic3r::resolution; my ($bridge_offset) = $expolygon->contour->offset($bridge_overlap, $Slic3r::resolution * 100, JT_MITER, 2); # calculate the new bridge @@ -436,7 +424,7 @@ sub process_bridges { [ $bridge_offset ], ); - push @{$self->bridges}, map Slic3r::Surface->cast_from_expolygon($_, + push @bridges, map Slic3r::Surface->cast_from_expolygon($_, surface_type => $surface->surface_type, bridge_angle => $bridge_angle, ), @$intersection; @@ -446,86 +434,52 @@ sub process_bridges { # now we need to merge bridges to avoid overlapping { # build a list of unique bridge types - my @surface_groups = Slic3r::Surface->group(@{$self->bridges}); + my @surface_groups = Slic3r::Surface->group(@bridges); # merge bridges of the same type, removing any of the bridges already merged; # the order of @surface_groups determines the priority between bridges having # different surface_type or bridge_angle - @{$self->bridges} = (); + @bridges = (); foreach my $surfaces (@surface_groups) { my $union = union_ex([ map $_->p, @$surfaces ]); my $diff = diff_ex( [ map @$_, @$union ], - [ map $_->p, @{$self->bridges} ], + [ map $_->p, @bridges ], ); - push @{$self->bridges}, map Slic3r::Surface->cast_from_expolygon($_, + push @bridges, map Slic3r::Surface->cast_from_expolygon($_, surface_type => $surfaces->[0]->surface_type, bridge_angle => $surfaces->[0]->bridge_angle, ), @$union; } } -} - -# generates a set of surfaces that will be used to make perimeters -# thus, we need to merge internal surfaces and bridges -sub detect_perimeter_surfaces { - my $self = shift; - # little optimization: skip the Clipper UNION if we have no bridges - if (!@{$self->bridges}) { - push @{$self->perimeter_surfaces}, @{$self->surfaces}; - } else { - my $union = union_ex([ - (map $_->p, grep $_->surface_type =~ /internal/, @{$self->surfaces}), - (map $_->p, @{$self->bridges}), - ]); + # apply bridges to layer + { + my @surfaces = @{$self->surfaces}; + @{$self->surfaces} = (); - # schedule perimeters for internal surfaces merged with bridges - push @{$self->perimeter_surfaces}, - map Slic3r::Surface->cast_from_expolygon($_, surface_type => 'internal'), - @$union; - - # schedule perimeters for the remaining surfaces - foreach my $type (qw(top bottom)) { - my $diff = diff_ex( - [ map $_->p, grep $_->surface_type eq $type, @{$self->surfaces} ], - [ map @$_, @$union ], - ); - push @{$self->perimeter_surfaces}, - map Slic3r::Surface->cast_from_expolygon($_, surface_type => $type), - @$diff; - } - } -} - -# splits fill_surfaces in internal and bridge surfaces -sub split_bridges_fills { - my $self = shift; - - foreach my $surfaces (@{$self->fill_surfaces}) { - my @surfaces = @$surfaces; - @$surfaces = (); - - # intersect fill_surfaces with bridges to get actual bridges - foreach my $bridge (@{$self->bridges}) { - my $intersection = intersection_ex( + # intersect layer surfaces with bridges to get actual bridges + foreach my $bridge (@bridges) { + my $actual_bridge = intersection_ex( [ map $_->p, @surfaces ], [ $bridge->p ], ); - push @$surfaces, map Slic3r::Surface->cast_from_expolygon($_, + push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_, surface_type => $bridge->surface_type, bridge_angle => $bridge->bridge_angle, - ), @$intersection; + ), @$actual_bridge; } - # difference between fill_surfaces and bridges are the other surfaces - foreach my $type (qw(top bottom internal internal-solid)) { - my @my_surfaces = grep $_->surface_type eq $type, @surfaces; - my $difference = diff_ex([ map $_->p, @my_surfaces ], [ map $_->p, @{$self->bridges} ]); - push @$surfaces, map Slic3r::Surface->cast_from_expolygon($_, - surface_type => $type), @$difference; + # difference between layer surfaces and bridges are the other surfaces + foreach my $group (Slic3r::Surface->group(@surfaces)) { + my $difference = diff_ex( + [ map $_->p, @$group ], + [ map $_->p, @bridges ], + ); + push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_, + surface_type => $group->[0]->surface_type), @$difference; } } } diff --git a/lib/Slic3r/Perimeter.pm b/lib/Slic3r/Perimeter.pm index 02c3a858..0e4d7c04 100644 --- a/lib/Slic3r/Perimeter.pm +++ b/lib/Slic3r/Perimeter.pm @@ -28,12 +28,12 @@ sub make_perimeter { # ) my @perimeters = (); # one item per depth; each item - # organize $layer->perimeter_surfaces using a shortest path search - @{ $layer->perimeter_surfaces } = @{shortest_path([ - map [ $_->contour->points->[0], $_ ], @{ $layer->perimeter_surfaces }, + # organize perimeter surfaces using a shortest path search + my @surfaces = @{shortest_path([ + map [ $_->contour->points->[0], $_ ], @{$layer->surfaces}, ])}; - foreach my $surface (@{ $layer->perimeter_surfaces }) { + foreach my $surface (@surfaces) { # the outer loop must be offsetted by half extrusion width inwards my @last_offsets = ($surface->expolygon); my $distance = $Slic3r::flow_width / 2 / $Slic3r::resolution; @@ -52,11 +52,11 @@ sub make_perimeter { # create one more offset to be used as boundary for fill { $distance -= $Slic3r::flow_width * $Slic3r::perimeter_infill_overlap_ratio / $Slic3r::resolution; - my @fill_surfaces = map Slic3r::Surface->cast_from_expolygon + my @fill_boundaries = map Slic3r::Surface->cast_from_expolygon ($_, surface_type => $surface->surface_type), map $_->offset_ex(-$distance), @last_offsets; - push @{ $layer->fill_surfaces }, [@fill_surfaces] if @fill_surfaces; + push @{ $layer->fill_boundaries }, @fill_boundaries if @fill_boundaries; } } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 85a9fc7b..dbbb7c63 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -90,6 +90,17 @@ sub detect_surfaces_type { @$expolygons; }; + # clip surfaces to the fill boundaries + foreach my $layer (@{$self->layers}) { + my $intersection = intersection_ex( + [ map $_->p, @{$layer->surfaces} ], + [ map $_->p, @{$layer->fill_boundaries} ], + ); + @{$layer->surfaces} = map Slic3r::Surface->cast_from_expolygon + ($_, surface_type => 'internal'), + @$intersection; + } + for (my $i = 0; $i < $self->layer_count; $i++) { my $layer = $self->layers->[$i]; Slic3r::debugf "Detecting solid surfaces for layer %d\n", $layer->id; @@ -110,22 +121,6 @@ sub detect_surfaces_type { @top = $surface_difference->($layer->surfaces, $upper_surfaces, 'top'); - # now check whether each resulting top surfaces is large enough to have its - # own perimeters or whether it may be sufficient to use the lower layer's - # perimeters - # offset upper layer's surfaces - my $upper_surfaces_offsetted; - { - my $distance = $Slic3r::flow_width * ($Slic3r::perimeters) / $Slic3r::resolution; - $upper_surfaces_offsetted = offset([ map $_->p, @{$upper_layer->surfaces} ], $distance, 100, JT_MITER, 2); - } - - @top = grep { - my $surface = $_; - my $diff = diff_ex([ map $_->p, $surface ], $upper_surfaces_offsetted); - @$diff; - } @top; - } else { # if no upper layer, all surfaces of this one are solid @top = @{$layer->surfaces}; @@ -152,18 +147,6 @@ sub detect_surfaces_type { @{$surface->contour->points} = map Slic3r::Point->new($_), @{ $offset->[0] }; } - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "layer_" . $layer->id . "_surfaces.svg", - green_polygons => [ map $_->p, @{$layer->surfaces} ], - red_polygons => [ map $_->p, @{$lower_layer->surfaces} ], - ); - Slic3r::SVG::output(undef, "layer_" . $layer->id . "_diff.svg", - red_polygons => [ map $_->p, @bottom ], - ); - exit if $layer->id == 3; - } - } else { # if no lower layer, all surfaces of this one are solid @bottom = @{$layer->surfaces}; @@ -185,13 +168,6 @@ sub detect_surfaces_type { # save surfaces to layer $layer->surfaces([ @bottom, @top, @internal ]); - #use Slic3r::SVG; - #Slic3r::SVG::output(undef, "layer_" . $layer->id . ".svg", - # white_polygons => [ map $_->p, @internal ], - # green_polygons => [ map $_->p, @bottom ], - # red_polygons => [ map $_->p, @top ], - #); - Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", $layer->id, scalar(@bottom), scalar(@top), scalar(@internal); } @@ -206,7 +182,7 @@ sub discover_horizontal_shells { my $layer = $self->layers->[$i]; foreach my $type (qw(top bottom)) { # find surfaces of current type for current layer - my @surfaces = grep $_->surface_type eq $type, map @$_, @{$layer->fill_surfaces} or next; + my @surfaces = grep $_->surface_type eq $type, @{$layer->surfaces} or next; my $surfaces_p = [ map $_->p, @surfaces ]; Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", $i, scalar(@surfaces), $type; @@ -218,56 +194,55 @@ sub discover_horizontal_shells { next if $n < 0 || $n >= $self->layer_count; Slic3r::debugf " looking for neighbors on layer %d...\n", $n; - foreach my $surfaces (@{$self->layers->[$n]->fill_surfaces}) { - my @neighbor = @$surfaces; - - # find intersection between @surfaces and current layer's surfaces - # intersections have contours and holes - my $new_internal_solid = intersection_ex( - $surfaces_p, - [ map $_->p, grep $_->surface_type =~ /internal/, @neighbor ], + my $surfaces = $self->layers->[$n]->surfaces; + my @neighbor = @$surfaces; + + # find intersection between @surfaces and current layer's surfaces + # intersections have contours and holes + my $new_internal_solid = intersection_ex( + $surfaces_p, + [ map $_->p, grep $_->surface_type =~ /internal/, @neighbor ], + ); + next if !@$new_internal_solid; + + # internal-solid are the union of the existing internal-solid surfaces + # and new ones + my $internal_solid = union_ex([ + ( map $_->p, grep $_->surface_type eq 'internal-solid', @neighbor ), + ( map @$_, @$new_internal_solid ), + ]); + + # subtract intersections from layer surfaces to get resulting inner surfaces + my $internal = diff_ex( + [ map $_->p, grep $_->surface_type eq 'internal', @neighbor ], + [ map @$_, @$internal_solid ], + ); + Slic3r::debugf " %d internal-solid and %d internal surfaces found\n", + scalar(@$internal_solid), scalar(@$internal); + + # Note: due to floating point math we're going to get some very small + # polygons as $internal; they will be removed by removed_small_features() + + # assign resulting inner surfaces to layer + @$surfaces = (); + push @$surfaces, Slic3r::Surface->cast_from_expolygon + ($_, surface_type => 'internal') + for @$internal; + + # assign new internal-solid surfaces to layer + push @$surfaces, Slic3r::Surface->cast_from_expolygon + ($_, surface_type => 'internal-solid') + for @$internal_solid; + + # assign top and bottom surfaces to layer + foreach my $s (Slic3r::Surface->group(grep $_->surface_type =~ /top|bottom/, @neighbor)) { + my $solid_surfaces = diff_ex( + [ map $_->p, @$s ], + [ map @$_, @$internal_solid, @$internal ], ); - next if !@$new_internal_solid; - - # internal-solid are the union of the existing internal-solid surfaces - # and new ones - my $internal_solid = union_ex([ - ( map $_->p, grep $_->surface_type eq 'internal-solid', @neighbor ), - ( map @$_, @$new_internal_solid ), - ]); - - # subtract intersections from layer surfaces to get resulting inner surfaces - my $internal = diff_ex( - [ map $_->p, grep $_->surface_type eq 'internal', @neighbor ], - [ map @$_, @$internal_solid ], - ); - Slic3r::debugf " %d internal-solid and %d internal surfaces found\n", - scalar(@$internal_solid), scalar(@$internal); - - # Note: due to floating point math we're going to get some very small - # polygons as $internal; they will be removed by removed_small_features() - - # assign resulting inner surfaces to layer - @$surfaces = (); push @$surfaces, Slic3r::Surface->cast_from_expolygon - ($_, surface_type => 'internal') - for @$internal; - - # assign new internal-solid surfaces to layer - push @$surfaces, Slic3r::Surface->cast_from_expolygon - ($_, surface_type => 'internal-solid') - for @$internal_solid; - - # assign top and bottom surfaces to layer - foreach my $s (Slic3r::Surface->group(grep $_->surface_type =~ /top|bottom/, @neighbor)) { - my $solid_surfaces = diff_ex( - [ map $_->p, @$s ], - [ map @$_, @$internal_solid, @$internal ], - ); - push @$surfaces, Slic3r::Surface->cast_from_expolygon - ($_, surface_type => $s->[0]->surface_type, bridge_angle => $s->[0]->bridge_angle) - for @$solid_surfaces; - } + ($_, surface_type => $s->[0]->surface_type, bridge_angle => $s->[0]->bridge_angle) + for @$solid_surfaces; } } } @@ -298,17 +273,6 @@ sub extrude_skirt { push @{$_->skirts}, @skirts for @layers; } -sub extrude_perimeters { - my $self = shift; - - my $perimeter_extruder = Slic3r::Perimeter->new; - - foreach my $layer (@{ $self->layers }) { - $layer->detect_perimeter_surfaces; - $perimeter_extruder->make_perimeter($layer); - } -} - # combine fill surfaces across layers sub infill_every_layers { my $self = shift; @@ -321,7 +285,7 @@ sub infill_every_layers { my $layer = $self->layer($i); # skip layer if no internal fill surfaces - next if !grep $_->surface_type eq 'internal', map @$_, @{$layer->fill_surfaces}; + next if !grep $_->surface_type eq 'internal', map @$_, @{$layer->surfaces}; # for each possible depth, look for intersections with the lower layer # we do this from the greater depth to the smaller @@ -331,10 +295,10 @@ sub infill_every_layers { # select surfaces of the lower layer having the depth we're looking for my @lower_surfaces = grep $_->depth_layers == $d && $_->surface_type eq 'internal', - map @$_, @{$lower_layer->fill_surfaces}; + map @$_, @{$lower_layer->surfaces}; next if !@lower_surfaces; # process each group of surfaces separately - foreach my $surfaces (@{$layer->fill_surfaces}) { + foreach my $surfaces (@{$layer->surfaces}) { # calculate intersection between our surfaces and theirs my $intersection = intersection_ex( [ map $_->p, grep $_->depth_layers <= $d, @lower_surfaces ], @@ -371,7 +335,7 @@ sub infill_every_layers { } # now we remove the intersections from lower layer - foreach my $lower_surfaces (@{$lower_layer->fill_surfaces}) { + foreach my $lower_surfaces (@{$lower_layer->surfaces}) { my @new_surfaces = (); push @new_surfaces, grep $_->surface_type ne 'internal', @$lower_surfaces; foreach my $depth (1..$Slic3r::infill_every_layers) { @@ -395,16 +359,6 @@ sub infill_every_layers { } } -sub extrude_fills { - my $self = shift; - - my $fill_extruder = Slic3r::Fill->new('print' => $self); - - foreach my $layer (@{ $self->layers }) { - $fill_extruder->make_fill($layer); - } -} - sub export_gcode { my $self = shift; my ($file) = @_; diff --git a/lib/Slic3r/Skein.pm b/lib/Slic3r/Skein.pm index 1e4d3dd5..b5426bde 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -19,33 +19,34 @@ sub go { # each layer has surfaces with holes my $print = Slic3r::Print->new_from_stl($self->input_file); - # this will detect the type of each surface (top/bottom/internal) - # by splitting them if necessary + # make skirt + $print->extrude_skirt; + + # make perimeters + # this will add a set of extrusion loops to each layer + # as well as generate infill boundaries + { + my $perimeter_maker = Slic3r::Perimeter->new; + $perimeter_maker->make_perimeter($_) for @{$print->layers}; + } + + # this will prepare surfaces for perimeters by merging all + # surfaces in each layer; it will also clip $layer->surfaces + # to infill boundaries and split them in top/bottom/internal surfaces $print->detect_surfaces_type; # this will remove unprintable surfaces # (those that are too tight for extrusion) $_->remove_small_surfaces for @{$print->layers}; - # make bridges printable - # this will add a set of bridges to each layer + # this will detect bridges and reverse bridges + # and rearrange top/bottom/internal surfaces $_->process_bridges for @{$print->layers}; - # make skirt - $print->extrude_skirt; - - # make perimeters - # this will add a set of extrusion loops to each layer - # as well as a set of surfaces to be filled - $print->extrude_perimeters; - # this will remove unprintable perimeter loops # (those that are too tight for extrusion) $_->remove_small_perimeters for @{$print->layers}; - # split fill_surfaces in internal and bridge surfaces - $_->split_bridges_fills for @{$print->layers}; - # detect which fill surfaces are near external layers # they will be split in internal and internal-solid surfaces $print->discover_horizontal_shells; @@ -54,7 +55,10 @@ sub go { $print->infill_every_layers; # this will generate extrusion paths for each layer - $print->extrude_fills; + { + my $fill_maker = Slic3r::Fill->new('print' => $print); + $fill_maker->make_fill($_) for @{$print->layers}; + } # output everything to a GCODE file if (!$self->output_file) { diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index dcb41569..81194537 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -52,9 +52,13 @@ sub cast_from_expolygon { # static method to group surfaces having same surface_type and bridge_angle sub group { my $class = shift; + my $params = ref $_[0] eq 'HASH' ? shift(@_) : {}; my (@surfaces) = @_; - my $unique_type = sub { $_[0]->surface_type . "_" . ($_[0]->bridge_angle || '') }; + my $unique_type = sub { + ($params->{merge_solid} && $_[0]->surface_type =~ /top|bottom|solid/ + ? 'solid' : $_[0]->surface_type) . "_" . ($_[0]->bridge_angle || ''); + }; my @unique_types = (); foreach my $surface (@surfaces) { my $type = $unique_type->($surface);