mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Bugfix: some bridges were not detected correctly. #57
This commit is contained in:
		
							parent
							
								
									52270d6b95
								
							
						
					
					
						commit
						b03afc7f1c
					
				
					 6 changed files with 87 additions and 59 deletions
				
			
		| 
						 | 
				
			
			@ -51,8 +51,8 @@ sub make_fill {
 | 
			
		|||
    # 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 @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layer->fill_surfaces};
 | 
			
		||||
        foreach my $group (Slic3r::Surface->group({merge_solid => 1}, @{$layer->fill_surfaces})) {
 | 
			
		||||
            my $union = union_ex([ map $_->p, @$group ]);
 | 
			
		||||
            
 | 
			
		||||
            # subtract surfaces having a defined bridge_angle from any other
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ sub make_fill {
 | 
			
		|||
        my $is_solid = $surface->surface_type =~ /^(top|bottom)$/;
 | 
			
		||||
        
 | 
			
		||||
        # force 100% density and rectilinear fill for external surfaces
 | 
			
		||||
        if (($surface->surface_type ne 'internal') && ($Slic3r::solid_layers >= 1)) {
 | 
			
		||||
        if ($surface->surface_type ne 'internal') {
 | 
			
		||||
            $density = 1;
 | 
			
		||||
            $filler = $is_bridge ? 'rectilinear' : $Slic3r::solid_fill_pattern;
 | 
			
		||||
            $flow_width = $Slic3r::nozzle_diameter if $is_bridge;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,13 @@ has 'surfaces' => (
 | 
			
		|||
    default => sub { [] },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
# collection of surfaces for infill
 | 
			
		||||
has 'fill_surfaces' => (
 | 
			
		||||
    is      => 'rw',
 | 
			
		||||
    #isa     => 'ArrayRef[Slic3r::Surface]',
 | 
			
		||||
    default => sub { [] },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
# ordered collection of extrusion paths to build all perimeters
 | 
			
		||||
has 'perimeters' => (
 | 
			
		||||
    is      => 'rw',
 | 
			
		||||
| 
						 | 
				
			
			@ -77,24 +84,6 @@ sub print_z {
 | 
			
		|||
        + ($self->id * $Slic3r::layer_height)) / $Slic3r::resolution;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_surface {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my (@vertices) = @_;
 | 
			
		||||
    
 | 
			
		||||
    # convert arrayref points to Point objects
 | 
			
		||||
    @vertices = map Slic3r::Point->new($_), @vertices;
 | 
			
		||||
    
 | 
			
		||||
    my $surface = Slic3r::Surface->new(
 | 
			
		||||
        contour => Slic3r::Polyline::Closed->new(points => \@vertices),
 | 
			
		||||
    );
 | 
			
		||||
    push @{ $self->surfaces }, $surface;
 | 
			
		||||
    
 | 
			
		||||
    # make sure our contour has its points in counter-clockwise order
 | 
			
		||||
    $surface->contour->make_counter_clockwise;
 | 
			
		||||
    
 | 
			
		||||
    return $surface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_line {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($line) = @_;
 | 
			
		||||
| 
						 | 
				
			
			@ -125,14 +114,52 @@ sub make_surfaces {
 | 
			
		|||
    #);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub prepare_fill_surfaces {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my @surfaces = @{$self->surfaces};
 | 
			
		||||
        
 | 
			
		||||
    # merge too small internal surfaces with their surrounding tops
 | 
			
		||||
    # (if they're too small, they can be treated as solid)
 | 
			
		||||
    {
 | 
			
		||||
        my $min_area = ((7 * $Slic3r::flow_width / $Slic3r::resolution)**2) * PI;
 | 
			
		||||
        my $small_internal = [
 | 
			
		||||
            grep { $_->expolygon->contour->area <= $min_area }
 | 
			
		||||
            grep { $_->surface_type eq 'internal' }
 | 
			
		||||
            @surfaces
 | 
			
		||||
        ];
 | 
			
		||||
        foreach my $s (@$small_internal) {
 | 
			
		||||
            @surfaces = grep $_ ne $s, @surfaces;
 | 
			
		||||
        }
 | 
			
		||||
        my $union = union_ex([
 | 
			
		||||
            (map $_->p, grep $_->surface_type eq 'top', @surfaces),
 | 
			
		||||
            (map @$_, map $_->expolygon->safety_offset, @$small_internal),
 | 
			
		||||
        ]);
 | 
			
		||||
        my @top = map Slic3r::Surface->cast_from_expolygon($_, surface_type => 'top'), @$union;
 | 
			
		||||
        @surfaces = (grep($_->surface_type ne 'top', @surfaces), @top);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # remove top/bottom surfaces
 | 
			
		||||
    if ($Slic3r::solid_layers == 0) {
 | 
			
		||||
        @surfaces = grep $_->surface_type eq 'internal', @surfaces;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # remove internal surfaces
 | 
			
		||||
    if ($Slic3r::fill_density == 0) {
 | 
			
		||||
        @surfaces = grep $_->surface_type ne 'internal', @surfaces;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    $self->fill_surfaces([@surfaces]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub remove_small_surfaces {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my @good_surfaces = ();
 | 
			
		||||
    
 | 
			
		||||
    my $distance = ($Slic3r::flow_width / 2 / $Slic3r::resolution);
 | 
			
		||||
    
 | 
			
		||||
    my @surfaces = @{$self->surfaces};
 | 
			
		||||
    @{$self->surfaces} = ();
 | 
			
		||||
    my @surfaces = @{$self->fill_surfaces};
 | 
			
		||||
    @{$self->fill_surfaces} = ();
 | 
			
		||||
    foreach my $surface (@surfaces) {
 | 
			
		||||
        # offset inwards
 | 
			
		||||
        my @offsets = $surface->expolygon->offset_ex(-$distance);
 | 
			
		||||
| 
						 | 
				
			
			@ -144,13 +171,13 @@ sub remove_small_surfaces {
 | 
			
		|||
        # the difference between $surface->expolygon and @offsets 
 | 
			
		||||
        # is what we can't print since it's too small
 | 
			
		||||
        
 | 
			
		||||
        push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
        push @{$self->fill_surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
            surface_type => $surface->surface_type), @offsets;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Slic3r::debugf "removed %d small surfaces at layer %d\n",
 | 
			
		||||
        (@surfaces - @{$self->surfaces}), $self->id 
 | 
			
		||||
        if @{$self->surfaces} != @surfaces;
 | 
			
		||||
        (@surfaces - @{$self->fill_surfaces}), $self->id 
 | 
			
		||||
        if @{$self->fill_surfaces} != @surfaces;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub remove_small_perimeters {
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +205,7 @@ sub process_bridges {
 | 
			
		|||
    
 | 
			
		||||
    my @solid_surfaces = grep {
 | 
			
		||||
        ($_->surface_type eq 'bottom' && $self->id > 0) || $_->surface_type eq 'top'
 | 
			
		||||
    } @{$self->surfaces} or return;
 | 
			
		||||
    } @{$self->fill_surfaces} or return;
 | 
			
		||||
    
 | 
			
		||||
    my @internal_surfaces = grep $_->surface_type =~ /internal/, @{$self->surfaces};
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -197,11 +224,13 @@ sub process_bridges {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
            #use Slic3r::SVG;
 | 
			
		||||
            #Slic3r::SVG::output(undef, "bridge.svg",
 | 
			
		||||
            #    green_polygons  => [ map $_->p, @supporting_surfaces ],
 | 
			
		||||
            #    red_polygons    => [ @$expolygon ],
 | 
			
		||||
            #);
 | 
			
		||||
        if (0) {
 | 
			
		||||
            require "Slic3r/SVG.pm";
 | 
			
		||||
            Slic3r::SVG::output(undef, "bridge.svg",
 | 
			
		||||
                green_polygons  => [ map $_->p, @supporting_surfaces ],
 | 
			
		||||
                red_polygons    => [ @$expolygon ],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        next SURFACE unless @supporting_surfaces;
 | 
			
		||||
        Slic3r::debugf "  Found $description on layer %d with %d support(s)\n", 
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +244,8 @@ sub process_bridges {
 | 
			
		|||
            my @edges = ();  # edges are POLYLINES
 | 
			
		||||
            foreach my $supporting_surface (@supporting_surfaces) {
 | 
			
		||||
                my @surface_edges = $supporting_surface->contour->clip_with_polygon($contour_offset);
 | 
			
		||||
                if (@surface_edges == 1 && @{$supporting_surface->contour->p} == @{$surface_edges[0]->p}) {
 | 
			
		||||
                if (@supporting_surfaces == 1 && @surface_edges == 1
 | 
			
		||||
                    && @{$supporting_surface->contour->p} == @{$surface_edges[0]->p}) {
 | 
			
		||||
                    $bridge_over_hole = 1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    foreach my $edge (@surface_edges) {
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +264,7 @@ sub process_bridges {
 | 
			
		|||
                Slic3r::SVG::output(undef, "bridge.svg",
 | 
			
		||||
                    polylines       => [ map $_->p, @edges ],
 | 
			
		||||
                );
 | 
			
		||||
                exit if $self->id == 30;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (@edges == 2) {
 | 
			
		||||
| 
						 | 
				
			
			@ -288,8 +319,8 @@ sub process_bridges {
 | 
			
		|||
    
 | 
			
		||||
    # apply bridges to layer
 | 
			
		||||
    {
 | 
			
		||||
        my @surfaces = @{$self->surfaces};
 | 
			
		||||
        @{$self->surfaces} = ();
 | 
			
		||||
        my @surfaces = @{$self->fill_surfaces};
 | 
			
		||||
        @{$self->fill_surfaces} = ();
 | 
			
		||||
        
 | 
			
		||||
        # intersect layer surfaces with bridges to get actual bridges
 | 
			
		||||
        foreach my $bridge (@bridges) {
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +329,7 @@ sub process_bridges {
 | 
			
		|||
                [ $bridge->p ],
 | 
			
		||||
            );
 | 
			
		||||
            
 | 
			
		||||
            push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
            push @{$self->fill_surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
                surface_type => $bridge->surface_type,
 | 
			
		||||
                bridge_angle => $bridge->bridge_angle,
 | 
			
		||||
            ), @$actual_bridge;
 | 
			
		||||
| 
						 | 
				
			
			@ -310,7 +341,7 @@ sub process_bridges {
 | 
			
		|||
                [ map $_->p, @$group ],
 | 
			
		||||
                [ map $_->p, @bridges ],
 | 
			
		||||
            );
 | 
			
		||||
            push @{$self->surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
            push @{$self->fill_surfaces}, map Slic3r::Surface->cast_from_expolygon($_,
 | 
			
		||||
                surface_type => $group->[0]->surface_type), @$difference;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -205,14 +205,7 @@ sub detect_surfaces_type {
 | 
			
		|||
        # find top surfaces (difference between current surfaces
 | 
			
		||||
        # of current layer and upper one)
 | 
			
		||||
        if ($upper_layer) {
 | 
			
		||||
            # only consider those upper surfaces that are not small
 | 
			
		||||
            # (if they're too small, the interface with them can be treated
 | 
			
		||||
            # like a continuous solid surface instead of cutting a little
 | 
			
		||||
            # internal surface in it)
 | 
			
		||||
            my $min_area = ((7 * $Slic3r::flow_width / $Slic3r::resolution)**2) * PI;
 | 
			
		||||
            my $upper_surfaces = [ grep { $_->expolygon->contour->area > $min_area } @{$upper_layer->surfaces} ];
 | 
			
		||||
            
 | 
			
		||||
            @top = $surface_difference->($layer->surfaces, $upper_surfaces, 'top');
 | 
			
		||||
            @top = $surface_difference->($layer->surfaces, $upper_layer->surfaces, 'top');
 | 
			
		||||
            
 | 
			
		||||
        } else {
 | 
			
		||||
            # if no upper layer, all surfaces of this one are solid
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +268,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, @{$layer->surfaces} or next;
 | 
			
		||||
            my @surfaces = grep $_->surface_type eq $type, @{$layer->fill_surfaces} or next;
 | 
			
		||||
            my $surfaces_p = [ map $_->p, @surfaces ];
 | 
			
		||||
            Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n",
 | 
			
		||||
                $i, scalar(@surfaces), $type;
 | 
			
		||||
| 
						 | 
				
			
			@ -287,7 +280,7 @@ sub discover_horizontal_shells {
 | 
			
		|||
                next if $n < 0 || $n >= $self->layer_count;
 | 
			
		||||
                Slic3r::debugf "  looking for neighbors on layer %d...\n", $n;
 | 
			
		||||
                
 | 
			
		||||
                my $surfaces = $self->layers->[$n]->surfaces;
 | 
			
		||||
                my $surfaces = $self->layers->[$n]->fill_surfaces;
 | 
			
		||||
                my @neighbor = @$surfaces;
 | 
			
		||||
                
 | 
			
		||||
                # find intersection between @surfaces and current layer's surfaces
 | 
			
		||||
| 
						 | 
				
			
			@ -378,7 +371,7 @@ sub infill_every_layers {
 | 
			
		|||
        my $layer = $self->layer($i);
 | 
			
		||||
        
 | 
			
		||||
        # skip layer if no internal fill surfaces
 | 
			
		||||
        next if !grep $_->surface_type eq 'internal', @{$layer->surfaces};
 | 
			
		||||
        next if !grep $_->surface_type eq 'internal', @{$layer->fill_surfaces};
 | 
			
		||||
        
 | 
			
		||||
        # for each possible depth, look for intersections with the lower layer
 | 
			
		||||
        # we do this from the greater depth to the smaller
 | 
			
		||||
| 
						 | 
				
			
			@ -388,13 +381,13 @@ 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',
 | 
			
		||||
                @{$lower_layer->surfaces};
 | 
			
		||||
                @{$lower_layer->fill_surfaces};
 | 
			
		||||
            next if !@lower_surfaces;
 | 
			
		||||
            
 | 
			
		||||
            # calculate intersection between our surfaces and theirs
 | 
			
		||||
            my $intersection = intersection_ex(
 | 
			
		||||
                [ map $_->p, grep $_->depth_layers <= $d, @lower_surfaces ],
 | 
			
		||||
                [ map $_->p, grep $_->surface_type eq 'internal', @{$layer->surfaces} ],
 | 
			
		||||
                [ map $_->p, grep $_->surface_type eq 'internal', @{$layer->fill_surfaces} ],
 | 
			
		||||
            );
 | 
			
		||||
            next if !@$intersection;
 | 
			
		||||
            my $intersection_offsetted = safety_offset([ map @$_, @$intersection ]);
 | 
			
		||||
| 
						 | 
				
			
			@ -405,7 +398,7 @@ sub infill_every_layers {
 | 
			
		|||
            # - any internal surface not belonging to the intersection (with its original depth)
 | 
			
		||||
            {
 | 
			
		||||
                my @new_surfaces = ();
 | 
			
		||||
                push @new_surfaces, grep $_->surface_type ne 'internal', @{$layer->surfaces};
 | 
			
		||||
                push @new_surfaces, grep $_->surface_type ne 'internal', @{$layer->fill_surfaces};
 | 
			
		||||
                push @new_surfaces, map Slic3r::Surface->cast_from_expolygon
 | 
			
		||||
                    ($_, surface_type => 'internal', depth_layers => $d + 1), @$intersection;
 | 
			
		||||
                
 | 
			
		||||
| 
						 | 
				
			
			@ -418,18 +411,18 @@ sub infill_every_layers {
 | 
			
		|||
                        @{diff_ex(
 | 
			
		||||
                            [
 | 
			
		||||
                                map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth, 
 | 
			
		||||
                                    @{$layer->surfaces},
 | 
			
		||||
                                    @{$layer->fill_surfaces},
 | 
			
		||||
                            ],
 | 
			
		||||
                            $intersection_offsetted,
 | 
			
		||||
                        )};
 | 
			
		||||
                }
 | 
			
		||||
                @{$layer->surfaces} = @new_surfaces;
 | 
			
		||||
                @{$layer->fill_surfaces} = @new_surfaces;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            # now we remove the intersections from lower layer
 | 
			
		||||
            {
 | 
			
		||||
                my @new_surfaces = ();
 | 
			
		||||
                push @new_surfaces, grep $_->surface_type ne 'internal', @{$lower_layer->surfaces};
 | 
			
		||||
                push @new_surfaces, grep $_->surface_type ne 'internal', @{$lower_layer->fill_surfaces};
 | 
			
		||||
                foreach my $depth (1..$Slic3r::infill_every_layers) {
 | 
			
		||||
                    push @new_surfaces, map Slic3r::Surface->cast_from_expolygon
 | 
			
		||||
                        ($_, surface_type => 'internal', depth_layers => $depth),
 | 
			
		||||
| 
						 | 
				
			
			@ -439,12 +432,12 @@ sub infill_every_layers {
 | 
			
		|||
                        @{diff_ex(
 | 
			
		||||
                            [
 | 
			
		||||
                                map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth, 
 | 
			
		||||
                                    @{$lower_layer->surfaces},
 | 
			
		||||
                                    @{$lower_layer->fill_surfaces},
 | 
			
		||||
                            ],
 | 
			
		||||
                            $intersection_offsetted,
 | 
			
		||||
                        )};
 | 
			
		||||
                }
 | 
			
		||||
                @{$lower_layer->surfaces} = @new_surfaces;
 | 
			
		||||
                @{$lower_layer->fill_surfaces} = @new_surfaces;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,10 @@ sub go {
 | 
			
		|||
    $self->status_cb->(30, "Detecting solid surfaces...");
 | 
			
		||||
    $print->detect_surfaces_type;
 | 
			
		||||
    
 | 
			
		||||
    # prepare fill surfaces
 | 
			
		||||
    $self->status_cb->(35, "Preparing infill surfaces...");
 | 
			
		||||
    $_->prepare_fill_surfaces for @{$print->layers};
 | 
			
		||||
    
 | 
			
		||||
    # this will remove unprintable surfaces
 | 
			
		||||
    # (those that are too tight for extrusion)
 | 
			
		||||
    $self->status_cb->(40, "Cleaning up...");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,8 +60,8 @@ sub group {
 | 
			
		|||
        my $type = ($params->{merge_solid} && $surface->surface_type =~ /top|bottom|solid/)
 | 
			
		||||
            ? 'solid'
 | 
			
		||||
            : $surface->surface_type;
 | 
			
		||||
        $type .= "_" . ($_[0]->bridge_angle || '');
 | 
			
		||||
        $type .= "_" . $_[0]->depth_layers;
 | 
			
		||||
        $type .= "_" . ($surface->bridge_angle || '');
 | 
			
		||||
        $type .= "_" . $surface->depth_layers;
 | 
			
		||||
        $unique_types{$type} ||= [];
 | 
			
		||||
        push @{ $unique_types{$type} }, $surface;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ sub make_loops {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    my $sparse_lines = [ map $_->line, @lines ];
 | 
			
		||||
    my $sparse_lines = [ map $_->line, grep $_, @lines ];
 | 
			
		||||
    
 | 
			
		||||
    # detect closed loops
 | 
			
		||||
    if (0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue