mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Refactoring: split support material code into several methods
This commit is contained in:
		
							parent
							
								
									e6efda4ba4
								
							
						
					
					
						commit
						bdf825d078
					
				
					 1 changed files with 149 additions and 97 deletions
				
			
		|  | @ -17,11 +17,52 @@ sub flow { | |||
| sub generate { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $flow = $self->flow; | ||||
|     # Determine the top surfaces of the support, defined as: | ||||
|     # contact = overhangs - margin | ||||
|     # This method is responsible for identifying what contact surfaces | ||||
|     # should the support material expose to the object in order to guarantee | ||||
|     # that it will be effective, regardless of how it's built below. | ||||
|     my ($contact, $overhang) = $self->contact_area; | ||||
|      | ||||
|     # Determine the top surfaces of the object. We need these to determine  | ||||
|     # the layer heights of support material and to clip support to the object | ||||
|     # silhouette. | ||||
|     my ($top) = $self->object_top($contact); | ||||
|      | ||||
|     # We now know the upper and lower boundaries for our support material object | ||||
|     # (@$contact_z and @$top_z), so we can generate intermediate layers. | ||||
|     my ($support_z) = $self->support_layers_z([ sort keys %$contact ], [ sort keys %$top ]); | ||||
|      | ||||
|     # 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 | ||||
|      | ||||
|     # Propagate contact layers downwards to generate interface layers | ||||
|     my ($interface) = $self->generate_interface_layers($support_z, $contact, $top); | ||||
|      | ||||
|     # Propagate contact layers and interface layers downwards to generate | ||||
|     # the main support layers. | ||||
|     my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top); | ||||
|      | ||||
|     # Install support layers into object. | ||||
|     push @{$self->object->support_layers}, map Slic3r::Layer::Support->new( | ||||
|         object  => $self->object, | ||||
|         id      => $_, | ||||
|         height  => ($_ == 0) ? $support_z->[$_] : ($support_z->[$_] - $support_z->[$_-1]), | ||||
|         print_z => $support_z->[$_], | ||||
|         slice_z => -1, | ||||
|         slices  => [], | ||||
|     ), 0 .. $#$support_z; | ||||
|      | ||||
|     # Generate the actual toolpaths and save them into each layer. | ||||
|     $self->generate_toolpaths($overhang, $contact, $interface, $base); | ||||
| } | ||||
| 
 | ||||
| sub contact_area { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     # how much we extend support around the actual contact area | ||||
|     #my $margin      = $flow->scaled_width / 2; | ||||
|     my $margin      = scale 3; | ||||
|     #my $margin = $flow->scaled_width / 2; | ||||
|     my $margin = scale 3; | ||||
|      | ||||
|     # increment used to reach $margin in steps to avoid trespassing thin objects | ||||
|     my $margin_step = $margin/3; | ||||
|  | @ -33,12 +74,6 @@ sub generate { | |||
|         Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); | ||||
|     } | ||||
|      | ||||
|     # shape of contact area | ||||
|     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 ] | ||||
|     my %overhang = ();  # contact_z => [ expolygons ] - this stores the actual overhang supported by each contact layer | ||||
|  | @ -131,7 +166,14 @@ sub generate { | |||
|             } | ||||
|         } | ||||
|     } | ||||
|     my @contact_z = sort keys %contact; | ||||
|      | ||||
|     return (\%contact, \%overhang); | ||||
| } | ||||
| 
 | ||||
| sub object_top { | ||||
|     my ($self, $contact) = @_; | ||||
|      | ||||
|     my $flow = $self->flow; | ||||
|      | ||||
|     # find object top surfaces | ||||
|     # we'll use them to clip our support and detect where does it stick | ||||
|  | @ -144,10 +186,10 @@ sub generate { | |||
|                 # first add all the 'new' contact areas to the current projection | ||||
|                 # ('new' means all the areas that are lower than the last top layer | ||||
|                 # we considered) | ||||
|                 my $min_top = min(keys %top) // max(keys %contact); | ||||
|                 my $min_top = min(keys %top) // max(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; | ||||
|                 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 ]); | ||||
|  | @ -164,25 +206,67 @@ sub generate { | |||
|             } | ||||
|         } | ||||
|     } | ||||
|     my @top_z = sort keys %top; | ||||
|      | ||||
|     # we now know the upper and lower boundaries for our support material object | ||||
|     # (@contact_z and @top_z), so we can generate intermediate layers | ||||
|     my @support_layers = $self->_compute_support_layers(\@contact_z, \@top_z); | ||||
|     return \%top; | ||||
| } | ||||
| 
 | ||||
| sub support_layers_z { | ||||
|     my ($self, $contact_z, $top_z) = @_; | ||||
|      | ||||
|     # 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 | ||||
|     my $flow = $self->flow; | ||||
|      | ||||
|     # quick table to check whether a given Z is a top surface | ||||
|     my %top = map { $_ => 1 } @$top_z; | ||||
|      | ||||
|     # determine layer height for any non-contact layer | ||||
|     # we use max() to prevent many ultra-thin layers to be inserted in case | ||||
|     # layer_height > nozzle_diameter * 0.75 | ||||
|     my $support_material_height = max($self->object->config->layer_height, $flow->nozzle_diameter * 0.75); | ||||
|      | ||||
|     my @z = sort { $a <=> $b } @$contact_z, @$top_z, | ||||
|         (map { $_ + $flow->nozzle_diameter } @$top_z); | ||||
|      | ||||
|     # enforce first layer height | ||||
|     my $first_layer_height = $self->object->config->get_value('first_layer_height'); | ||||
|     shift @z while @z && $z[0] <= $first_layer_height; | ||||
|     unshift @z, $first_layer_height; | ||||
|      | ||||
|     for (my $i = $#z; $i >= 0; $i--) { | ||||
|         my $target_height = $support_material_height; | ||||
|         if ($i > 0 && $top{ $z[$i-1] }) { | ||||
|             $target_height = $flow->nozzle_diameter; | ||||
|         } | ||||
|          | ||||
|         # enforce first layer height | ||||
|         if (($i == 0 && $z[$i] > $target_height + $first_layer_height) | ||||
|             || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { | ||||
|             splice @z, $i, 0, ($z[$i] - $target_height); | ||||
|             $i++; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # remove duplicates and make sure all 0.x values have the leading 0 | ||||
|     { | ||||
|         my %sl = map { 1 * $_ => 1 } @z; | ||||
|         @z = sort { $a <=> $b } keys %sl; | ||||
|     } | ||||
|      | ||||
|     return \@z; | ||||
| } | ||||
| 
 | ||||
| sub generate_interface_layers { | ||||
|     my ($self, $support_z, $contact, $top) = @_; | ||||
|      | ||||
|     # let's now generate interface layers below contact areas | ||||
|     my %interface = ();  # layer_id => [ polygons ] | ||||
|     my $interface_layers = $self->object->config->support_material_interface_layers; | ||||
|     for my $layer_id (0 .. $#support_layers) { | ||||
|         my $z = $support_layers[$layer_id]; | ||||
|         my $this = $contact{$z} // next; | ||||
|     for my $layer_id (0 .. $#$support_z) { | ||||
|         my $z = $support_z->[$layer_id]; | ||||
|         my $this = $contact->{$z} // next; | ||||
|          | ||||
|         # count contact layer as interface layer | ||||
|         for (my $i = $layer_id-1; $i >= 0 && $i > $layer_id-$interface_layers; $i--) { | ||||
|             $z = $support_layers[$i]; | ||||
|             $z = $support_z->[$i]; | ||||
|             # Compute interface area on this layer as diff of upper contact area | ||||
|             # (or upper interface area) and layer slices. | ||||
|             # This diff is responsible of the contact between support material and | ||||
|  | @ -194,43 +278,54 @@ sub generate { | |||
|                     @{ $interface{$i} || [] },      # interface regions already applied to this layer | ||||
|                 ], | ||||
|                 [ | ||||
|                     @{ $top{$z} || [] },            # top slices on this layer | ||||
|                     @{ $contact{$z} || [] },        # contact regions on this layer | ||||
|                     @{ $top->{$z} || [] },          # top slices on this layer | ||||
|                     @{ $contact->{$z} || [] },      # contact regions on this layer | ||||
|                 ], | ||||
|                 1, | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return \%interface; | ||||
| } | ||||
| 
 | ||||
| sub generate_base_layers { | ||||
|     my ($self, $support_z, $contact, $interface, $top) = @_; | ||||
|      | ||||
|     # let's now generate support layers under interface layers | ||||
|     my %support   = ();  # layer_id => [ polygons ] | ||||
|     my $base = {};  # layer_id => [ polygons ] | ||||
|     { | ||||
|         for my $i (reverse 0 .. $#support_layers-1) { | ||||
|             my $z = $support_layers[$i]; | ||||
|             $support{$i} = diff( | ||||
|         for my $i (reverse 0 .. $#$support_z-1) { | ||||
|             my $z = $support_z->[$i]; | ||||
|             $base->{$i} = diff( | ||||
|                 [ | ||||
|                     @{ $support{$i+1} || [] },      # support regions on upper layer | ||||
|                     @{ $interface{$i+1} || [] },    # interface regions on upper layer | ||||
|                     @{ $base->{$i+1} || [] },         # support regions on upper layer | ||||
|                     @{ $interface->{$i+1} || [] },    # interface regions on upper layer | ||||
|                 ], | ||||
|                 [ | ||||
|                     @{ $top{$z} || [] },            # top slices on this layer | ||||
|                     @{ $interface{$i} || [] },      # interface regions on this layer | ||||
|                     @{ $contact{$z} || [] },        # contact regions on this layer | ||||
|                     @{ $top->{$z} || [] },            # top slices on this layer | ||||
|                     @{ $interface->{$i} || [] },      # interface regions on this layer | ||||
|                     @{ $contact->{$z} || [] },        # contact regions on this layer | ||||
|                 ], | ||||
|                 1, | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     push @{$self->object->support_layers}, map Slic3r::Layer::Support->new( | ||||
|         object  => $self->object, | ||||
|         id      => $_, | ||||
|         height  => ($_ == 0) ? $support_layers[$_] : ($support_layers[$_] - $support_layers[$_-1]), | ||||
|         print_z => $support_layers[$_], | ||||
|         slice_z => -1, | ||||
|         slices  => [], | ||||
|     ), 0 .. $#support_layers; | ||||
|     return $base; | ||||
| } | ||||
| 
 | ||||
| sub generate_toolpaths { | ||||
|     my ($self, $overhang, $contact, $interface, $base) = @_; | ||||
|      | ||||
|     my $flow = $self->flow; | ||||
|      | ||||
|     # shape of contact area | ||||
|     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)); | ||||
|      | ||||
|     Slic3r::debugf "Generating patterns\n"; | ||||
|      | ||||
|     # prepare fillers | ||||
|  | @ -255,22 +350,23 @@ sub generate { | |||
|     my $process_layer = sub { | ||||
|         my ($layer_id) = @_; | ||||
|         my $layer = $self->object->support_layers->[$layer_id]; | ||||
|         my $z = $layer->print_z; | ||||
|          | ||||
|         my $overhang    = $overhang{$support_layers[$layer_id]} || []; | ||||
|         my $contact     = $contact{$support_layers[$layer_id]}  || []; | ||||
|         my $interface   = $interface{$layer_id} || []; | ||||
|         my $support     = $support{$layer_id}   || []; | ||||
|         my $overhang    = $overhang->{$z}           || []; | ||||
|         my $contact     = $contact->{$z}            || []; | ||||
|         my $interface   = $interface->{$layer_id}   || []; | ||||
|         my $base        = $base->{$layer_id}        || []; | ||||
|          | ||||
|         if (0) { | ||||
|             require "Slic3r/SVG.pm"; | ||||
|             Slic3r::SVG::output("layer_" . $support_layers[$layer_id] . ".svg", | ||||
|             Slic3r::SVG::output("layer_" . $z . ".svg", | ||||
|                 red_expolygons      => union_ex($contact), | ||||
|                 green_expolygons    => union_ex($interface), | ||||
|             ); | ||||
|         } | ||||
|          | ||||
|         # islands | ||||
|         $layer->support_islands->append(@{union_ex([ @$interface, @$support, @$contact ])}); | ||||
|         $layer->support_islands->append(@{union_ex([ @$interface, @$base, @$contact ])}); | ||||
|          | ||||
|         # contact | ||||
|         my $contact_infill = []; | ||||
|  | @ -325,11 +421,11 @@ sub generate { | |||
|             # steal some space from support | ||||
|             $interface = intersection( | ||||
|                 offset([ @$interface, @$contact_infill ], scale 3), | ||||
|                 [ @$interface, @$support, @$contact_infill ], | ||||
|                 [ @$interface, @$base, @$contact_infill ], | ||||
|                 1, | ||||
|             ); | ||||
|             $support = diff( | ||||
|                 $support, | ||||
|             $base = diff( | ||||
|                 $base, | ||||
|                 $interface, | ||||
|             ); | ||||
|              | ||||
|  | @ -354,14 +450,14 @@ sub generate { | |||
|         } | ||||
|          | ||||
|         # support or flange | ||||
|         if (@$support) { | ||||
|         if (@$base) { | ||||
|             my $filler = $fillers{support}; | ||||
|             $filler->angle($angles[ ($layer_id) % @angles ]); | ||||
|             my $density         = $support_density; | ||||
|             my $flow_spacing    = $flow->spacing; | ||||
|              | ||||
|             # TODO: use offset2_ex() | ||||
|             my $to_infill = union_ex($support, 1); | ||||
|             my $to_infill = union_ex($base, 1); | ||||
|             my @paths = (); | ||||
|              | ||||
|             # base flange | ||||
|  | @ -406,7 +502,7 @@ sub generate { | |||
|          | ||||
|         if (0) { | ||||
|             require "Slic3r/SVG.pm"; | ||||
|             Slic3r::SVG::output("islands_" . $support_layers[$layer_id] . ".svg", | ||||
|             Slic3r::SVG::output("islands_" . $z . ".svg", | ||||
|                 red_expolygons      => union_ex($contact), | ||||
|                 green_expolygons    => union_ex($interface), | ||||
|                 green_polylines     => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ], | ||||
|  | @ -429,48 +525,4 @@ sub generate { | |||
|     ); | ||||
| } | ||||
| 
 | ||||
| sub _compute_support_layers { | ||||
|     my ($self, $contact_z, $top_z) = @_; | ||||
|      | ||||
|     my $flow = $self->flow; | ||||
|      | ||||
|     # quick table to check whether a given Z is a top surface | ||||
|     my %top = map { $_ => 1 } @$top_z; | ||||
|      | ||||
|     # determine layer height for any non-contact layer | ||||
|     # we use max() to prevent many ultra-thin layers to be inserted in case | ||||
|     # layer_height > nozzle_diameter * 0.75 | ||||
|     my $support_material_height = max($self->object->config->layer_height, $flow->nozzle_diameter * 0.75); | ||||
|      | ||||
|     my @support_layers = sort { $a <=> $b } @$contact_z, @$top_z, | ||||
|         (map { $_ + $flow->nozzle_diameter } @$top_z); | ||||
|      | ||||
|     # enforce first layer height | ||||
|     my $first_layer_height = $self->object->config->get_value('first_layer_height'); | ||||
|     shift @support_layers while @support_layers && $support_layers[0] <= $first_layer_height; | ||||
|     unshift @support_layers, $first_layer_height; | ||||
|      | ||||
|     for (my $i = $#support_layers; $i >= 0; $i--) { | ||||
|         my $target_height = $support_material_height; | ||||
|         if ($i > 0 && $top{ $support_layers[$i-1] }) { | ||||
|             $target_height = $flow->nozzle_diameter; | ||||
|         } | ||||
|          | ||||
|         # enforce first layer height | ||||
|         if (($i == 0 && $support_layers[$i] > $target_height + $first_layer_height) | ||||
|             || ($support_layers[$i] - $support_layers[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { | ||||
|             splice @support_layers, $i, 0, ($support_layers[$i] - $target_height); | ||||
|             $i++; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # remove duplicates and make sure all 0.x values have the leading 0 | ||||
|     { | ||||
|         my %sl = map { 1 * $_ => 1 } @support_layers; | ||||
|         @support_layers = sort { $a <=> $b } keys %sl; | ||||
|     } | ||||
|      | ||||
|     return @support_layers; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci